diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2021-12-23 00:32:35 -0800 |
---|---|---|
committer | Greg Becker <becker33@llnl.gov> | 2022-01-12 06:14:18 -0800 |
commit | 572fbf4f49564669d8157567c6d18684cc6e086a (patch) | |
tree | 6348e411c8efa08bcaded9a9f4480fce7894931e | |
parent | 396c37d82f1d05084a9d4ba3a08775ef160f4604 (diff) | |
download | spack-572fbf4f49564669d8157567c6d18684cc6e086a.tar.gz spack-572fbf4f49564669d8157567c6d18684cc6e086a.tar.bz2 spack-572fbf4f49564669d8157567c6d18684cc6e086a.tar.xz spack-572fbf4f49564669d8157567c6d18684cc6e086a.zip |
unparser: handle unicode string literals consistently across Python versions
Python 2 and 3 represent string literals differently in the AST. Python 2 requires '\x'
literals, and Python 3 source is always unicode, and allows unicode to be written
directly. These also unparse differently by default.
- [x] modify unparser to write both out the way `repr` would in Python 2 when
`py_ver_consistent` is provided.
-rw-r--r-- | lib/spack/spack/util/unparse/unparser.py | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/lib/spack/spack/util/unparse/unparser.py b/lib/spack/spack/util/unparse/unparser.py index 5396cadc6a..529ed5209f 100644 --- a/lib/spack/spack/util/unparse/unparser.py +++ b/lib/spack/spack/util/unparse/unparser.py @@ -82,6 +82,7 @@ class Unparser: regardless of the python version, because Python 2's AST does not have sufficient information to reconstruct star-arg order. 2. Always unparsing print as a function. + 3. Unparsing Python3 unicode literals the way Python 2 would. Without these changes, the same source can generate different code for Python 2 and Python 3, depending on subtle AST differences. The first of these two @@ -544,7 +545,13 @@ class Unparser: def _Str(self, tree): if six.PY3: - self.write(repr(tree.s)) + # Python 3.5, 3.6, and 3.7 can't tell if something was written as a + # unicode constant. Try to make that consistent with 'u' for '\u- literals + if self._py_ver_consistent and repr(tree.s).startswith("'\\u"): + self.write("u") + self._write_constant(tree.s) + elif self._py_ver_consistent: + self.write(repr(tree.s)) # just do a python 2 repr for consistency else: # if from __future__ import unicode_literals is in effect, # then we want to output string literals using a 'b' prefix @@ -603,7 +610,7 @@ class Unparser: write("{") expr = StringIO() - unparser = type(self)(py_ver_consistent=self.py_ver_consistent) + unparser = type(self)(py_ver_consistent=self._py_ver_consistent) unparser.set_precedence(pnext(_Precedence.TEST), t.value) unparser.visit(t.value, expr) expr = expr.getvalue().rstrip("\n") @@ -636,6 +643,13 @@ class Unparser: if isinstance(value, (float, complex)): # Substitute overflowing decimal literal for AST infinities. self.write(repr(value).replace("inf", INFSTR)) + elif isinstance(value, str) and self._py_ver_consistent: + # emulate a python 2 repr with raw unicode escapes + # see _Str for python 2 counterpart + raw = repr(value.encode("raw_unicode_escape")).lstrip('b') + if raw.startswith(r"'\\u"): + raw = "'\\" + raw[3:] + self.write(raw) else: self.write(repr(value)) |