Skip to content

Commit 8334fd9

Browse files
committed
Add an "optimize" parameter to compile() to control the optimization level, and provide an interface to it in py_compile, compileall and PyZipFile.
1 parent 427d314 commit 8334fd9

17 files changed

Lines changed: 283 additions & 100 deletions

Doc/c-api/veryhigh.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ the same library that the Python runtime is using.
230230
231231
.. c:function:: PyObject* Py_CompileStringFlags(const char *str, const char *filename, int start, PyCompilerFlags *flags)
232232
233+
This is a simplified interface to :c:func:`Py_CompileStringExFlags` below, with
234+
*optimize* set to ``-1``.
235+
236+
237+
.. c:function:: PyObject* Py_CompileStringExFlags(const char *str, const char *filename, int start, PyCompilerFlags *flags, int optimize)
238+
233239
Parse and compile the Python source code in *str*, returning the resulting code
234240
object. The start token is given by *start*; this can be used to constrain the
235241
code which can be compiled and should be :const:`Py_eval_input`,
@@ -238,6 +244,14 @@ the same library that the Python runtime is using.
238244
:exc:`SyntaxError` exception messages. This returns *NULL* if the code cannot
239245
be parsed or compiled.
240246
247+
The integer *optimize* specifies the optimization level of the compiler; a
248+
value of ``-1`` selects the optimization level of the interpreter as given by
249+
:option:`-O` options. Explicit levels are ``0`` (no optimization;
250+
``__debug__`` is true), ``1`` (asserts are removed, ``__debug__`` is false)
251+
or ``2`` (docstrings are removed too).
252+
253+
.. versionadded:: 3.2
254+
241255
242256
.. c:function:: PyObject* PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
243257

Doc/library/compileall.rst

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ compile Python sources.
5858
Public functions
5959
----------------
6060

61-
.. function:: compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None, quiet=False, legacy=False)
61+
.. function:: compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None, quiet=False, legacy=False, optimize=-1)
6262

6363
Recursively descend the directory tree named by *dir*, compiling all :file:`.py`
6464
files along the way. The *maxlevels* parameter is used to limit the depth of
@@ -76,14 +76,23 @@ Public functions
7676
If *legacy* is true, old-style ``.pyc`` file path names are written,
7777
otherwise (the default), :pep:`3147`-style path names are written.
7878

79+
*optimize* specifies the optimization level for the compiler. It is passed to
80+
the built-in :func:`compile` function.
7981

80-
.. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, legacy=False)
82+
.. versionchanged:: 3.2
83+
Added the *optimize* parameter.
84+
85+
86+
.. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, legacy=False, optimize=-1)
8187

8288
Byte-compile all the :file:`.py` files found along ``sys.path``. If
8389
*skip_curdir* is true (the default), the current directory is not included in
84-
the search. The *maxlevels* parameter defaults to ``0``, and the *force*
85-
and *legacy* parameters default to ``False``. All are
86-
passed to the :func:`compile_dir` function.
90+
the search. All other parameters are passed to the :func:`compile_dir`
91+
function.
92+
93+
.. versionchanged:: 3.2
94+
Added the *optimize* parameter.
95+
8796

8897
To force a recompile of all the :file:`.py` files in the :file:`Lib/`
8998
subdirectory and all its subdirectories::

Doc/library/functions.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ are always available. They are listed here in alphabetical order.
174174
type hierarchy in :ref:`types`.
175175

176176

177-
.. function:: compile(source, filename, mode, flags=0, dont_inherit=False)
177+
.. function:: compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
178178

179179
Compile the *source* into a code or AST object. Code objects can be executed
180180
by :func:`exec` or :func:`eval`. *source* can either be a string or an AST
@@ -206,6 +206,12 @@ are always available. They are listed here in alphabetical order.
206206
can be found as the :attr:`compiler_flag` attribute on the :class:`_Feature`
207207
instance in the :mod:`__future__` module.
208208

209+
The argument *optimize* specifies the optimization level of the compiler; the
210+
default value of ``-1`` selects the optimization level of the interpreter as
211+
given by :option:`-O` options. Explicit levels are ``0`` (no optimization;
212+
``__debug__`` is true), ``1`` (asserts are removed, ``__debug__`` is false)
213+
or ``2`` (docstrings are removed too).
214+
209215
This function raises :exc:`SyntaxError` if the compiled source is invalid,
210216
and :exc:`TypeError` if the source contains null bytes.
211217

