Issue #23731: Implement PEP 488.

The concept of .pyo files no longer exists. Now .pyc files have an
optional `opt-` tag which specifies if any extra optimizations beyond
the peepholer were applied.
This commit is contained in:
Brett Cannon 2015-04-13 14:21:02 -04:00
parent a63cc21234
commit f299abdafa
56 changed files with 4731 additions and 4621 deletions

View File

@ -183,9 +183,9 @@ Importing Modules
.. c:function:: long PyImport_GetMagicNumber() .. c:function:: long PyImport_GetMagicNumber()
Return the magic number for Python bytecode files (a.k.a. :file:`.pyc` and Return the magic number for Python bytecode files (a.k.a. :file:`.pyc` file).
:file:`.pyo` files). The magic number should be present in the first four bytes The magic number should be present in the first four bytes of the bytecode
of the bytecode file, in little-endian byte order. Returns -1 on error. file, in little-endian byte order. Returns -1 on error.
.. versionchanged:: 3.3 .. versionchanged:: 3.3
Return value of -1 upon failure. Return value of -1 upon failure.

View File

@ -1193,12 +1193,12 @@ other utility module.
.. function:: byte_compile(py_files[, optimize=0, force=0, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None]) .. function:: byte_compile(py_files[, optimize=0, force=0, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None])
Byte-compile a collection of Python source files to either :file:`.pyc` or Byte-compile a collection of Python source files to :file:`.pyc` files in a
:file:`.pyo` files in a :file:`__pycache__` subdirectory (see :pep:`3147`). :file:`__pycache__` subdirectory (see :pep:`3147` and :pep:`488`).
*py_files* is a list of files to compile; any files that don't end in *py_files* is a list of files to compile; any files that don't end in
:file:`.py` are silently skipped. *optimize* must be one of the following: :file:`.py` are silently skipped. *optimize* must be one of the following:
* ``0`` - don't optimize (generate :file:`.pyc`) * ``0`` - don't optimize
* ``1`` - normal optimization (like ``python -O``) * ``1`` - normal optimization (like ``python -O``)
* ``2`` - extra optimization (like ``python -OO``) * ``2`` - extra optimization (like ``python -OO``)
@ -1222,10 +1222,13 @@ other utility module.
doing, leave it set to ``None``. doing, leave it set to ``None``.
.. versionchanged:: 3.2.3 .. versionchanged:: 3.2.3
Create ``.pyc`` or ``.pyo`` files with an :func:`import magic tag Create ``.pyc`` files with an :func:`import magic tag
<imp.get_tag>` in their name, in a :file:`__pycache__` subdirectory <imp.get_tag>` in their name, in a :file:`__pycache__` subdirectory
instead of files without tag in the current directory. instead of files without tag in the current directory.
.. versionchanged: 3.5
Create ``.pyc`` files according to :pep:`488`.
.. function:: rfc822_escape(header) .. function:: rfc822_escape(header)

View File

@ -156,8 +156,8 @@ module
pure Python module pure Python module
a module written in Python and contained in a single :file:`.py` file (and a module written in Python and contained in a single :file:`.py` file (and
possibly associated :file:`.pyc` and/or :file:`.pyo` files). Sometimes referred possibly associated :file:`.pyc` files). Sometimes referred to as a
to as a "pure module." "pure module."
extension module extension module
a module written in the low-level language of the Python implementation: C/C++ a module written in the low-level language of the Python implementation: C/C++
@ -210,5 +210,3 @@ distribution root
the top-level directory of your source tree (or source distribution); the the top-level directory of your source tree (or source distribution); the
directory where :file:`setup.py` exists. Generally :file:`setup.py` will be directory where :file:`setup.py` exists. Generally :file:`setup.py` will be
run from this directory. run from this directory.

View File

@ -93,6 +93,10 @@ compile Python sources.
.. versionchanged:: 3.5 .. versionchanged:: 3.5
``-q`` option was changed to a multilevel value. ``-q`` option was changed to a multilevel value.
.. versionchanged:: 3.5
``-b`` will always produce a byte-code file ending in ``.pyc``, never
``.pyo``.
There is no command-line option to control the optimization level used by the There is no command-line option to control the optimization level used by the
:func:`compile` function, because the Python interpreter itself already :func:`compile` function, because the Python interpreter itself already
@ -150,6 +154,10 @@ Public functions
.. versionchanged:: 3.5 .. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value. *quiet* parameter was changed to a multilevel value.
.. versionchanged:: 3.5
The *legacy* parameter only writes out ``.pyc`` files, not ``.pyo`` files
no matter what the value of *optimize* is.
.. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1) .. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1)
Compile the file with path *fullname*. Compile the file with path *fullname*.
@ -182,6 +190,10 @@ Public functions
.. versionchanged:: 3.5 .. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value. *quiet* parameter was changed to a multilevel value.
.. versionchanged:: 3.5
The *legacy* parameter only writes out ``.pyc`` files, not ``.pyo`` files
no matter what the value of *optimize* is.
.. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, quiet=0, legacy=False, optimize=-1) .. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, quiet=0, legacy=False, optimize=-1)
Byte-compile all the :file:`.py` files found along ``sys.path``. If Byte-compile all the :file:`.py` files found along ``sys.path``. If
@ -196,6 +208,10 @@ Public functions
.. versionchanged:: 3.5 .. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value. *quiet* parameter was changed to a multilevel value.
.. versionchanged:: 3.5
The *legacy* parameter only writes out ``.pyc`` files, not ``.pyo`` files
no matter what the value of *optimize* is.
To force a recompile of all the :file:`.py` files in the :file:`Lib/` To force a recompile of all the :file:`.py` files in the :file:`Lib/`
subdirectory and all its subdirectories:: subdirectory and all its subdirectories::

View File

@ -203,11 +203,9 @@ file paths.
value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2.
The ``cpython-32`` string comes from the current magic tag (see The ``cpython-32`` string comes from the current magic tag (see
:func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
:exc:`NotImplementedError` will be raised). The returned path will end in :exc:`NotImplementedError` will be raised). By passing in ``True`` or
``.pyc`` when ``__debug__`` is ``True`` or ``.pyo`` for an optimized Python ``False`` for *debug_override* you can override the system's value for
(i.e. ``__debug__`` is ``False``). By passing in ``True`` or ``False`` for ``__debug__``, leading to optimized bytecode.
*debug_override* you can override the system's value for ``__debug__`` for
extension selection.
*path* need not exist. *path* need not exist.
@ -218,6 +216,9 @@ file paths.
.. deprecated:: 3.4 .. deprecated:: 3.4
Use :func:`importlib.util.cache_from_source` instead. Use :func:`importlib.util.cache_from_source` instead.
.. versionchanged:: 3.5
The *debug_override* parameter no longer creates a ``.pyo`` file.
.. function:: source_from_cache(path) .. function:: source_from_cache(path)

View File

@ -711,6 +711,9 @@ find and load modules.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. deprecated:: 3.5
Use :attr:`BYTECODE_SUFFIXES` instead.
.. attribute:: OPTIMIZED_BYTECODE_SUFFIXES .. attribute:: OPTIMIZED_BYTECODE_SUFFIXES
A list of strings representing the file suffixes for optimized bytecode A list of strings representing the file suffixes for optimized bytecode
@ -718,14 +721,19 @@ find and load modules.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. deprecated:: 3.5
Use :attr:`BYTECODE_SUFFIXES` instead.
.. attribute:: BYTECODE_SUFFIXES .. attribute:: BYTECODE_SUFFIXES
A list of strings representing the recognized file suffixes for bytecode A list of strings representing the recognized file suffixes for bytecode
modules. Set to either :attr:`DEBUG_BYTECODE_SUFFIXES` or modules (including the leading dot).
:attr:`OPTIMIZED_BYTECODE_SUFFIXES` based on whether ``__debug__`` is true.
.. versionadded:: 3.3 .. versionadded:: 3.3
.. versionchanged:: 3.5
The value is no longer dependent on ``__debug__``.
.. attribute:: EXTENSION_SUFFIXES .. attribute:: EXTENSION_SUFFIXES
A list of strings representing the recognized file suffixes for A list of strings representing the recognized file suffixes for
@ -1074,23 +1082,37 @@ an :term:`importer`.
.. versionadded:: 3.4 .. versionadded:: 3.4
.. function:: cache_from_source(path, debug_override=None) .. function:: cache_from_source(path, debug_override=None, *, optimization=None)
Return the :pep:`3147` path to the byte-compiled file associated with the Return the :pep:`3147`/:pep:`488` path to the byte-compiled file associated
source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return with the source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return
value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. value would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2.
The ``cpython-32`` string comes from the current magic tag (see The ``cpython-32`` string comes from the current magic tag (see
:func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then :func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
:exc:`NotImplementedError` will be raised). The returned path will end in :exc:`NotImplementedError` will be raised).
``.pyc`` when ``__debug__`` is ``True`` or ``.pyo`` for an optimized Python
(i.e. ``__debug__`` is ``False``). By passing in ``True`` or ``False`` for
*debug_override* you can override the system's value for ``__debug__`` for
extension selection.
*path* need not exist. The *optimization* parameter is used to specify the optimization level of the
bytecode file. An empty string represents no optimization, so
``/foo/bar/baz.py`` with an *optimization* of ``''`` will result in a
bytecode path of ``/foo/bar/__pycache__/baz.cpython-32.pyc``. ``None`` causes
the interpter's optimization level to be used. Any other value's string
representation being used, so ``/foo/bar/baz.py`` with an *optimization* of
``2`` will lead to the bytecode path of
``/foo/bar/__pycache__/baz.cpython-32.opt-2.pyc``. The string representation
of *optimization* can only be alphanumeric, else :exc:`ValueError` is raised.
The *debug_override* parameter is deprecated and can be used to override
the system's value for ``__debug__``. A ``True`` value is the equivalent of
setting *optimization* to the empty string. A ``False`` value is the same as
setting *optimization* to ``1``. If both *debug_override* an *optimization*
are not ``None`` then :exc:`TypeError` is raised.
.. versionadded:: 3.4 .. versionadded:: 3.4
.. versionchanged ::3.5
The *optimization* parameter was added and the *debug_override* parameter
was deprecated.
.. function:: source_from_cache(path) .. function:: source_from_cache(path)
@ -1098,7 +1120,7 @@ an :term:`importer`.
file path. For example, if *path* is file path. For example, if *path* is
``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be ``/foo/bar/__pycache__/baz.cpython-32.pyc`` the returned path would be
``/foo/bar/baz.py``. *path* need not exist, however if it does not conform ``/foo/bar/baz.py``. *path* need not exist, however if it does not conform
to :pep:`3147` format, a ``ValueError`` is raised. If to :pep:`3147` or :pep`488` format, a ``ValueError`` is raised. If
:attr:`sys.implementation.cache_tag` is not defined, :attr:`sys.implementation.cache_tag` is not defined,
:exc:`NotImplementedError` is raised. :exc:`NotImplementedError` is raised.

View File

@ -29,9 +29,9 @@ byte-code cache files in the directory containing the source code.
.. function:: compile(file, cfile=None, dfile=None, doraise=False, optimize=-1) .. function:: compile(file, cfile=None, dfile=None, doraise=False, optimize=-1)
Compile a source file to byte-code and write out the byte-code cache file. Compile a source file to byte-code and write out the byte-code cache file.
The source code is loaded from the file name *file*. The byte-code is The source code is loaded from the file name *file*. The byte-code is
written to *cfile*, which defaults to the :PEP:`3147` path, ending in written to *cfile*, which defaults to the :pep:`3147`/:pep`488` path, ending
``.pyc`` (``.pyo`` if optimization is enabled in the current interpreter). in ``.pyc``.
For example, if *file* is ``/foo/bar/baz.py`` *cfile* will default to For example, if *file* is ``/foo/bar/baz.py`` *cfile* will default to
``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. If *dfile* is ``/foo/bar/__pycache__/baz.cpython-32.pyc`` for Python 3.2. If *dfile* is
specified, it is used as the name of the source file in error messages when specified, it is used as the name of the source file in error messages when
@ -68,7 +68,7 @@ byte-code cache files in the directory containing the source code.
.. function:: main(args=None) .. function:: main(args=None)
Compile several source files. The files named in *args* (or on the command Compile several source files. The files named in *args* (or on the command
line, if *args* is ``None``) are compiled and the resulting bytecode is line, if *args* is ``None``) are compiled and the resulting byte-code is
cached in the normal manner. This function does not search a directory cached in the normal manner. This function does not search a directory
structure to locate source files; it only compiles files named explicitly. structure to locate source files; it only compiles files named explicitly.
If ``'-'`` is the only parameter in args, the list of files is taken from If ``'-'`` is the only parameter in args, the list of files is taken from
@ -86,4 +86,3 @@ could not be compiled.
Module :mod:`compileall` Module :mod:`compileall`
Utilities to compile all Python source files in a directory tree. Utilities to compile all Python source files in a directory tree.

