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