@@ -218,7 +224,7 @@ are always available. They are listed here in alphabetical order.
218224

219225
.. versionchanged:: 3.2
220226
Allowed use of Windows and Mac newlines. Also input in ``'exec'`` mode
221-
does not have to end in a newline anymore.
227+
does not have to end in a newline anymore. Added the *optimize* parameter.
222228

223229

224230
.. function:: complex([real[, imag]])

Doc/library/py_compile.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ byte-code cache files in the directory containing the source code.
2222
Exception raised when an error occurs while attempting to compile the file.
2323

2424

25-
.. function:: compile(file, cfile=None, dfile=None, doraise=False)
25+
.. function:: compile(file, cfile=None, dfile=None, doraise=False, optimize=-1)
2626

2727
Compile a source file to byte-code and write out the byte-code cache file. The
2828
source code is loaded from the file name *file*. The byte-code is written to
@@ -37,6 +37,13 @@ byte-code cache files in the directory containing the source code.
3737
returns the path to byte-compiled file, i.e. whatever *cfile* value was
3838
used.
3939

40+
*optimize* controls the optimization level and is passed to the built-in
41+
:func:`compile` function. The default of ``-1`` selects the optimization
42+
level of the current interpreter.
43+
44+
.. versionchanged:: 3.2
45+
Added the *optimize* parameter.
46+
4047

4148
.. function:: main(args=None)
4249

Doc/library/zipfile.rst

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ The module defines the following items:
5151

5252

5353
.. class:: PyZipFile
54+
:noindex:
5455

5556
Class for creating ZIP archives containing Python libraries.
5657

@@ -318,37 +319,53 @@ The following data attributes are also available:
318319
string no longer than 65535 bytes. Comments longer than this will be
319320
truncated in the written archive when :meth:`ZipFile.close` is called.
320321

322+
321323
.. _pyzipfile-objects:
322324

323325
PyZipFile Objects
324326
-----------------
325327

326328
The :class:`PyZipFile` constructor takes the same parameters as the
327-
:class:`ZipFile` constructor. Instances have one method in addition to those of
328-
:class:`ZipFile` objects.
329-
330-
331-
.. method:: PyZipFile.writepy(pathname, basename='')
332-
333-
Search for files :file:`\*.py` and add the corresponding file to the archive.
334-
The corresponding file is a :file:`\*.pyo` file if available, else a
335-
:file:`\*.pyc` file, compiling if necessary. If the pathname is a file, the
336-
filename must end with :file:`.py`, and just the (corresponding
337-
:file:`\*.py[co]`) file is added at the top level (no path information). If the
338-
pathname is a file that does not end with :file:`.py`, a :exc:`RuntimeError`
339-
will be raised. If it is a directory, and the directory is not a package
340-
directory, then all the files :file:`\*.py[co]` are added at the top level. If
341-
the directory is a package directory, then all :file:`\*.py[co]` are added under
342-
the package name as a file path, and if any subdirectories are package
343-
directories, all of these are added recursively. *basename* is intended for
344-
internal use only. The :meth:`writepy` method makes archives with file names
345-
like this::
346-
347-
string.pyc # Top level name
348-
test/__init__.pyc # Package directory
349-
test/testall.pyc # Module test.testall
350-
test/bogus/__init__.pyc # Subpackage directory
351-
test/bogus/myfile.pyc # Submodule test.bogus.myfile
329+
:class:`ZipFile` constructor, and one additional parameter, *optimize*.
330+
331+
.. class:: PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=False, \
332+
optimize=-1)
333+
334+
.. versionadded:: 3.2
335+
The *optimize* parameter.
336+
337+
Instances have one method in addition to those of :class:`ZipFile` objects:
338+
339+
.. method:: PyZipFile.writepy(pathname, basename='')
340+
341+
Search for files :file:`\*.py` and add the corresponding file to the
342+
archive.
343+
344+
If the *optimize* parameter to :class:`PyZipFile` was not given or ``-1``,
345+
the corresponding file is a :file:`\*.pyo` file if available, else a
346+
:file:`\*.pyc` file, compiling if necessary.
347+
348+
If the *optimize* parameter to :class:`PyZipFile` was ``0``, ``1`` or
349+
``2``, only files with that optimization level (see :func:`compile`) are
350+
added to the archive, compiling if necessary.
351+
352+
If the pathname is a file, the filename must end with :file:`.py`, and
353+
just the (corresponding :file:`\*.py[co]`) file is added at the top level
354+
(no path information). If the pathname is a file that does not end with
355+
:file:`.py`, a :exc:`RuntimeError` will be raised. If it is a directory,
356+
and the directory is not a package directory, then all the files
357+
:file:`\*.py[co]` are added at the top level. If the directory is a
358+
package directory, then all :file:`\*.py[co]` are added under the package
359+
name as a file path, and if any subdirectories are package directories,
360+
all of these are added recursively. *basename* is intended for internal
361+
use only. The :meth:`writepy` method makes archives with file names like
362+
this::
363+
364+
string.pyc # Top level name
365+
test/__init__.pyc # Package directory
366+
test/testall.pyc # Module test.testall
367+
test/bogus/__init__.pyc # Subpackage directory
368+
test/bogus/myfile.pyc # Submodule test.bogus.myfile
352369

