diff --git a/packagetree.py b/packagetree.py index c9ce29e..d3fd8bc 100644 --- a/packagetree.py +++ b/packagetree.py @@ -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) @@ -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): @@ -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) @@ -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. diff --git a/tests/test_container.py b/tests/test_container.py index 1a4bec1..c42e13f 100644 --- a/tests/test_container.py +++ b/tests/test_container.py @@ -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) diff --git a/tests/test_get_full_directory.py b/tests/test_get_full_directory.py new file mode 100644 index 0000000..97fecd2 --- /dev/null +++ b/tests/test_get_full_directory.py @@ -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