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()
Return the magic number for Python bytecode files (a.k.a. :file:`.pyc` and
:file:`.pyo` files). The magic number should be present in the first four bytes
of the bytecode file, in little-endian byte order. Returns -1 on error.
Return the magic number for Python bytecode files (a.k.a. :file:`.pyc` file).
The magic number should be present in the first four bytes of the bytecode
file, in little-endian byte order. Returns -1 on error.
.. versionchanged:: 3.3
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])
Byte-compile a collection of Python source files to either :file:`.pyc` or
:file:`.pyo` files in a :file:`__pycache__` subdirectory (see :pep:`3147`).
Byte-compile a collection of Python source files to :file:`.pyc` files in a
: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
: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``)
* ``2`` - extra optimization (like ``python -OO``)
@ -1222,10 +1222,13 @@ other utility module.
doing, leave it set to ``None``.
.. 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
instead of files without tag in the current directory.
.. versionchanged: 3.5
Create ``.pyc`` files according to :pep:`488`.
.. function:: rfc822_escape(header)

View File

@ -156,8 +156,8 @@ module
pure Python module
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
to as a "pure module."
possibly associated :file:`.pyc` files). Sometimes referred to as a
"pure module."
extension module
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
directory where :file:`setup.py` exists. Generally :file:`setup.py` will be
run from this directory.

View File

@ -93,6 +93,10 @@ compile Python sources.
.. versionchanged:: 3.5
``-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
:func:`compile` function, because the Python interpreter itself already
@ -150,6 +154,10 @@ Public functions
.. versionchanged:: 3.5
*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)
Compile the file with path *fullname*.
@ -182,6 +190,10 @@ Public functions
.. versionchanged:: 3.5
*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)
Byte-compile all the :file:`.py` files found along ``sys.path``. If
@ -196,6 +208,10 @@ Public functions
.. versionchanged:: 3.5
*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/`
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.
The ``cpython-32`` string comes from the current magic tag (see
:func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
:exc:`NotImplementedError` will be raised). The returned path will end in
``.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.
:exc:`NotImplementedError` will be raised). By passing in ``True`` or
``False`` for *debug_override* you can override the system's value for
``__debug__``, leading to optimized bytecode.
*path* need not exist.
@ -218,6 +216,9 @@ file paths.
.. deprecated:: 3.4
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)

View File

@ -711,6 +711,9 @@ find and load modules.
.. versionadded:: 3.3
.. deprecated:: 3.5
Use :attr:`BYTECODE_SUFFIXES` instead.
.. attribute:: OPTIMIZED_BYTECODE_SUFFIXES
A list of strings representing the file suffixes for optimized bytecode
@ -718,14 +721,19 @@ find and load modules.
.. versionadded:: 3.3
.. deprecated:: 3.5
Use :attr:`BYTECODE_SUFFIXES` instead.
.. attribute:: BYTECODE_SUFFIXES
A list of strings representing the recognized file suffixes for bytecode
modules. Set to either :attr:`DEBUG_BYTECODE_SUFFIXES` or
:attr:`OPTIMIZED_BYTECODE_SUFFIXES` based on whether ``__debug__`` is true.
modules (including the leading dot).
.. versionadded:: 3.3
.. versionchanged:: 3.5
The value is no longer dependent on ``__debug__``.
.. attribute:: EXTENSION_SUFFIXES
A list of strings representing the recognized file suffixes for
@ -1074,23 +1082,37 @@ an :term:`importer`.
.. 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
source *path*. For example, if *path* is ``/foo/bar/baz.py`` the return
Return the :pep:`3147`/:pep:`488` path to the byte-compiled file associated
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.
The ``cpython-32`` string comes from the current magic tag (see
:func:`get_tag`; if :attr:`sys.implementation.cache_tag` is not defined then
:exc:`NotImplementedError` will be raised). The returned path will end in
``.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.
:exc:`NotImplementedError` will be raised).
*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
.. versionchanged ::3.5
The *optimization* parameter was added and the *debug_override* parameter
was deprecated.
.. function:: source_from_cache(path)
@ -1098,7 +1120,7 @@ an :term:`importer`.
file path. For example, if *path* is
``/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
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,
: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)
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
written to *cfile*, which defaults to the :PEP:`3147` path, ending in
``.pyc`` (``.pyo`` if optimization is enabled in the current interpreter).
The source code is loaded from the file name *file*. The byte-code is
written to *cfile*, which defaults to the :pep:`3147`/:pep`488` path, ending
in ``.pyc``.
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
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)
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
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
@ -86,4 +86,3 @@ could not be compiled.
Module :mod:`compileall`
Utilities to compile all Python source files in a directory tree.

View File

@ -167,7 +167,7 @@ always available.
.. 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
``False`` depending on the :option:`-B` command line option and the
:envvar:`PYTHONDONTWRITEBYTECODE` environment variable, but you can set it
@ -1231,4 +1231,3 @@ always available.
.. 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\ .

View File

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

View File

@ -405,8 +405,7 @@ The :class:`PyZipFile` constructor takes the same parameters as the
archive.
If the *optimize* parameter to :class:`PyZipFile` was not given or ``-1``,
the corresponding file is a :file:`\*.pyo` file if available, else a
:file:`\*.pyc` file, compiling if necessary.
the corresponding file is a :file:`\*.pyc` file, compiling if necessary.
If the *optimize* parameter to :class:`PyZipFile` was ``0``, ``1`` or
``2``, only files with that optimization level (see :func:`compile`) are
@ -569,4 +568,3 @@ Instances have the following attributes:
.. attribute:: ZipInfo.file_size
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.
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:`.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.
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
>>> jwzthreading.__file__
'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
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`
module in the standard library, the default path entry finders also handle
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__
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
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.
* 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
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
:option:`-O` is used) for all modules in a directory.
* The module :mod:`compileall` can create .pyc files for all modules in a
directory.
* There is more detail on this process, including a flow chart of the
decisions, in PEP 3147.
@ -548,4 +548,3 @@ modules found in a package.
.. [#] In fact function definitions are also 'statements' that are 'executed'; the
execution of a module-level function definition enters the function name in
the module's global symbol table.

View File

@ -199,7 +199,7 @@ Miscellaneous options
.. 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`.

