def test_typing_typed_dict(s, expected): assert _fix_py36_plus(s) == expected
def test_typing_typed_dict_noop(s): assert _fix_py36_plus(s) == s
def shed( source_code: str, *, refactor: bool = False, first_party_imports: FrozenSet[str] = frozenset(), ) -> str: """Process the source code of a single module.""" assert isinstance(source_code, str) assert isinstance(refactor, bool) assert isinstance(first_party_imports, frozenset) assert all(isinstance(name, str) for name in first_party_imports) assert all(name.isidentifier() for name in first_party_imports) # Use black to autodetect our target versions target_versions = { v for v in black.detect_target_versions( black.lib2to3_parse(source_code.lstrip(), set(_version_map))) if v.value >= black.TargetVersion.PY36.value } assert target_versions min_version = _version_map[min(target_versions, key=attrgetter("value"))] if refactor: # Some tools assume that the file is multi-line, but empty files are valid input. source_code += "\n" # Use com2ann to comvert type comments to annotations on Python 3.8+ source_code, _ = com2ann( source_code, drop_ellipsis=True, silent=True, python_minor_version=min_version[1], ) # Use teyit to replace old unittest.assertX methods on Python 3.9+ source_code, _ = _teyit_refactor(source_code) # Then apply pybetter's fixes with libcst tree = libcst.parse_module(source_code) for fixer in _pybetter_fixers: tree = fixer(tree) source_code = tree.code # Then shed.docshed (below) formats any code blocks in documentation source_code = docshed(source=source_code, first_party_imports=first_party_imports) # And pyupgrade - see pyupgrade._fix_file - is our last stable fixer # Calculate separate minver because pyupgrade doesn't have py39-specific logic yet pyupgrade_min_ver = min(min_version, max(pyupgrade.IMPORT_REMOVALS.keys())) source_code = pyupgrade._fix_tokens(source_code, min_version=pyupgrade_min_ver) source_code = pyupgrade._fix_percent_format(source_code) source_code = pyupgrade._fix_py3_plus(source_code, min_version=pyupgrade_min_ver) source_code = pyupgrade._fix_py36_plus(source_code) # One tricky thing: running `isort` or `autoflake` can "unlock" further fixes # for `black`, e.g. "pass;#" -> "pass\n#\n" -> "#\n". We therefore loop until # neither of them have made a change in the last loop body, trusting that # `black` itself is idempotent because that's tested upstream. prev = "" black_mode = black.FileMode(target_versions=target_versions) while prev != source_code: prev = source_code = black.format_str(source_code, mode=black_mode) source_code = autoflake.fix_code( source_code, expand_star_imports=True, remove_all_unused_imports=True, remove_duplicate_keys=True, remove_unused_variables=True, ) source_code = isort.code( source_code, known_first_party=first_party_imports, profile="black", combine_as_imports=True, ) # Remove any extra trailing whitespace return source_code.rstrip() + "\n"
def test_typing_named_tuple_noop(s): assert _fix_py36_plus(s) == s
def test_fix_typing_named_tuple(s, expected): assert _fix_py36_plus(s) == expected
def test_fix_fstrings(s, expected): assert _fix_py36_plus(s) == expected
def test_fix_fstrings_noop(s): assert _fix_py36_plus(s) == s