# Режим розробки Python

Нове в версії 3.7.

Режим розробки Python вводить додаткові перевірки під час виконання, які занадто дорогі, щоб їх можна було ввімкнути за замовчуванням. Якщо код правильний, він не повинен бути більш детальним, ніж стандартний; нові попередження видаються лише тоді, коли виявляється проблема.

Його можна ввімкнути за допомогою параметра командного рядка `-X dev` або встановивши для змінної середовища `PYTHONDEVMODE` значення `1`.

Дивіться також Python debug build.

# Ефекти режиму розробки Python

Увімкнення режиму розробки Python подібне до наступної команди, але з додатковими ефектами, описаними нижче:

    PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler

Наслідки режиму розробки Python:

- Додайте `за замовчуванням` фільтр попереджень. Відображаються такі попередження:

  - `DeprecationWarning`

  - `ImportWarning`

  - `PendingDeprecationWarning`

  - `ResourceWarning`

  Зазвичай наведені вище попередження фільтруються стандартними фільтрами попереджень.

  Він поводиться так, ніби використовується параметр командного рядка `-W за замовчуванням`.

  Використовуйте параметр командного рядка `-W error` або встановіть для змінної середовища `PYTHONWARNINGS` значення `error`, щоб розглядати попередження як помилки.

- Встановіть перехоплювачі налагодження на розподілювачі пам’яті, щоб перевірити:

  - Недоповнення буфера

  - Переповнення буфера

  - Порушення API розподілювача пам’яті

  - Небезпечне використання GIL

  Перегляньте функцію C `PyMem_SetupDebugHooks()`.

  Він поводиться так, ніби для змінної середовища `PYTHONMALLOC` встановлено значення `debug`.

  Щоб увімкнути режим розробки Python, не встановлюючи налагоджувальні засоби розподілення пам’яті, встановіть для змінної середовища `PYTHONMALLOC` значення `default`.

- Call `faulthandler.enable()` at Python startup to install handlers for the `SIGSEGV`, `SIGFPE`, `SIGABRT`, `SIGBUS` and `SIGILL` signals to dump the Python traceback on a crash.

  Він поводиться так, ніби використовується параметр командного рядка `-X faulthandler` або якщо змінна середовища `PYTHONFAULTHANDLER` має значення `1`.

- Увімкнути асинхронний режим налагодження. Наприклад, `asyncio` перевіряє співпрограми, які не були очікувані, і реєструє їх.

  Він поводиться так, ніби для змінної середовища `PYTHONASYNCIODEBUG` встановлено значення `1`.

- Перевірте аргументи encoding і errors для операцій кодування та декодування рядків. Приклади: `open()`, `str.encode()` і `bytes.decode()`.

  За замовчуванням для найкращої продуктивності аргумент errors перевіряється лише при першій помилці кодування/декодування, а аргумент encoding іноді ігнорується для порожніх рядків.

- Деструктор `io.IOBase` реєструє винятки `close()`.

- Set the `dev_mode` attribute of `sys.flags` to `True`.

Режим розробки Python не вмикає модуль `tracemalloc` за замовчуванням, оскільки накладні витрати (для продуктивності та пам’яті) будуть занадто великими. Увімкнення модуля `tracemalloc` надає додаткову інформацію про походження деяких помилок. Наприклад, `ResourceWarning` реєструє відстеження, де було виділено ресурс, а помилка переповнення буфера реєструє відстеження, де було виділено блок пам’яті.

Режим розробки Python не заважає параметру командного рядка `-O` видаляти оператори `assert` або встановлювати `__debug__` значення `False`.

Режим розробки Python можна ввімкнути лише під час запуску Python. Його значення можна прочитати з `sys.flags.dev_mode`.

Змінено в версії 3.8: Деструктор `io.IOBase` тепер реєструє винятки `close()`.

Змінено в версії 3.9: Аргументи encoding і errors тепер перевіряються на наявність операцій кодування та декодування рядків.

# Приклад ResourceWarning

Приклад сценарію підрахунку кількості рядків текстового файлу, вказаного в командному рядку:

    import sys

    def main():
        fp = open(sys.argv[1])
        nlines = len(fp.readlines())
        print(nlines)
        # The file is closed implicitly

    if __name__ == "__main__":
        main()

Сценарій не закриває файл явно. За замовчуванням Python не видає жодних попереджень. Приклад використання README.txt, який містить 269 рядків:

    $ python3 script.py README.txt
    269

Увімкнення режиму розробки Python відображає попередження `ResourceWarning`:

    $ python3 -X dev script.py README.txt
    269
    script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
      main()
    ResourceWarning: Enable tracemalloc to get the object allocation traceback

Крім того, увімкнення `tracemalloc` показує рядок, де було відкрито файл:

    $ python3 -X dev -X tracemalloc=5 script.py README.rst
    269
    script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'>
      main()
    Object allocated at (most recent call last):
      File "script.py", lineno 10
        main()
      File "script.py", lineno 4
        fp = open(sys.argv[1])

Виправлення полягає в тому, щоб явно закрити файл. Приклад використання контекстного менеджера:

    def main():
        # Close the file explicitly when exiting the with block
        with open(sys.argv[1]) as fp:
            nlines = len(fp.readlines())
        print(nlines)

Якщо ресурс не закрити явно, він може залишитися відкритим набагато довше, ніж очікувалося; це може спричинити серйозні проблеми після виходу з Python. Це погано в CPython, але ще гірше в PyPy. Закриття ресурсів явно робить додаток більш детермінованим і надійнішим.

# Приклад помилки неправильного дескриптора файлу

Сценарій, що відображає перший рядок самого себе:

    import os

    def main():
        fp = open(__file__)
        firstline = fp.readline()
        print(firstline.rstrip())
        os.close(fp.fileno())
        # The file is closed implicitly

    main()

За замовчуванням Python не видає жодного попередження:

    $ python3 script.py
    import os

Режим розробки Python показує `ResourceWarning` і реєструє помилку «Поганий дескриптор файлу» під час завершення об’єкта файлу:

    $ python3 script.py
    import os
    script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
      main()
    ResourceWarning: Enable tracemalloc to get the object allocation traceback
    Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'>
    Traceback (most recent call last):
      File "script.py", line 10, in <module>
        main()
    OSError: [Errno 9] Bad file descriptor

`os.close(fp.fileno())` закриває дескриптор файлу. Коли фіналізатор файлового об’єкта знову намагається закрити файловий дескриптор, це не вдається з помилкою `Поганий файловий дескриптор`. Файловий дескриптор має бути закрито лише один раз. У гіршому випадку подвійне закриття може призвести до збою (див. bpo-18748 для прикладу).

Виправлення полягає у видаленні рядка `os.close(fp.fileno())` або відкритті файлу `closefd=False`.