View File

@ -167,7 +167,7 @@ always available.
.. data:: dont_write_bytecode .. data:: dont_write_bytecode
If this is true, Python won't try to write ``.pyc`` or ``.pyo`` files on the If this is true, Python won't try to write ``.pyc`` files on the
import of source modules. This value is initially set to ``True`` or import of source modules. This value is initially set to ``True`` or
``False`` depending on the :option:`-B` command line option and the ``False`` depending on the :option:`-B` command line option and the
:envvar:`PYTHONDONTWRITEBYTECODE` environment variable, but you can set it :envvar:`PYTHONDONTWRITEBYTECODE` environment variable, but you can set it
@ -1231,4 +1231,3 @@ always available.
.. rubric:: Citations .. rubric:: Citations
.. [C99] ISO/IEC 9899:1999. "Programming languages -- C." A public draft of this standard is available at http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf\ . .. [C99] ISO/IEC 9899:1999. "Programming languages -- C." A public draft of this standard is available at http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf\ .

View File

@ -363,7 +363,7 @@ Filter
Filter on traces of memory blocks. Filter on traces of memory blocks.
See the :func:`fnmatch.fnmatch` function for the syntax of See the :func:`fnmatch.fnmatch` function for the syntax of
*filename_pattern*. The ``'.pyc'`` and ``'.pyo'`` file extensions are *filename_pattern*. The ``'.pyc'`` file extension is
replaced with ``'.py'``. replaced with ``'.py'``.
Examples: Examples:
@ -374,6 +374,10 @@ Filter
:mod:`tracemalloc` module :mod:`tracemalloc` module
* ``Filter(False, "<unknown>")`` excludes empty tracebacks * ``Filter(False, "<unknown>")`` excludes empty tracebacks
.. versionchanged:: 3.5
The ``'.pyo'`` file extension is no longer replaced with ``'.py'``.
.. attribute:: inclusive .. attribute:: inclusive
If *inclusive* is ``True`` (include), only trace memory blocks allocated If *inclusive* is ``True`` (include), only trace memory blocks allocated
@ -631,4 +635,3 @@ Traceback
obj = Object() obj = Object()
File "test.py", line 12 File "test.py", line 12
tb = tracemalloc.get_object_traceback(f()) tb = tracemalloc.get_object_traceback(f())

View File

@ -405,8 +405,7 @@ The :class:`PyZipFile` constructor takes the same parameters as the
archive. archive.
If the *optimize* parameter to :class:`PyZipFile` was not given or ``-1``, If the *optimize* parameter to :class:`PyZipFile` was not given or ``-1``,
the corresponding file is a :file:`\*.pyo` file if available, else a the corresponding file is a :file:`\*.pyc` file, compiling if necessary.
:file:`\*.pyc` file, compiling if necessary.
If the *optimize* parameter to :class:`PyZipFile` was ``0``, ``1`` or If the *optimize* parameter to :class:`PyZipFile` was ``0``, ``1`` or
``2``, only files with that optimization level (see :func:`compile`) are ``2``, only files with that optimization level (see :func:`compile`) are
@ -569,4 +568,3 @@ Instances have the following attributes:
.. attribute:: ZipInfo.file_size .. attribute:: ZipInfo.file_size
Size of the uncompressed file. Size of the uncompressed file.

View File

@ -20,10 +20,10 @@ subdirectory. For example, the path :file:`example.zip/lib/` would only
import from the :file:`lib/` subdirectory within the archive. import from the :file:`lib/` subdirectory within the archive.
Any files may be present in the ZIP archive, but only files :file:`.py` and Any files may be present in the ZIP archive, but only files :file:`.py` and
:file:`.py[co]` are available for import. ZIP import of dynamic modules :file:`.pyc` are available for import. ZIP import of dynamic modules
(:file:`.pyd`, :file:`.so`) is disallowed. Note that if an archive only contains (:file:`.pyd`, :file:`.so`) is disallowed. Note that if an archive only contains
:file:`.py` files, Python will not attempt to modify the archive by adding the :file:`.py` files, Python will not attempt to modify the archive by adding the
corresponding :file:`.pyc` or :file:`.pyo` file, meaning that if a ZIP archive corresponding :file:`.pyc` file, meaning that if a ZIP archive
doesn't contain :file:`.pyc` files, importing may be rather slow. doesn't contain :file:`.pyc` files, importing may be rather slow.
ZIP archives with an archive comment are currently not supported. ZIP archives with an archive comment are currently not supported.
@ -161,4 +161,3 @@ Here is an example that imports a module from a ZIP archive - note that the
>>> import jwzthreading >>> import jwzthreading
>>> jwzthreading.__file__ >>> jwzthreading.__file__
'example.zip/jwzthreading.py' 'example.zip/jwzthreading.py'

View File

@ -646,7 +646,7 @@ path entry finder that knows how to handle that particular kind of path.
The default set of path entry finders implement all the semantics for finding The default set of path entry finders implement all the semantics for finding
modules on the file system, handling special file types such as Python source modules on the file system, handling special file types such as Python source
code (``.py`` files), Python byte code (``.pyc`` and ``.pyo`` files) and code (``.py`` files), Python byte code (``.pyc`` files) and
shared libraries (e.g. ``.so`` files). When supported by the :mod:`zipimport` shared libraries (e.g. ``.so`` files). When supported by the :mod:`zipimport`
module in the standard library, the default path entry finders also handle module in the standard library, the default path entry finders also handle
loading all of these file types (other than shared libraries) from zipfiles. loading all of these file types (other than shared libraries) from zipfiles.

View File

