Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
Add namespace helper module for analyzing names pulled in via imports
Browse files Browse the repository at this point in the history
  • Loading branch information
mschwager committed Aug 16, 2019
1 parent 84111ce commit c1bfd1c
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
1 change: 1 addition & 0 deletions dlint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
)

from . import linters # noqa F401
from . import namespace # noqa F401
from . import test # noqa F401
from . import tree # noqa F401
from . import util # noqa F401
Expand Down
49 changes: 49 additions & 0 deletions dlint/namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python

from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)

import ast
import copy


class Namespace(object):
def __init__(self, imports, from_imports):
self.imports = imports
self.from_imports = from_imports

@classmethod
def from_module_node(cls, module_node):
# For now only add top-level, module imports. Let's avoid the rabbit
# hole of looking at things like function and class-scope imports and
# conditional imports in 'if' or 'try' statements
if not isinstance(module_node, ast.Module):
raise TypeError("only module-level imports are supported")

imports = []
from_imports = []

for node in module_node.body:
if isinstance(node, ast.Import):
imports.append(copy.copy(node))
elif isinstance(node, ast.ImportFrom):
from_imports.append(copy.copy(node))

return cls(imports, from_imports)

def name_imported(self, name):
def alias_includes_name(alias):
return (
(alias.name == name and alias.asname is None)
or (alias.asname == name)
)

return any(
alias_includes_name(alias)
for imp in self.imports + self.from_imports
for alias in imp.names
)
106 changes: 106 additions & 0 deletions tests/test_namespace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python

from __future__ import (
absolute_import,
division,
print_function,
unicode_literals,
)

import unittest

import dlint


class TestNamespace(dlint.test.base.BaseTest):

def test_basic_import(self):
python_string = self.get_ast_node(
"""
import foo
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = namespace.name_imported("foo")
expected = True

assert result == expected

def test_basic_from_import(self):
python_string = self.get_ast_node(
"""
from foo import bar
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = namespace.name_imported("bar")
expected = True

assert result == expected

def test_basic_as_import(self):
python_string = self.get_ast_node(
"""
import foo as bar
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = namespace.name_imported("bar")
expected = True

assert result == expected

def test_basic_as_from_import(self):
python_string = self.get_ast_node(
"""
from foo import bar as baz
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = namespace.name_imported("baz")
expected = True

assert result == expected

def test_as_import_mismatch(self):
python_string = self.get_ast_node(
"""
import foo as bar
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = namespace.name_imported("foo")
expected = False

assert result == expected

def test_as_from_import_mismatch(self):
python_string = self.get_ast_node(
"""
from foo import bar as baz
"""
)

namespace = dlint.namespace.Namespace.from_module_node(python_string)

result = (
namespace.name_imported("foo")
or namespace.name_imported("bar")
)
expected = False

assert result == expected


if __name__ == "__main__":
unittest.main()

0 comments on commit c1bfd1c

Please sign in to comment.