Victor Stinner | b9783d2 | 2020-01-24 10:22:18 +0100 | [diff] [blame] | 1 | .. _devmode: |
| 2 | |
| 3 | Python Development Mode |
| 4 | ======================= |
| 5 | |
| 6 | .. versionadded:: 3.7 |
| 7 | |
| 8 | The Python Development Mode introduces additional runtime checks that are too |
| 9 | expensive to be enabled by default. It should not be more verbose than the |
| 10 | default if the code is correct; new warnings are only emitted when an issue is |
| 11 | detected. |
| 12 | |
| 13 | It can be enabled using the :option:`-X dev <-X>` command line option or by |
| 14 | setting the :envvar:`PYTHONDEVMODE` environment variable to ``1``. |
| 15 | |
Victor Stinner | a41782c | 2021-04-08 22:32:21 +0200 | [diff] [blame] | 16 | See also :ref:`Python debug build <debug-build>`. |
| 17 | |
Victor Stinner | b9783d2 | 2020-01-24 10:22:18 +0100 | [diff] [blame] | 18 | Effects of the Python Development Mode |
| 19 | ====================================== |
| 20 | |
| 21 | Enabling the Python Development Mode is similar to the following command, but |
| 22 | with additional effects described below:: |
| 23 | |
| 24 | PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler |
| 25 | |
| 26 | Effects of the Python Development Mode: |
| 27 | |
| 28 | * Add ``default`` :ref:`warning filter <describing-warning-filters>`. The |
| 29 | following warnings are shown: |
| 30 | |
| 31 | * :exc:`DeprecationWarning` |
| 32 | * :exc:`ImportWarning` |
| 33 | * :exc:`PendingDeprecationWarning` |
| 34 | * :exc:`ResourceWarning` |
| 35 | |
| 36 | Normally, the above warnings are filtered by the default :ref:`warning |
| 37 | filters <describing-warning-filters>`. |
| 38 | |
| 39 | It behaves as if the :option:`-W default <-W>` command line option is used. |
| 40 | |
| 41 | Use the :option:`-W error <-W>` command line option or set the |
| 42 | :envvar:`PYTHONWARNINGS` environment variable to ``error`` to treat warnings |
| 43 | as errors. |
| 44 | |
| 45 | * Install debug hooks on memory allocators to check for: |
| 46 | |
| 47 | * Buffer underflow |
| 48 | * Buffer overflow |
| 49 | * Memory allocator API violation |
| 50 | * Unsafe usage of the GIL |
| 51 | |
| 52 | See the :c:func:`PyMem_SetupDebugHooks` C function. |
| 53 | |
| 54 | It behaves as if the :envvar:`PYTHONMALLOC` environment variable is set to |
| 55 | ``debug``. |
| 56 | |
| 57 | To enable the Python Development Mode without installing debug hooks on |
| 58 | memory allocators, set the :envvar:`PYTHONMALLOC` environment variable to |
| 59 | ``default``. |
| 60 | |
| 61 | * Call :func:`faulthandler.enable` at Python startup to install handlers for |
| 62 | the :const:`SIGSEGV`, :const:`SIGFPE`, :const:`SIGABRT`, :const:`SIGBUS` and |
| 63 | :const:`SIGILL` signals to dump the Python traceback on a crash. |
| 64 | |
| 65 | It behaves as if the :option:`-X faulthandler <-X>` command line option is |
| 66 | used or if the :envvar:`PYTHONFAULTHANDLER` environment variable is set to |
| 67 | ``1``. |
| 68 | |
| 69 | * Enable :ref:`asyncio debug mode <asyncio-debug-mode>`. For example, |
| 70 | :mod:`asyncio` checks for coroutines that were not awaited and logs them. |
| 71 | |
| 72 | It behaves as if the :envvar:`PYTHONASYNCIODEBUG` environment variable is set |
| 73 | to ``1``. |
| 74 | |
| 75 | * Check the *encoding* and *errors* arguments for string encoding and decoding |
| 76 | operations. Examples: :func:`open`, :meth:`str.encode` and |
| 77 | :meth:`bytes.decode`. |
| 78 | |
| 79 | By default, for best performance, the *errors* argument is only checked at |
| 80 | the first encoding/decoding error and the *encoding* argument is sometimes |
| 81 | ignored for empty strings. |
| 82 | |
| 83 | * The :class:`io.IOBase` destructor logs ``close()`` exceptions. |
| 84 | * Set the :attr:`~sys.flags.dev_mode` attribute of :attr:`sys.flags` to |
| 85 | ``True``. |
| 86 | |
| 87 | The Python Development Mode does not enable the :mod:`tracemalloc` module by |
| 88 | default, because the overhead cost (to performance and memory) would be too |
| 89 | large. Enabling the :mod:`tracemalloc` module provides additional information |
| 90 | on the origin of some errors. For example, :exc:`ResourceWarning` logs the |
| 91 | traceback where the resource was allocated, and a buffer overflow error logs |
| 92 | the traceback where the memory block was allocated. |
| 93 | |
| 94 | The Python Development Mode does not prevent the :option:`-O` command line |
| 95 | option from removing :keyword:`assert` statements nor from setting |
| 96 | :const:`__debug__` to ``False``. |
| 97 | |
Victor Stinner | 4b9aad4 | 2020-11-02 16:49:54 +0100 | [diff] [blame] | 98 | The Python Development Mode can only be enabled at the Python startup. Its |
| 99 | value can be read from :data:`sys.flags.dev_mode <sys.flags>`. |
| 100 | |
Victor Stinner | b9783d2 | 2020-01-24 10:22:18 +0100 | [diff] [blame] | 101 | .. versionchanged:: 3.8 |
| 102 | The :class:`io.IOBase` destructor now logs ``close()`` exceptions. |
| 103 | |
| 104 | .. versionchanged:: 3.9 |
| 105 | The *encoding* and *errors* arguments are now checked for string encoding |
| 106 | and decoding operations. |
| 107 | |
| 108 | |
| 109 | ResourceWarning Example |
| 110 | ======================= |
| 111 | |
| 112 | Example of a script counting the number of lines of the text file specified in |
| 113 | the command line:: |
| 114 | |
| 115 | import sys |
| 116 | |
| 117 | def main(): |
| 118 | fp = open(sys.argv[1]) |
| 119 | nlines = len(fp.readlines()) |
| 120 | print(nlines) |
| 121 | # The file is closed implicitly |
| 122 | |
| 123 | if __name__ == "__main__": |
| 124 | main() |
| 125 | |
| 126 | The script does not close the file explicitly. By default, Python does not emit |
| 127 | any warning. Example using README.txt, which has 269 lines: |
| 128 | |
| 129 | .. code-block:: shell-session |
| 130 | |
| 131 | $ python3 script.py README.txt |
| 132 | 269 |
| 133 | |
| 134 | Enabling the Python Development Mode displays a :exc:`ResourceWarning` warning: |
| 135 | |
| 136 | .. code-block:: shell-session |
| 137 | |
| 138 | $ python3 -X dev script.py README.txt |
| 139 | 269 |
| 140 | script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'> |
| 141 | main() |
| 142 | ResourceWarning: Enable tracemalloc to get the object allocation traceback |
| 143 | |
| 144 | In addition, enabling :mod:`tracemalloc` shows the line where the file was |
| 145 | opened: |
| 146 | |
| 147 | .. code-block:: shell-session |
| 148 | |
| 149 | $ python3 -X dev -X tracemalloc=5 script.py README.rst |
| 150 | 269 |
| 151 | script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='README.rst' mode='r' encoding='UTF-8'> |
| 152 | main() |
| 153 | Object allocated at (most recent call last): |
| 154 | File "script.py", lineno 10 |
| 155 | main() |
| 156 | File "script.py", lineno 4 |
| 157 | fp = open(sys.argv[1]) |
| 158 | |
| 159 | The fix is to close explicitly the file. Example using a context manager:: |
| 160 | |
| 161 | def main(): |
| 162 | # Close the file explicitly when exiting the with block |
| 163 | with open(sys.argv[1]) as fp: |
| 164 | nlines = len(fp.readlines()) |
| 165 | print(nlines) |
| 166 | |
| 167 | Not closing a resource explicitly can leave a resource open for way longer than |
| 168 | expected; it can cause severe issues upon exiting Python. It is bad in |
| 169 | CPython, but it is even worse in PyPy. Closing resources explicitly makes an |
| 170 | application more deterministic and more reliable. |
| 171 | |
| 172 | |
| 173 | Bad file descriptor error example |
| 174 | ================================= |
| 175 | |
| 176 | Script displaying the first line of itself:: |
| 177 | |
| 178 | import os |
| 179 | |
| 180 | def main(): |
| 181 | fp = open(__file__) |
| 182 | firstline = fp.readline() |
| 183 | print(firstline.rstrip()) |
| 184 | os.close(fp.fileno()) |
| 185 | # The file is closed implicitly |
| 186 | |
| 187 | main() |
| 188 | |
| 189 | By default, Python does not emit any warning: |
| 190 | |
| 191 | .. code-block:: shell-session |
| 192 | |
| 193 | $ python3 script.py |
| 194 | import os |
| 195 | |
| 196 | The Python Development Mode shows a :exc:`ResourceWarning` and logs a "Bad file |
| 197 | descriptor" error when finalizing the file object: |
| 198 | |
| 199 | .. code-block:: shell-session |
| 200 | |
| 201 | $ python3 script.py |
| 202 | import os |
| 203 | script.py:10: ResourceWarning: unclosed file <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'> |
| 204 | main() |
| 205 | ResourceWarning: Enable tracemalloc to get the object allocation traceback |
| 206 | Exception ignored in: <_io.TextIOWrapper name='script.py' mode='r' encoding='UTF-8'> |
| 207 | Traceback (most recent call last): |
| 208 | File "script.py", line 10, in <module> |
| 209 | main() |
| 210 | OSError: [Errno 9] Bad file descriptor |
| 211 | |
| 212 | ``os.close(fp.fileno())`` closes the file descriptor. When the file object |
| 213 | finalizer tries to close the file descriptor again, it fails with the ``Bad |
| 214 | file descriptor`` error. A file descriptor must be closed only once. In the |
| 215 | worst case scenario, closing it twice can lead to a crash (see :issue:`18748` |
| 216 | for an example). |
| 217 | |
| 218 | The fix is to remove the ``os.close(fp.fileno())`` line, or open the file with |
| 219 | ``closefd=False``. |