View File

@ -111,7 +111,7 @@ of available options is shown below.
| | launcher is also installed. | |
+---------------------------+--------------------------------------+--------------------------+
| CompileAll | Compile all ``.py`` files to | 0 |
| | ``.pyc`` and ``.pyo``. | |
| | ``.pyc``. | |
+---------------------------+--------------------------------------+--------------------------+
| PrependPath | Add install and Scripts directories | 0 |
| | 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``,
``.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
will be used, and therefore you can use the same facilities described above to
have the script specify the version which should be used.
@ -796,5 +796,3 @@ Other resources
:pep:`397` - Python launcher for Windows
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
(:issue:`19977`).
* :pep:`488`, the elimination of ``.pyo`` files.
Significantly Improved Library Modules:
* None yet.
@ -195,6 +197,24 @@ environment will be used.
: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
======================
@ -535,7 +555,7 @@ The following performance enhancements have been added:
``FindFirstFile``/``FindNextFile`` system calls. (Contributed by
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
allocate memory for these objects.
@ -630,6 +650,8 @@ removed:
3.4, and has now been removed.
(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
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
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
--------------------

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
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
if os.path.isfile(fullname):
if legacy:
cfile = fullname + ('c' if __debug__ else 'o')
cfile = fullname + 'c'
else:
if optimize >= 0:
opt = optimize if optimize >= 1 else ''
cfile = importlib.util.cache_from_source(
fullname, debug_override=not optimize)
fullname, optimization=opt)
else:
cfile = importlib.util.cache_from_source(fullname)
cache_dir = os.path.dirname(cfile)

View File

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

View File

@ -22,15 +22,15 @@ class install_lib(Command):
# possible scenarios:
# 1) no compilation at all (--no-compile --no-optimize)
# 2) compile .pyc only (--compile --no-optimize; default)
# 3) compile .pyc and "level 1" .pyo (--compile --optimize)
# 4) compile "level 1" .pyo only (--no-compile --optimize)
# 5) compile .pyc and "level 2" .pyo (--compile --optimize-more)
# 6) compile "level 2" .pyo only (--no-compile --optimize-more)
# 3) compile .pyc and "opt-1" .pyc (--compile --optimize)
# 4) compile "opt-1" .pyc only (--no-compile --optimize)
# 5) compile .pyc and "opt-2" .pyc (--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
# 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.
user_options = [
@ -166,10 +166,10 @@ class install_lib(Command):
continue
if self.compile:
bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=True))
py_file, optimization=''))
if self.optimize > 0:
bytecode_files.append(importlib.util.cache_from_source(
py_file, debug_override=False))
py_file, optimization=self.optimize))
return bytecode_files

