From 2cac1727e17d4ce344e8924633634e6adf9864db Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Wed, 8 Apr 2026 13:26:35 +0200 Subject: [PATCH] Add feature to get the names of all sub-namespaces of a core.python.namespaces.PythonPackageNamespace --- docs/CHANGELOG.md | 3 +- src/jcloud_docsgen/core/python/namespaces.py | 36 +++++++++++++++++-- .../namespaces/test_PythonPackageNamespace.py | 21 +++++++---- 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8392eaa..5ee0752 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,4 +10,5 @@ - Add python project documentation generator class - Add python namespaces classes - Add type checking for the `name` attribute at the constructor of `core.python.namespaces.PythonNamespace` -- Add check whether sub namespaces are not empty in `core.python.namespaces.PythonPackageNamespace.namespace` \ No newline at end of file +- Add check whether sub-namespaces are not empty in `core.python.namespaces.PythonPackageNamespace.namespace` +- Add feature to get the names of all sub-namespaces of a `core.python.namespaces.PythonPackageNamespace` \ No newline at end of file diff --git a/src/jcloud_docsgen/core/python/namespaces.py b/src/jcloud_docsgen/core/python/namespaces.py index 5e8c5b5..3526cc4 100644 --- a/src/jcloud_docsgen/core/python/namespaces.py +++ b/src/jcloud_docsgen/core/python/namespaces.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from ...exceptions import InvalidNamespaceError, NamespaceNotFoundError from ...utils import assert_that_is_instance @@ -31,6 +32,9 @@ class PythonNamespace: def __init__(self, name: str) -> None: assert_that_is_instance(name, str) + if not name.strip(): + raise InvalidNamespaceError('invalid namespace', namespace_identifier = name) + self.name = name class PythonPackageNamespace(PythonNamespace): @@ -39,7 +43,7 @@ class PythonPackageNamespace(PythonNamespace): :param name: The name of the namespace. :type name: str - :param sub_namespaces: The sub namespaces. + :param sub_namespaces: The sub-namespaces. :type sub_namespaces: list[PythonNamespace] ''' @@ -61,7 +65,7 @@ class PythonPackageNamespace(PythonNamespace): if not sn: raise InvalidNamespaceError('invalid namespace') - # check whether the sub namespace exists + # check whether the sub-namespace exists if sub_namespaces[0] not in self.sub_namespaces: raise NamespaceNotFoundError('no such namespace', namespace_identifier = sub_namespaces[0]) @@ -75,6 +79,34 @@ class PythonPackageNamespace(PythonNamespace): # module namespace else: return sub_namespace + + def _sub_namespaces_names(self, package: PythonPackageNamespace) -> list[str]: + ''' + Returns the names of all sub-namespaces of the specified package + namespace and keeps the package structure. + + :param package: The package. + :type package: PythonPackageNamespace + + :return: The names if all sub-namespaces. + :rtype: list[str] + ''' + + return { + n: self._sub_namespaces_names(sn) if isinstance(sn, PythonPackageNamespace) else sn.name + for n, sn in package.sub_namespaces.items() + } + + def sub_namespace_names(self) -> list[str]: + ''' + Returns the names of all sub-namespaces and keeps the package + structure. + + :return: The names if all sub-namespaces. + :rtype: list[str] + ''' + + return self._sub_namespaces_names(self) class PythonModuleNamespace(PythonNamespace): ''' diff --git a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py index 2b7a547..3a4758a 100644 --- a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py @@ -5,11 +5,9 @@ from src.jcloud_docsgen.exceptions import NamespaceNotFoundError, InvalidNamespa class StrSubclass(str): ... @pytest.mark.parametrize('namespace,expected_name', [ - (PythonPackageNamespace('', {}), ''), - (PythonPackageNamespace(' ', {}), ' '), (PythonPackageNamespace('a', {}), 'a'), (PythonPackageNamespace('1', {}), '1'), - (PythonPackageNamespace(StrSubclass(''), {}), StrSubclass('')), + (PythonPackageNamespace(StrSubclass('a'), {}), StrSubclass('a')), ]) def test_PythonPackageNamespace_name_attribute(namespace, expected_name): assert namespace.name == expected_name @@ -29,15 +27,15 @@ namespace_e = PythonPackageNamespace('e', {'f': namespace_f}) namespace_d = PythonPackageNamespace('d', {'e': namespace_e}) @pytest.mark.parametrize('namespace,sub_namespaces,expected', [ - (PythonPackageNamespace('', { + (PythonPackageNamespace('name', { 'a': namespace_a, 'c': namespace_c }), ['c'], namespace_c), - (PythonPackageNamespace('', { + (PythonPackageNamespace('name', { 'a': namespace_a, 'c': namespace_c }), ['a'], namespace_a), - (PythonPackageNamespace('', { + (PythonPackageNamespace('name', { 'a': namespace_a, 'c': namespace_c }), ['a', 'b'], namespace_b), @@ -58,4 +56,13 @@ def test_PythonPackageNamespace_namespace(namespace: PythonPackageNamespace, sub def test_PythonPackageNamespace_namespace_exceptions(namespace, sub_namespaces, expected_exception, expected_exception_msg): with pytest.raises(expected_exception) as exc_info: namespace.namespace(sub_namespaces) - assert str(exc_info.value) == expected_exception_msg \ No newline at end of file + assert str(exc_info.value) == expected_exception_msg + +@pytest.mark.parametrize('namespace,expected', [ + (PythonPackageNamespace('a', {'b': PythonModuleNamespace('b')}), {'b': 'b'}), + (PythonPackageNamespace('a', {'b': PythonModuleNamespace('b'), 'c': PythonPackageNamespace('c', {'d': PythonModuleNamespace('d')})}), {'b': 'b', 'c': {'d': 'd'}}), + (PythonPackageNamespace('a', {}), {}) +]) +def test_PythonPackageNamespace_sub_namespace_names(namespace, expected): + print('NAMESPACE NAMESPACE NAMESPACE NAMESPACE NAMESPACE:', namespace) + assert namespace.sub_namespace_names() == expected \ No newline at end of file