https://github.com/python/cpython/commit/d8e6bdc0d083f4e76ac49574544555ad91257592
commit: d8e6bdc0d083f4e76ac49574544555ad91257592
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-11-13T13:21:32+02:00
summary:
gh-135801: Add the module parameter to compile() etc (GH-139652)
Many functions related to compiling or parsing Python code, such as
compile(), ast.parse(), symtable.symtable(),
and importlib.abc.InspectLoader.source_to_code() now allow to pass
the module name used when filtering syntax warnings.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-14-19-47.gh-issue-135801.OhxEZS.rst
M Doc/library/ast.rst
M Doc/library/functions.rst
M Doc/library/importlib.rst
M Doc/library/symtable.rst
M Doc/whatsnew/3.15.rst
M Include/internal/pycore_compile.h
M Include/internal/pycore_parser.h
M Include/internal/pycore_pyerrors.h
M Include/internal/pycore_pythonrun.h
M Include/internal/pycore_symtable.h
M Lib/ast.py
M Lib/importlib/_bootstrap_external.py
M Lib/importlib/abc.py
M Lib/modulefinder.py
M Lib/profiling/sampling/_sync_coordinator.py
M Lib/profiling/tracing/__init__.py
M Lib/runpy.py
M Lib/symtable.py
M Lib/test/test_ast/test_ast.py
M Lib/test/test_builtin.py
M Lib/test/test_cmd_line_script.py
M Lib/test/test_compile.py
M Lib/test/test_import/__init__.py
M Lib/test/test_runpy.py
M Lib/test/test_symtable.py
M Lib/test/test_zipimport_support.py
M Lib/zipimport.py
M Modules/clinic/symtablemodule.c.h
M Modules/symtablemodule.c
M Parser/lexer/state.c
M Parser/lexer/state.h
M Parser/peg_api.c
M Parser/pegen.c
M Parser/pegen.h
M Parser/string_parser.c
M Parser/tokenizer/helpers.c
M Programs/_freeze_module.py
M Programs/freeze_test_frozenmain.py
M Python/ast_preprocess.c
M Python/bltinmodule.c
M Python/clinic/bltinmodule.c.h
M Python/compile.c
M Python/errors.c
M Python/pythonrun.c
M Python/symtable.c
M Tools/peg_generator/peg_extension/peg_extension.c
diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst
index 494621672171f2..0ea3c3c59a660d 100644
--- a/Doc/library/ast.rst
+++ b/Doc/library/ast.rst
@@ -2205,10 +2205,10 @@ Async and await
Apart from the node classes, the :mod:`ast` module defines these utility
functions
and classes for traversing abstract syntax trees:
-.. function:: parse(source, filename='<unknown>', mode='exec', *,
type_comments=False, feature_version=None, optimize=-1)
+.. function:: parse(source, filename='<unknown>', mode='exec', *,
type_comments=False, feature_version=None, optimize=-1, module=None)
Parse the source into an AST node. Equivalent to ``compile(source,
- filename, mode, flags=FLAGS_VALUE, optimize=optimize)``,
+ filename, mode, flags=FLAGS_VALUE, optimize=optimize, module=module)``,
where ``FLAGS_VALUE`` is ``ast.PyCF_ONLY_AST`` if ``optimize <= 0``
and ``ast.PyCF_OPTIMIZED_AST`` otherwise.
@@ -2261,6 +2261,9 @@ and classes for traversing abstract syntax trees:
The minimum supported version for ``feature_version`` is now ``(3, 7)``.
The ``optimize`` argument was added.
+ .. versionadded:: next
+ Added the *module* parameter.
+
.. function:: unparse(ast_obj)
diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index e98793975556ef..3257daf89d327b 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -292,7 +292,9 @@ are always available. They are listed here in alphabetical
order.
:func:`property`.
-.. function:: compile(source, filename, mode, flags=0, dont_inherit=False,
optimize=-1)
+.. function:: compile(source, filename, mode, flags=0, \
+ dont_inherit=False, optimize=-1, \
+ *, module=None)
Compile the *source* into a code or AST object. Code objects can be
executed
by :func:`exec` or :func:`eval`. *source* can either be a normal string, a
@@ -334,6 +336,10 @@ are always available. They are listed here in
alphabetical order.
``__debug__`` is true), ``1`` (asserts are removed, ``__debug__`` is false)
or ``2`` (docstrings are removed too).
+ The optional argument *module* specifies the module name.
+ It is needed to unambiguous :ref:`filter <warning-filter>` syntax warnings
+ by module name.
+
This function raises :exc:`SyntaxError` if the compiled source is invalid,
and :exc:`ValueError` if the source contains null bytes.
@@ -371,6 +377,9 @@ are always available. They are listed here in alphabetical
order.
``ast.PyCF_ALLOW_TOP_LEVEL_AWAIT`` can now be passed in flags to enable
support for top-level ``await``, ``async for``, and ``async with``.
+ .. versionadded:: next
+ Added the *module* parameter.
+
.. class:: complex(number=0, /)
complex(string, /)
diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
index 602a7100a12350..03ba23b6216cbf 100644
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -459,7 +459,7 @@ ABC hierarchy::
.. versionchanged:: 3.4
Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
- .. staticmethod:: source_to_code(data, path='<string>')
+ .. staticmethod:: source_to_code(data, path='<string>', fullname=None)
Create a code object from Python source.
@@ -471,11 +471,19 @@ ABC hierarchy::
With the subsequent code object one can execute it in a module by
running ``exec(code, module.__dict__)``.
+ The optional argument *fullname* specifies the module name.
+ It is needed to unambiguous :ref:`filter <warning-filter>` syntax
+ warnings by module name.
+
.. versionadded:: 3.4
.. versionchanged:: 3.5
Made the method static.
+ .. versionadded:: next
+ Added the *fullname* parameter.
+
+
.. method:: exec_module(module)
Implementation of :meth:`Loader.exec_module`.
diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst
index 54e19af4bd69a6..c0d9e79197de7c 100644
--- a/Doc/library/symtable.rst
+++ b/Doc/library/symtable.rst
@@ -21,11 +21,17 @@ tables.
Generating Symbol Tables
------------------------
-.. function:: symtable(code, filename, compile_type)
+.. function:: symtable(code, filename, compile_type, *, module=None)
Return the toplevel :class:`SymbolTable` for the Python source *code*.
*filename* is the name of the file containing the code. *compile_type* is
like the *mode* argument to :func:`compile`.
+ The optional argument *module* specifies the module name.
+ It is needed to unambiguous :ref:`filter <warning-filter>` syntax warnings
+ by module name.
+
+ .. versionadded:: next
+ Added the *module* parameter.
Examining Symbol Tables
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index c6089f63dee2cb..3cb766978a7217 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -307,6 +307,13 @@ Other language changes
not only integers or floats, although this does not improve precision.
(Contributed by Serhiy Storchaka in :gh:`67795`.)
+* Many functions related to compiling or parsing Python code, such as
+ :func:`compile`, :func:`ast.parse`, :func:`symtable.symtable`,
+ and :func:`importlib.abc.InspectLoader.source_to_code`, now allow to pass
+ the module name. It is needed to unambiguous :ref:`filter <warning-filter>`
+ syntax warnings by module name.
+ (Contributed by Serhiy Storchaka in :gh:`135801`.)
+
New modules
===========
diff --git a/Include/internal/pycore_compile.h
b/Include/internal/pycore_compile.h
index 1c60834fa2058c..527141b54d0dca 100644
--- a/Include/internal/pycore_compile.h
+++ b/Include/internal/pycore_compile.h
@@ -32,7 +32,8 @@ PyAPI_FUNC(PyCodeObject*) _PyAST_Compile(
PyObject *filename,
PyCompilerFlags *flags,
int optimize,
- struct _arena *arena);
+ struct _arena *arena,
+ PyObject *module);
/* AST preprocessing */
extern int _PyCompile_AstPreprocess(
@@ -41,7 +42,8 @@ extern int _PyCompile_AstPreprocess(
PyCompilerFlags *flags,
int optimize,
struct _arena *arena,
- int syntax_check_only);
+ int syntax_check_only,
+ PyObject *module);
extern int _PyAST_Preprocess(
struct _mod *,
@@ -50,7 +52,8 @@ extern int _PyAST_Preprocess(
int optimize,
int ff_features,
int syntax_check_only,
- int enable_warnings);
+ int enable_warnings,
+ PyObject *module);
typedef struct {
diff --git a/Include/internal/pycore_parser.h b/Include/internal/pycore_parser.h
index 2885dee63dcf94..2c46f59ab7da9f 100644
--- a/Include/internal/pycore_parser.h
+++ b/Include/internal/pycore_parser.h
@@ -48,7 +48,8 @@ extern struct _mod* _PyParser_ASTFromString(
PyObject* filename,
int mode,
PyCompilerFlags *flags,
- PyArena *arena);
+ PyArena *arena,
+ PyObject *module);
extern struct _mod* _PyParser_ASTFromFile(
FILE *fp,
diff --git a/Include/internal/pycore_pyerrors.h
b/Include/internal/pycore_pyerrors.h
index 2c2048f7e1272a..f80808fcc8c4d7 100644
--- a/Include/internal/pycore_pyerrors.h
+++ b/Include/internal/pycore_pyerrors.h
@@ -123,7 +123,8 @@ extern void _PyErr_SetNone(PyThreadState *tstate, PyObject
*exception);
extern PyObject* _PyErr_NoMemory(PyThreadState *tstate);
extern int _PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int
lineno, int col_offset,
- int end_lineno, int end_col_offset);
+ int end_lineno, int end_col_offset,
+ PyObject *module);
extern void _PyErr_RaiseSyntaxError(PyObject *msg, PyObject *filename, int
lineno, int col_offset,
int end_lineno, int end_col_offset);
diff --git a/Include/internal/pycore_pythonrun.h
b/Include/internal/pycore_pythonrun.h
index c2832098ddb3e7..f954f1b63ef67c 100644
--- a/Include/internal/pycore_pythonrun.h
+++ b/Include/internal/pycore_pythonrun.h
@@ -33,6 +33,12 @@ extern const char* _Py_SourceAsString(
PyCompilerFlags *cf,
PyObject **cmd_copy);
+extern PyObject * _Py_CompileStringObjectWithModule(
+ const char *str,
+ PyObject *filename, int start,
+ PyCompilerFlags *flags, int optimize,
+ PyObject *module);
+
/* Stack size, in "pointers". This must be large enough, so
* no two calls to check recursion depth are more than this far
diff --git a/Include/internal/pycore_symtable.h
b/Include/internal/pycore_symtable.h
index 98099b4a497b01..9dbfa913219afa 100644
--- a/Include/internal/pycore_symtable.h
+++ b/Include/internal/pycore_symtable.h
@@ -188,7 +188,8 @@ extern struct symtable* _Py_SymtableStringObjectFlags(
const char *str,
PyObject *filename,
int start,
- PyCompilerFlags *flags);
+ PyCompilerFlags *flags,
+ PyObject *module);
int _PyFuture_FromAST(
struct _mod * mod,
diff --git a/Lib/ast.py b/Lib/ast.py
index 983ac1710d0205..d9743ba7ab40b1 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -24,7 +24,7 @@
def parse(source, filename='<unknown>', mode='exec', *,
- type_comments=False, feature_version=None, optimize=-1):
+ type_comments=False, feature_version=None, optimize=-1, module=None):
"""
Parse the source into an AST node.
Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
@@ -44,7 +44,8 @@ def parse(source, filename='<unknown>', mode='exec', *,
feature_version = minor
# Else it should be an int giving the minor version for 3.x.
return compile(source, filename, mode, flags,
- _feature_version=feature_version, optimize=optimize)
+ _feature_version=feature_version, optimize=optimize,
+ module=module)
def literal_eval(node_or_string):
diff --git a/Lib/importlib/_bootstrap_external.py
b/Lib/importlib/_bootstrap_external.py
index 035ae0fcae14e8..4ab0e79ea6efeb 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -819,13 +819,14 @@ def get_source(self, fullname):
name=fullname) from exc
return decode_source(source_bytes)
- def source_to_code(self, data, path, *, _optimize=-1):
+ def source_to_code(self, data, path, fullname=None, *, _optimize=-1):
"""Return the code object compiled from source.
The 'data' argument can be any object type that compile() supports.
"""
return _bootstrap._call_with_frames_removed(compile, data, path,
'exec',
- dont_inherit=True, optimize=_optimize)
+ dont_inherit=True, optimize=_optimize,
+ module=fullname)
def get_code(self, fullname):
"""Concrete implementation of InspectLoader.get_code.
@@ -894,7 +895,7 @@ def get_code(self, fullname):
source_path=source_path)
if source_bytes is None:
source_bytes = self.get_data(source_path)
- code_object = self.source_to_code(source_bytes, source_path)
+ code_object = self.source_to_code(source_bytes, source_path, fullname)
_bootstrap._verbose_message('code object from {}', source_path)
if (not sys.dont_write_bytecode and bytecode_path is not None and
source_mtime is not None):
@@ -1186,7 +1187,7 @@ def get_source(self, fullname):
return ''
def get_code(self, fullname):
- return compile('', '<string>', 'exec', dont_inherit=True)
+ return compile('', '<string>', 'exec', dont_inherit=True,
module=fullname)
def create_module(self, spec):
"""Use default semantics for module creation."""
diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py
index 1e47495f65fa02..5c13432b5bda8c 100644
--- a/Lib/importlib/abc.py
+++ b/Lib/importlib/abc.py
@@ -108,7 +108,7 @@ def get_code(self, fullname):
source = self.get_source(fullname)
if source is None:
return None
- return self.source_to_code(source)
+ return self.source_to_code(source, '<string>', fullname)
@abc.abstractmethod
def get_source(self, fullname):
@@ -120,12 +120,12 @@ def get_source(self, fullname):
raise ImportError
@staticmethod
- def source_to_code(data, path='<string>'):
+ def source_to_code(data, path='<string>', fullname=None):
"""Compile 'data' into a code object.
The 'data' argument can be anything that compile() can handle.
The'path'
argument should be where the data was retrieved (when applicable)."""
- return compile(data, path, 'exec', dont_inherit=True)
+ return compile(data, path, 'exec', dont_inherit=True, module=fullname)
exec_module = _bootstrap_external._LoaderBasics.exec_module
load_module = _bootstrap_external._LoaderBasics.load_module
@@ -163,9 +163,8 @@ def get_code(self, fullname):
try:
path = self.get_filename(fullname)
except ImportError:
- return self.source_to_code(source)
- else:
- return self.source_to_code(source, path)
+ path = '<string>'
+ return self.source_to_code(source, path, fullname)
_register(
ExecutionLoader,
diff --git a/Lib/modulefinder.py b/Lib/modulefinder.py
index ac478ee7f51722..b115d99ab30ff1 100644
--- a/Lib/modulefinder.py
+++ b/Lib/modulefinder.py
@@ -334,7 +334,7 @@ def load_module(self, fqname, fp, pathname, file_info):
self.msgout(2, "load_module ->", m)
return m
if type == _PY_SOURCE:
- co = compile(fp.read(), pathname, 'exec')
+ co = compile(fp.read(), pathname, 'exec', module=fqname)
elif type == _PY_COMPILED:
try:
data = fp.read()
diff --git a/Lib/profiling/sampling/_sync_coordinator.py
b/Lib/profiling/sampling/_sync_coordinator.py
index 8716e654104791..adb040e89cc7b1 100644
--- a/Lib/profiling/sampling/_sync_coordinator.py
+++ b/Lib/profiling/sampling/_sync_coordinator.py
@@ -182,7 +182,7 @@ def _execute_script(script_path: str, script_args:
List[str], cwd: str) -> None:
try:
# Compile and execute the script
- code = compile(source_code, script_path, 'exec')
+ code = compile(source_code, script_path, 'exec', module='__main__')
exec(code, {'__name__': '__main__', '__file__': script_path})
except SyntaxError as e:
raise TargetError(f"Syntax error in script {script_path}: {e}") from e
diff --git a/Lib/profiling/tracing/__init__.py
b/Lib/profiling/tracing/__init__.py
index 2dc7ea92c8ca4d..a6b8edf721611f 100644
--- a/Lib/profiling/tracing/__init__.py
+++ b/Lib/profiling/tracing/__init__.py
@@ -185,7 +185,7 @@ def main():
progname = args[0]
sys.path.insert(0, os.path.dirname(progname))
with io.open_code(progname) as fp:
- code = compile(fp.read(), progname, 'exec')
+ code = compile(fp.read(), progname, 'exec', module='__main__')
spec = importlib.machinery.ModuleSpec(name='__main__', loader=None,
origin=progname)
module = importlib.util.module_from_spec(spec)
diff --git a/Lib/runpy.py b/Lib/runpy.py
index ef54d3282eee06..f072498f6cb405 100644
--- a/Lib/runpy.py
+++ b/Lib/runpy.py
@@ -247,7 +247,7 @@ def _get_main_module_details(error=ImportError):
sys.modules[main_name] = saved_main
-def _get_code_from_file(fname):
+def _get_code_from_file(fname, module):
# Check for a compiled file first
from pkgutil import read_code
code_path = os.path.abspath(fname)
@@ -256,7 +256,7 @@ def _get_code_from_file(fname):
if code is None:
# That didn't work, so try it as normal source code
with io.open_code(code_path) as f:
- code = compile(f.read(), fname, 'exec')
+ code = compile(f.read(), fname, 'exec', module=module)
return code
def run_path(path_name, init_globals=None, run_name=None):
@@ -283,7 +283,7 @@ def run_path(path_name, init_globals=None, run_name=None):
if isinstance(importer, type(None)):
# Not a valid sys.path entry, so run the code directly
# execfile() doesn't help as we want to allow compiled files
- code = _get_code_from_file(path_name)
+ code = _get_code_from_file(path_name, run_name)
return _run_module_code(code, init_globals, run_name,
pkg_name=pkg_name, script_name=path_name)
else:
diff --git a/Lib/symtable.py b/Lib/symtable.py
index 77475c3ffd9224..4c832e68f94cbd 100644
--- a/Lib/symtable.py
+++ b/Lib/symtable.py
@@ -17,13 +17,13 @@
__all__ = ["symtable", "SymbolTableType", "SymbolTable", "Class", "Function",
"Symbol"]
-def symtable(code, filename, compile_type):
+def symtable(code, filename, compile_type, *, module=None):
""" Return the toplevel *SymbolTable* for the source code.
*filename* is the name of the file with the code
and *compile_type* is the *compile()* mode argument.
"""
- top = _symtable.symtable(code, filename, compile_type)
+ top = _symtable.symtable(code, filename, compile_type, module=module)
return _newSymbolTable(top, filename)
class SymbolTableFactory:
diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index 551de5851daace..fb4a441ca64772 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -1083,6 +1083,16 @@ def test_filter_syntax_warnings_by_module(self):
self.assertEqual(wm.filename, '<unknown>')
self.assertIs(wm.category, SyntaxWarning)
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=r'package\.module\z')
+ warnings.filterwarnings('error', module=r'<unknown>')
+ ast.parse(source, filename, module='package.module')
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10])
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
class CopyTests(unittest.TestCase):
"""Test copying and pickling AST nodes."""
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index fba46af6617640..ce60a5d095dd52 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -1103,7 +1103,8 @@ def test_exec_filter_syntax_warnings_by_module(self):
with warnings.catch_warnings(record=True) as wlog:
warnings.simplefilter('error')
- warnings.filterwarnings('always', module=r'<string>\z')
+ warnings.filterwarnings('always', module=r'package.module\z')
+ warnings.filterwarnings('error', module=r'<string>')
exec(source, {'__name__': 'package.module', '__file__': filename})
self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
for wm in wlog:
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
index f8115cc8300df7..cc1a625a5097d8 100644
--- a/Lib/test/test_cmd_line_script.py
+++ b/Lib/test/test_cmd_line_script.py
@@ -814,15 +814,26 @@ def test_filter_syntax_warnings_by_module(self):
filename = support.findfile('test_import/data/syntax_warnings.py')
rc, out, err = assert_python_ok(
'-Werror',
- '-Walways:::test.test_import.data.syntax_warnings',
+ '-Walways:::__main__',
+ '-Werror:::test.test_import.data.syntax_warnings',
+ '-Werror:::syntax_warnings',
filename)
self.assertEqual(err.count(b': SyntaxWarning: '), 6)
- rc, out, err = assert_python_ok(
- '-Werror',
- '-Walways:::syntax_warnings',
- filename)
- self.assertEqual(err.count(b': SyntaxWarning: '), 6)
+ def test_zipfile_run_filter_syntax_warnings_by_module(self):
+ filename = support.findfile('test_import/data/syntax_warnings.py')
+ with open(filename, 'rb') as f:
+ source = f.read()
+ with os_helper.temp_dir() as script_dir:
+ zip_name, _ = make_zip_pkg(
+ script_dir, 'test_zip', 'test_pkg', '__main__', source)
+ rc, out, err = assert_python_ok(
+ '-Werror',
+ '-Walways:::__main__',
+ '-Werror:::test_pkg.__main__',
+ os.path.join(zip_name, 'test_pkg')
+ )
+ self.assertEqual(err.count(b': SyntaxWarning: '), 12)
def tearDownModule():
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 9c2364491fe08d..30f21875b22ab3 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -1759,6 +1759,16 @@ def test_filter_syntax_warnings_by_module(self):
self.assertEqual(wm.filename, filename)
self.assertIs(wm.category, SyntaxWarning)
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=r'package\.module\z')
+ warnings.filterwarnings('error', module=module_re)
+ compile(source, filename, 'exec', module='package.module')
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
@support.subTests('src', [
textwrap.dedent("""
def f():
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index e87d8b7e7bbb1f..fe669bb04df02a 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -1259,20 +1259,7 @@ def test_filter_syntax_warnings_by_module(self):
warnings.catch_warnings(record=True) as wlog):
warnings.simplefilter('error')
warnings.filterwarnings('always', module=module_re)
- import test.test_import.data.syntax_warnings
- self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
- filename = test.test_import.data.syntax_warnings.__file__
- for wm in wlog:
- self.assertEqual(wm.filename, filename)
- self.assertIs(wm.category, SyntaxWarning)
-
- module_re = r'syntax_warnings\z'
- unload('test.test_import.data.syntax_warnings')
- with (os_helper.temp_dir() as tmpdir,
- temporary_pycache_prefix(tmpdir),
- warnings.catch_warnings(record=True) as wlog):
- warnings.simplefilter('error')
- warnings.filterwarnings('always', module=module_re)
+ warnings.filterwarnings('error', module='syntax_warnings')
import test.test_import.data.syntax_warnings
self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
filename = test.test_import.data.syntax_warnings.__file__
diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py
index a2a07c04f58ef2..cc76b72b9639eb 100644
--- a/Lib/test/test_runpy.py
+++ b/Lib/test/test_runpy.py
@@ -20,9 +20,11 @@
requires_subprocess,
verbose,
)
+from test import support
from test.support.import_helper import forget, make_legacy_pyc, unload
from test.support.os_helper import create_empty_file, temp_dir, FakePath
from test.support.script_helper import make_script, make_zip_script
+from test.test_importlib.util import temporary_pycache_prefix
import runpy
@@ -763,6 +765,47 @@ def test_encoding(self):
result = run_path(filename)
self.assertEqual(result['s'], "non-ASCII: h\xe9")
+ def test_run_module_filter_syntax_warnings_by_module(self):
+ module_re = r'test\.test_import\.data\.syntax_warnings\z'
+ with (temp_dir() as tmpdir,
+ temporary_pycache_prefix(tmpdir),
+ warnings.catch_warnings(record=True) as wlog):
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=module_re)
+ warnings.filterwarnings('error', module='syntax_warnings')
+ ns = run_module('test.test_import.data.syntax_warnings')
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
+ filename = ns['__file__']
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
+ def test_run_path_filter_syntax_warnings_by_module(self):
+ filename = support.findfile('test_import/data/syntax_warnings.py')
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=r'<run_path>\z')
+ warnings.filterwarnings('error', module='test')
+ warnings.filterwarnings('error', module='syntax_warnings')
+ warnings.filterwarnings('error',
+ module=r'test\.test_import\.data\.syntax_warnings')
+ run_path(filename)
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=r'package\.script\z')
+ warnings.filterwarnings('error', module='<run_path>')
+ warnings.filterwarnings('error', module='test')
+ warnings.filterwarnings('error', module='syntax_warnings')
+ warnings.filterwarnings('error',
+ module=r'test\.test_import\.data\.syntax_warnings')
+ run_path(filename, run_name='package.script')
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14,
21])
+
@force_not_colorized_test_class
class TestExit(unittest.TestCase):
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index ef2c00e04b820c..094ab8f573e7ba 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -601,6 +601,16 @@ def test_filter_syntax_warnings_by_module(self):
self.assertEqual(wm.filename, filename)
self.assertIs(wm.category, SyntaxWarning)
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always', module=r'package\.module\z')
+ warnings.filterwarnings('error', module=module_re)
+ symtable.symtable(source, filename, 'exec',
module='package.module')
+ self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10])
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
class ComprehensionTests(unittest.TestCase):
def get_identifiers_recursive(self, st, res):
diff --git a/Lib/test/test_zipimport_support.py
b/Lib/test/test_zipimport_support.py
index ae8a8c99762313..2b28f46149b4ff 100644
--- a/Lib/test/test_zipimport_support.py
+++ b/Lib/test/test_zipimport_support.py
@@ -13,9 +13,12 @@
import inspect
import linecache
import unittest
+import warnings
+from test import support
from test.support import os_helper
from test.support.script_helper import (spawn_python, kill_python,
assert_python_ok,
make_script, make_zip_script)
+from test.support import import_helper
verbose = test.support.verbose
@@ -236,6 +239,26 @@ def f():
# bdb/pdb applies normcase to its filename before displaying
self.assertIn(os.path.normcase(run_name.encode('utf-8')), data)
+ def test_import_filter_syntax_warnings_by_module(self):
+ filename = support.findfile('test_import/data/syntax_warnings.py')
+ with (os_helper.temp_dir() as tmpdir,
+ import_helper.DirsOnSysPath()):
+ zip_name, _ = make_zip_script(tmpdir, "test_zip",
+ filename, 'test_pkg/test_mod.py')
+ sys.path.insert(0, zip_name)
+ import_helper.unload('test_pkg.test_mod')
+ with warnings.catch_warnings(record=True) as wlog:
+ warnings.simplefilter('error')
+ warnings.filterwarnings('always',
module=r'test_pkg\.test_mod\z')
+ warnings.filterwarnings('error', module='test_mod')
+ import test_pkg.test_mod
+ self.assertEqual(sorted(wm.lineno for wm in wlog),
+ sorted([4, 7, 10, 13, 14, 21]*2))
+ filename = test_pkg.test_mod.__file__
+ for wm in wlog:
+ self.assertEqual(wm.filename, filename)
+ self.assertIs(wm.category, SyntaxWarning)
+
def tearDownModule():
test.support.reap_children()
diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 340a7e07112504..19279d1c2bea36 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -742,9 +742,9 @@ def _normalize_line_endings(source):
# Given a string buffer containing Python source code, compile it
# and return a code object.
-def _compile_source(pathname, source):
+def _compile_source(pathname, source, module):
source = _normalize_line_endings(source)
- return compile(source, pathname, 'exec', dont_inherit=True)
+ return compile(source, pathname, 'exec', dont_inherit=True, module=module)
# Convert the date/time values found in the Zip archive to a value
# that's compatible with the time stamp stored in .pyc files.
@@ -815,7 +815,7 @@ def _get_module_code(self, fullname):
except ImportError as exc:
import_error = exc
else:
- code = _compile_source(modpath, data)
+ code = _compile_source(modpath, data, fullname)
if code is None:
# bad magic number or non-matching mtime
# in byte code, try next
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-14-19-47.gh-issue-135801.OhxEZS.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-14-19-47.gh-issue-135801.OhxEZS.rst
new file mode 100644
index 00000000000000..96226a7c525e80
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-06-14-19-47.gh-issue-135801.OhxEZS.rst
@@ -0,0 +1,6 @@
+Many functions related to compiling or parsing Python code, such as
+:func:`compile`, :func:`ast.parse`, :func:`symtable.symtable`, and
+:func:`importlib.abc.InspectLoader.source_to_code` now allow to specify
+the module name.
+It is needed to unambiguous :ref:`filter <warning-filter>` syntax warnings
+by module name.
diff --git a/Modules/clinic/symtablemodule.c.h
b/Modules/clinic/symtablemodule.c.h
index bd55d77c5409e9..65352593f94802 100644
--- a/Modules/clinic/symtablemodule.c.h
+++ b/Modules/clinic/symtablemodule.c.h
@@ -2,30 +2,67 @@
preserve
[clinic start generated code]*/
-#include "pycore_modsupport.h" // _PyArg_CheckPositional()
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
+#endif
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(_symtable_symtable__doc__,
-"symtable($module, source, filename, startstr, /)\n"
+"symtable($module, source, filename, startstr, /, *, module=None)\n"
"--\n"
"\n"
"Return symbol and scope dictionaries used internally by compiler.");
#define _SYMTABLE_SYMTABLE_METHODDEF \
- {"symtable", _PyCFunction_CAST(_symtable_symtable), METH_FASTCALL,
_symtable_symtable__doc__},
+ {"symtable", _PyCFunction_CAST(_symtable_symtable),
METH_FASTCALL|METH_KEYWORDS, _symtable_symtable__doc__},
static PyObject *
_symtable_symtable_impl(PyObject *module, PyObject *source,
- PyObject *filename, const char *startstr);
+ PyObject *filename, const char *startstr,
+ PyObject *modname);
static PyObject *
-_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+_symtable_symtable(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)
{
PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ Py_hash_t ob_hash;
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_hash = -1,
+ .ob_item = { &_Py_ID(module), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"", "", "", "module", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "symtable",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[4];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) -
3;
PyObject *source;
PyObject *filename = NULL;
const char *startstr;
+ PyObject *modname = Py_None;
- if (!_PyArg_CheckPositional("symtable", nargs, 3, 3)) {
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
+ /*minpos*/ 3, /*maxpos*/ 3, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+ if (!args) {
goto exit;
}
source = args[0];
@@ -45,7 +82,12 @@ _symtable_symtable(PyObject *module, PyObject *const *args,
Py_ssize_t nargs)
PyErr_SetString(PyExc_ValueError, "embedded null character");
goto exit;
}
- return_value = _symtable_symtable_impl(module, source, filename, startstr);
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ modname = args[3];
+skip_optional_kwonly:
+ return_value = _symtable_symtable_impl(module, source, filename, startstr,
modname);
exit:
/* Cleanup for filename */
@@ -53,4 +95,4 @@ _symtable_symtable(PyObject *module, PyObject *const *args,
Py_ssize_t nargs)
return return_value;
}
-/*[clinic end generated code: output=7a8545d9a1efe837 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0137be60c487c841 input=a9049054013a1b77]*/
diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c
index d353f406831ecd..a24927a9db64db 100644
--- a/Modules/symtablemodule.c
+++ b/Modules/symtablemodule.c
@@ -16,14 +16,17 @@ _symtable.symtable
filename: unicode_fs_decoded
startstr: str
/
+ *
+ module as modname: object = None
Return symbol and scope dictionaries used internally by compiler.
[clinic start generated code]*/
static PyObject *
_symtable_symtable_impl(PyObject *module, PyObject *source,
- PyObject *filename, const char *startstr)
-/*[clinic end generated code: output=59eb0d5fc7285ac4 input=436ffff90d02e4f6]*/
+ PyObject *filename, const char *startstr,
+ PyObject *modname)
+/*[clinic end generated code: output=235ec5a87a9ce178 input=fbf9adaa33c7070d]*/
{
struct symtable *st;
PyObject *t;
@@ -50,7 +53,17 @@ _symtable_symtable_impl(PyObject *module, PyObject *source,
Py_XDECREF(source_copy);
return NULL;
}
- st = _Py_SymtableStringObjectFlags(str, filename, start, &cf);
+ if (modname == Py_None) {
+ modname = NULL;
+ }
+ else if (!PyUnicode_Check(modname)) {
+ PyErr_Format(PyExc_TypeError,
+ "symtable() argument 'module' must be str or None, not
%T",
+ modname);
+ Py_XDECREF(source_copy);
+ return NULL;
+ }
+ st = _Py_SymtableStringObjectFlags(str, filename, start, &cf, modname);
Py_XDECREF(source_copy);
if (st == NULL) {
return NULL;
diff --git a/Parser/lexer/state.c b/Parser/lexer/state.c
index 2de9004fe084f2..3663dc3eb7f9f6 100644
--- a/Parser/lexer/state.c
+++ b/Parser/lexer/state.c
@@ -43,6 +43,7 @@ _PyTokenizer_tok_new(void)
tok->encoding = NULL;
tok->cont_line = 0;
tok->filename = NULL;
+ tok->module = NULL;
tok->decoding_readline = NULL;
tok->decoding_buffer = NULL;
tok->readline = NULL;
@@ -91,6 +92,7 @@ _PyTokenizer_Free(struct tok_state *tok)
Py_XDECREF(tok->decoding_buffer);
Py_XDECREF(tok->readline);
Py_XDECREF(tok->filename);
+ Py_XDECREF(tok->module);
if ((tok->readline != NULL || tok->fp != NULL ) && tok->buf != NULL) {
PyMem_Free(tok->buf);
}
diff --git a/Parser/lexer/state.h b/Parser/lexer/state.h
index 877127125a7652..9cd196a114c7cb 100644
--- a/Parser/lexer/state.h
+++ b/Parser/lexer/state.h
@@ -102,6 +102,7 @@ struct tok_state {
int parenlinenostack[MAXLEVEL];
int parencolstack[MAXLEVEL];
PyObject *filename;
+ PyObject *module;
/* Stuff for checking on different tab sizes */
int altindstack[MAXINDENT]; /* Stack of alternate indents */
/* Stuff for PEP 0263 */
diff --git a/Parser/peg_api.c b/Parser/peg_api.c
index d4acc3e4935d10..e30ca0453bd3e1 100644
--- a/Parser/peg_api.c
+++ b/Parser/peg_api.c
@@ -4,13 +4,15 @@
mod_ty
_PyParser_ASTFromString(const char *str, PyObject* filename, int mode,
- PyCompilerFlags *flags, PyArena *arena)
+ PyCompilerFlags *flags, PyArena *arena,
+ PyObject *module)
{
if (PySys_Audit("compile", "yO", str, filename) < 0) {
return NULL;
}
- mod_ty result = _PyPegen_run_parser_from_string(str, mode, filename,
flags, arena);
+ mod_ty result = _PyPegen_run_parser_from_string(str, mode, filename, flags,
+ arena, module);
return result;
}
diff --git a/Parser/pegen.c b/Parser/pegen.c
index 70493031656028..a38e973b3f64c6 100644
--- a/Parser/pegen.c
+++ b/Parser/pegen.c
@@ -1010,6 +1010,11 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int
start_rule, PyObject *filena
// From here on we need to clean up even if there's an error
mod_ty result = NULL;
+ tok->module = PyUnicode_FromString("__main__");
+ if (tok->module == NULL) {
+ goto error;
+ }
+
int parser_flags = compute_parser_flags(flags);
Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags,
PY_MINOR_VERSION,
errcode, NULL, arena);
@@ -1036,7 +1041,7 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int
start_rule, PyObject *filena
mod_ty
_PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject
*filename_ob,
- PyCompilerFlags *flags, PyArena *arena)
+ PyCompilerFlags *flags, PyArena *arena, PyObject
*module)
{
int exec_input = start_rule == Py_file_input;
@@ -1054,6 +1059,7 @@ _PyPegen_run_parser_from_string(const char *str, int
start_rule, PyObject *filen
}
// This transfers the ownership to the tokenizer
tok->filename = Py_NewRef(filename_ob);
+ tok->module = Py_XNewRef(module);
// We need to clear up from here on
mod_ty result = NULL;
diff --git a/Parser/pegen.h b/Parser/pegen.h
index 6b49b3537a04b2..b8f887608b104e 100644
--- a/Parser/pegen.h
+++ b/Parser/pegen.h
@@ -378,7 +378,7 @@ mod_ty _PyPegen_run_parser_from_file_pointer(FILE *, int,
PyObject *, const char
const char *, const char *,
PyCompilerFlags *, int *, PyObject **,
PyArena *);
void *_PyPegen_run_parser(Parser *);
-mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *,
PyCompilerFlags *, PyArena *);
+mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *,
PyCompilerFlags *, PyArena *, PyObject *);
asdl_stmt_seq *_PyPegen_interactive_exit(Parser *);
// Generated function in parse.c - function definition in python.gram
diff --git a/Parser/string_parser.c b/Parser/string_parser.c
index ebe68989d1af58..b164dfbc81a933 100644
--- a/Parser/string_parser.c
+++ b/Parser/string_parser.c
@@ -88,7 +88,7 @@ warn_invalid_escape_sequence(Parser *p, const char* buffer,
const char *first_in
}
if (PyErr_WarnExplicitObject(category, msg, p->tok->filename,
- lineno, NULL, NULL) < 0) {
+ lineno, p->tok->module, NULL) < 0) {
if (PyErr_ExceptionMatches(category)) {
/* Replace the Syntax/DeprecationWarning exception with a
SyntaxError
to get a more accurate error report */
diff --git a/Parser/tokenizer/helpers.c b/Parser/tokenizer/helpers.c
index e5e2eed2d34aee..a03531a744136d 100644
--- a/Parser/tokenizer/helpers.c
+++ b/Parser/tokenizer/helpers.c
@@ -127,7 +127,7 @@ _PyTokenizer_warn_invalid_escape_sequence(struct tok_state
*tok, int first_inval
}
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, tok->filename,
- tok->lineno, NULL, NULL) < 0) {
+ tok->lineno, tok->module, NULL) < 0) {
Py_DECREF(msg);
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
@@ -166,7 +166,7 @@ _PyTokenizer_parser_warn(struct tok_state *tok, PyObject
*category, const char *
}
if (PyErr_WarnExplicitObject(category, errmsg, tok->filename,
- tok->lineno, NULL, NULL) < 0) {
+ tok->lineno, tok->module, NULL) < 0) {
if (PyErr_ExceptionMatches(category)) {
/* Replace the DeprecationWarning exception with a SyntaxError
to get a more accurate error report */
diff --git a/Programs/_freeze_module.py b/Programs/_freeze_module.py
index ba638eef6c4cd6..62274e4aa9ce11 100644
--- a/Programs/_freeze_module.py
+++ b/Programs/_freeze_module.py
@@ -23,7 +23,7 @@ def read_text(inpath: str) -> bytes:
def compile_and_marshal(name: str, text: bytes) -> bytes:
filename = f"<frozen {name}>"
# exec == Py_file_input
- code = compile(text, filename, "exec", optimize=0, dont_inherit=True)
+ code = compile(text, filename, "exec", optimize=0, dont_inherit=True,
module=name)
return marshal.dumps(code)
diff --git a/Programs/freeze_test_frozenmain.py
b/Programs/freeze_test_frozenmain.py
index 848fc31b3d6f44..1a986bbac2afc7 100644
--- a/Programs/freeze_test_frozenmain.py
+++ b/Programs/freeze_test_frozenmain.py
@@ -24,7 +24,7 @@ def dump(fp, filename, name):
with tokenize.open(filename) as source_fp:
source = source_fp.read()
- code = compile(source, code_filename, 'exec')
+ code = compile(source, code_filename, 'exec', module=name)
data = marshal.dumps(code)
writecode(fp, name, data)
diff --git a/Python/ast_preprocess.c b/Python/ast_preprocess.c
index fe6fd9479d1531..d45435257cc8ac 100644
--- a/Python/ast_preprocess.c
+++ b/Python/ast_preprocess.c
@@ -16,6 +16,7 @@ typedef struct {
typedef struct {
PyObject *filename;
+ PyObject *module;
int optimize;
int ff_features;
int syntax_check_only;
@@ -71,7 +72,8 @@ control_flow_in_finally_warning(const char *kw, stmt_ty n,
_PyASTPreprocessState
}
int ret = _PyErr_EmitSyntaxWarning(msg, state->filename, n->lineno,
n->col_offset + 1, n->end_lineno,
- n->end_col_offset + 1);
+ n->end_col_offset + 1,
+ state->module);
Py_DECREF(msg);
return ret < 0 ? 0 : 1;
}
@@ -969,11 +971,13 @@ astfold_type_param(type_param_ty node_, PyArena *ctx_,
_PyASTPreprocessState *st
int
_PyAST_Preprocess(mod_ty mod, PyArena *arena, PyObject *filename, int optimize,
- int ff_features, int syntax_check_only, int enable_warnings)
+ int ff_features, int syntax_check_only, int enable_warnings,
+ PyObject *module)
{
_PyASTPreprocessState state;
memset(&state, 0, sizeof(_PyASTPreprocessState));
state.filename = filename;
+ state.module = module;
state.optimize = optimize;
state.ff_features = ff_features;
state.syntax_check_only = syntax_check_only;
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index f6fadd936bb8ff..c2d780ac9b9270 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -751,6 +751,7 @@ compile as builtin_compile
dont_inherit: bool = False
optimize: int = -1
*
+ module as modname: object = None
_feature_version as feature_version: int = -1
Compile source into a code object that can be executed by exec() or eval().
@@ -770,8 +771,8 @@ in addition to any features explicitly specified.
static PyObject *
builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
const char *mode, int flags, int dont_inherit,
- int optimize, int feature_version)
-/*[clinic end generated code: output=b0c09c84f116d3d7 input=8f0069edbdac381b]*/
+ int optimize, PyObject *modname, int feature_version)
+/*[clinic end generated code: output=9a0dce1945917a86 input=ddeae1e0253459dc]*/
{
PyObject *source_copy;
const char *str;
@@ -800,6 +801,15 @@ builtin_compile_impl(PyObject *module, PyObject *source,
PyObject *filename,
"compile(): invalid optimize value");
goto error;
}
+ if (modname == Py_None) {
+ modname = NULL;
+ }
+ else if (!PyUnicode_Check(modname)) {
+ PyErr_Format(PyExc_TypeError,
+ "compile() argument 'module' must be str or None, not %T",
+ modname);
+ goto error;
+ }
if (!dont_inherit) {
PyEval_MergeCompilerFlags(&cf);
@@ -845,8 +855,9 @@ builtin_compile_impl(PyObject *module, PyObject *source,
PyObject *filename,
goto error;
}
int syntax_check_only = ((flags & PyCF_OPTIMIZED_AST) ==
PyCF_ONLY_AST); /* unoptiomized AST */
- if (_PyCompile_AstPreprocess(mod, filename, &cf, optimize,
- arena, syntax_check_only) < 0) {
+ if (_PyCompile_AstPreprocess(mod, filename, &cf, optimize, arena,
+ syntax_check_only, modname) < 0)
+ {
_PyArena_Free(arena);
goto error;
}
@@ -859,7 +870,7 @@ builtin_compile_impl(PyObject *module, PyObject *source,
PyObject *filename,
goto error;
}
result = (PyObject*)_PyAST_Compile(mod, filename,
- &cf, optimize, arena);
+ &cf, optimize, arena, modname);
}
_PyArena_Free(arena);
goto finally;
@@ -877,7 +888,9 @@ builtin_compile_impl(PyObject *module, PyObject *source,
PyObject *filename,
tstate->suppress_co_const_immortalization++;
#endif
- result = Py_CompileStringObject(str, filename, start[compile_mode], &cf,
optimize);
+ result = _Py_CompileStringObjectWithModule(str, filename,
+ start[compile_mode], &cf,
+ optimize, modname);
#ifdef Py_GIL_DISABLED
tstate->suppress_co_const_immortalization--;
diff --git a/Python/clinic/bltinmodule.c.h b/Python/clinic/bltinmodule.c.h
index adb82f45c25b5d..f08e5847abe32a 100644
--- a/Python/clinic/bltinmodule.c.h
+++ b/Python/clinic/bltinmodule.c.h
@@ -238,7 +238,8 @@ PyDoc_STRVAR(builtin_chr__doc__,
PyDoc_STRVAR(builtin_compile__doc__,
"compile($module, /, source, filename, mode, flags=0,\n"
-" dont_inherit=False, optimize=-1, *, _feature_version=-1)\n"
+" dont_inherit=False, optimize=-1, *, module=None,\n"
+" _feature_version=-1)\n"
"--\n"
"\n"
"Compile source into a code object that can be executed by exec() or eval().\n"
@@ -260,7 +261,7 @@ PyDoc_STRVAR(builtin_compile__doc__,
static PyObject *
builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
const char *mode, int flags, int dont_inherit,
- int optimize, int feature_version);
+ int optimize, PyObject *modname, int feature_version);
static PyObject *
builtin_compile(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)
@@ -268,7 +269,7 @@ builtin_compile(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObj
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 7
+ #define NUM_KEYWORDS 8
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
@@ -277,7 +278,7 @@ builtin_compile(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObj
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_hash = -1,
- .ob_item = { &_Py_ID(source), &_Py_ID(filename), &_Py_ID(mode),
&_Py_ID(flags), &_Py_ID(dont_inherit), &_Py_ID(optimize),
&_Py_ID(_feature_version), },
+ .ob_item = { &_Py_ID(source), &_Py_ID(filename), &_Py_ID(mode),
&_Py_ID(flags), &_Py_ID(dont_inherit), &_Py_ID(optimize), &_Py_ID(module),
&_Py_ID(_feature_version), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -286,14 +287,14 @@ builtin_compile(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObj
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"source", "filename", "mode",
"flags", "dont_inherit", "optimize", "_feature_version", NULL};
+ static const char * const _keywords[] = {"source", "filename", "mode",
"flags", "dont_inherit", "optimize", "module", "_feature_version", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "compile",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[7];
+ PyObject *argsbuf[8];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) -
3;
PyObject *source;
PyObject *filename = NULL;
@@ -301,6 +302,7 @@ builtin_compile(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObj
int flags = 0;
int dont_inherit = 0;
int optimize = -1;
+ PyObject *modname = Py_None;
int feature_version = -1;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
@@ -359,12 +361,18 @@ builtin_compile(PyObject *module, PyObject *const *args,
Py_ssize_t nargs, PyObj
if (!noptargs) {
goto skip_optional_kwonly;
}
- feature_version = PyLong_AsInt(args[6]);
+ if (args[6]) {
+ modname = args[6];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ feature_version = PyLong_AsInt(args[7]);
if (feature_version == -1 && PyErr_Occurred()) {
goto exit;
}
skip_optional_kwonly:
- return_value = builtin_compile_impl(module, source, filename, mode, flags,
dont_inherit, optimize, feature_version);
+ return_value = builtin_compile_impl(module, source, filename, mode, flags,
dont_inherit, optimize, modname, feature_version);
exit:
/* Cleanup for filename */
@@ -1277,4 +1285,4 @@ builtin_issubclass(PyObject *module, PyObject *const
*args, Py_ssize_t nargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=7eada753dc2e046f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=06500bcc9a341e68 input=a9049054013a1b77]*/
diff --git a/Python/compile.c b/Python/compile.c
index e2f1c7e8eb5bce..6951c98500dfec 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -104,11 +104,13 @@ typedef struct _PyCompiler {
* (including instructions for nested code
objects)
*/
int c_disable_warning;
+ PyObject *c_module;
} compiler;
static int
compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
- PyCompilerFlags *flags, int optimize, PyArena *arena)
+ PyCompilerFlags *flags, int optimize, PyArena *arena,
+ PyObject *module)
{
PyCompilerFlags local_flags = _PyCompilerFlags_INIT;
@@ -126,6 +128,7 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
if (!_PyFuture_FromAST(mod, filename, &c->c_future)) {
return ERROR;
}
+ c->c_module = Py_XNewRef(module);
if (!flags) {
flags = &local_flags;
}
@@ -136,7 +139,9 @@ compiler_setup(compiler *c, mod_ty mod, PyObject *filename,
c->c_optimize = (optimize == -1) ? _Py_GetConfig()->optimization_level :
optimize;
c->c_save_nested_seqs = false;
- if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged, 0, 1))
{
+ if (!_PyAST_Preprocess(mod, arena, filename, c->c_optimize, merged,
+ 0, 1, module))
+ {
return ERROR;
}
c->c_st = _PySymtable_Build(mod, filename, &c->c_future);
@@ -156,6 +161,7 @@ compiler_free(compiler *c)
_PySymtable_Free(c->c_st);
}
Py_XDECREF(c->c_filename);
+ Py_XDECREF(c->c_module);
Py_XDECREF(c->c_const_cache);
Py_XDECREF(c->c_stack);
PyMem_Free(c);
@@ -163,13 +169,13 @@ compiler_free(compiler *c)
static compiler*
new_compiler(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags,
- int optimize, PyArena *arena)
+ int optimize, PyArena *arena, PyObject *module)
{
compiler *c = PyMem_Calloc(1, sizeof(compiler));
if (c == NULL) {
return NULL;
}
- if (compiler_setup(c, mod, filename, pflags, optimize, arena) < 0) {
+ if (compiler_setup(c, mod, filename, pflags, optimize, arena, module) < 0)
{
compiler_free(c);
return NULL;
}
@@ -1221,7 +1227,8 @@ _PyCompile_Warn(compiler *c, location loc, const char
*format, ...)
return ERROR;
}
int ret = _PyErr_EmitSyntaxWarning(msg, c->c_filename, loc.lineno,
loc.col_offset + 1,
- loc.end_lineno, loc.end_col_offset + 1);
+ loc.end_lineno, loc.end_col_offset + 1,
+ c->c_module);
Py_DECREF(msg);
return ret;
}
@@ -1476,10 +1483,10 @@ _PyCompile_OptimizeAndAssemble(compiler *c, int addNone)
PyCodeObject *
_PyAST_Compile(mod_ty mod, PyObject *filename, PyCompilerFlags *pflags,
- int optimize, PyArena *arena)
+ int optimize, PyArena *arena, PyObject *module)
{
assert(!PyErr_Occurred());
- compiler *c = new_compiler(mod, filename, pflags, optimize, arena);
+ compiler *c = new_compiler(mod, filename, pflags, optimize, arena, module);
if (c == NULL) {
return NULL;
}
@@ -1492,7 +1499,8 @@ _PyAST_Compile(mod_ty mod, PyObject *filename,
PyCompilerFlags *pflags,
int
_PyCompile_AstPreprocess(mod_ty mod, PyObject *filename, PyCompilerFlags *cf,
- int optimize, PyArena *arena, int no_const_folding)
+ int optimize, PyArena *arena, int no_const_folding,
+ PyObject *module)
{
_PyFutureFeatures future;
if (!_PyFuture_FromAST(mod, filename, &future)) {
@@ -1502,7 +1510,9 @@ _PyCompile_AstPreprocess(mod_ty mod, PyObject *filename,
PyCompilerFlags *cf,
if (optimize == -1) {
optimize = _Py_GetConfig()->optimization_level;
}
- if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags,
no_const_folding, 0)) {
+ if (!_PyAST_Preprocess(mod, arena, filename, optimize, flags,
+ no_const_folding, 0, module))
+ {
return -1;
}
return 0;
@@ -1627,7 +1637,7 @@ _PyCompile_CodeGen(PyObject *ast, PyObject *filename,
PyCompilerFlags *pflags,
return NULL;
}
- compiler *c = new_compiler(mod, filename, pflags, optimize, arena);
+ compiler *c = new_compiler(mod, filename, pflags, optimize, arena, NULL);
if (c == NULL) {
_PyArena_Free(arena);
return NULL;
diff --git a/Python/errors.c b/Python/errors.c
index 9fe95cec0ab794..5c6ac48371a0ff 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -1960,10 +1960,11 @@ _PyErr_RaiseSyntaxError(PyObject *msg, PyObject
*filename, int lineno, int col_o
*/
int
_PyErr_EmitSyntaxWarning(PyObject *msg, PyObject *filename, int lineno, int
col_offset,
- int end_lineno, int end_col_offset)
+ int end_lineno, int end_col_offset,
+ PyObject *module)
{
- if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg,
- filename, lineno, NULL, NULL) < 0)
+ if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, filename, lineno,
+ module, NULL) < 0)
{
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
/* Replace the SyntaxWarning exception with a SyntaxError
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 45211e1b075042..49ce0a97d4742f 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1252,12 +1252,19 @@ _PyRun_StringFlagsWithName(const char *str, PyObject*
name, int start,
} else {
name = &_Py_STR(anon_string);
}
+ PyObject *module = NULL;
+ if (globals && PyDict_GetItemStringRef(globals, "__name__", &module) < 0) {
+ goto done;
+ }
- mod = _PyParser_ASTFromString(str, name, start, flags, arena);
+ mod = _PyParser_ASTFromString(str, name, start, flags, arena, module);
+ Py_XDECREF(module);
- if (mod != NULL) {
+ if (mod != NULL) {
ret = run_mod(mod, name, globals, locals, flags, arena, source,
generate_new_source);
}
+
+done:
Py_XDECREF(source);
_PyArena_Free(arena);
return ret;
@@ -1407,8 +1414,17 @@ run_mod(mod_ty mod, PyObject *filename, PyObject
*globals, PyObject *locals,
return NULL;
}
}
+ PyObject *module = NULL;
+ if (globals && PyDict_GetItemStringRef(globals, "__name__", &module) < 0) {
+ if (interactive_src) {
+ Py_DECREF(interactive_filename);
+ }
+ return NULL;
+ }
- PyCodeObject *co = _PyAST_Compile(mod, interactive_filename, flags, -1,
arena);
+ PyCodeObject *co = _PyAST_Compile(mod, interactive_filename, flags, -1,
+ arena, module);
+ Py_XDECREF(module);
if (co == NULL) {
if (interactive_src) {
Py_DECREF(interactive_filename);
@@ -1507,6 +1523,14 @@ run_pyc_file(FILE *fp, PyObject *globals, PyObject
*locals,
PyObject *
Py_CompileStringObject(const char *str, PyObject *filename, int start,
PyCompilerFlags *flags, int optimize)
+{
+ return _Py_CompileStringObjectWithModule(str, filename, start,
+ flags, optimize, NULL);
+}
+
+PyObject *
+_Py_CompileStringObjectWithModule(const char *str, PyObject *filename, int
start,
+ PyCompilerFlags *flags, int optimize, PyObject *module)
{
PyCodeObject *co;
mod_ty mod;
@@ -1514,14 +1538,16 @@ Py_CompileStringObject(const char *str, PyObject
*filename, int start,
if (arena == NULL)
return NULL;
- mod = _PyParser_ASTFromString(str, filename, start, flags, arena);
+ mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module);
if (mod == NULL) {
_PyArena_Free(arena);
return NULL;
}
if (flags && (flags->cf_flags & PyCF_ONLY_AST)) {
int syntax_check_only = ((flags->cf_flags & PyCF_OPTIMIZED_AST) ==
PyCF_ONLY_AST); /* unoptiomized AST */
- if (_PyCompile_AstPreprocess(mod, filename, flags, optimize, arena,
syntax_check_only) < 0) {
+ if (_PyCompile_AstPreprocess(mod, filename, flags, optimize, arena,
+ syntax_check_only, module) < 0)
+ {
_PyArena_Free(arena);
return NULL;
}
@@ -1529,7 +1555,7 @@ Py_CompileStringObject(const char *str, PyObject
*filename, int start,
_PyArena_Free(arena);
return result;
}
- co = _PyAST_Compile(mod, filename, flags, optimize, arena);
+ co = _PyAST_Compile(mod, filename, flags, optimize, arena, module);
_PyArena_Free(arena);
return (PyObject *)co;
}
diff --git a/Python/symtable.c b/Python/symtable.c
index bcd7365f8e1f14..29cf9190a4e95b 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -3137,7 +3137,7 @@ symtable_raise_if_not_coroutine(struct symtable *st,
const char *msg, _Py_Source
struct symtable *
_Py_SymtableStringObjectFlags(const char *str, PyObject *filename,
- int start, PyCompilerFlags *flags)
+ int start, PyCompilerFlags *flags, PyObject
*module)
{
struct symtable *st;
mod_ty mod;
@@ -3147,7 +3147,7 @@ _Py_SymtableStringObjectFlags(const char *str, PyObject
*filename,
if (arena == NULL)
return NULL;
- mod = _PyParser_ASTFromString(str, filename, start, flags, arena);
+ mod = _PyParser_ASTFromString(str, filename, start, flags, arena, module);
if (mod == NULL) {
_PyArena_Free(arena);
return NULL;
diff --git a/Tools/peg_generator/peg_extension/peg_extension.c
b/Tools/peg_generator/peg_extension/peg_extension.c
index 1587d53d59472e..2fec5b0512940f 100644
--- a/Tools/peg_generator/peg_extension/peg_extension.c
+++ b/Tools/peg_generator/peg_extension/peg_extension.c
@@ -8,7 +8,7 @@ _build_return_object(mod_ty module, int mode, PyObject
*filename_ob, PyArena *ar
PyObject *result = NULL;
if (mode == 2) {
- result = (PyObject *)_PyAST_Compile(module, filename_ob, NULL, -1,
arena);
+ result = (PyObject *)_PyAST_Compile(module, filename_ob, NULL, -1,
arena, NULL);
} else if (mode == 1) {
result = PyAST_mod2obj(module);
} else {
@@ -93,7 +93,7 @@ parse_string(PyObject *self, PyObject *args, PyObject *kwds)
PyCompilerFlags flags = _PyCompilerFlags_INIT;
mod_ty res = _PyPegen_run_parser_from_string(the_string, Py_file_input,
filename_ob,
- &flags, arena);
+ &flags, arena, NULL);
if (res == NULL) {
goto error;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]