Changes from Python 3.14 to Python 3.15

--- /home/docs/checkouts/readthedocs.org/user_builds/restrictedpython/checkouts/stable/docs/contributing/ast/python3_14.ast
+++ /home/docs/checkouts/readthedocs.org/user_builds/restrictedpython/checkouts/stable/docs/contributing/ast/python3_15.ast
@@ -1,8 +1,8 @@
--- Python 3.14 AST
+-- Python 3.15 AST
 -- ASDL's 4 builtin types are:
 -- identifier, int, string, constant
 
-module Python version "3.14"
+module Python version "3.15"
 {
     mod = Module(stmt* body, type_ignore* type_ignores)
         | Interactive(stmt* body)
@@ -54,8 +54,8 @@
          | TryStar(stmt* body, excepthandler* handlers, stmt* orelse, stmt* finalbody)
          | Assert(expr test, expr? msg)
 
-         | Import(alias* names)
-         | ImportFrom(identifier? module, alias* names, int? level)
+         | Import(alias* names, int? is_lazy)
+         | ImportFrom(identifier? module, alias* names, int? level, int? is_lazy)
 
          | Global(identifier* names)
          | Nonlocal(identifier* names)
@@ -78,7 +78,7 @@
          | Set(expr* elts)
          | ListComp(expr elt, comprehension* generators)
          | SetComp(expr elt, comprehension* generators)
-         | DictComp(expr key, expr value, comprehension* generators)
+         | DictComp(expr key, expr? value, comprehension* generators)
          | GeneratorExp(expr elt, comprehension* generators)
          -- the grammar constrains where yield expressions can occur
          | Await(expr value)

Security audit of the Python 3.15 changes

Lazy imports (PEP 810)

lazy import statements do not introduce a new AST node. They only add a new field is_lazy to the existing Import and ImportFrom nodes, so the default-deny mechanism of RestrictingNodeTransformer.generic_visit does not apply to them. At run time a lazy import is resolved through the new __lazy_import__ builtin instead of __import__, thus bypassing a guarded __import__. Therefore lazy imports are explicitly not allowed. Assigning __lazy_modules__ was already blocked by the rule denying names which start with an underscore.

Unpacking in comprehensions (PEP 798)

[*x for x in seq], {*x for x in seq}, (*x for x in seq) and {**x for x in seq} reuse the existing Starred node (resp. a DictComp node without a value), so they compiled silently. The unpacked value is iterated by the bytecode without calling the _getiter_ guard — unlike the equivalent nested comprehension [y for x in seq for y in x]. Therefore unpacking in comprehensions is explicitly not allowed.

Unary + in match literal patterns

No action needed as the match statement is not allowed in RestrictedPython.

New builtins frozendict (PEP 814) and sentinel (PEP 661)

No action needed as safe_builtins is an allow list, so the new builtins are not available in restricted code.

New inspect attributes gi_state, cr_state and ag_state

They only reveal the state of a (async) generator resp. coroutine as a string, so they are treated like the other harmless attributes (e. g. gi_running) and remain accessible. Reviewing INSPECT_ATTRIBUTES also revealed that the attributes of asynchronous generator objects (ag_await, ag_frame, ag_code) were missing from the list; they are now blocked.

Removed ast classes (ast.Num, ast.Str, ast.Bytes, ast.NameConstant, ast.Ellipsis)

No action needed as they are no longer used by RestrictedPython.