Comment
The patch is a straightforward compatibility update for Python 3.14 AST changes in Mozilla build tooling. It only replaces deprecated ast.Str handling with ast.Constant in parser/rewrite code, and does not introduce network access, privilege escalation, persistence, or packaging changes. The only notable behavior change is that one helper now rejects non-Constant string-like nodes more strictly, but this is in internal build metadata parsing and is not a security concern in the AUR packaging context.
@@ -0,0 +1,109 @@
+
+# HG changeset patch
+# User Mike Hommey <mh+mozilla@glandium.org>
+# Date 1755580149 0
+# Node ID 23efd75219786d71acff0b4e7c1b0de297b84c4e
+# Parent dbf9702ed87ea5c88c2a1ee615998532ac8f10cc
+Bug 1969769 - Change uses of ast.Str with ast.Constant. r=firefox-build-system-reviewers,ahochheiden
+
+ast.Str was deprecated in python 3.12 and removed in 3.14. It inherited
+from ast.Constant, `Str.s` was equivalent to `Constant.value`, so we can
+use the latter on both old and newer python versions.
+
+Differential Revision: https://phabricator.services.mozilla.com/D261512
+
+
+diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py
+--- a/python/mozbuild/mozbuild/frontend/reader.py
++++ b/python/mozbuild/mozbuild/frontend/reader.py
+@@ -465,17 +465,17 @@ class TemplateFunction:
+ return node
+
+ def c(new_node):
+ return ast.copy_location(new_node, node)
+
+ return c(
+ ast.Subscript(
+ value=c(ast.Name(id=self._global_name, ctx=ast.Load())),
+- slice=c(ast.Index(value=c(ast.Str(s=node.id)))),
++ slice=c(ast.Index(value=c(ast.Constant(value=node.id)))),
+ ctx=node.ctx,
+ )
+ )
+
+
+ class SandboxValidationError(Exception):
+ """Represents an error encountered when validating sandbox results."""
+
+@@ -1034,33 +1034,33 @@ class BuildReader:
+ # We need to branch to deal with python version differences.
+ if isinstance(target.slice, ast.Constant):
+ # Python >= 3.9
+ assert isinstance(target.slice.value, str)
+ key = target.slice.value
+ else:
+ # Others
+ assert isinstance(target.slice, ast.Index)
+- assert isinstance(target.slice.value, ast.Str)
+- key = target.slice.value.s
++ assert isinstance(target.slice.value, ast.Constant)
++ key = target.slice.value.value
+ elif isinstance(target, ast.Attribute):
+ assert isinstance(target.attr, str)
+ key = target.attr
+
+ return name, key
+
+ def assigned_values(node):
+ value = node.value
+ if isinstance(value, ast.List):
+ for v in value.elts:
+- assert isinstance(v, ast.Str)
+- yield v.s
++ assert isinstance(v, ast.Constant)
++ yield v.value
+ else:
+- assert isinstance(value, ast.Str)
+- yield value.s
++ assert isinstance(value, ast.Constant)
++ yield value.value
+
+ assignments = []
+
+ class Visitor(ast.NodeVisitor):
+ def helper(self, node):
+ name, key = assigned_variable(node)
+ if not name:
+ return
+diff --git a/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py b/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py
+--- a/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py
++++ b/python/mozbuild/mozbuild/vendor/rewrite_mozbuild.py
+@@ -322,25 +322,23 @@ def assignment_node_to_source_filename_l
+ If this happens, we'll return an empty list. The consequence of this is that we
+ won't be able to match a file against this list, so we may not be able to add it.
+
+ (But if the file matches a generated list, perhaps it will be included in the
+ Sources list automatically?)
+ """
+ if isinstance(node.value, ast.List) and "elts" in node.value._fields:
+ for f in node.value.elts:
+- if not isinstance(f, ast.Constant) and not isinstance(f, ast.Str):
++ if not isinstance(f, ast.Constant):
+ log(
+ "Found non-constant source file name in list: ",
+ ast_get_source_segment(code, f),
+ )
+ return []
+- return [
+- f.value if isinstance(f, ast.Constant) else f.s for f in node.value.elts
+- ]
++ return [f.value for f in node.value.elts]
+ elif isinstance(node.value, ast.ListComp):
+ # SOURCES += [f for f in foo if blah]
+ log("Could not find the files for " + ast_get_source_segment(code, node.value))
+ elif isinstance(node.value, ast.Name) or isinstance(node.value, ast.Subscript):
+ # SOURCES += other_var
+ # SOURCES += files['X64_SOURCES']
+ log("Could not find the files for " + ast_get_source_segment(code, node))
+ elif isinstance(node.value, ast.Call):
+