@ -216,15 +216,15 @@ Some tips for experts:
statements, the ``-OO`` switch removes both assert statements and __doc__ statements, the ``-OO`` switch removes both assert statements and __doc__
strings. Since some programs may rely on having these available, you should strings. Since some programs may rely on having these available, you should
only use this option if you know what you're doing. "Optimized" modules have only use this option if you know what you're doing. "Optimized" modules have
a .pyo rather than a .pyc suffix and are usually smaller. Future releases may an ``opt-`` tag and are usually smaller. Future releases may
change the effects of optimization. change the effects of optimization.
* A program doesn't run any faster when it is read from a ``.pyc`` or ``.pyo`` * A program doesn't run any faster when it is read from a ``.pyc``
file than when it is read from a ``.py`` file; the only thing that's faster file than when it is read from a ``.py`` file; the only thing that's faster
about ``.pyc`` or ``.pyo`` files is the speed with which they are loaded. about ``.pyc`` files is the speed with which they are loaded.
* The module :mod:`compileall` can create .pyc files (or .pyo files when * The module :mod:`compileall` can create .pyc files for all modules in a
:option:`-O` is used) for all modules in a directory. directory.
* There is more detail on this process, including a flow chart of the * There is more detail on this process, including a flow chart of the
decisions, in PEP 3147. decisions, in PEP 3147.
@ -548,4 +548,3 @@ modules found in a package.
.. [#] In fact function definitions are also 'statements' that are 'executed'; the .. [#] In fact function definitions are also 'statements' that are 'executed'; the
execution of a module-level function definition enters the function name in execution of a module-level function definition enters the function name in
the module's global symbol table. the module's global symbol table.

View File

@ -199,7 +199,7 @@ Miscellaneous options
.. cmdoption:: -B .. cmdoption:: -B
If given, Python won't try to write ``.pyc`` or ``.pyo`` files on the If given, Python won't try to write ``.pyc``` files on the
import of source modules. See also :envvar:`PYTHONDONTWRITEBYTECODE`. import of source modules. See also :envvar:`PYTHONDONTWRITEBYTECODE`.

View File

@ -111,7 +111,7 @@ of available options is shown below.
| | launcher is also installed. | | | | launcher is also installed. | |
+---------------------------+--------------------------------------+--------------------------+ +---------------------------+--------------------------------------+--------------------------+
| CompileAll | Compile all ``.py`` files to | 0 | | CompileAll | Compile all ``.py`` files to | 0 |
| | ``.pyc`` and ``.pyo``. | | | | ``.pyc``. | |
+---------------------------+--------------------------------------+--------------------------+ +---------------------------+--------------------------------------+--------------------------+
| PrependPath | Add install and Scripts directories | 0 | | PrependPath | Add install and Scripts directories | 0 |
| | tho :envvar:`PATH` and ``.PY`` to | | | | tho :envvar:`PATH` and ``.PY`` to | |
@ -451,7 +451,7 @@ From file associations
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
The launcher should have been associated with Python files (i.e. ``.py``, The launcher should have been associated with Python files (i.e. ``.py``,
``.pyw``, ``.pyc``, ``.pyo`` files) when it was installed. This means that ``.pyw``, ``.pyc`` files) when it was installed. This means that
when you double-click on one of these files from Windows explorer the launcher when you double-click on one of these files from Windows explorer the launcher
will be used, and therefore you can use the same facilities described above to will be used, and therefore you can use the same facilities described above to
have the script specify the version which should be used. have the script specify the version which should be used.
@ -796,5 +796,3 @@ Other resources
:pep:`397` - Python launcher for Windows :pep:`397` - Python launcher for Windows
The proposal for the launcher to be included in the Python distribution. The proposal for the launcher to be included in the Python distribution.

View File

@ -88,6 +88,8 @@ Implementation improvements:
``surrogateescape`` error handler, instead of the ``strict`` error handler ``surrogateescape`` error handler, instead of the ``strict`` error handler
(:issue:`19977`). (:issue:`19977`).
* :pep:`488`, the elimination of ``.pyo`` files.
Significantly Improved Library Modules: Significantly Improved Library Modules:
* None yet. * None yet.
@ -195,6 +197,24 @@ environment will be used.
:pep:`486` -- Make the Python Launcher aware of virtual environments :pep:`486` -- Make the Python Launcher aware of virtual environments
PEP 488: Elimination of PYO files
---------------------------------
:pep:`488` does away with the concept of ``.pyo`` files. This means that
``.pyc`` files represent both unoptimized and optimized bytecode. To prevent
the need to constantly regenerate bytecode files, ``.pyc`` files now have an
optional ``opt-`` tag in their name when the bytecode is optimized. This has
the side-effect of no more bytecode file name clashes when running under either
``-O`` or ``-OO``, thus allowing unoptimized, ``-O``, and ``-OO`` bytecode files
to all exist simultaneously. :func:`importlib.util.cache_from_source` has an
updated API to help with this change.
.. seealso::
:pep:`488` -- Elimination of PYO files
Other Language Changes Other Language Changes
====================== ======================
@ -535,7 +555,7 @@ The following performance enhancements have been added:
``FindFirstFile``/``FindNextFile`` system calls. (Contributed by ``FindFirstFile``/``FindNextFile`` system calls. (Contributed by
Ben Hoyt with help from Victor Stinner in :issue:`23605`.) Ben Hoyt with help from Victor Stinner in :issue:`23605`.)
* Construction of ``bytes(int)`` (filled by zero bytes) is faster and use less * Construction of ``bytes(int)`` (filled by zero bytes) is faster and uses less
memory for large objects. ``calloc()`` is used instead of ``malloc()`` to memory for large objects. ``calloc()`` is used instead of ``malloc()`` to
allocate memory for these objects. allocate memory for these objects.
@ -630,6 +650,8 @@ removed:
3.4, and has now been removed. 3.4, and has now been removed.
(Contributed by Matt Chaput in :issue:`6623`.) (Contributed by Matt Chaput in :issue:`6623`.)
* The concept of ``.pyo`` files has been removed.
* The JoinableQueue class in the provisional asyncio module was deprecated * The JoinableQueue class in the provisional asyncio module was deprecated
in 3.4.4 and is now removed (:issue:`23464`). in 3.4.4 and is now removed (:issue:`23464`).
@ -759,6 +781,17 @@ Changes in the Python API
*LegalChars* parameter of :func:`~http.cookies.Morsel.set` is deprecated and *LegalChars* parameter of :func:`~http.cookies.Morsel.set` is deprecated and
is now ignored. (:issue:`2211`) is now ignored. (:issue:`2211`)
* :pep:`488` has removed ``.pyo`` files from Python and introduced the optional
``opt-`` tag in ``.pyc`` file names. The
:func:`importlib.util.cache_from_source` has gained an *optimization*
parameter to help control the ``opt-`` tag. Because of this, the
*debug_override* parameter of the function is now deprecated. `.pyo` files
are also no longer supported as a file argument to the Python interpreter and
thus serve no purpose when distributed on their own (i.e. sourcless code
distribution). Due to the fact that the magic number for bytecode has changed
in Python 3.5, all old `.pyo` files from previous versions of Python are
invalid regardless of this PEP.
Changes in the C API Changes in the C API
-------------------- --------------------

View File

@ -1,4 +1,4 @@
"""Module/script to byte-compile all .py files to .pyc (or .pyo) files. """Module/script to byte-compile all .py files to .pyc files.
When called as a script with arguments, this compiles the directories When called as a script with arguments, this compiles the directories
given as arguments recursively; the -l option prevents it from given as arguments recursively; the -l option prevents it from
@ -118,11 +118,12 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
return success return success
if os.path.isfile(fullname): if os.path.isfile(fullname):
if legacy: if legacy:
cfile = fullname + ('c' if __debug__ else 'o') cfile = fullname + 'c'
else: else:
if optimize >= 0: if optimize >= 0:
opt = optimize if optimize >= 1 else ''
cfile = importlib.util.cache_from_source( cfile = importlib.util.cache_from_source(
fullname, debug_override=not optimize) fullname, optimization=opt)
else: else:
cfile = importlib.util.cache_from_source(fullname) cfile = importlib.util.cache_from_source(fullname)
cache_dir = os.path.dirname(cfile) cache_dir = os.path.dirname(cfile)

View File

@ -314,10 +314,10 @@ class build_py (Command):
if include_bytecode: if include_bytecode:
if self.compile: if self.compile:
outputs.append(importlib.util.cache_from_source( outputs.append(importlib.util.cache_from_source(
filename, debug_override=True)) filename, optimization=''))
if self.optimize > 0: if self.optimize > 0:
outputs.append(importlib.util.cache_from_source( outputs.append(importlib.util.cache_from_source(
filename, debug_override=False)) filename, optimization=self.optimize))
outputs += [ outputs += [
os.path.join(build_dir, filename) os.path.join(build_dir, filename)

View File

@ -22,15 +22,15 @@ class install_lib(Command):
# possible scenarios: # possible scenarios:
# 1) no compilation at all (--no-compile --no-optimize) # 1) no compilation at all (--no-compile --no-optimize)
# 2) compile .pyc only (--compile --no-optimize; default) # 2) compile .pyc only (--compile --no-optimize; default)
# 3) compile .pyc and "level 1" .pyo (--compile --optimize) # 3) compile .pyc and "opt-1" .pyc (--compile --optimize)
# 4) compile "level 1" .pyo only (--no-compile --optimize) # 4) compile "opt-1" .pyc only (--no-compile --optimize)
# 5) compile .pyc and "level 2" .pyo (--compile --optimize-more) # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more)
# 6) compile "level 2" .pyo only (--no-compile --optimize-more) # 6) compile "opt-2" .pyc only (--no-compile --optimize-more)
# #
# The UI for this is two option, 'compile' and 'optimize'. # The UI for this is two options, 'compile' and 'optimize'.
# 'compile' is strictly boolean, and only decides whether to # 'compile' is strictly boolean, and only decides whether to
# generate .pyc files. 'optimize' is three-way (0, 1, or 2), and # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and
# decides both whether to generate .pyo files and what level of # decides both whether to generate .pyc files and what level of
# optimization to use. # optimization to use.
user_options = [ user_options = [
@ -166,10 +166,10 @@ class install_lib(Command):
continue continue
if self.compile: if self.compile:
bytecode_files.append(importlib.util.cache_from_source( bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=True)) py_file, optimization=''))
if self.optimize > 0: if self.optimize > 0:
bytecode_files.append(importlib.util.cache_from_source( bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=False)) py_file, optimization=self.optimize))
return bytecode_files return bytecode_files

View File

@ -120,8 +120,8 @@ class BuildPyTestCase(support.TempdirManager,
found = os.listdir(cmd.build_lib) found = os.listdir(cmd.build_lib)
self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
self.assertEqual(sorted(found), expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag)
['boiledeggs.%s.pyo' % sys.implementation.cache_tag]) self.assertEqual(sorted(found), [expect])
def test_dir_in_package_data(self): def test_dir_in_package_data(self):
""" """

View File

@ -44,12 +44,11 @@ class InstallLibTestCase(support.TempdirManager,
f = os.path.join(project_dir, 'foo.py') f = os.path.join(project_dir, 'foo.py')
self.write_file(f, '# python file') self.write_file(f, '# python file')
cmd.byte_compile([f]) cmd.byte_compile([f])
pyc_file = importlib.util.cache_from_source('foo.py', pyc_file = importlib.util.cache_from_source('foo.py', optimization='')
debug_override=True) pyc_opt_file = importlib.util.cache_from_source('foo.py',
pyo_file = importlib.util.cache_from_source('foo.py', optimization=cmd.optimize)
debug_override=False)
self.assertTrue(os.path.exists(pyc_file)) self.assertTrue(os.path.exists(pyc_file))
self.assertTrue(os.path.exists(pyo_file)) self.assertTrue(os.path.exists(pyc_opt_file))
def test_get_outputs(self): def test_get_outputs(self):
project_dir, dist = self.create_dist() project_dir, dist = self.create_dist()
@ -66,8 +65,8 @@ class InstallLibTestCase(support.TempdirManager,
cmd.distribution.packages = ['spam'] cmd.distribution.packages = ['spam']
cmd.distribution.script_name = 'setup.py' cmd.distribution.script_name = 'setup.py'
# get_outputs should return 4 elements: spam/__init__.py, .pyc and # get_outputs should return 4 elements: spam/__init__.py and .pyc,
# .pyo, foo.import-tag-abiflags.so / foo.pyd # foo.import-tag-abiflags.so / foo.pyd
outputs = cmd.get_outputs() outputs = cmd.get_outputs()
self.assertEqual(len(outputs), 4, outputs) self.assertEqual(len(outputs), 4, outputs)

View File

@ -322,11 +322,11 @@ def byte_compile (py_files,
prefix=None, base_dir=None, prefix=None, base_dir=None,
verbose=1, dry_run=0, verbose=1, dry_run=0,
direct=None): direct=None):
"""Byte-compile a collection of Python source files to either .pyc """Byte-compile a collection of Python source files to .pyc
or .pyo files in a __pycache__ subdirectory. 'py_files' is a list files in a __pycache__ subdirectory. 'py_files' is a list
of files to compile; any files that don't end in ".py" are silently of files to compile; any files that don't end in ".py" are silently
skipped. 'optimize' must be one of the following: skipped. 'optimize' must be one of the following:
0 - don't optimize (generate .pyc) 0 - don't optimize
1 - normal optimization (like "python -O") 1 - normal optimization (like "python -O")
2 - extra optimization (like "python -OO") 2 - extra optimization (like "python -OO")
If 'force' is true, all files are recompiled regardless of If 'force' is true, all files are recompiled regardless of
@ -438,8 +438,9 @@ byte_compile(files, optimize=%r, force=%r,
# cfile - byte-compiled file # cfile - byte-compiled file
# dfile - purported source filename (same as 'file' by default) # dfile - purported source filename (same as 'file' by default)
if optimize >= 0: if optimize >= 0:
opt = '' if optimize == 0 else optimize
cfile = importlib.util.cache_from_source( cfile = importlib.util.cache_from_source(
file, debug_override=not optimize) file, optimization=opt)
else: else:
cfile = importlib.util.cache_from_source(file) cfile = importlib.util.cache_from_source(file)
dfile = file dfile = file

View File

@ -1051,7 +1051,7 @@ class DocTestFinder:
filename = None filename = None
else: else:
filename = getattr(module, '__file__', module.__name__) filename = getattr(module, '__file__', module.__name__)
if filename[-4:] in (".pyc", ".pyo"): if filename[-4:] == ".pyc":
filename = filename[:-1] filename = filename[:-1]
return self._parser.get_doctest(docstring, globs, name, return self._parser.get_doctest(docstring, globs, name,
filename, lineno) filename, lineno)
@ -2378,7 +2378,7 @@ def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
continue continue
if not test.filename: if not test.filename:
filename = module.__file__ filename = module.__file__
if filename[-4:] in (".pyc", ".pyo"): if filename[-4:] == ".pyc":
filename = filename[:-1] filename = filename[:-1]
test.filename = filename test.filename = filename
suite.addTest(DocTestCase(test, **options)) suite.addTest(DocTestCase(test, **options))

View File

@ -58,24 +58,23 @@ def new_module(name):
def get_magic(): def get_magic():
"""**DEPRECATED** """**DEPRECATED**
Return the magic number for .pyc or .pyo files. Return the magic number for .pyc files.
""" """
return util.MAGIC_NUMBER return util.MAGIC_NUMBER
def get_tag(): def get_tag():
"""Return the magic tag for .pyc or .pyo files.""" """Return the magic tag for .pyc files."""
return sys.implementation.cache_tag return sys.implementation.cache_tag
def cache_from_source(path, debug_override=None): def cache_from_source(path, debug_override=None):
"""**DEPRECATED** """**DEPRECATED**
Given the path to a .py file, return the path to its .pyc/.pyo file. Given the path to a .py file, return the path to its .pyc file.
The .py file does not need to exist; this simply returns the path to the The .py file does not need to exist; this simply returns the path to the
.pyc/.pyo file calculated as if the .py file were imported. The extension .pyc file calculated as if the .py file were imported.
will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo.
If debug_override is not None, then it must be a boolean and is used in If debug_override is not None, then it must be a boolean and is used in
place of sys.flags.optimize. place of sys.flags.optimize.
@ -83,16 +82,18 @@ def cache_from_source(path, debug_override=None):
If sys.implementation.cache_tag is None then NotImplementedError is raised. If sys.implementation.cache_tag is None then NotImplementedError is raised.
""" """
return util.cache_from_source(path, debug_override) with warnings.catch_warnings():
warnings.simplefilter('ignore')
return util.cache_from_source(path, debug_override)
def source_from_cache(path): def source_from_cache(path):
"""**DEPRECATED** """**DEPRECATED**
Given the path to a .pyc./.pyo file, return the path to its .py file. Given the path to a .pyc. file, return the path to its .py file.
The .pyc/.pyo file does not need to exist; this simply returns the path to The .pyc file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc/.pyo file. If path does the .py file calculated to correspond to the .pyc file. If path does
not conform to PEP 3147 format, ValueError will be raised. If not conform to PEP 3147 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised. sys.implementation.cache_tag is None then NotImplementedError is raised.

View File

@ -429,45 +429,64 @@ MAGIC_NUMBER = (3320).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__' _PYCACHE = '__pycache__'
_OPT = 'opt-'
SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed. SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed.
DEBUG_BYTECODE_SUFFIXES = ['.pyc'] BYTECODE_SUFFIXES = ['.pyc']
OPTIMIZED_BYTECODE_SUFFIXES = ['.pyo'] # Deprecated.
DEBUG_BYTECODE_SUFFIXES = OPTIMIZED_BYTECODE_SUFFIXES = BYTECODE_SUFFIXES
def cache_from_source(path, debug_override=None): def cache_from_source(path, debug_override=None, *, optimization=None):
"""Given the path to a .py file, return the path to its .pyc/.pyo file. """Given the path to a .py file, return the path to its .pyc file.
The .py file does not need to exist; this simply returns the path to the The .py file does not need to exist; this simply returns the path to the
.pyc/.pyo file calculated as if the .py file were imported. The extension .pyc file calculated as if the .py file were imported.
will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo.
If debug_override is not None, then it must be a boolean and is used in The 'optimization' parameter controls the presumed optimization level of
place of sys.flags.optimize. the bytecode file. If 'optimization' is not None, the string representation
of the argument is taken and verified to be alphanumeric (else ValueError
is raised).
The debug_override parameter is deprecated. If debug_override is not None,
a True value is the same as setting 'optimization' to the empty string
while a False value is equivalent to setting 'optimization' to '1'.
If sys.implementation.cache_tag is None then NotImplementedError is raised. If sys.implementation.cache_tag is None then NotImplementedError is raised.
""" """
debug = not sys.flags.optimize if debug_override is None else debug_override if debug_override is not None:
if debug: _warnings.warn('the debug_override parameter is deprecated; use '
suffixes = DEBUG_BYTECODE_SUFFIXES "'optimization' instead", DeprecationWarning)
else: if optimization is not None:
suffixes = OPTIMIZED_BYTECODE_SUFFIXES message = 'debug_override or optimization must be set to None'
raise TypeError(message)
optimization = '' if debug_override else 1
head, tail = _path_split(path) head, tail = _path_split(path)
base, sep, rest = tail.rpartition('.') base, sep, rest = tail.rpartition('.')
tag = sys.implementation.cache_tag tag = sys.implementation.cache_tag
if tag is None: if tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None') raise NotImplementedError('sys.implementation.cache_tag is None')
filename = ''.join([(base if base else rest), sep, tag, suffixes[0]]) almost_filename = ''.join([(base if base else rest), sep, tag])
return _path_join(head, _PYCACHE, filename) if optimization is None:
if sys.flags.optimize == 0:
optimization = ''
else:
optimization = sys.flags.optimize
optimization = str(optimization)
if optimization != '':
if not optimization.isalnum():
raise ValueError('{!r} is not alphanumeric'.format(optimization))
almost_filename = '{}.{}{}'.format(almost_filename, _OPT, optimization)
return _path_join(head, _PYCACHE, almost_filename + BYTECODE_SUFFIXES[0])
def source_from_cache(path): def source_from_cache(path):
"""Given the path to a .pyc./.pyo file, return the path to its .py file. """Given the path to a .pyc. file, return the path to its .py file.
The .pyc/.pyo file does not need to exist; this simply returns the path to The .pyc file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc/.pyo file. If path does the .py file calculated to correspond to the .pyc file. If path does
not conform to PEP 3147 format, ValueError will be raised. If not conform to PEP 3147/488 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised. sys.implementation.cache_tag is None then NotImplementedError is raised.
""" """
@ -478,9 +497,19 @@ def source_from_cache(path):
if pycache != _PYCACHE: if pycache != _PYCACHE:
raise ValueError('{} not bottom-level directory in ' raise ValueError('{} not bottom-level directory in '
'{!r}'.format(_PYCACHE, path)) '{!r}'.format(_PYCACHE, path))
if pycache_filename.count('.') != 2: dot_count = pycache_filename.count('.')
raise ValueError('expected only 2 dots in ' if dot_count not in {2, 3}:
raise ValueError('expected only 2 or 3 dots in '
'{!r}'.format(pycache_filename)) '{!r}'.format(pycache_filename))
elif dot_count == 3:
optimization = pycache_filename.rsplit('.', 2)[-2]
if not optimization.startswith(_OPT):
raise ValueError("optimization portion of filename does not start "
"with {!r}".format(_OPT))
opt_level = optimization[len(_OPT):]
if not opt_level.isalnum():
raise ValueError("optimization level {!r} is not an alphanumeric "
"value".format(optimization))
base_filename = pycache_filename.partition('.')[0] base_filename = pycache_filename.partition('.')[0]
return _path_join(head, base_filename + SOURCE_SUFFIXES[0]) return _path_join(head, base_filename + SOURCE_SUFFIXES[0])
@ -2337,15 +2366,10 @@ def _setup(sys_module, _imp_module):
modules, those two modules must be explicitly passed in. modules, those two modules must be explicitly passed in.
""" """
global _imp, sys, BYTECODE_SUFFIXES global _imp, sys
_imp = _imp_module _imp = _imp_module
sys = sys_module sys = sys_module
if sys.flags.optimize:
BYTECODE_SUFFIXES = OPTIMIZED_BYTECODE_SUFFIXES
else:
BYTECODE_SUFFIXES = DEBUG_BYTECODE_SUFFIXES
# Set up the spec for existing builtin/frozen modules. # Set up the spec for existing builtin/frozen modules.
module_type = type(sys) module_type = type(sys)
for name, module in sys.modules.items(): for name, module in sys.modules.items():

View File

@ -223,7 +223,7 @@ class ModuleFinder:
if not m.__path__: if not m.__path__:
return return
modules = {} modules = {}
# 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"]. # 'suffixes' used to be a list hardcoded to [".py", ".pyc"].
# But we must also collect Python extension modules - although # But we must also collect Python extension modules - although
# we cannot separate normal dlls from Python extensions. # we cannot separate normal dlls from Python extensions.
suffixes = [] suffixes = []

View File

@ -361,7 +361,7 @@ class Directory:
# [(logical, 0, filehash.IntegerData(1), # [(logical, 0, filehash.IntegerData(1),
# filehash.IntegerData(2), filehash.IntegerData(3), # filehash.IntegerData(2), filehash.IntegerData(3),
# filehash.IntegerData(4))]) # filehash.IntegerData(4))])
# Automatically remove .pyc/.pyo files on uninstall (2) # Automatically remove .pyc files on uninstall (2)
# XXX: adding so many RemoveFile entries makes installer unbelievably # XXX: adding so many RemoveFile entries makes installer unbelievably
# slow. So instead, we have to use wildcard remove entries # slow. So instead, we have to use wildcard remove entries
if file.endswith(".py"): if file.endswith(".py"):
@ -382,10 +382,9 @@ class Directory:
return files return files
def remove_pyc(self): def remove_pyc(self):
"Remove .pyc/.pyo files on uninstall" "Remove .pyc files on uninstall"
add_data(self.db, "RemoveFile", add_data(self.db, "RemoveFile",
[(self.component+"c", self.component, "*.pyc", self.logical, 2), [(self.component+"c", self.component, "*.pyc", self.logical, 2)])
(self.component+"o", self.component, "*.pyo", self.logical, 2)])
class Binary: class Binary:
def __init__(self, fname): def __init__(self, fname):

View File

@ -1,4 +1,4 @@
"""Routine to "compile" a .py file to a .pyc (or .pyo) file. """Routine to "compile" a .py file to a .pyc file.
This module has intimate knowledge of the format of .pyc files. This module has intimate knowledge of the format of .pyc files.
""" """
@ -67,7 +67,7 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
:param file: The source file name. :param file: The source file name.
:param cfile: The target byte compiled file name. When not given, this :param cfile: The target byte compiled file name. When not given, this
defaults to the PEP 3147 location. defaults to the PEP 3147/PEP 488 location.
:param dfile: Purported file name, i.e. the file name that shows up in :param dfile: Purported file name, i.e. the file name that shows up in
error messages. Defaults to the source file name. error messages. Defaults to the source file name.
:param doraise: Flag indicating whether or not an exception should be :param doraise: Flag indicating whether or not an exception should be
@ -85,12 +85,12 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
Note that it isn't necessary to byte-compile Python modules for Note that it isn't necessary to byte-compile Python modules for
execution efficiency -- Python itself byte-compiles a module when execution efficiency -- Python itself byte-compiles a module when
it is loaded, and if it can, writes out the bytecode to the it is loaded, and if it can, writes out the bytecode to the
corresponding .pyc (or .pyo) file. corresponding .pyc file.
However, if a Python installation is shared between users, it is a However, if a Python installation is shared between users, it is a
good idea to byte-compile all modules upon installation, since good idea to byte-compile all modules upon installation, since
other users may not be able to write in the source directories, other users may not be able to write in the source directories,
and thus they won't be able to write the .pyc/.pyo file, and then and thus they won't be able to write the .pyc file, and then
they would be byte-compiling every module each time it is loaded. they would be byte-compiling every module each time it is loaded.
This can slow down program start-up considerably. This can slow down program start-up considerably.
@ -105,8 +105,9 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
""" """
if cfile is None: if cfile is None:
if optimize >= 0: if optimize >= 0:
optimization = optimize if optimize >= 1 else ''
cfile = importlib.util.cache_from_source(file, cfile = importlib.util.cache_from_source(file,
debug_override=not optimize) optimization=optimization)
else: else:
cfile = importlib.util.cache_from_source(file) cfile = importlib.util.cache_from_source(file)
if os.path.islink(cfile): if os.path.islink(cfile):

View File

@ -213,7 +213,7 @@ def classify_class_attrs(object):
def ispackage(path): def ispackage(path):
"""Guess whether a path refers to a package directory.""" """Guess whether a path refers to a package directory."""
if os.path.isdir(path): if os.path.isdir(path):
for ext in ('.py', '.pyc', '.pyo'): for ext in ('.py', '.pyc'):
if os.path.isfile(os.path.join(path, '__init__' + ext)): if os.path.isfile(os.path.join(path, '__init__' + ext)):
return True return True
return False return False

View File

@ -376,36 +376,32 @@ def rmtree(path):
pass pass
def make_legacy_pyc(source): def make_legacy_pyc(source):
"""Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location. """Move a PEP 3147/488 pyc file to its legacy pyc location.
The choice of .pyc or .pyo extension is done based on the __debug__ flag
value.
:param source: The file system path to the source file. The source file :param source: The file system path to the source file. The source file
does not need to exist, however the PEP 3147 pyc file must exist. does not need to exist, however the PEP 3147/488 pyc file must exist.
:return: The file system path to the legacy pyc file. :return: The file system path to the legacy pyc file.
""" """
pyc_file = importlib.util.cache_from_source(source) pyc_file = importlib.util.cache_from_source(source)
up_one = os.path.dirname(os.path.abspath(source)) up_one = os.path.dirname(os.path.abspath(source))
legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o')) legacy_pyc = os.path.join(up_one, source + 'c')
os.rename(pyc_file, legacy_pyc) os.rename(pyc_file, legacy_pyc)
return legacy_pyc return legacy_pyc
def forget(modname): def forget(modname):
"""'Forget' a module was ever imported. """'Forget' a module was ever imported.
This removes the module from sys.modules and deletes any PEP 3147 or This removes the module from sys.modules and deletes any PEP 3147/488 or
legacy .pyc and .pyo files. legacy .pyc files.
""" """
unload(modname) unload(modname)
for dirname in sys.path: for dirname in sys.path:
source = os.path.join(dirname, modname + '.py') source = os.path.join(dirname, modname + '.py')
# It doesn't matter if they exist or not, unlink all possible # It doesn't matter if they exist or not, unlink all possible
# combinations of PEP 3147 and legacy pyc and pyo files. # combinations of PEP 3147/488 and legacy pyc files.
unlink(source + 'c') unlink(source + 'c')
unlink(source + 'o') for opt in ('', 1, 2):
unlink(importlib.util.cache_from_source(source, debug_override=True)) unlink(importlib.util.cache_from_source(source, optimization=opt))
unlink(importlib.util.cache_from_source(source, debug_override=False))
# Check whether a gui is actually available # Check whether a gui is actually available
def _is_gui_available(): def _is_gui_available():

View File

@ -425,7 +425,7 @@ if 1:
def test_compile_ast(self): def test_compile_ast(self):
fname = __file__ fname = __file__
if fname.lower().endswith(('pyc', 'pyo')): if fname.lower().endswith('pyc'):
fname = fname[:-1] fname = fname[:-1]
with open(fname, 'r') as f: with open(fname, 'r') as f:
fcontents = f.read() fcontents = f.read()

View File

@ -101,16 +101,16 @@ class CompileallTests(unittest.TestCase):
def test_optimize(self): def test_optimize(self):
# make sure compiling with different optimization settings than the # make sure compiling with different optimization settings than the
# interpreter's creates the correct file names # interpreter's creates the correct file names
optimize = 1 if __debug__ else 0 optimize, opt = (1, 1) if __debug__ else (0, '')
compileall.compile_dir(self.directory, quiet=True, optimize=optimize) compileall.compile_dir(self.directory, quiet=True, optimize=optimize)
cached = importlib.util.cache_from_source(self.source_path, cached = importlib.util.cache_from_source(self.source_path,
debug_override=not optimize) optimization=opt)
self.assertTrue(os.path.isfile(cached)) self.assertTrue(os.path.isfile(cached))
cached2 = importlib.util.cache_from_source(self.source_path2, cached2 = importlib.util.cache_from_source(self.source_path2,
debug_override=not optimize) optimization=opt)
self.assertTrue(os.path.isfile(cached2)) self.assertTrue(os.path.isfile(cached2))
cached3 = importlib.util.cache_from_source(self.source_path3, cached3 = importlib.util.cache_from_source(self.source_path3,
debug_override=not optimize) optimization=opt)
self.assertTrue(os.path.isfile(cached3)) self.assertTrue(os.path.isfile(cached3))
@mock.patch('compileall.ProcessPoolExecutor') @mock.patch('compileall.ProcessPoolExecutor')
@ -237,11 +237,11 @@ class CommandLineTests(unittest.TestCase):
self.assertNotIn(b'Listing ', quiet) self.assertNotIn(b'Listing ', quiet)
# Ensure that the default behavior of compileall's CLI is to create # Ensure that the default behavior of compileall's CLI is to create
# PEP 3147 pyc/pyo files. # PEP 3147/PEP 488 pyc files.
for name, ext, switch in [ for name, ext, switch in [
('normal', 'pyc', []), ('normal', 'pyc', []),
('optimize', 'pyo', ['-O']), ('optimize', 'opt-1.pyc', ['-O']),
('doubleoptimize', 'pyo', ['-OO']), ('doubleoptimize', 'opt-2.pyc', ['-OO']),
]: ]:
def f(self, ext=ext, switch=switch): def f(self, ext=ext, switch=switch):
script_helper.assert_python_ok(*(switch + script_helper.assert_python_ok(*(switch +
@ -258,13 +258,12 @@ class CommandLineTests(unittest.TestCase):
def test_legacy_paths(self): def test_legacy_paths(self):
# Ensure that with the proper switch, compileall leaves legacy # Ensure that with the proper switch, compileall leaves legacy
# pyc/pyo files, and no __pycache__ directory. # pyc files, and no __pycache__ directory.
self.assertRunOK('-b', '-q', self.pkgdir) self.assertRunOK('-b', '-q', self.pkgdir)
# Verify the __pycache__ directory contents. # Verify the __pycache__ directory contents.
self.assertFalse(os.path.exists(self.pkgdir_cachedir)) self.assertFalse(os.path.exists(self.pkgdir_cachedir))
opt = 'c' if __debug__ else 'o' expected = sorted(['__init__.py', '__init__.pyc', 'bar.py',
expected = sorted(['__init__.py', '__init__.py' + opt, 'bar.py', 'bar.pyc'])
'bar.py' + opt])
self.assertEqual(sorted(os.listdir(self.pkgdir)), expected) self.assertEqual(sorted(os.listdir(self.pkgdir)), expected)
def test_multiple_runs(self): def test_multiple_runs(self):

View File

@ -111,7 +111,6 @@ class ImportTests(unittest.TestCase):
del sys.path[0] del sys.path[0]
support.unlink(temp_mod_name + '.py') support.unlink(temp_mod_name + '.py')
support.unlink(temp_mod_name + '.pyc') support.unlink(temp_mod_name + '.pyc')
support.unlink(temp_mod_name + '.pyo')
def test_issue5604(self): def test_issue5604(self):
# Test cannot cover imp.load_compiled function. # Test cannot cover imp.load_compiled function.
@ -194,7 +193,7 @@ class ImportTests(unittest.TestCase):
self.assertEqual(package.b, 2) self.assertEqual(package.b, 2)
finally: finally:
del sys.path[0] del sys.path[0]
for ext in ('.py', '.pyc', '.pyo'): for ext in ('.py', '.pyc'):
support.unlink(temp_mod_name + ext) support.unlink(temp_mod_name + ext)
support.unlink(init_file_name + ext) support.unlink(init_file_name + ext)
support.rmtree(test_package_name) support.rmtree(test_package_name)
@ -346,56 +345,6 @@ class PEP3147Tests(unittest.TestCase):
'qux.{}.pyc'.format(self.tag)) 'qux.{}.pyc'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, True), expect) self.assertEqual(imp.cache_from_source(path, True), expect)
def test_cache_from_source_no_cache_tag(self):
# Non cache tag means NotImplementedError.
with support.swap_attr(sys.implementation, 'cache_tag', None):
with self.assertRaises(NotImplementedError):
imp.cache_from_source('whatever.py')
def test_cache_from_source_no_dot(self):
# Directory with a dot, filename without dot.
path = os.path.join('foo.bar', 'file')
expect = os.path.join('foo.bar', '__pycache__',
'file{}.pyc'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, True), expect)
def test_cache_from_source_optimized(self):
# Given the path to a .py file, return the path to its PEP 3147
# defined .pyo file (i.e. under __pycache__).
path = os.path.join('foo', 'bar', 'baz', 'qux.py')
expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
'qux.{}.pyo'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, False), expect)
def test_cache_from_source_cwd(self):
path = 'foo.py'
expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, True), expect)
def test_cache_from_source_override(self):
# When debug_override is not None, it can be any true-ish or false-ish
# value.
path = os.path.join('foo', 'bar', 'baz.py')
partial_expect = os.path.join('foo', 'bar', '__pycache__',
'baz.{}.py'.format(self.tag))
self.assertEqual(imp.cache_from_source(path, []), partial_expect + 'o')
self.assertEqual(imp.cache_from_source(path, [17]),
partial_expect + 'c')
# However if the bool-ishness can't be determined, the exception
# propagates.
class Bearish:
def __bool__(self): raise RuntimeError
with self.assertRaises(RuntimeError):
imp.cache_from_source('/foo/bar/baz.py', Bearish())
@unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
'test meaningful only where os.altsep is defined')
def test_sep_altsep_and_sep_cache_from_source(self):
# Windows path and PEP 3147 where sep is right of altsep.
self.assertEqual(
imp.cache_from_source('\\foo\\bar\\baz/qux.py', True),
'\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
@unittest.skipUnless(sys.implementation.cache_tag is not None, @unittest.skipUnless(sys.implementation.cache_tag is not None,
'requires sys.implementation.cache_tag to not be ' 'requires sys.implementation.cache_tag to not be '
'None') 'None')
@ -407,68 +356,6 @@ class PEP3147Tests(unittest.TestCase):
expect = os.path.join('foo', 'bar', 'baz', 'qux.py') expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
self.assertEqual(imp.source_from_cache(path), expect) self.assertEqual(imp.source_from_cache(path), expect)
def test_source_from_cache_no_cache_tag(self):
# If sys.implementation.cache_tag is None, raise NotImplementedError.
path = os.path.join('blah', '__pycache__', 'whatever.pyc')
with support.swap_attr(sys.implementation, 'cache_tag', None):
with self.assertRaises(NotImplementedError):
imp.source_from_cache(path)
def test_source_from_cache_bad_path(self):
# When the path to a pyc file is not in PEP 3147 format, a ValueError
# is raised.
self.assertRaises(
ValueError, imp.source_from_cache, '/foo/bar/bazqux.pyc')
def test_source_from_cache_no_slash(self):
# No slashes at all in path -> ValueError
self.assertRaises(
ValueError, imp.source_from_cache, 'foo.cpython-32.pyc')
def test_source_from_cache_too_few_dots(self):
# Too few dots in final path component -> ValueError
self.assertRaises(
ValueError, imp.source_from_cache, '__pycache__/foo.pyc')
def test_source_from_cache_too_many_dots(self):
# Too many dots in final path component -> ValueError
self.assertRaises(
ValueError, imp.source_from_cache,
'__pycache__/foo.cpython-32.foo.pyc')
def test_source_from_cache_no__pycache__(self):
# Another problem with the path -> ValueError
self.assertRaises(
ValueError, imp.source_from_cache,
'/foo/bar/foo.cpython-32.foo.pyc')
def test_package___file__(self):
try:
m = __import__('pep3147')
except ImportError:
pass
else:
self.fail("pep3147 module already exists: %r" % (m,))
# Test that a package's __file__ points to the right source directory.
os.mkdir('pep3147')
sys.path.insert(0, os.curdir)
def cleanup():
if sys.path[0] == os.curdir:
del sys.path[0]
shutil.rmtree('pep3147')
self.addCleanup(cleanup)
# Touch the __init__.py file.
support.create_empty_file('pep3147/__init__.py')
importlib.invalidate_caches()
expected___file__ = os.sep.join(('.', 'pep3147', '__init__.py'))
m = __import__('pep3147')
self.assertEqual(m.__file__, expected___file__, (m.__file__, m.__path__, sys.path, sys.path_importer_cache))
# Ensure we load the pyc file.
support.unload('pep3147')
m = __import__('pep3147')
support.unload('pep3147')
self.assertEqual(m.__file__, expected___file__, (m.__file__, m.__path__, sys.path, sys.path_importer_cache))
class NullImporterTests(unittest.TestCase): class NullImporterTests(unittest.TestCase):
@unittest.skipIf(support.TESTFN_UNENCODABLE is None, @unittest.skipIf(support.TESTFN_UNENCODABLE is None,

View File

@ -32,7 +32,6 @@ skip_if_dont_write_bytecode = unittest.skipIf(
def remove_files(name): def remove_files(name):
for f in (name + ".py", for f in (name + ".py",
name + ".pyc", name + ".pyc",
name + ".pyo",
name + ".pyw", name + ".pyw",
name + "$py.class"): name + "$py.class"):
unlink(f) unlink(f)
@ -84,7 +83,6 @@ class ImportTests(unittest.TestCase):
def test_with_extension(ext): def test_with_extension(ext):
# The extension is normally ".py", perhaps ".pyw". # The extension is normally ".py", perhaps ".pyw".
source = TESTFN + ext source = TESTFN + ext
pyo = TESTFN + ".pyo"
if is_jython: if is_jython:
pyc = TESTFN + "$py.class" pyc = TESTFN + "$py.class"
else: else:
@ -115,7 +113,6 @@ class ImportTests(unittest.TestCase):
forget(TESTFN) forget(TESTFN)
unlink(source) unlink(source)
unlink(pyc) unlink(pyc)
unlink(pyo)
sys.path.insert(0, os.curdir) sys.path.insert(0, os.curdir)
try: try:
@ -138,7 +135,7 @@ class ImportTests(unittest.TestCase):
f.write(']') f.write(']')
try: try:
# Compile & remove .py file; we only need .pyc (or .pyo). # Compile & remove .py file; we only need .pyc.
# Bytecode must be relocated from the PEP 3147 bytecode-only location. # Bytecode must be relocated from the PEP 3147 bytecode-only location.
py_compile.compile(filename) py_compile.compile(filename)
finally: finally:
@ -252,7 +249,7 @@ class ImportTests(unittest.TestCase):
importlib.invalidate_caches() importlib.invalidate_caches()
mod = __import__(TESTFN) mod = __import__(TESTFN)
base, ext = os.path.splitext(mod.__file__) base, ext = os.path.splitext(mod.__file__)
self.assertIn(ext, ('.pyc', '.pyo')) self.assertEqual(ext, '.pyc')
finally: finally:
del sys.path[0] del sys.path[0]
remove_files(TESTFN) remove_files(TESTFN)
@ -328,7 +325,7 @@ class ImportTests(unittest.TestCase):
@skip_if_dont_write_bytecode @skip_if_dont_write_bytecode
class FilePermissionTests(unittest.TestCase): class FilePermissionTests(unittest.TestCase):
# tests for file mode on cached .pyc/.pyo files # tests for file mode on cached .pyc files
@unittest.skipUnless(os.name == 'posix', @unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems") "test meaningful only on posix systems")
@ -339,7 +336,7 @@ class FilePermissionTests(unittest.TestCase):
module = __import__(name) module = __import__(name)
if not os.path.exists(cached_path): if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of " self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file") "a .pyc file")
stat_info = os.stat(cached_path) stat_info = os.stat(cached_path)
# Check that the umask is respected, and the executable bits # Check that the umask is respected, and the executable bits
@ -358,7 +355,7 @@ class FilePermissionTests(unittest.TestCase):
__import__(name) __import__(name)
if not os.path.exists(cached_path): if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of " self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file") "a .pyc file")
stat_info = os.stat(cached_path) stat_info = os.stat(cached_path)
self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode)) self.assertEqual(oct(stat.S_IMODE(stat_info.st_mode)), oct(mode))
@ -373,7 +370,7 @@ class FilePermissionTests(unittest.TestCase):
__import__(name) __import__(name)
if not os.path.exists(cached_path): if not os.path.exists(cached_path):
self.fail("__import__ did not result in creation of " self.fail("__import__ did not result in creation of "
"either a .pyc or .pyo file") "a .pyc file")
stat_info = os.stat(cached_path) stat_info = os.stat(cached_path)
expected = mode | 0o200 # Account for fix for issue #6074 expected = mode | 0o200 # Account for fix for issue #6074
@ -404,10 +401,7 @@ class FilePermissionTests(unittest.TestCase):
unlink(path) unlink(path)
unload(name) unload(name)
importlib.invalidate_caches() importlib.invalidate_caches()
if __debug__: bytecode_only = path + "c"
bytecode_only = path + "c"
else:
bytecode_only = path + "o"
os.rename(importlib.util.cache_from_source(path), bytecode_only) os.rename(importlib.util.cache_from_source(path), bytecode_only)
m = __import__(name) m = __import__(name)
self.assertEqual(m.x, 'rewritten') self.assertEqual(m.x, 'rewritten')
@ -631,9 +625,7 @@ class OverridingImportBuiltinTests(unittest.TestCase):
class PycacheTests(unittest.TestCase): class PycacheTests(unittest.TestCase):
# Test the various PEP 3147 related behaviors. # Test the various PEP 3147/488-related behaviors.
tag = sys.implementation.cache_tag
def _clean(self): def _clean(self):
forget(TESTFN) forget(TESTFN)
@ -658,9 +650,10 @@ class PycacheTests(unittest.TestCase):
self.assertFalse(os.path.exists('__pycache__')) self.assertFalse(os.path.exists('__pycache__'))
__import__(TESTFN) __import__(TESTFN)
self.assertTrue(os.path.exists('__pycache__')) self.assertTrue(os.path.exists('__pycache__'))
self.assertTrue(os.path.exists(os.path.join( pyc_path = importlib.util.cache_from_source(self.source)
'__pycache__', '{}.{}.py{}'.format( self.assertTrue(os.path.exists(pyc_path),
TESTFN, self.tag, 'c' if __debug__ else 'o')))) 'bytecode file {!r} for {!r} does not '
'exist'.format(pyc_path, TESTFN))
@unittest.skipUnless(os.name == 'posix', @unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems") "test meaningful only on posix systems")
@ -673,8 +666,10 @@ class PycacheTests(unittest.TestCase):
with temp_umask(0o222): with temp_umask(0o222):
__import__(TESTFN) __import__(TESTFN)
self.assertTrue(os.path.exists('__pycache__')) self.assertTrue(os.path.exists('__pycache__'))
self.assertFalse(os.path.exists(os.path.join( pyc_path = importlib.util.cache_from_source(self.source)
'__pycache__', '{}.{}.pyc'.format(TESTFN, self.tag)))) self.assertFalse(os.path.exists(pyc_path),
'bytecode file {!r} for {!r} '
'exists'.format(pyc_path, TESTFN))
@skip_if_dont_write_bytecode @skip_if_dont_write_bytecode
def test_missing_source(self): def test_missing_source(self):

View File

@ -5,6 +5,7 @@ machinery = util.import_importlib('importlib.machinery')
importlib_util = util.import_importlib('importlib.util') importlib_util = util.import_importlib('importlib.util')
import os import os
import string
import sys import sys
from test import support from test import support
import types import types
@ -562,7 +563,8 @@ class PEP3147Tests:
path = os.path.join('foo', 'bar', 'baz', 'qux.py') path = os.path.join('foo', 'bar', 'baz', 'qux.py')
expect = os.path.join('foo', 'bar', 'baz', '__pycache__', expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
'qux.{}.pyc'.format(self.tag)) 'qux.{}.pyc'.format(self.tag))
self.assertEqual(self.util.cache_from_source(path, True), expect) self.assertEqual(self.util.cache_from_source(path, optimization=''),
expect)
def test_cache_from_source_no_cache_tag(self): def test_cache_from_source_no_cache_tag(self):
# No cache tag means NotImplementedError. # No cache tag means NotImplementedError.
@ -575,43 +577,103 @@ class PEP3147Tests:
path = os.path.join('foo.bar', 'file') path = os.path.join('foo.bar', 'file')
expect = os.path.join('foo.bar', '__pycache__', expect = os.path.join('foo.bar', '__pycache__',
'file{}.pyc'.format(self.tag)) 'file{}.pyc'.format(self.tag))
self.assertEqual(self.util.cache_from_source(path, True), expect) self.assertEqual(self.util.cache_from_source(path, optimization=''),
expect)
def test_cache_from_source_optimized(self): def test_cache_from_source_debug_override(self):
# Given the path to a .py file, return the path to its PEP 3147 # Given the path to a .py file, return the path to its PEP 3147/PEP 488
# defined .pyo file (i.e. under __pycache__). # defined .pyc file (i.e. under __pycache__).
path = os.path.join('foo', 'bar', 'baz', 'qux.py') path = os.path.join('foo', 'bar', 'baz', 'qux.py')
expect = os.path.join('foo', 'bar', 'baz', '__pycache__', with warnings.catch_warnings():
'qux.{}.pyo'.format(self.tag)) warnings.simplefilter('ignore')
self.assertEqual(self.util.cache_from_source(path, False), expect) self.assertEqual(self.util.cache_from_source(path, False),
self.util.cache_from_source(path, optimization=1))
self.assertEqual(self.util.cache_from_source(path, True),
self.util.cache_from_source(path, optimization=''))
with warnings.catch_warnings():
warnings.simplefilter('error')
with self.assertRaises(DeprecationWarning):
self.util.cache_from_source(path, False)
with self.assertRaises(DeprecationWarning):
self.util.cache_from_source(path, True)
def test_cache_from_source_cwd(self): def test_cache_from_source_cwd(self):
path = 'foo.py' path = 'foo.py'
expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag)) expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
self.assertEqual(self.util.cache_from_source(path, True), expect) self.assertEqual(self.util.cache_from_source(path, optimization=''),
expect)
def test_cache_from_source_override(self): def test_cache_from_source_override(self):
# When debug_override is not None, it can be any true-ish or false-ish # When debug_override is not None, it can be any true-ish or false-ish
# value. # value.
path = os.path.join('foo', 'bar', 'baz.py') path = os.path.join('foo', 'bar', 'baz.py')
partial_expect = os.path.join('foo', 'bar', '__pycache__',
'baz.{}.py'.format(self.tag))
self.assertEqual(self.util.cache_from_source(path, []), partial_expect + 'o')
self.assertEqual(self.util.cache_from_source(path, [17]),
partial_expect + 'c')
# However if the bool-ishness can't be determined, the exception # However if the bool-ishness can't be determined, the exception
# propagates. # propagates.
class Bearish: class Bearish:
def __bool__(self): raise RuntimeError def __bool__(self): raise RuntimeError
with self.assertRaises(RuntimeError): with warnings.catch_warnings():
self.util.cache_from_source('/foo/bar/baz.py', Bearish()) warnings.simplefilter('ignore')
self.assertEqual(self.util.cache_from_source(path, []),
self.util.cache_from_source(path, optimization=1))
self.assertEqual(self.util.cache_from_source(path, [17]),
self.util.cache_from_source(path, optimization=''))
with self.assertRaises(RuntimeError):
self.util.cache_from_source('/foo/bar/baz.py', Bearish())
def test_cache_from_source_optimization_empty_string(self):
# Setting 'optimization' to '' leads to no optimization tag (PEP 488).
path = 'foo.py'
expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
self.assertEqual(self.util.cache_from_source(path, optimization=''),
expect)
def test_cache_from_source_optimization_None(self):
# Setting 'optimization' to None uses the interpreter's optimization.
# (PEP 488)
path = 'foo.py'
optimization_level = sys.flags.optimize
almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
if optimization_level == 0:
expect = almost_expect + '.pyc'
elif optimization_level <= 2:
expect = almost_expect + '.opt-{}.pyc'.format(optimization_level)
else:
msg = '{!r} is a non-standard optimization level'.format(optimization_level)
self.skipTest(msg)
self.assertEqual(self.util.cache_from_source(path, optimization=None),
expect)
def test_cache_from_source_optimization_set(self):
# The 'optimization' parameter accepts anything that has a string repr
# that passes str.alnum().
path = 'foo.py'
valid_characters = string.ascii_letters + string.digits
almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
got = self.util.cache_from_source(path, optimization=valid_characters)
# Test all valid characters are accepted.
self.assertEqual(got,
almost_expect + '.opt-{}.pyc'.format(valid_characters))
# str() should be called on argument.
self.assertEqual(self.util.cache_from_source(path, optimization=42),
almost_expect + '.opt-42.pyc')
# Invalid characters raise ValueError.
with self.assertRaises(ValueError):
self.util.cache_from_source(path, optimization='path/is/bad')
def test_cache_from_source_debug_override_optimization_both_set(self):
# Can only set one of the optimization-related parameters.
with warnings.catch_warnings():
warnings.simplefilter('ignore')
with self.assertRaises(TypeError):
self.util.cache_from_source('foo.py', False, optimization='')
@unittest.skipUnless(os.sep == '\\' and os.altsep == '/', @unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
'test meaningful only where os.altsep is defined') 'test meaningful only where os.altsep is defined')
def test_sep_altsep_and_sep_cache_from_source(self): def test_sep_altsep_and_sep_cache_from_source(self):
# Windows path and PEP 3147 where sep is right of altsep. # Windows path and PEP 3147 where sep is right of altsep.
self.assertEqual( self.assertEqual(
self.util.cache_from_source('\\foo\\bar\\baz/qux.py', True), self.util.cache_from_source('\\foo\\bar\\baz/qux.py', optimization=''),
'\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag)) '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
@unittest.skipUnless(sys.implementation.cache_tag is not None, @unittest.skipUnless(sys.implementation.cache_tag is not None,
@ -649,7 +711,12 @@ class PEP3147Tests:
ValueError, self.util.source_from_cache, '__pycache__/foo.pyc') ValueError, self.util.source_from_cache, '__pycache__/foo.pyc')
def test_source_from_cache_too_many_dots(self): def test_source_from_cache_too_many_dots(self):
# Too many dots in final path component -> ValueError with self.assertRaises(ValueError):
self.util.source_from_cache(
'__pycache__/foo.cpython-32.opt-1.foo.pyc')
def test_source_from_cache_not_opt(self):
# Non-`opt-` path component -> ValueError
self.assertRaises( self.assertRaises(
ValueError, self.util.source_from_cache, ValueError, self.util.source_from_cache,
'__pycache__/foo.cpython-32.foo.pyc') '__pycache__/foo.cpython-32.foo.pyc')
@ -660,6 +727,17 @@ class PEP3147Tests:
ValueError, self.util.source_from_cache, ValueError, self.util.source_from_cache,
'/foo/bar/foo.cpython-32.foo.pyc') '/foo/bar/foo.cpython-32.foo.pyc')
def test_source_from_cache_optimized_bytecode(self):
# Optimized bytecode is not an issue.
path = os.path.join('__pycache__', 'foo.{}.opt-1.pyc'.format(self.tag))
self.assertEqual(self.util.source_from_cache(path), 'foo.py')
def test_source_from_cache_missing_optimization(self):
# An empty optimization level is a no-no.
path = os.path.join('__pycache__', 'foo.{}.opt-.pyc'.format(self.tag))
with self.assertRaises(ValueError):
self.util.source_from_cache(path)
(Frozen_PEP3147Tests, (Frozen_PEP3147Tests,
Source_PEP3147Tests Source_PEP3147Tests

View File

@ -119,6 +119,10 @@ class PyCompileTests(unittest.TestCase):
self.assertTrue(os.path.exists(cache_path)) self.assertTrue(os.path.exists(cache_path))
self.assertFalse(os.path.exists(pyc_path)) self.assertFalse(os.path.exists(pyc_path))
def test_optimization_path(self):
# Specifying optimized bytecode should lead to a path reflecting that.
self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -269,7 +269,7 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
if verbose > 1: print(ex) # Persist with cleaning up if verbose > 1: print(ex) # Persist with cleaning up
def _fix_ns_for_legacy_pyc(self, ns, alter_sys): def _fix_ns_for_legacy_pyc(self, ns, alter_sys):
char_to_add = "c" if __debug__ else "o" char_to_add = "c"
ns["__file__"] += char_to_add ns["__file__"] += char_to_add
ns["__cached__"] = ns["__file__"] ns["__cached__"] = ns["__file__"]
spec = ns["__spec__"] spec = ns["__spec__"]

View File

@ -13,8 +13,8 @@ from test.tracedmodules import testmod
#------------------------------- Utilities -----------------------------------# #------------------------------- Utilities -----------------------------------#
def fix_ext_py(filename): def fix_ext_py(filename):
"""Given a .pyc/.pyo filename converts it to the appropriate .py""" """Given a .pyc filename converts it to the appropriate .py"""
if filename.endswith(('.pyc', '.pyo')): if filename.endswith('.pyc'):
filename = filename[:-1] filename = filename[:-1]
return filename return filename

View File

@ -660,11 +660,9 @@ class TestFilters(unittest.TestCase):
self.assertFalse(fnmatch('abcdd', 'a*c*e')) self.assertFalse(fnmatch('abcdd', 'a*c*e'))
self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg')) self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
# replace .pyc and .pyo suffix with .py # replace .pyc suffix with .py
self.assertTrue(fnmatch('a.pyc', 'a.py')) self.assertTrue(fnmatch('a.pyc', 'a.py'))
self.assertTrue(fnmatch('a.pyo', 'a.py'))
self.assertTrue(fnmatch('a.py', 'a.pyc')) self.assertTrue(fnmatch('a.py', 'a.pyc'))
self.assertTrue(fnmatch('a.py', 'a.pyo'))
if os.name == 'nt': if os.name == 'nt':
# case insensitive # case insensitive
@ -674,7 +672,6 @@ class TestFilters(unittest.TestCase):
self.assertTrue(fnmatch('a.pyc', 'a.PY')) self.assertTrue(fnmatch('a.pyc', 'a.PY'))
self.assertTrue(fnmatch('a.PYO', 'a.py')) self.assertTrue(fnmatch('a.PYO', 'a.py'))
self.assertTrue(fnmatch('a.py', 'a.PYC')) self.assertTrue(fnmatch('a.py', 'a.PYC'))
self.assertTrue(fnmatch('a.PY', 'a.pyo'))
else: else:
# case sensitive # case sensitive
self.assertFalse(fnmatch('aBC', 'ABc')) self.assertFalse(fnmatch('aBC', 'ABc'))
@ -683,7 +680,6 @@ class TestFilters(unittest.TestCase):
self.assertFalse(fnmatch('a.pyc', 'a.PY')) self.assertFalse(fnmatch('a.pyc', 'a.PY'))
self.assertFalse(fnmatch('a.PYO', 'a.py')) self.assertFalse(fnmatch('a.PYO', 'a.py'))
self.assertFalse(fnmatch('a.py', 'a.PYC')) self.assertFalse(fnmatch('a.py', 'a.PYC'))
self.assertFalse(fnmatch('a.PY', 'a.pyo'))
if os.name == 'nt': if os.name == 'nt':
# normalize alternate separator "/" to the standard separator "\" # normalize alternate separator "/" to the standard separator "\"

View File

@ -689,7 +689,7 @@ class PyZipFileTests(unittest.TestCase):
self.requiresWriteAccess(os.path.dirname(__file__)) self.requiresWriteAccess(os.path.dirname(__file__))
with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
fn = __file__ fn = __file__
if fn.endswith('.pyc') or fn.endswith('.pyo'): if fn.endswith('.pyc'):
path_split = fn.split(os.sep) path_split = fn.split(os.sep)
if os.altsep is not None: if os.altsep is not None:
path_split.extend(fn.split(os.altsep)) path_split.extend(fn.split(os.altsep))
@ -706,7 +706,7 @@ class PyZipFileTests(unittest.TestCase):
with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp: with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
fn = __file__ fn = __file__
if fn.endswith(('.pyc', '.pyo')): if fn.endswith('.pyc'):
fn = fn[:-1] fn = fn[:-1]
zipfp.writepy(fn, "testpackage") zipfp.writepy(fn, "testpackage")
@ -762,10 +762,8 @@ class PyZipFileTests(unittest.TestCase):
import email import email
packagedir = os.path.dirname(email.__file__) packagedir = os.path.dirname(email.__file__)
self.requiresWriteAccess(packagedir) self.requiresWriteAccess(packagedir)
# use .pyc if running test in optimization mode,
# use .pyo if running test in debug mode
optlevel = 1 if __debug__ else 0 optlevel = 1 if __debug__ else 0
ext = '.pyo' if optlevel == 1 else '.pyc' ext = '.pyc'
with TemporaryFile() as t, \ with TemporaryFile() as t, \
zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp: zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
@ -839,11 +837,10 @@ class PyZipFileTests(unittest.TestCase):
self.assertIn("SyntaxError", s.getvalue()) self.assertIn("SyntaxError", s.getvalue())
# as it will not have compiled the python file, it will # as it will not have compiled the python file, it will
# include the .py file not .pyc or .pyo # include the .py file not .pyc
names = zipfp.namelist() names = zipfp.namelist()
self.assertIn('mod1.py', names) self.assertIn('mod1.py', names)
self.assertNotIn('mod1.pyc', names) self.assertNotIn('mod1.pyc', names)
self.assertNotIn('mod1.pyo', names)
finally: finally:
rmtree(TESTFN2) rmtree(TESTFN2)

View File

@ -51,7 +51,7 @@ TESTPACK2 = "ziptestpackage2"
TEMP_ZIP = os.path.abspath("junk95142.zip") TEMP_ZIP = os.path.abspath("junk95142.zip")
pyc_file = importlib.util.cache_from_source(TESTMOD + '.py') pyc_file = importlib.util.cache_from_source(TESTMOD + '.py')
pyc_ext = ('.pyc' if __debug__ else '.pyo') pyc_ext = '.pyc'
class ImportHooksBaseTestCase(unittest.TestCase): class ImportHooksBaseTestCase(unittest.TestCase):

View File

@ -1852,7 +1852,7 @@ class Tk(Misc, Wm):
import os import os
baseName = os.path.basename(sys.argv[0]) baseName = os.path.basename(sys.argv[0])
baseName, ext = os.path.splitext(baseName) baseName, ext = os.path.splitext(baseName)
if ext not in ('.py', '.pyc', '.pyo'): if ext not in ('.py', '.pyc'):
baseName = baseName + ext baseName = baseName + ext
interactive = 0 interactive = 0
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)

View File

@ -16,7 +16,7 @@ this_dir_path = os.path.abspath(os.path.dirname(__file__))
def is_package(path): def is_package(path):
for name in os.listdir(path): for name in os.listdir(path):
if name in ('__init__.py', '__init__.pyc', '__init.pyo'): if name in ('__init__.py', '__init__.pyc'):
return True return True
return False return False

View File

@ -305,7 +305,7 @@ class CoverageResults:
if self.is_ignored_filename(filename): if self.is_ignored_filename(filename):
continue continue
if filename.endswith((".pyc", ".pyo")): if filename.endswith(".pyc"):
filename = filename[:-1] filename = filename[:-1]
if coverdir is None: if coverdir is None:

View File

@ -297,7 +297,7 @@ class _Traces(Sequence):
def _normalize_filename(filename): def _normalize_filename(filename):
filename = os.path.normcase(filename) filename = os.path.normcase(filename)
if filename.endswith(('.pyc', '.pyo')): if filename.endswith('.pyc'):
filename = filename[:-1] filename = filename[:-1]
return filename return filename

View File

@ -14,9 +14,9 @@ from . import case, suite, util
__unittest = True __unittest = True
# what about .pyc or .pyo (etc) # what about .pyc (etc)
# we would need to avoid loading the same tests multiple times # we would need to avoid loading the same tests multiple times
# from '.py', '.pyc' *and* '.pyo' # from '.py', *and* '.pyc'
VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE) VALID_MODULE_NAME = re.compile(r'[_a-z]\w*\.py$', re.IGNORECASE)

View File

@ -188,7 +188,7 @@ def warn(message, category=None, stacklevel=1):
filename = globals.get('__file__') filename = globals.get('__file__')
if filename: if filename:
fnl = filename.lower() fnl = filename.lower()
if fnl.endswith((".pyc", ".pyo")): if fnl.endswith(".pyc"):
filename = filename[:-1] filename = filename[:-1]
else: else:
if module == "__main__": if module == "__main__":

View File

@ -1731,7 +1731,7 @@ class PyZipFile(ZipFile):
the modules into the archive. If pathname is a plain the modules into the archive. If pathname is a plain
directory, listdir *.py and enter all modules. Else, pathname directory, listdir *.py and enter all modules. Else, pathname
must be a Python *.py file and the module will be put into the must be a Python *.py file and the module will be put into the
archive. Added modules are always module.pyo or module.pyc. archive. Added modules are always module.pyc.
This method will compile the module.py into module.pyc if This method will compile the module.py into module.pyc if
necessary. necessary.
If filterfunc(pathname) is given, it is called with every argument. If filterfunc(pathname) is given, it is called with every argument.
@ -1824,46 +1824,59 @@ class PyZipFile(ZipFile):
file_py = pathname + ".py" file_py = pathname + ".py"
file_pyc = pathname + ".pyc" file_pyc = pathname + ".pyc"
file_pyo = pathname + ".pyo" pycache_opt0 = importlib.util.cache_from_source(file_py, optimization='')
pycache_pyc = importlib.util.cache_from_source(file_py, True) pycache_opt1 = importlib.util.cache_from_source(file_py, optimization=1)
pycache_pyo = importlib.util.cache_from_source(file_py, False) pycache_opt2 = importlib.util.cache_from_source(file_py, optimization=2)
if self._optimize == -1: if self._optimize == -1:
# legacy mode: use whatever file is present # legacy mode: use whatever file is present
if (os.path.isfile(file_pyo) and if (os.path.isfile(file_pyc) and
os.stat(file_pyo).st_mtime >= os.stat(file_py).st_mtime):
# Use .pyo file.
arcname = fname = file_pyo
elif (os.path.isfile(file_pyc) and
os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime): os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime):
# Use .pyc file. # Use .pyc file.
arcname = fname = file_pyc arcname = fname = file_pyc
elif (os.path.isfile(pycache_pyc) and elif (os.path.isfile(pycache_opt0) and
os.stat(pycache_pyc).st_mtime >= os.stat(file_py).st_mtime): os.stat(pycache_opt0).st_mtime >= os.stat(file_py).st_mtime):
# Use the __pycache__/*.pyc file, but write it to the legacy pyc # Use the __pycache__/*.pyc file, but write it to the legacy pyc
# file name in the archive. # file name in the archive.
fname = pycache_pyc fname = pycache_opt0
arcname = file_pyc arcname = file_pyc
elif (os.path.isfile(pycache_pyo) and elif (os.path.isfile(pycache_opt1) and
os.stat(pycache_pyo).st_mtime >= os.stat(file_py).st_mtime): os.stat(pycache_opt1).st_mtime >= os.stat(file_py).st_mtime):
# Use the __pycache__/*.pyo file, but write it to the legacy pyo # Use the __pycache__/*.pyc file, but write it to the legacy pyc
# file name in the archive. # file name in the archive.
fname = pycache_pyo fname = pycache_opt1
arcname = file_pyo arcname = file_pyc
elif (os.path.isfile(pycache_opt2) and
os.stat(pycache_opt2).st_mtime >= os.stat(file_py).st_mtime):
# Use the __pycache__/*.pyc file, but write it to the legacy pyc
# file name in the archive.
fname = pycache_opt2
arcname = file_pyc
else: else:
# Compile py into PEP 3147 pyc file. # Compile py into PEP 3147 pyc file.
if _compile(file_py): if _compile(file_py):
fname = (pycache_pyc if __debug__ else pycache_pyo) if sys.flags.optimize == 0:
arcname = (file_pyc if __debug__ else file_pyo) fname = pycache_opt0
elif sys.flags.optimize == 1:
fname = pycache_opt1
else:
fname = pycache_opt2
arcname = file_pyc
else: else:
fname = arcname = file_py fname = arcname = file_py
else: else:
# new mode: use given optimization level # new mode: use given optimization level
if self._optimize == 0: if self._optimize == 0:
fname = pycache_pyc fname = pycache_opt0
arcname = file_pyc arcname = file_pyc
else: else:
fname = pycache_pyo arcname = file_pyc
arcname = file_pyo if self._optimize == 1:
fname = pycache_opt1
elif self._optimize == 2:
fname = pycache_opt2
else:
msg = "invalid value for 'optimize': {!r}".format(self._optimize)
raise ValueError(msg)
if not (os.path.isfile(fname) and if not (os.path.isfile(fname) and
os.stat(fname).st_mtime >= os.stat(file_py).st_mtime): os.stat(fname).st_mtime >= os.stat(file_py).st_mtime):
if not _compile(file_py, optimize=self._optimize): if not _compile(file_py, optimize=self._optimize):

View File

@ -1263,7 +1263,12 @@ libinstall: build_all $(srcdir)/Lib/$(PLATDIR) $(srcdir)/Modules/xxmodule.c
-d $(LIBDEST) -f \ -d $(LIBDEST) -f \
-x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \ -x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \
$(DESTDIR)$(LIBDEST) $(DESTDIR)$(LIBDEST)
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
$(PYTHON_FOR_BUILD) -Wi -OO $(DESTDIR)$(LIBDEST)/compileall.py \
-d $(LIBDEST) -f \
-x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \
$(DESTDIR)$(LIBDEST)
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
$(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \ $(PYTHON_FOR_BUILD) -Wi $(DESTDIR)$(LIBDEST)/compileall.py \
-d $(LIBDEST)/site-packages -f \ -d $(LIBDEST)/site-packages -f \
-x badsyntax $(DESTDIR)$(LIBDEST)/site-packages -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages
@ -1271,6 +1276,10 @@ libinstall: build_all $(srcdir)/Lib/$(PLATDIR) $(srcdir)/Modules/xxmodule.c
$(PYTHON_FOR_BUILD) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \ $(PYTHON_FOR_BUILD) -Wi -O $(DESTDIR)$(LIBDEST)/compileall.py \
-d $(LIBDEST)/site-packages -f \ -d $(LIBDEST)/site-packages -f \
-x badsyntax $(DESTDIR)$(LIBDEST)/site-packages -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
$(PYTHON_FOR_BUILD) -Wi -OO $(DESTDIR)$(LIBDEST)/compileall.py \
-d $(LIBDEST)/site-packages -f \
-x badsyntax $(DESTDIR)$(LIBDEST)/site-packages
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \
$(PYTHON_FOR_BUILD) -m lib2to3.pgen2.driver $(DESTDIR)$(LIBDEST)/lib2to3/Grammar.txt $(PYTHON_FOR_BUILD) -m lib2to3.pgen2.driver $(DESTDIR)$(LIBDEST)/lib2to3/Grammar.txt
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \

View File

@ -10,6 +10,8 @@ Release date: XXX
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #23731: Implement PEP 488: removal of .pyo files.
- Issue #23726: Don't enable GC for user subclasses of non-GC types that - Issue #23726: Don't enable GC for user subclasses of non-GC types that
don't add any new fields. Patch by Eugene Toder. don't add any new fields. Patch by Eugene Toder.

View File

@ -104,10 +104,10 @@ Python is also adaptable as an extension language for existing
applications. applications.
See the internal documentation for hints. See the internal documentation for hints.
.PP .PP
Documentation for installed Python modules and packages can be Documentation for installed Python modules and packages can be
viewed by running the viewed by running the
.B pydoc .B pydoc
program. program.
.SH COMMAND LINE OPTIONS .SH COMMAND LINE OPTIONS
.TP .TP
.B \-B .B \-B
@ -150,23 +150,20 @@ Further restrictions may be imposed to prevent the user from injecting
malicious code. malicious code.
.TP .TP
.BI "\-m " module-name .BI "\-m " module-name
Searches Searches
.I sys.path .I sys.path
for the named module and runs the corresponding for the named module and runs the corresponding
.I .py .I .py
file as a script. file as a script.
.TP .TP
.B \-O .B \-O
Turn on basic optimizations. This changes the filename extension for Turn on basic optimizations. Given twice, causes docstrings to be discarded.
compiled (bytecode) files from
.I .pyc
to \fI.pyo\fP. Given twice, causes docstrings to be discarded.
.TP .TP
.B \-OO .B \-OO
Discard docstrings in addition to the \fB-O\fP optimizations. Discard docstrings in addition to the \fB-O\fP optimizations.
.TP .TP
.B \-q .B \-q
Do not print the version and copyright messages. These messages are Do not print the version and copyright messages. These messages are
also suppressed in non-interactive mode. also suppressed in non-interactive mode.
.TP .TP
.B \-s .B \-s
@ -193,7 +190,7 @@ The text I/O layer will still be line-buffered.
.B \-v .B \-v
Print a message each time a module is initialized, showing the place Print a message each time a module is initialized, showing the place
(filename or built-in module) from which it is loaded. When given (filename or built-in module) from which it is loaded. When given
twice, print a message for each file that is checked for when twice, print a message for each file that is checked for when
searching for a module. Also provides information on module cleanup searching for a module. Also provides information on module cleanup
at exit. at exit.
.TP .TP
@ -418,7 +415,7 @@ the \fB\-u\fP option.
.IP PYTHONVERBOSE .IP PYTHONVERBOSE
If this is set to a non-empty string it is equivalent to specifying If this is set to a non-empty string it is equivalent to specifying
the \fB\-v\fP option. If set to an integer, it is equivalent to the \fB\-v\fP option. If set to an integer, it is equivalent to
specifying \fB\-v\fP multiple times. specifying \fB\-v\fP multiple times.
.IP PYTHONWARNINGS .IP PYTHONWARNINGS
If this is set to a comma-separated string it is equivalent to If this is set to a comma-separated string it is equivalent to
specifying the \fB\-W\fP option for each separate value. specifying the \fB\-W\fP option for each separate value.

View File

@ -170,14 +170,14 @@ isfile(wchar_t *filename) /* Is file, not directory */
static int static int
ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */ ismodule(wchar_t *filename) /* Is module -- check for .pyc too */
{ {
if (isfile(filename)) if (isfile(filename))
return 1; return 1;
/* Check for the compiled version of prefix. */ /* Check for the compiled version of prefix. */
if (wcslen(filename) < MAXPATHLEN) { if (wcslen(filename) < MAXPATHLEN) {
wcscat(filename, Py_OptimizeFlag ? L"o" : L"c"); wcscat(filename, L"c");
if (isfile(filename)) if (isfile(filename))
return 1; return 1;
} }
@ -891,4 +891,3 @@ Py_GetProgramFullPath(void)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -20,15 +20,13 @@ _Py_IDENTIFIER(replace);
/* zip_searchorder defines how we search for a module in the Zip /* zip_searchorder defines how we search for a module in the Zip
archive: we first search for a package __init__, then for archive: we first search for a package __init__, then for
non-package .pyc, .pyo and .py entries. The .pyc and .pyo entries non-package .pyc, and .py entries. The .pyc entries
are swapped by initzipimport() if we run in optimized mode. Also, are swapped by initzipimport() if we run in optimized mode. Also,
'/' is replaced by SEP there. */ '/' is replaced by SEP there. */
static struct st_zip_searchorder zip_searchorder[] = { static struct st_zip_searchorder zip_searchorder[] = {
{"/__init__.pyc", IS_PACKAGE | IS_BYTECODE}, {"/__init__.pyc", IS_PACKAGE | IS_BYTECODE},
{"/__init__.pyo", IS_PACKAGE | IS_BYTECODE},
{"/__init__.py", IS_PACKAGE | IS_SOURCE}, {"/__init__.py", IS_PACKAGE | IS_SOURCE},
{".pyc", IS_BYTECODE}, {".pyc", IS_BYTECODE},
{".pyo", IS_BYTECODE},
{".py", IS_SOURCE}, {".py", IS_SOURCE},
{"", 0} {"", 0}
}; };
@ -1318,7 +1316,7 @@ parse_dostime(int dostime, int dosdate)
return mktime(&stm); return mktime(&stm);
} }
/* Given a path to a .pyc or .pyo file in the archive, return the /* Given a path to a .pyc file in the archive, return the
modification time of the matching .py file, or 0 if no source modification time of the matching .py file, or 0 if no source
is available. */ is available. */
static time_t static time_t
@ -1481,17 +1479,6 @@ PyInit_zipimport(void)
/* Correct directory separator */ /* Correct directory separator */
zip_searchorder[0].suffix[0] = SEP; zip_searchorder[0].suffix[0] = SEP;
zip_searchorder[1].suffix[0] = SEP; zip_searchorder[1].suffix[0] = SEP;
zip_searchorder[2].suffix[0] = SEP;
if (Py_OptimizeFlag) {
/* Reverse *.pyc and *.pyo */
struct st_zip_searchorder tmp;
tmp = zip_searchorder[0];
zip_searchorder[0] = zip_searchorder[1];
zip_searchorder[1] = tmp;
tmp = zip_searchorder[3];
zip_searchorder[3] = zip_searchorder[4];
zip_searchorder[4] = tmp;
}
mod = PyModule_Create(&zipimportmodule); mod = PyModule_Create(&zipimportmodule);
if (mod == NULL) if (mod == NULL)

View File

@ -563,13 +563,12 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
data = PyUnicode_DATA(*filename); data = PyUnicode_DATA(*filename);
#define ascii_lower(c) ((c <= 127) ? Py_TOLOWER(c) : 0) #define ascii_lower(c) ((c <= 127) ? Py_TOLOWER(c) : 0)
/* if filename.lower().endswith((".pyc", ".pyo")): */ /* if filename.lower().endswith(".pyc"): */
if (len >= 4 && if (len >= 4 &&
PyUnicode_READ(kind, data, len-4) == '.' && PyUnicode_READ(kind, data, len-4) == '.' &&
ascii_lower(PyUnicode_READ(kind, data, len-3)) == 'p' && ascii_lower(PyUnicode_READ(kind, data, len-3)) == 'p' &&
ascii_lower(PyUnicode_READ(kind, data, len-2)) == 'y' && ascii_lower(PyUnicode_READ(kind, data, len-2)) == 'y' &&
(ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'c' || ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'c')
ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'o'))
{ {
*filename = PyUnicode_Substring(*filename, 0, *filename = PyUnicode_Substring(*filename, 0,
PyUnicode_GET_LENGTH(*filename)-1); PyUnicode_GET_LENGTH(*filename)-1);

File diff suppressed because it is too large Load Diff

View File

@ -265,7 +265,7 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename_str, PyCompilerFlags *f
static int static int
maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit) maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit)
{ {
if (strcmp(ext, ".pyc") == 0 || strcmp(ext, ".pyo") == 0) if (strcmp(ext, ".pyc") == 0)
return 1; return 1;
/* Only look into the file if we are allowed to close it, since /* Only look into the file if we are allowed to close it, since
@ -371,9 +371,6 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
fprintf(stderr, "python: Can't reopen .pyc file\n"); fprintf(stderr, "python: Can't reopen .pyc file\n");
goto done; goto done;
} }
/* Turn on optimization if a .pyo file is given */
if (strcmp(ext, ".pyo") == 0)
Py_OptimizeFlag = 1;
if (set_main_loader(d, filename, "SourcelessFileLoader") < 0) { if (set_main_loader(d, filename, "SourcelessFileLoader") < 0) {
fprintf(stderr, "python: failed to set __main__.__loader__\n"); fprintf(stderr, "python: failed to set __main__.__loader__\n");