353370

354371
.. _zipinfo-objects:

Include/compile.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ typedef struct {
2929
#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"
3030

3131
struct _mod; /* Declare the existence of this type */
32-
PyAPI_FUNC(PyCodeObject *) PyAST_Compile(struct _mod *, const char *,
33-
PyCompilerFlags *, PyArena *);
32+
#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)
33+
PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(struct _mod *, const char *,
34+
PyCompilerFlags *, int, PyArena *);
3435
PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(struct _mod *, const char *);
3536

3637

Include/pythonrun.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ PyAPI_FUNC(PyObject *) PyRun_FileExFlags(FILE *, const char *, int,
7676
#ifdef Py_LIMITED_API
7777
PyAPI_FUNC(PyObject *) Py_CompileStringFlags(const char *, const char *, int);
7878
#else
79-
#define Py_CompileString(str, p, s) Py_CompileStringFlags(str, p, s, NULL)
80-
PyAPI_FUNC(PyObject *) Py_CompileStringFlags(const char *, const char *, int,
81-
PyCompilerFlags *);
79+
#define Py_CompileString(str, p, s) Py_CompileStringExFlags(str, p, s, NULL, -1)
80+
#define Py_CompileStringFlags(str, p, s, f) Py_CompileStringExFlags(str, p, s, f, -1)
81+
PyAPI_FUNC(PyObject *) Py_CompileStringExFlags(const char *, const char *, int,
82+
PyCompilerFlags *, int);
8283
#endif
8384
PyAPI_FUNC(struct symtable *) Py_SymtableString(const char *, const char *, int);
8485

Lib/compileall.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
__all__ = ["compile_dir","compile_file","compile_path"]
2121

22-
def compile_dir(dir, maxlevels=10, ddir=None,
23-
force=False, rx=None, quiet=False, legacy=False):
22+
def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
23+
quiet=False, legacy=False, optimize=-1):
2424
"""Byte-compile all modules in the given directory tree.
2525
2626
Arguments (only dir is required):
@@ -32,6 +32,7 @@ def compile_dir(dir, maxlevels=10, ddir=None,
3232
force: if True, force compilation, even if timestamps are up-to-date
3333
quiet: if True, be quiet during compilation
3434
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
35+
optimize: optimization level or -1 for level of the interpreter
3536
"""
3637
if not quiet:
3738
print('Listing', dir, '...')
@@ -51,7 +52,8 @@ def compile_dir(dir, maxlevels=10, ddir=None,
5152
else:
5253
dfile = None
5354
if not os.path.isdir(fullname):
54-
if not compile_file(fullname, ddir, force, rx, quiet, legacy):
55+
if not compile_file(fullname, ddir, force, rx, quiet,
56+
legacy, optimize):
5557
success = 0
5658
elif (maxlevels > 0 and name != os.curdir and name != os.pardir and
5759
os.path.isdir(fullname) and not os.path.islink(fullname)):
@@ -61,14 +63,15 @@ def compile_dir(dir, maxlevels=10, ddir=None,
6163
return success
6264

6365
def compile_file(fullname, ddir=None, force=0, rx=None, quiet=False,
64-
legacy=False):
66+
legacy=False, optimize=-1):
6567
"""Byte-compile file.
6668
fullname: the file to byte-compile
6769
ddir: if given, purported directory name (this is the
6870
directory name that will show up in error messages)
6971
force: if True, force compilation, even if timestamps are up-to-date
7072
quiet: if True, be quiet during compilation
7173
legacy: if True, produce legacy pyc paths instead of PEP 3147 paths
74+
optimize: optimization level or -1 for level of the interpreter
7275
"""
7376
success = 1
7477
name = os.path.basename(fullname)
@@ -84,7 +87,11 @@ def compile_file(fullname, ddir=None, force=0, rx=None, quiet=False,
8487
if legacy:
8588
cfile = fullname + ('c' if __debug__ else 'o')
8689
else:
87-
cfile = imp.cache_from_source(fullname)
90+
if optimize >= 0:
91+
cfile = imp.cache_from_source(fullname,
92+
debug_override=not optimize)
93+
else:
94+
cfile = imp.cache_from_source(fullname)
8895
cache_dir = os.path.dirname(cfile)
8996
head, tail = name[:-3], name[-3:]
9097
if tail == '.py':
@@ -101,7 +108,8 @@ def compile_file(fullname, ddir=None, force=0, rx=None, quiet=False,
101108
if not quiet:
102109
print('Compiling', fullname, '...')
103110
try:
104-
ok = py_compile.compile(fullname, cfile, dfile, True)
111+
ok = py_compile.compile(fullname, cfile, dfile, True,
112+
optimize=optimize)
105113
except py_compile.PyCompileError as err:
106114
if quiet:
107115
print('*** Error compiling', fullname, '...')
@@ -126,7 +134,7 @@ def compile_file(fullname, ddir=None, force=0, rx=None, quiet=False,
126134
return success
127135

128136
def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
129-
legacy=False):
137+
legacy=False, optimize=-1):
130138
"""Byte-compile all module on sys.path.
131139
132140
Arguments (all optional):
@@ -136,6 +144,7 @@ def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
136144
force: as for compile_dir() (default False)
137145
quiet: as for compile_dir() (default False)
138146
legacy: as for compile_dir() (default False)
147+
optimize: as for compile_dir() (default -1)
139148
"""
140149
success = 1
141150
for dir in sys.path:
@@ -144,7 +153,7 @@ def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
144153
else:
145154
success = success and compile_dir(dir, maxlevels, None,
146155
force, quiet=quiet,
147-
legacy=legacy)
156+
legacy=legacy, optimize=optimize)
148157
return success
149158

150159

Lib/py_compile.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def wr_long(f, x):
7272
(x >> 16) & 0xff,
7373
(x >> 24) & 0xff]))
7474

