diff --git a/src/jcloud_docsgen/core/python/_core.py b/src/jcloud_docsgen/core/python/_core.py index 78b3f14..5e2cd6b 100644 --- a/src/jcloud_docsgen/core/python/_core.py +++ b/src/jcloud_docsgen/core/python/_core.py @@ -37,7 +37,11 @@ from .definitions import ( PythonFunctionDefinition, ) from .docstrings import PythonDocstring -from .namespaces import PythonModuleNamespace, PythonPackageNamespace +from .namespaces import ( + PythonModuleNamespace, + PythonPackageNamespace, + validate_namespace_name, +) __all__ = [ 'PythonDefinitionDocumentationIncludeSections', @@ -926,9 +930,12 @@ class PythonModuleDocumentationGenerator: ), **kwargs, ) -> None: - if module_path.name.count('.') > 1: + if not ( + module_path.suffix == '.py' + and validate_namespace_name(module_path.stem) + ): raise InvalidNamespaceError( - 'namespace identifier cannot contain more than one dot', + 'invalid module file name', namespace_identifier=module_path.name, ) diff --git a/src/jcloud_docsgen/core/python/namespaces.py b/src/jcloud_docsgen/core/python/namespaces.py index 706a816..4391cff 100644 --- a/src/jcloud_docsgen/core/python/namespaces.py +++ b/src/jcloud_docsgen/core/python/namespaces.py @@ -14,6 +14,7 @@ from __future__ import annotations +import keyword from typing import TypeAlias, Union from ...exceptions import ( @@ -24,27 +25,47 @@ from ...exceptions import ( from ...utils import assert_that_is_instance __all__ = [ + 'validate_namespace_name', 'PythonNamespace', 'PythonPackageNamespace', 'PythonModuleNamespace', ] +def validate_namespace_name(name: str) -> bool: + """ + Checks whether a namespace name is valid. Namespace names are valid + if they contain no dots and are valid Python identifiers. + + :param name: The namespace name to validate. + :type name: str + + :returns: Whether the namespace name is valid. + :rtype: bool + """ + + if not isinstance(name, str): + return False + + return name.isidentifier() and not keyword.iskeyword(name) + + class PythonNamespace: """ The base class for all Python namespaces such as modules or packages. :param name: The name of the namespace. :type name: str + + :raises InvalidNamespaceError: If the namespace name is invalid. """ def __init__(self, name: str) -> None: assert_that_is_instance(name, str) - if not name.strip(): + if not validate_namespace_name(name): raise InvalidNamespaceError( - 'invalid namespace', - namespace_identifier=name, + 'invalid namespace', namespace_identifier=name ) self.name = name @@ -70,6 +91,8 @@ class PythonPackageNamespace(PythonNamespace): :type name: str :param sub_namespaces: The sub-namespaces. :type sub_namespaces: list[PythonNamespace] + + :raises InvalidNamespaceError: If the namespace name is invalid. """ def __init__( diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c b/tests/unit/core/python/_core/invalid_module_names/a.b.c similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c rename to tests/unit/core/python/_core/invalid_module_names/a.b.c diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.h.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.g.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.f.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.e.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.d.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.d.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py b/tests/unit/core/python/_core/invalid_module_names/a.b.c.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.c.py diff --git a/tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py b/tests/unit/core/python/_core/invalid_module_names/a.b.py similarity index 100% rename from tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py rename to tests/unit/core/python/_core/invalid_module_names/a.b.py diff --git a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py index 33c63b7..03921c9 100644 --- a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py @@ -25,176 +25,134 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError [ ( pathlib.Path( - ( - 'tests/unit/core/python/_core/fil' - 'es_with_more_than_one_dot/a.b.c' - ), + ('tests/unit/core/python/_core/invalid_module_names/a.b.c'), ), None, InvalidNamespaceError, - 'namespace identifier cannot contain more than one dot: a.b.c', + 'invalid module file name: a.b.c', + ), + ( + pathlib.Path( + ('tests/unit/core/python/_core/invalid_module_names/a.b.c.d'), + ), + None, + InvalidNamespaceError, + 'invalid module file name: a.b.c.d', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/file' - 's_with_more_than_one_dot/a.b.c.d' + 'tests/unit/core/python/_core/i' + 'nvalid_module_names/a.b.c.d.e' ), ), None, InvalidNamespaceError, - 'namespace identifier cannot contain more than one dot: a.b.c.d', + 'invalid module file name: a.b.c.d.e', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files' - '_with_more_than_one_dot/a.b.c.d.e' + 'tests/unit/core/python/_core/in' + 'valid_module_names/a.b.c.d.e.f' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot cont' - 'ain more than one dot: a.b.c.d.e' - ), + 'invalid module file name: a.b.c.d.e.f', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_' - 'with_more_than_one_dot/a.b.c.d.e.f' + 'tests/unit/core/python/_core/inv' + 'alid_module_names/a.b.c.d.e.f.g' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot conta' - 'in more than one dot: a.b.c.d.e.f' - ), + 'invalid module file name: a.b.c.d.e.f.g', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_w' - 'ith_more_than_one_dot/a.b.c.d.e.f.g' + 'tests/unit/core/python/_core/inva' + 'lid_module_names/a.b.c.d.e.f.g.h' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contai' - 'n more than one dot: a.b.c.d.e.f.g' - ), + 'invalid module file name: a.b.c.d.e.f.g.h', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_wi' - 'th_more_than_one_dot/a.b.c.d.e.f.g.h' + 'tests/unit/core/python/_core/inval' + 'id_module_names/a.b.c.d.e.f.g.h.py' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contain' - ' more than one dot: a.b.c.d.e.f.g.h' - ), + 'invalid module file name: a.b.c.d.e.f.g.h.py', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_wit' - 'h_more_than_one_dot/a.b.c.d.e.f.g.h.py' + 'tests/unit/core/python/_core/inva' + 'lid_module_names/a.b.c.d.e.f.g.py' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contain m' - 'ore than one dot: a.b.c.d.e.f.g.h.py' - ), + 'invalid module file name: a.b.c.d.e.f.g.py', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_wi' - 'th_more_than_one_dot/a.b.c.d.e.f.g.py' + 'tests/unit/core/python/_core/in' + 'lid_module_names/a.b.c.d.e.f.py' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contain ' - 'more than one dot: a.b.c.d.e.f.g.py' - ), + 'invalid module file name: a.b.c.d.e.f.py', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_w' - 'ith_more_than_one_dot/a.b.c.d.e.f.py' + 'tests/unit/core/python/_core/in' + 'valid_module_names/a.b.c.d.e.py' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contain' - ' more than one dot: a.b.c.d.e.f.py' - ), + 'invalid module file name: a.b.c.d.e.py', ), ( pathlib.Path( ( - 'tests/unit/core/python/_core/files_' - 'with_more_than_one_dot/a.b.c.d.e.py' + 'tests/unit/core/python/_core/i' + 'nvalid_module_names/a.b.c.d.py' ), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot contai' - 'n more than one dot: a.b.c.d.e.py' - ), + 'invalid module file name: a.b.c.d.py', ), ( pathlib.Path( - ( - 'tests/unit/core/python/_core/files' - '_with_more_than_one_dot/a.b.c.d.py' - ), + ('tests/unit/core/python/_core/invalid_module_names/a.b.c.py'), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot conta' - 'in more than one dot: a.b.c.d.py' - ), + 'invalid module file name: a.b.c.py', ), ( pathlib.Path( - ( - 'tests/unit/core/python/_core/file' - 's_with_more_than_one_dot/a.b.c.py' - ), + ('tests/unit/core/python/_core/invalid_module_names/a.b.py'), ), None, InvalidNamespaceError, - ( - 'namespace identifier cannot cont' - 'ain more than one dot: a.b.c.py' - ), - ), - ( - pathlib.Path( - ( - 'tests/unit/core/python/_core/fil' - 'es_with_more_than_one_dot/a.b.py' - ), - ), - None, - InvalidNamespaceError, - 'namespace identifier cannot contain more than one dot: a.b.py', + 'invalid module file name: a.b.py', ), ( pathlib.Path( @@ -205,7 +163,7 @@ from src.jcloud_docsgen.exceptions import InvalidNamespaceError ), None, InvalidNamespaceError, - 'namespace identifier cannot contain more than one dot: a.b.py', + 'invalid module file name: a.b.py', ), ], ) diff --git a/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py b/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py index 53d7e0e..3f86f7f 100644 --- a/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonModuleNamespace.py @@ -28,7 +28,6 @@ class StrSubclass(str): ... 'namespace,expected_name', [ (PythonModuleNamespace('a'), 'a'), - (PythonModuleNamespace('1'), '1'), (PythonModuleNamespace(StrSubclass('a')), StrSubclass('a')), ], ) @@ -41,11 +40,7 @@ def test_PythonModuleNamespace_name_attribute(namespace, expected_name): [ (PythonModuleNamespace('a'), PythonModuleNamespace('a'), True), (PythonModuleNamespace('b'), PythonModuleNamespace('b'), True), - (PythonModuleNamespace('1'), PythonModuleNamespace('1'), True), - (PythonModuleNamespace('42'), PythonModuleNamespace('42'), True), (PythonModuleNamespace('a'), PythonModuleNamespace('b'), False), - (PythonModuleNamespace('0'), PythonModuleNamespace('-0'), False), - (PythonModuleNamespace('1'), PythonModuleNamespace('a'), False), (PythonModuleNamespace('a'), 'a', False), (PythonModuleNamespace('a'), 'b', False), (PythonModuleNamespace('a'), PythonPackageNamespace('a', []), False), @@ -61,7 +56,6 @@ def test_PythonModuleNamespace___eq__(namespace1, namespace2, expected): 'namespace,expected', [ (PythonModuleNamespace('a'), "PythonModuleNamespace('a')"), - (PythonModuleNamespace('1'), "PythonModuleNamespace('1')"), ( PythonModuleNamespace(StrSubclass('a')), "PythonModuleNamespace('a')", diff --git a/tests/unit/core/python/namespaces/test_PythonNamespace.py b/tests/unit/core/python/namespaces/test_PythonNamespace.py index e4ee640..ba56d89 100644 --- a/tests/unit/core/python/namespaces/test_PythonNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonNamespace.py @@ -24,7 +24,6 @@ class StrSubclass(str): ... 'namespace,expected_name', [ (PythonNamespace('a'), 'a'), - (PythonNamespace('1'), '1'), (PythonNamespace(StrSubclass('a')), StrSubclass('a')), ], ) @@ -36,7 +35,6 @@ def test_PythonNamespace_name_attribute(namespace, expected_name): 'namespace,expected', [ (PythonNamespace('a'), "PythonNamespace('a')"), - (PythonNamespace('1'), "PythonNamespace('1')"), (PythonNamespace(StrSubclass('a')), "PythonNamespace('a')"), ], ) diff --git a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py index 1087cf0..30ca226 100644 --- a/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py +++ b/tests/unit/core/python/namespaces/test_PythonPackageNamespace.py @@ -38,13 +38,6 @@ class StrSubclass(str): ... ), 'a', ), - ( - PythonPackageNamespace( - '1', - [], - ), - '1', - ), ( PythonPackageNamespace( StrSubclass('a'), @@ -291,10 +284,6 @@ def test_PythonPackageNamespace___eq__(namespace1, namespace2, expected): PythonPackageNamespace('a', []), "PythonPackageNamespace('a', [])", ), - ( - PythonPackageNamespace('1', []), - "PythonPackageNamespace('1', [])", - ), ( PythonPackageNamespace(StrSubclass('a'), []), "PythonPackageNamespace('a', [])",