View File

@ -120,8 +120,8 @@ class BuildPyTestCase(support.TempdirManager,
found = os.listdir(cmd.build_lib)
self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
self.assertEqual(sorted(found),
['boiledeggs.%s.pyo' % sys.implementation.cache_tag])
expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag)
self.assertEqual(sorted(found), [expect])
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')
self.write_file(f, '# python file')
cmd.byte_compile([f])
pyc_file = importlib.util.cache_from_source('foo.py',
debug_override=True)
pyo_file = importlib.util.cache_from_source('foo.py',
debug_override=False)
pyc_file = importlib.util.cache_from_source('foo.py', optimization='')
pyc_opt_file = importlib.util.cache_from_source('foo.py',
optimization=cmd.optimize)
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):
project_dir, dist = self.create_dist()
@ -66,8 +65,8 @@ class InstallLibTestCase(support.TempdirManager,
cmd.distribution.packages = ['spam']
cmd.distribution.script_name = 'setup.py'
# get_outputs should return 4 elements: spam/__init__.py, .pyc and
# .pyo, foo.import-tag-abiflags.so / foo.pyd
# get_outputs should return 4 elements: spam/__init__.py and .pyc,
# foo.import-tag-abiflags.so / foo.pyd
outputs = cmd.get_outputs()
self.assertEqual(len(outputs), 4, outputs)

View File

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

View File

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

View File

@ -58,24 +58,23 @@ def new_module(name):
def get_magic():
"""**DEPRECATED**
Return the magic number for .pyc or .pyo files.
Return the magic number for .pyc files.
"""
return util.MAGIC_NUMBER
def get_tag():
"""Return the magic tag for .pyc or .pyo files."""
"""Return the magic tag for .pyc files."""
return sys.implementation.cache_tag
def cache_from_source(path, debug_override=None):
"""**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
.pyc/.pyo file calculated as if the .py file were imported. The extension
will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo.
.pyc file calculated as if the .py file were imported.
If debug_override is not None, then it must be a boolean and is used in
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.
"""
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):
"""**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 .py file calculated to correspond to the .pyc/.pyo file. If path does
The .pyc file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc file. If path does
not conform to PEP 3147 format, ValueError will be raised. If
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
_PYCACHE = '__pycache__'
_OPT = 'opt-'
SOURCE_SUFFIXES = ['.py'] # _setup() adds .pyw as needed.
DEBUG_BYTECODE_SUFFIXES = ['.pyc']
OPTIMIZED_BYTECODE_SUFFIXES = ['.pyo']
BYTECODE_SUFFIXES = ['.pyc']
# Deprecated.
DEBUG_BYTECODE_SUFFIXES = OPTIMIZED_BYTECODE_SUFFIXES = BYTECODE_SUFFIXES
def cache_from_source(path, debug_override=None):
"""Given the path to a .py file, return the path to its .pyc/.pyo file.
def cache_from_source(path, debug_override=None, *, optimization=None):
"""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
.pyc/.pyo file calculated as if the .py file were imported. The extension
will be .pyc unless sys.flags.optimize is non-zero, then it will be .pyo.
.pyc file calculated as if the .py file were imported.
If debug_override is not None, then it must be a boolean and is used in
place of sys.flags.optimize.
The 'optimization' parameter controls the presumed optimization level of
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.
"""
debug = not sys.flags.optimize if debug_override is None else debug_override
if debug:
suffixes = DEBUG_BYTECODE_SUFFIXES
else:
suffixes = OPTIMIZED_BYTECODE_SUFFIXES
if debug_override is not None:
_warnings.warn('the debug_override parameter is deprecated; use '
"'optimization' instead", DeprecationWarning)
if optimization is not None:
message = 'debug_override or optimization must be set to None'
raise TypeError(message)
optimization = '' if debug_override else 1
head, tail = _path_split(path)
base, sep, rest = tail.rpartition('.')
tag = sys.implementation.cache_tag
if tag is None:
raise NotImplementedError('sys.implementation.cache_tag is None')
filename = ''.join([(base if base else rest), sep, tag, suffixes[0]])
return _path_join(head, _PYCACHE, filename)
almost_filename = ''.join([(base if base else rest), sep, tag])
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):
"""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 .py file calculated to correspond to the .pyc/.pyo file. If path does
not conform to PEP 3147 format, ValueError will be raised. If
The .pyc file does not need to exist; this simply returns the path to
the .py file calculated to correspond to the .pyc file. If path does
not conform to PEP 3147/488 format, ValueError will be raised. If
sys.implementation.cache_tag is None then NotImplementedError is raised.
"""
@ -478,9 +497,19 @@ def source_from_cache(path):
if pycache != _PYCACHE:
raise ValueError('{} not bottom-level directory in '
'{!r}'.format(_PYCACHE, path))
if pycache_filename.count('.') != 2:
raise ValueError('expected only 2 dots in '
dot_count = pycache_filename.count('.')
if dot_count not in {2, 3}:
raise ValueError('expected only 2 or 3 dots in '
'{!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]
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.
"""
global _imp, sys, BYTECODE_SUFFIXES
global _imp, sys
_imp = _imp_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.
module_type = type(sys)
for name, module in sys.modules.items():

View File

@ -223,7 +223,7 @@ class ModuleFinder:
if not m.__path__:
return
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
# we cannot separate normal dlls from Python extensions.
suffixes = []

View File

@ -361,7 +361,7 @@ class Directory:
# [(logical, 0, filehash.IntegerData(1),
# filehash.IntegerData(2), filehash.IntegerData(3),
# 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
# slow. So instead, we have to use wildcard remove entries
if file.endswith(".py"):
@ -382,10 +382,9 @@ class Directory:
return files
def remove_pyc(self):
"Remove .pyc/.pyo files on uninstall"
"Remove .pyc files on uninstall"
add_data(self.db, "RemoveFile",
[(self.component+"c", self.component, "*.pyc", self.logical, 2),
(self.component+"o", self.component, "*.pyo", self.logical, 2)])
[(self.component+"c", self.component, "*.pyc", self.logical, 2)])
class Binary:
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.
"""
@ -67,7 +67,7 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
:param file: The source file name.
: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
error messages. Defaults to the source file name.
: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
execution efficiency -- Python itself byte-compiles a module when
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
good idea to byte-compile all modules upon installation, since
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.
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 optimize >= 0:
optimization = optimize if optimize >= 1 else ''
cfile = importlib.util.cache_from_source(file,
debug_override=not optimize)
optimization=optimization)
else:
cfile = importlib.util.cache_from_source(file)
if os.path.islink(cfile):

View File

@ -213,7 +213,7 @@ def classify_class_attrs(object):
def ispackage(path):
"""Guess whether a path refers to a package directory."""
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)):
return True
return False

