Skip to content
Merged
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
58 changes: 52 additions & 6 deletions packagetree.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ def __init__(self, module, root=None, directory=None):
self.module = module
self.root = root
directory = directory or ""
self.directory = "{}/{}".format(directory, self.module)

self.full_directory = self._get_full_directory(
self.module,
self.root,
directory
)

if root is not None:
self.module = ".{}".format(module)
Expand All @@ -38,10 +43,46 @@ def __init__(self, module, root=None, directory=None):

self._gather_subpackages()

def _get_full_directory(self, module, root, directory):
"""Build a full directory path from the module and directory.
This path is used to glob for Python packages.
"""
rv = module

format_args = []
d_format = ""
r_format = ""
dir_parts = directory.split('/')

if directory is not None:
module_parts = module.split('.')
m_u = [item for item in module_parts if item not in dir_parts]
module_path = '/'.join(m_u)

d_format = "{}/"
format_args.append(directory)

if root is not None:
root_parts = root.split('.')
r_u = [item for item in root_parts if item not in dir_parts]
root_parts = '/'.join(r_u)

r_format = "{}/"
format_args.append(root_parts)

# Always add module_path
format_args.append(module_path)

format_string = d_format + r_format + "{}"

rv = format_string.format(*format_args).replace('.', '/')
rv = rv.replace('//', '/')
return rv

def __repr__(self):
m = self.module
r = self.root
d = self.directory
d = self.full_directory
return f"PackageTree(module={m}, root={r}, directory={d})"

def __import_classes(self):
Expand All @@ -54,6 +95,7 @@ def __import_classes(self):
imported_module = importlib.import_module(
self.module, package=self.root
)

classes = inspect.getmembers(imported_module, inspect.isclass)
return dict(classes)

Expand Down Expand Up @@ -120,23 +162,27 @@ def _gather_subpackages(self):
child Container relative to the subfolder's root.
"""
# Get all the subfolders of the root.
root = pathlib.Path(self.directory)
root = pathlib.Path(self.full_directory)
allowed_folders = self._filter_directories(root)

# If the root was a relative python path, split it into parts.
root_parts = self.directory.split('/')
if self.full_directory is not None:
root_parts = self.full_directory.split('/')
else:
root_parts = [self.module]

for path in allowed_folders:
parts = list(path.parts)
for part in root_parts:
parts.remove(part)
if part in parts:
parts.remove(part)

parts = '.'.join(parts)

child_container = PackageTree(
module=parts,
root=".".join(root_parts[1:]),
directory=self.directory
directory=self.full_directory
)

# Add each subfolder as a child of this PackageTree.
Expand Down
35 changes: 35 additions & 0 deletions tests/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,38 @@ def test_container_attribute_not_present():
container.foobar

assert "foobar is not an imported Subpackage or Class." == str(e.value)


def test_from_subpackage():
"""Build the package tree from a package inside another.
"""
container = PackageTree(
module='packageB', root='TopLevelPackage', directory="tests"
)

assert 2 == len(container.classes)
assert 1 == len(container.subpackages)


def test_from_subpackage_level_2():
"""Build the package tree from a package inside another.
"""
container = PackageTree(
module='packageBB', root='TopLevelPackage.packageB', directory="tests"
)

assert 2 == len(container.classes)
assert 1 == len(container.subpackages)


def test_from_subpackage_level_3():
"""Build the package tree from a package inside another.
"""
container = PackageTree(
module='packageBBB',
root='TopLevelPackage.packageB.packageBB',
directory="tests"
)

assert 2 == len(container.classes)
assert 1 == len(container.subpackages)
27 changes: 27 additions & 0 deletions tests/test_get_full_directory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from packagetree import PackageTree


def test_get_full_directory():
"""Ensure _get_full_directory combines the module, root, and directory
correctly."""
x = PackageTree._get_full_directory(
None,
module="zab",
root=None,
directory="foo/bar/biz/bin/bat"
)

assert "foo/bar/biz/bin/bat/zab" == x


def test_get_full_directory_nested_module():
"""Ensure _get_full_directory combines the module, root, and directory
correctly."""
x = PackageTree._get_full_directory(
None,
module="zab.zim",
root=None,
directory="foo/bar/biz/bin/bat"
)

assert "foo/bar/biz/bin/bat/zab/zim" == x