75-
def compile(file, cfile=None, dfile=None, doraise=False):
75+
def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
7676
"""Byte-compile one Python source file to Python bytecode.
7777
7878
:param file: The source file name.
@@ -86,6 +86,10 @@ def compile(file, cfile=None, dfile=None, doraise=False):
8686
will be printed, and the function will return to the caller. If an
8787
exception occurs and this flag is set to True, a PyCompileError
8888
exception will be raised.
89+
:param optimize: The optimization level for the compiler. Valid values
90+
are -1, 0, 1 and 2. A value of -1 means to use the optimization
91+
level of the current interpreter, as given by -O command line options.
92+
8993
:return: Path to the resulting byte compiled file.
9094
9195
Note that it isn't necessary to byte-compile Python modules for
@@ -111,7 +115,8 @@ def compile(file, cfile=None, dfile=None, doraise=False):
111115
timestamp = int(os.stat(file).st_mtime)
112116
codestring = f.read()
113117
try:
114-
codeobject = builtins.compile(codestring, dfile or file,'exec')
118+
codeobject = builtins.compile(codestring, dfile or file, 'exec',
119+
optimize=optimize)
115120
except Exception as err:
116121
py_exc = PyCompileError(err.__class__, err, dfile or file)
117122
if doraise:
@@ -120,7 +125,10 @@ def compile(file, cfile=None, dfile=None, doraise=False):
120125
sys.stderr.write(py_exc.msg + '\n')
121126
return
122127
if cfile is None:
123-
cfile = imp.cache_from_source(file)
128+
if optimize >= 0:
129+
cfile = imp.cache_from_source(file, debug_override=not optimize)
130+
else:
131+
cfile = imp.cache_from_source(file)
124132
try:
125133
os.makedirs(os.path.dirname(cfile))
126134
except OSError as error:

0 commit comments

Comments
 (0)