View File

@ -376,36 +376,32 @@ def rmtree(path):
pass
def make_legacy_pyc(source):
"""Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location.
The choice of .pyc or .pyo extension is done based on the __debug__ flag
value.
"""Move a PEP 3147/488 pyc file to its legacy pyc location.
: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.
"""
pyc_file = importlib.util.cache_from_source(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)
return legacy_pyc
def forget(modname):
"""'Forget' a module was ever imported.
This removes the module from sys.modules and deletes any PEP 3147 or
legacy .pyc and .pyo files.
This removes the module from sys.modules and deletes any PEP 3147/488 or
legacy .pyc files.
"""
unload(modname)
for dirname in sys.path:
source = os.path.join(dirname, modname + '.py')
# 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 + 'o')
unlink(importlib.util.cache_from_source(source, debug_override=True))
unlink(importlib.util.cache_from_source(source, debug_override=False))
for opt in ('', 1, 2):
unlink(importlib.util.cache_from_source(source, optimization=opt))
# Check whether a gui is actually available
def _is_gui_available():

View File

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

View File

@ -101,16 +101,16 @@ class CompileallTests(unittest.TestCase):
def test_optimize(self):
# make sure compiling with different optimization settings than the
# 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)
cached = importlib.util.cache_from_source(self.source_path,
debug_override=not optimize)
optimization=opt)
self.assertTrue(os.path.isfile(cached))
cached2 = importlib.util.cache_from_source(self.source_path2,
debug_override=not optimize)
optimization=opt)
self.assertTrue(os.path.isfile(cached2))
cached3 = importlib.util.cache_from_source(self.source_path3,
debug_override=not optimize)
optimization=opt)
self.assertTrue(os.path.isfile(cached3))
@mock.patch('compileall.ProcessPoolExecutor')
@ -237,11 +237,11 @@ class CommandLineTests(unittest.TestCase):
self.assertNotIn(b'Listing ', quiet)
# 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 [
('normal', 'pyc', []),
('optimize', 'pyo', ['-O']),
('doubleoptimize', 'pyo', ['-OO']),
('optimize', 'opt-1.pyc', ['-O']),
('doubleoptimize', 'opt-2.pyc', ['-OO']),
]:
def f(self, ext=ext, switch=switch):
script_helper.assert_python_ok(*(switch +
@ -258,13 +258,12 @@ class CommandLineTests(unittest.TestCase):
def test_legacy_paths(self):
# 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)
# Verify the __pycache__ directory contents.
self.assertFalse(os.path.exists(self.pkgdir_cachedir))
opt = 'c' if __debug__ else 'o'
expected = sorted(['__init__.py', '__init__.py' + opt, 'bar.py',
'bar.py' + opt])
expected = sorted(['__init__.py', '__init__.pyc', 'bar.py',
'bar.pyc'])
self.assertEqual(sorted(os.listdir(self.pkgdir)), expected)
def test_multiple_runs(self):

View File

@ -111,7 +111,6 @@ class ImportTests(unittest.TestCase):
del sys.path[0]
support.unlink(temp_mod_name + '.py')
support.unlink(temp_mod_name + '.pyc')
support.unlink(temp_mod_name + '.pyo')
def test_issue5604(self):
# Test cannot cover imp.load_compiled function.
@ -194,7 +193,7 @@ class ImportTests(unittest.TestCase):
self.assertEqual(package.b, 2)
finally:
del sys.path[0]
for ext in ('.py', '.pyc', '.pyo'):
for ext in ('.py', '.pyc'):
support.unlink(temp_mod_name + ext)
support.unlink(init_file_name + ext)
support.rmtree(test_package_name)
@ -346,56 +345,6 @@ class PEP3147Tests(unittest.TestCase):
'qux.{}.pyc'.format(self.tag))
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,
'requires sys.implementation.cache_tag to not be '
'None')
@ -407,68 +356,6 @@ class PEP3147Tests(unittest.TestCase):
expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
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):
@unittest.skipIf(support.TESTFN_UNENCODABLE is None,

View File

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

View File

@ -5,6 +5,7 @@ machinery = util.import_importlib('importlib.machinery')
importlib_util = util.import_importlib('importlib.util')
import os
import string
import sys
from test import support
import types
@ -562,7 +563,8 @@ class PEP3147Tests:
path = os.path.join('foo', 'bar', 'baz', 'qux.py')
expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
'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):
# No cache tag means NotImplementedError.
@ -575,43 +577,103 @@ class PEP3147Tests:
path = os.path.join('foo.bar', 'file')
expect = os.path.join('foo.bar', '__pycache__',
'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):
# Given the path to a .py file, return the path to its PEP 3147
# defined .pyo file (i.e. under __pycache__).
def test_cache_from_source_debug_override(self):
# Given the path to a .py file, return the path to its PEP 3147/PEP 488
# defined .pyc 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(self.util.cache_from_source(path, False), expect)
with warnings.catch_warnings():
warnings.simplefilter('ignore')
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):
path = 'foo.py'
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):
# 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(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
# propagates.
class Bearish:
def __bool__(self): raise RuntimeError
with self.assertRaises(RuntimeError):
self.util.cache_from_source('/foo/bar/baz.py', Bearish())
with warnings.catch_warnings():
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 == '/',
'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(
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))
@unittest.skipUnless(sys.implementation.cache_tag is not None,
@ -649,7 +711,12 @@ class PEP3147Tests:
ValueError, self.util.source_from_cache, '__pycache__/foo.pyc')
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(
ValueError, self.util.source_from_cache,
'__pycache__/foo.cpython-32.foo.pyc')
@ -660,6 +727,17 @@ class PEP3147Tests:
ValueError, self.util.source_from_cache,
'/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,
Source_PEP3147Tests

View File

@ -119,6 +119,10 @@ class PyCompileTests(unittest.TestCase):
self.assertTrue(os.path.exists(cache_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__":
unittest.main()

View File

@ -269,7 +269,7 @@ class RunModuleTestCase(unittest.TestCase, CodeExecutionMixin):
if verbose > 1: print(ex) # Persist with cleaning up
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["__cached__"] = ns["__file__"]
spec = ns["__spec__"]

View File

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

View File

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

View File

@ -689,7 +689,7 @@ class PyZipFileTests(unittest.TestCase):
self.requiresWriteAccess(os.path.dirname(__file__))
with TemporaryFile() as t, zipfile.PyZipFile(t, "w") as zipfp:
fn = __file__
if fn.endswith('.pyc') or fn.endswith('.pyo'):
if fn.endswith('.pyc'):
path_split = fn.split(os.sep)
if os.altsep is not None:
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:
fn = __file__
if fn.endswith(('.pyc', '.pyo')):
if fn.endswith('.pyc'):
fn = fn[:-1]
zipfp.writepy(fn, "testpackage")
@ -762,10 +762,8 @@ class PyZipFileTests(unittest.TestCase):
import email
packagedir = os.path.dirname(email.__file__)
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
ext = '.pyo' if optlevel == 1 else '.pyc'
ext = '.pyc'
with TemporaryFile() as t, \
zipfile.PyZipFile(t, "w", optimize=optlevel) as zipfp:
@ -839,11 +837,10 @@ class PyZipFileTests(unittest.TestCase):
self.assertIn("SyntaxError", s.getvalue())
# 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()
self.assertIn('mod1.py', names)
self.assertNotIn('mod1.pyc', names)
self.assertNotIn('mod1.pyo', names)
finally:
rmtree(TESTFN2)

View File

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

View File

@ -1852,7 +1852,7 @@ class Tk(Misc, Wm):
import os
baseName = os.path.basename(sys.argv[0])
baseName, ext = os.path.splitext(baseName)
if ext not in ('.py', '.pyc', '.pyo'):
if ext not in ('.py', '.pyc'):
baseName = baseName + ext
interactive = 0
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):
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 False

View File

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

View File

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

View File

@ -14,9 +14,9 @@ from . import case, suite, util
__unittest = True
# what about .pyc or .pyo (etc)
# what about .pyc (etc)
# 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)

View File

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

View File

@ -1731,7 +1731,7 @@ class PyZipFile(ZipFile):
the modules into the archive. If pathname is a plain
directory, listdir *.py and enter all modules. Else, pathname
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
necessary.
If filterfunc(pathname) is given, it is called with every argument.
@ -1824,46 +1824,59 @@ class PyZipFile(ZipFile):
file_py = pathname + ".py"
file_pyc = pathname + ".pyc"
file_pyo = pathname + ".pyo"
pycache_pyc = importlib.util.cache_from_source(file_py, True)
pycache_pyo = importlib.util.cache_from_source(file_py, False)
pycache_opt0 = importlib.util.cache_from_source(file_py, optimization='')
pycache_opt1 = importlib.util.cache_from_source(file_py, optimization=1)
pycache_opt2 = importlib.util.cache_from_source(file_py, optimization=2)
if self._optimize == -1:
# legacy mode: use whatever file is present
if (os.path.isfile(file_pyo) 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
if (os.path.isfile(file_pyc) and
os.stat(file_pyc).st_mtime >= os.stat(file_py).st_mtime):
# Use .pyc file.
arcname = fname = file_pyc
elif (os.path.isfile(pycache_pyc) and
os.stat(pycache_pyc).st_mtime >= os.stat(file_py).st_mtime):
elif (os.path.isfile(pycache_opt0) and
os.stat(pycache_opt0).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_pyc
fname = pycache_opt0
arcname = file_pyc
elif (os.path.isfile(pycache_pyo) and
os.stat(pycache_pyo).st_mtime >= os.stat(file_py).st_mtime):
# Use the __pycache__/*.pyo file, but write it to the legacy pyo
elif (os.path.isfile(pycache_opt1) and
os.stat(pycache_opt1).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_pyo
arcname = file_pyo
fname = pycache_opt1
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:
# Compile py into PEP 3147 pyc file.
if _compile(file_py):
fname = (pycache_pyc if __debug__ else pycache_pyo)
arcname = (file_pyc if __debug__ else file_pyo)
if sys.flags.optimize == 0:
fname = pycache_opt0
elif sys.flags.optimize == 1:
fname = pycache_opt1
else:
fname = pycache_opt2
arcname = file_pyc
else:
fname = arcname = file_py
else:
# new mode: use given optimization level
if self._optimize == 0:
fname = pycache_pyc
fname = pycache_opt0
arcname = file_pyc
else:
fname = pycache_pyo
arcname = file_pyo
arcname = file_pyc
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
os.stat(fname).st_mtime >= os.stat(file_py).st_mtime):
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 \
-x 'bad_coding|badsyntax|site-packages|lib2to3/tests/data' \
$(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 \
-d $(LIBDEST)/site-packages -f \
-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 \
-d $(LIBDEST)/site-packages -f \
-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) \
$(PYTHON_FOR_BUILD) -m lib2to3.pgen2.driver $(DESTDIR)$(LIBDEST)/lib2to3/Grammar.txt
-PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \

View File

@ -10,6 +10,8 @@ Release date: XXX
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
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.
See the internal documentation for hints.
.PP
Documentation for installed Python modules and packages can be
viewed by running the
Documentation for installed Python modules and packages can be
viewed by running the
.B pydoc
program.
program.
.SH COMMAND LINE OPTIONS
.TP
.B \-B
@ -150,23 +150,20 @@ Further restrictions may be imposed to prevent the user from injecting
malicious code.
.TP
.BI "\-m " module-name
Searches
.I sys.path
for the named module and runs the corresponding
.I .py
Searches
.I sys.path
for the named module and runs the corresponding
.I .py
file as a script.
.TP
.B \-O
Turn on basic optimizations. This changes the filename extension for
compiled (bytecode) files from
.I .pyc
to \fI.pyo\fP. Given twice, causes docstrings to be discarded.
Turn on basic optimizations. Given twice, causes docstrings to be discarded.
.TP
.B \-OO
Discard docstrings in addition to the \fB-O\fP optimizations.
.TP
.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.
.TP
.B \-s
@ -193,7 +190,7 @@ The text I/O layer will still be line-buffered.
.B \-v
Print a message each time a module is initialized, showing the place
(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
at exit.
.TP
@ -418,7 +415,7 @@ the \fB\-u\fP option.
.IP PYTHONVERBOSE
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
specifying \fB\-v\fP multiple times.
specifying \fB\-v\fP multiple times.
.IP PYTHONWARNINGS
If this is set to a comma-separated string it is equivalent to
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
ismodule(wchar_t *filename) /* Is module -- check for .pyc/.pyo too */
ismodule(wchar_t *filename) /* Is module -- check for .pyc too */
{
if (isfile(filename))
return 1;
/* Check for the compiled version of prefix. */
if (wcslen(filename) < MAXPATHLEN) {
wcscat(filename, Py_OptimizeFlag ? L"o" : L"c");
wcscat(filename, L"c");
if (isfile(filename))
return 1;
}
@ -891,4 +891,3 @@ Py_GetProgramFullPath(void)
#ifdef __cplusplus
}
#endif

View File

@ -20,15 +20,13 @@ _Py_IDENTIFIER(replace);
/* zip_searchorder defines how we search for a module in the Zip
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,
'/' is replaced by SEP there. */
static struct st_zip_searchorder zip_searchorder[] = {
{"/__init__.pyc", IS_PACKAGE | IS_BYTECODE},
{"/__init__.pyo", IS_PACKAGE | IS_BYTECODE},
{"/__init__.py", IS_PACKAGE | IS_SOURCE},
{".pyc", IS_BYTECODE},
{".pyo", IS_BYTECODE},
{".py", IS_SOURCE},
{"", 0}
};
@ -1318,7 +1316,7 @@ parse_dostime(int dostime, int dosdate)
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
is available. */
static time_t
@ -1481,17 +1479,6 @@ PyInit_zipimport(void)
/* Correct directory separator */
zip_searchorder[0].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);
if (mod == NULL)

View File

@ -563,13 +563,12 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
data = PyUnicode_DATA(*filename);
#define ascii_lower(c) ((c <= 127) ? Py_TOLOWER(c) : 0)
/* if filename.lower().endswith((".pyc", ".pyo")): */
/* if filename.lower().endswith(".pyc"): */
if (len >= 4 &&
PyUnicode_READ(kind, data, len-4) == '.' &&
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-1)) == 'c' ||
ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'o'))
ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'c')
{
*filename = PyUnicode_Substring(*filename, 0,
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
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;
/* 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");
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) {
fprintf(stderr, "python: failed to set __main__.__loader__\n");