Skip to content

bpo-13272: 2to3 string constants fixer #14835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Doc/library/2to3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ and off individually. They are described here in more detail.

Renames :exc:`StandardError` to :exc:`Exception`.

.. 2to3fixer:: string

Renames :const:`lowercase`, :const:`uppercase`, and :const:`letters` to
:const:`ascii_lowercase`, :const:`ascii_uppercase` and
:const:`ascii_letters`.

Caution, some user-defined variables with these names might get renamed
also.

.. versionadded:: 3.9

.. 2to3fixer:: sys_exc

Changes the deprecated :data:`sys.exc_value`, :data:`sys.exc_type`,
Expand Down
104 changes: 104 additions & 0 deletions Lib/lib2to3/fixes/fix_string.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Fixer for string constants.

Renames `lowercase`, `uppercase`, and `letters` with the prefix `ascii_`.

Caution, some user-defined variables with these names might get renamed also.
"""

# Local imports
from .. import fixer_base
from ..fixer_util import Name, token, find_binding, syms, is_import


class FixString(fixer_base.BaseFix):

BM_compatible = True
constants = ["uppercase","lowercase","letters"]
PATTERN = None

def compile_pattern(self):
constants = "('"+("'|'".join(self.constants))+"')"
self.PATTERN = """
power< 'string'
trailer< '.' const=%(constants)s > >
|
const=%(constants)s
""" % dict(constants=constants)
super(FixString, self).compile_pattern()


def start_tree(self, tree, filename):
super(FixString, self).start_tree(tree, filename)

# create list of names imported from 'string' library
self.string_defs = self.constants
self.find_stringdot = False

# from string import * => use all constants
if find_binding(None, tree, "string"):
return

# import string => use all constants and only change string.name
if find_binding("string", tree, None):
self.find_stringdot = True
return

# from string import ... as ...
# see comment of test_from_import_as_with_package:
# 'fail if there is an "from ... import ... as ...'
# find_bindings won't help, so create a local find_imports()

def find_imports(node):
for child in node.children:
if child.type == syms.simple_stmt:
#recursive
find_imports(child)
elif child.type == syms.import_from:

# only more testing if it's for string library
if not child.children[1].type == token.NAME or \
( child.children[1].type == token.NAME \
and not child.children[1].value == 'string' ):
continue

# from string import name (one import)
if child.children[3].type == token.NAME:
name = child.children[3].value
if name in self.constants:
self.string_defs.add(name)
continue

# from string import name as foo (one import as)
if child.children[3].type == syms.import_as_name:
name = child.children[3].children[0].value
if name in self.constants:
self.string_defs.add(name)

# from string import something, something_else, ...
# (multiple imports)
if child.children[3].type == syms.import_as_names:
for something in child.children[3].children:
# ... import name as foo, something_else ...
if something.type == syms.import_as_name:
name = something.children[0].value
if name in self.constants:
self.string_defs.add(name)
# ... import name, something_else as bar, ...
else:
if something.type == token.NAME:
name = something.value
if name in self.constants:
self.string_defs.add(name)

self.string_defs = set()
find_imports(tree)


def transform(self, node, results):
if not results.get('const', None) \
or ( self.find_stringdot and not syms.power ):
return
const = results['const'][0]
if const.value in self.string_defs:
assert const.type == token.NAME
const.replace(Name(("ascii_" + const.value), prefix=node.prefix))
57 changes: 57 additions & 0 deletions Lib/lib2to3/tests/test_fixers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3659,6 +3659,63 @@ def test_future(self):
def test_run_order(self):
self.assert_runs_after('print')


class Test_string(FixerTestCase):
fixer = "string"

def test_import(self):
b = "from string import lowercase, uppercase, letters"
a = "from string import ascii_lowercase, ascii_uppercase"
a += ", ascii_letters"
self.check(b, a)

def test_import_as(self):
b = "from string import uppercase as foo"
a = "from string import ascii_uppercase as foo"
self.check(b, a)

def test_import_as_mix(self):
b = "from string import lowercase, uppercase as foo, letters"
a = "from string import ascii_lowercase, ascii_uppercase as foo"
a += ", ascii_letters"
self.check(b, a)

def test_import_dont_crash(self):
s = "from a.b import lowercase"
self.unchanged(s)

def test_import_string(self):
b = "import string\nprint string.lowercase"
a = "import string\nprint string.ascii_lowercase"
self.check(b, a)

def test_star(self):
b = "from string import *\nprint letters"
a = "from string import *\nprint ascii_letters"
self.check(b, a)

def test_replace(self):
b = "from string import lowercase\nprint lowercase"
a = "from string import ascii_lowercase\nprint ascii_lowercase"
self.check(b, a)

def test_no_import(self):
s = "lowercase = 'foo'\nprint lowercase"
self.unchanged(s)

def test_nonstring_import(self):
s = "from mystringpackage import uppercase\nprint uppercase"
self.unchanged(s)

def test_nonstring_import_def(self):
s = "from foo import bar as letters\nprint letters"
self.unchanged(s)

def test_functions(self):
s = "def letters():\n return 'abc'\nletters()"
self.unchanged(s)


class Test_itertools(FixerTestCase):
fixer = "itertools"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2to3 string constants fixer. Patch by Aldwin Pollefeyt