<jupyter_start><jupyter_text>Python для анализа данных Постановка скриптов на расписание*Автор: <NAME>, НИУ ВШЭ* Думаю, многим из вас хотелось бы автоматизировать какие-то рутинные процессы в быту или на работе, например, сборку одинакового вида отчетов, подгрузку актуальных данных о курсе валют в вашу базу данных или сводку курсов акций на сегодня. Для этого вам необходимо написать код, который решает задачу, а потом заставить его выполняться с некоторой периодичностью. В Python для этого существует механизм "постановки скриптов на расписание" – в прямом смысле слова, по часам. В Unix-подобных системах такую штуку решают с помощью [CRON](https://ru.wikipedia.org/wiki/Cron), а в Python, как всегда, придумали человеческий интерфейс :) Представляю вм библиотеку schedule. Поехали!<jupyter_code>!pip install schedule
import schedule 
import time

def greet(name):
    print('Hello', name)

schedule.every(2).seconds.do(greet, name='Alice').tag('my_shiny_job')<jupyter_output><empty_output><jupyter_text>Чтобы завести какой-то процесс, надо запустить его в цикле такого вида:<jupyter_code>while True:
    schedule.run_pending()
    time.sleep(1)<jupyter_output>Hello Alice
Hello Alice<jupyter_text>Как посмотреть список job'ов?<jupyter_code>schedule.jobs<jupyter_output><empty_output><jupyter_text>Как прекратить job?<jupyter_code>schedule.clear('my_shiny_job')
schedule.jobs<jupyter_output><empty_output><jupyter_text>Варианты постановок на расписание могут быть всякие:<jupyter_code>def job():
    print("Работаю")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)

# Отдельно стоит упомянуть вот это – это запуск со случайным интервалом от 5 до 10 минут.
schedule.every(5).to(10).minutes.do(job)


schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)<jupyter_output><empty_output><jupyter_text>**ВАЖНО:** Библиотека сама не умеет обрабатывать исключения в задачах, поэтому, возможно, понадобится  декоратор, который будет отменять работу, если произошло исключение.<jupyter_code>import functools

# декоратор для отлова исключений
def catch_exceptions(cancel_on_failure=False):
    
    def catch_exceptions_decorator(job_func):
        @functools.wraps(job_func)
        def wrapper(*args, **kwargs):
            try:
                return job_func(*args, **kwargs)
            except:
                import traceback
                print(traceback.format_exc())
                if cancel_on_failure:
                    return schedule.CancelJob
        return wrapper
    
    return catch_exceptions_decorator


@catch_exceptions(cancel_on_failure=True)
def bad_task():
    # даст исключение, но декоратор отменяет эту задачу
    return 1 / 0  

schedule.every(5).seconds.do(bad_task)<jupyter_output><empty_output><jupyter_text>Как почистить список job'ов?<jupyter_code>schedule.clear()
while True:
    schedule.run_pending()
    time.sleep(1)<jupyter_output>Traceback (most recent call last):
  File "<ipython-input-27-062bcafb0c44>", line 10, in wrapper
    return job_func(*args, **kwargs)
  File "<ipython-input-27-062bcafb0c44>", line 24, in bad_task
    return 1 / 0
ZeroDivisionError: division by zero<jupyter_text>Если нужно отменить группу заданий, то к ним добавляют тэги:<jupyter_code>schedule.every().day.do(greet, 'Monica').tag('daily-tasks')
schedule.every().day.do(greet, 'Derek').tag('daily-tasks')
schedule.clear('daily-tasks')  # массовая отмена по тэгу<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10477266308952274, 'fraction_numerical': 0.012990680598700932, 'mean_word_length': 5.181500872600349, 'pattern_counts': {'":': 1, '<': 34, '<?xml version=': 0, '>': 34, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '118863', 'license': 'MIT', 'n_tokens_mistral': 1383, 'n_tokens_neox': 1446, 'n_words': 280, 'path': 'lect07_Classes/2021_DPO_7_2_Scheduler.ipynb', 'repo_name': 'weqrwer/Python_DPO_2021_fall', 'size': '3542'}