Skip to content

Commit

Permalink
__future__ imports are always first
Browse files Browse the repository at this point in the history
  • Loading branch information
tlocke committed Apr 10, 2021
1 parent 6c580ca commit 3ceeca6
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 42 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,4 @@ dmypy.json
.pyre/

*.swp
README.html
13 changes: 10 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ codes:
| Import statements are in the wrong order

| AZ200
| The names in the \'from\' import are in the wrong order
| The names in the 'from' import are in the wrong order

|===

Expand All @@ -40,12 +40,14 @@ codes:

Here are the ordering rules that Alphabetize follows:

* Imports from the standard library come first, followed by third party imports,
* The special case `pass:macros[from __future__]` import comes first.

* Imports from the standard library come next, followed by third party imports,
followed by application imports.

* Relative imports are assumed to be application imports.

* Alphabetize only looks at imports at the module level, any import within the code
* Alphabetize only looks at imports at the module level, any imports within the code
are ignored.


Expand All @@ -65,6 +67,11 @@ twine upload dist/*

== Release Notes

=== Version 0.0.7, 2021-04-10

* Import of `\_\_future\_\_`. Should always be first.


=== Version 0.0.6, 2021-04-10

* Third party libraries should be grouped by top-level name.
Expand Down
15 changes: 10 additions & 5 deletions alphabetize/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ def _make_error(node, code, message):


class GroupEnum(IntEnum):
STDLIB = 1
THIRD_PARTY = 2
APPLICATION = 3
FUTURE = 1
STDLIB = 2
THIRD_PARTY = 3
APPLICATION = 4


class NodeTypeEnum(IntEnum):
Expand All @@ -40,6 +41,7 @@ def __init__(self, ast_node):
self.node = ast_node
self.error = None
self.level = None
self.group = None

if isinstance(ast_node, ast.Import):
self.node_type = NodeTypeEnum.IMPORT
Expand All @@ -51,8 +53,9 @@ def __init__(self, ast_node):
self.level = 0

elif isinstance(ast_node, ast.ImportFrom):
self.node_type = NodeTypeEnum.IMPORT_FROM
self.module_name = ast_node.module
self.node_type = NodeTypeEnum.IMPORT_FROM

ast_names = ast_node.names
names = [n.name for n in ast_names]
expected_names = sorted(names)
Expand All @@ -68,7 +71,9 @@ def __init__(self, ast_node):
else:
raise AlphabetizeException(f"Node type {type(ast_node)} not recognized")

if in_stdlib(self.module_name):
if self.module_name == "__future__":
self.group = GroupEnum.FUTURE
elif in_stdlib(self.module_name):
self.group = GroupEnum.STDLIB
elif self.level > 0:
self.group = GroupEnum.APPLICATION
Expand Down
73 changes: 39 additions & 34 deletions test/test_alphabetize.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@ def test_find_imports():
assert _find_imports(parse(pystr)) == []


@pytest.mark.parametrize(
"pystr,error",
[
[
"from pg8000.converters import BIGINT_ARRAY, BIGINT",
(
1,
0,
"AZ200 Imported names are in the wrong order. Should be BIGINT, "
"BIGINT_ARRAY",
Alphabetize,
),
],
],
)
def test_AzImport_init(pystr, error):
node = parse(pystr)
imports = _find_imports(node)

az = AzImport(imports[0])

assert az.error == error


@pytest.mark.parametrize(
"pystr_a,pystr_b,is_lt",
[
Expand Down Expand Up @@ -43,6 +67,11 @@ def test_find_imports():
"from pg8000.converters import pg_interval_in",
True,
],
[
"from __future__ import print_function",
"import decimal",
True,
],
],
)
def test_AzImport_lt(pystr_a, pystr_b, is_lt):
Expand All @@ -57,6 +86,16 @@ def test_AzImport_lt(pystr_a, pystr_b, is_lt):
assert (az_a < az_b) == is_lt


def test_AzImport_str():
pystr = "from .version import version"
node = parse(pystr)
imports = _find_imports(node)

az = AzImport(imports[0])

assert str(az) == pystr


@pytest.mark.parametrize(
"pystr,errors",
[
Expand Down Expand Up @@ -99,37 +138,3 @@ def test_find_errors(pystr, errors):
actual_errors = _find_errors(tree)

assert actual_errors == errors


@pytest.mark.parametrize(
"pystr,error",
[
[
"from pg8000.converters import BIGINT_ARRAY, BIGINT",
(
1,
0,
"AZ200 Imported names are in the wrong order. Should be BIGINT, "
"BIGINT_ARRAY",
Alphabetize,
),
],
],
)
def test_AzImport_init(pystr, error):
node = parse(pystr)
imports = _find_imports(node)

az = AzImport(imports[0])

assert az.error == error


def test_AzImport_str():
pystr = "from .version import version"
node = parse(pystr)
imports = _find_imports(node)

az = AzImport(imports[0])

assert str(az) == pystr

0 comments on commit 3ceeca6

Please sign in to comment.