From 5d8dbd659d2e920c65cb7d01901421ee3dc4bbeb Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Sat, 5 Sep 2026 14:24:19 +0200 Subject: [PATCH 1/4] chore(utils): mark ExistingDirectory and ExistingFile as deprecated --- docs/CHANGELOG.md | 7 +++++++ src/jcloud_docsgen/utils.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index aba051d..7e3b690 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Deprecated + +- utility class for existing directories +- utility class for existing files + ## [0.1.0] - 2026-09-01 ### Added diff --git a/src/jcloud_docsgen/utils.py b/src/jcloud_docsgen/utils.py index fcda1cc..29c8e98 100644 --- a/src/jcloud_docsgen/utils.py +++ b/src/jcloud_docsgen/utils.py @@ -22,6 +22,12 @@ import errno from typing import Union import types +try: + from warnings import deprecated +except ImportError: + from typing_extensions import deprecated + +@deprecated('Use pathlib.Path instead.') class ExistingDirectory(pathlib.Path): ''' pathlib.Path subclass that is a directory. @@ -37,6 +43,7 @@ class ExistingDirectory(pathlib.Path): raise NotADirectoryError(errno.ENOTDIR, 'not a directory', path) super().__init__(*(path, *args), **kwargs) +@deprecated('Use pathlib.Path instead.') class ExistingFile(pathlib.Path): ''' pathlib.Path subclass that is a file. From d54c0fb20879eeff2adc81951882c8e93bcd86de Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Sat, 5 Sep 2026 14:28:29 +0200 Subject: [PATCH 2/4] refactor: remove or update all instances of ExistingFile and ExistingDirectory in tests --- .../test_PythonDocumentationGenerator.py | 26 ++-------- ...test_PythonModuleDocumentationGenerator.py | 52 +++++++------------ .../python/_core/test__collect_definitions.py | 21 ++++---- tests/unit/utils/test_ExistingDirectory.py | 42 --------------- tests/unit/utils/test_ExistingFile.py | 35 ------------- 5 files changed, 35 insertions(+), 141 deletions(-) delete mode 100644 tests/unit/utils/test_ExistingDirectory.py delete mode 100644 tests/unit/utils/test_ExistingFile.py diff --git a/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py index d544790..cbdacaf 100644 --- a/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonDocumentationGenerator.py @@ -14,29 +14,13 @@ from src.jcloud_docsgen.core.python import PythonDocumentationGenerator from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace, PythonPackageNamespace -from src.jcloud_docsgen.utils import ExistingDirectory import pytest import pathlib -@pytest.mark.parametrize('project_directory,docs_directory,expected_exception,expected_exception_msg', [ - (1, 1, TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (1, None, TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (42, None, TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (ExistingDirectory('tests'), 1, TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (ExistingDirectory('tests/'), 1, TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (1, ExistingDirectory('tests/'), TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (1, ExistingDirectory('tests'), TypeError, 'expected \'ExistingDirectory\', got \'int\''), - (None, ExistingDirectory('tests'), TypeError, 'expected \'ExistingDirectory\', got \'NoneType\''), -]) -def test_PythonDocumentationGenerator_exceptions(project_directory, docs_directory, expected_exception, expected_exception_msg): - with pytest.raises(expected_exception) as exc_info: - PythonDocumentationGenerator(project_directory, docs_directory) - assert str(exc_info.value) == expected_exception_msg - @pytest.mark.parametrize('python_documentation_generator,expected', [ - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), ]) def test_PythonDocumentationGenerator_collect_modules(python_documentation_generator: PythonDocumentationGenerator, expected): assert python_documentation_generator.namespace() == expected @@ -48,14 +32,14 @@ def test_PythonDocumentationGenerator_collect_modules(python_documentation_gener def test_generate_documentation(dir, expectdir): with expectdir('test_PythonDocumentationGenerator/test_generate_documentation/' + dir) as d: PythonDocumentationGenerator( - ExistingDirectory( + pathlib.Path( str( pathlib.Path('tests/unit/core/python/_core/test_PythonDocumentationGenerator/test_generate_documentation/') / dir / 'project' ) ), - ExistingDirectory(str(d)), + pathlib.Path(str(d)), allow_tables = True, allow_html = False ).generate_documentation( diff --git a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py index bf6da0d..ee24c8e 100644 --- a/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py +++ b/tests/unit/core/python/_core/test_PythonModuleDocumentationGenerator.py @@ -14,36 +14,24 @@ from src.jcloud_docsgen.core.python import PythonModuleDocumentationGenerator from src.jcloud_docsgen.exceptions import InvalidNamespaceError +import pathlib import pytest -from src.jcloud_docsgen.utils import ExistingFile, ExistingDirectory @pytest.mark.parametrize('module_path,src_directory,expected_exception,expected_exception_msg', [ - (1, 1, TypeError, 'expected \'ExistingFile\', got \'int\''), - (1, None, TypeError, 'expected \'ExistingFile\', got \'int\''), - (42, None, TypeError, 'expected \'ExistingFile\', got \'int\''), - (ExistingFile('tests/unit/utils/test_file'), 1, TypeError, 'expected either \'ExistingDirectory\' or \'NoneType\', got \'int\''), - (ExistingDirectory('tests/'), 1, TypeError, 'expected \'ExistingFile\', got \'ExistingDirectory\''), - (ExistingDirectory('tests'), 1, TypeError, 'expected \'ExistingFile\', got \'ExistingDirectory\''), - (1, ExistingDirectory('tests/'), TypeError, 'expected \'ExistingFile\', got \'int\''), - (1, ExistingDirectory('tests'), TypeError, 'expected \'ExistingFile\', got \'int\''), - (None, ExistingDirectory('tests'), TypeError, 'expected \'ExistingFile\', got \'NoneType\''), - (None, None, TypeError, 'expected \'ExistingFile\', got \'NoneType\''), - (1, None, TypeError, 'expected \'ExistingFile\', got \'int\''), - - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.h'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/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.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.py'), - (ExistingFile('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), - (ExistingFile('tests/unit/core/python/_core/directory.with.more.than.one.dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: 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.g'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.h'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.h'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/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.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.g.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.g.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.f.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.f.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.e.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.e.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.d.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.d.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.c.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.c.py'), + (pathlib.Path('tests/unit/core/python/_core/files_with_more_than_one_dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), + (pathlib.Path('tests/unit/core/python/_core/directory.with.more.than.one.dot/a.b.py'), None, InvalidNamespaceError, 'namespace identifier cannot contain more than one dot: a.b.py'), ]) def test_PythonModuleDocumentationGenerator_exceptions(module_path, src_directory, expected_exception, expected_exception_msg): with pytest.raises(expected_exception) as exc_info: @@ -51,7 +39,7 @@ def test_PythonModuleDocumentationGenerator_exceptions(module_path, src_director assert str(exc_info.value) == expected_exception_msg @pytest.mark.parametrize('module_documentation_generator,expected', [ - (PythonModuleDocumentationGenerator(ExistingFile('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/'), allow_tables = True, allow_html = False), '''# Module `pkg.module` + (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/'), allow_tables = True, allow_html = False), '''# Module `pkg.module` A test module. @@ -220,7 +208,7 @@ Barks. A dog.'''), - (PythonModuleDocumentationGenerator(ExistingFile('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = False), '''# Module `module` + (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = False), '''# Module `module` A test module. @@ -389,7 +377,7 @@ Barks. A dog.'''), - (PythonModuleDocumentationGenerator(ExistingFile('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = True), '''# Module `module` + (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = True, allow_html = True), '''# Module `module` A test module. @@ -548,7 +536,7 @@ Return type: `None`

Docstring

A dog.
'''), - (PythonModuleDocumentationGenerator(ExistingFile('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = True), '''# Module `module` + (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = True), '''# Module `module` A test module. @@ -749,7 +737,7 @@ Return type: `None`

Docstring

A dog.
'''), - (PythonModuleDocumentationGenerator(ExistingFile('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = False), '''# Module `module` + (PythonModuleDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/src/pkg/module.py'), None, allow_tables = False, allow_html = False), '''# Module `module` A test module. diff --git a/tests/unit/core/python/_core/test__collect_definitions.py b/tests/unit/core/python/_core/test__collect_definitions.py index 713f4c4..cdfadcf 100644 --- a/tests/unit/core/python/_core/test__collect_definitions.py +++ b/tests/unit/core/python/_core/test__collect_definitions.py @@ -83,37 +83,36 @@ class Something: (module('''from src.jcloud_docsgen.core.python import PythonDocumentationGenerator from src.jcloud_docsgen.core.python.namespaces import PythonModuleNamespace, PythonPackageNamespace import pytest -from src.jcloud_docsgen.utils import ExistingDirectory @pytest.mark.parametrize('project_directory,docs_directory', [ (1, 1), (1, None), (42, None), - (ExistingDirectory('tests'), 1), - (ExistingDirectory('tests/'), 1), - (1, ExistingDirectory('tests/')), - (1, ExistingDirectory('tests')), - (None, ExistingDirectory('tests')), + (pathlib.Path('tests'), 1), + (pathlib.Path('tests/'), 1), + (1, pathlib.Path('tests/')), + (1, pathlib.Path('tests')), + (None, pathlib.Path('tests')), ]) def test_PythonDocumentationGenerator_type_exceptions(project_directory, docs_directory): with pytest.raises(TypeError): PythonDocumentationGenerator(project_directory, docs_directory) @pytest.mark.parametrize('python_documentation_generator,expected', [ - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), - (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), + (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])]), ]) def test_PythonDocumentationGenerator_collect_modules(python_documentation_generator: PythonDocumentationGenerator, expected): assert python_documentation_generator.namespace() == expected'''), [ PythonFunctionDefinition('test_PythonDocumentationGenerator_type_exceptions', [ PythonFunctionArgument('project_directory', PythonArgumentKind.NORMAL, None, None), PythonFunctionArgument('docs_directory', PythonArgumentKind.NORMAL, None, None), - ], None, None, ['''pytest.mark.parametrize('project_directory,docs_directory', [(1, 1), (1, None), (42, None), (ExistingDirectory('tests'), 1), (ExistingDirectory('tests/'), 1), (1, ExistingDirectory('tests/')), (1, ExistingDirectory('tests')), (None, ExistingDirectory('tests'))])''']), + ], None, None, ['''pytest.mark.parametrize('project_directory,docs_directory', [(1, 1), (1, None), (42, None), (pathlib.Path('tests'), 1), (pathlib.Path('tests/'), 1), (1, pathlib.Path('tests/')), (1, pathlib.Path('tests')), (None, pathlib.Path('tests'))])''']), PythonFunctionDefinition('test_PythonDocumentationGenerator_collect_modules', [ PythonFunctionArgument('python_documentation_generator', PythonArgumentKind.NORMAL, None, 'PythonDocumentationGenerator'), PythonFunctionArgument('expected', PythonArgumentKind.NORMAL, None, None) - ], None, None, ['''pytest.mark.parametrize('python_documentation_generator,expected', [(PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), (PythonDocumentationGenerator(ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3'), ExistingDirectory('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])])])''']) + ], None, None, ['''pytest.mark.parametrize('python_documentation_generator,expected', [(PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_1/docs')), []), (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_2/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module')])]), (PythonDocumentationGenerator(pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3'), pathlib.Path('tests/unit/core/python/_core/test_project_dirs/pdir_3/docs')), [PythonPackageNamespace('pkg', [PythonModuleNamespace('module'), PythonPackageNamespace('pkg', [PythonModuleNamespace('module2')])])])])''']) ]), (module('''class C1: class C2: diff --git a/tests/unit/utils/test_ExistingDirectory.py b/tests/unit/utils/test_ExistingDirectory.py deleted file mode 100644 index e637cec..0000000 --- a/tests/unit/utils/test_ExistingDirectory.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2026 jCloud - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from src.jcloud_docsgen.utils import ExistingDirectory -import pytest -import pathlib - -@pytest.mark.parametrize('path,expected', [ - ('tests/unit/utils/test_directory/', 'tests/unit/utils/test_directory'), - ('tests/unit/utils/test_directory', 'tests/unit/utils/test_directory'), - ('tests/unit/utils/', 'tests/unit/utils'), - ('tests/unit/utils', 'tests/unit/utils'), - ('/', '/'), - ('./', '.'), - ('.', '.'), - ('../', '..'), - ('..', '..'), -]) -def test_ExistingDirectory(path, expected): - assert ExistingDirectory(path).as_posix() == expected - -@pytest.mark.parametrize('path,expected_exception,expected_exception_message', [ - ('tests/unit/utils/does_not_exist/', FileNotFoundError, '[Errno 2] no such file or directory: \'tests/unit/utils/does_not_exist/\''), - ('tests/unit/utils/does_not_exist', FileNotFoundError, '[Errno 2] no such file or directory: \'tests/unit/utils/does_not_exist\''), - ('', FileNotFoundError, '[Errno 2] no such file or directory: \'\''), - ('README.md', NotADirectoryError, '[Errno 20] not a directory: \'README.md\''), -]) -def test_ExistingDirectory_exceptions(path, expected_exception, expected_exception_message): - with pytest.raises(expected_exception) as exc_info: - ExistingDirectory(path) - assert str(exc_info.value) == expected_exception_message diff --git a/tests/unit/utils/test_ExistingFile.py b/tests/unit/utils/test_ExistingFile.py deleted file mode 100644 index e27d053..0000000 --- a/tests/unit/utils/test_ExistingFile.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2026 jCloud - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from src.jcloud_docsgen.utils import ExistingFile -import pytest -import pathlib - -@pytest.mark.parametrize('path,expected', [ - ('tests/unit/utils/test_file', 'tests/unit/utils/test_file'), -]) -def test_ExistingDirectory(path, expected): - assert ExistingFile(path).as_posix() == expected - -@pytest.mark.parametrize('path,expected_exception,expected_exception_message', [ - ('tests/unit/utils/does_not_exist', FileNotFoundError, '[Errno 2] no such file or directory: \'tests/unit/utils/does_not_exist\''), - ('', FileNotFoundError, '[Errno 2] no such file or directory: \'\''), - ('tests/unit/utils/test_directory', IsADirectoryError, '[Errno 21] is a directory: \'tests/unit/utils/test_directory\''), - ('.', IsADirectoryError, '[Errno 21] is a directory: \'.\''), - ('./', IsADirectoryError, '[Errno 21] is a directory: \'./\''), -]) -def test_ExistingDirectory_exceptions(path, expected_exception, expected_exception_message): - with pytest.raises(expected_exception) as exc_info: - ExistingFile(path) - assert str(exc_info.value) == expected_exception_message From 36cedb390b0701e9dfadcdf95b86431e966a93de Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Sat, 5 Sep 2026 14:31:00 +0200 Subject: [PATCH 3/4] refactor: update all instances of ExistingFile and ExistingDirectory in source code --- src/jcloud_docsgen/cli.py | 5 ++--- src/jcloud_docsgen/core/python/_core.py | 29 ++++++++++--------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/jcloud_docsgen/cli.py b/src/jcloud_docsgen/cli.py index 33c3300..0ff0a18 100644 --- a/src/jcloud_docsgen/cli.py +++ b/src/jcloud_docsgen/cli.py @@ -22,11 +22,10 @@ __all__ = [ import argparse import pathlib -from .utils import ExistingDirectory -def existing_directory(path: str, *args, **kwargs) -> pathlib.Path: +def existing_directory(path: str) -> pathlib.Path: try: - return ExistingDirectory(path, *args, **kwargs) + return pathlib.Path(path) except (FileNotFoundError, NotADirectoryError) as e: raise argparse.ArgumentTypeError(f'{e.filename}{": " + e.args[1] if len(e.args) > 1 else ""}') diff --git a/src/jcloud_docsgen/core/python/_core.py b/src/jcloud_docsgen/core/python/_core.py index 4b2405f..a4db189 100644 --- a/src/jcloud_docsgen/core/python/_core.py +++ b/src/jcloud_docsgen/core/python/_core.py @@ -13,7 +13,7 @@ # limitations under the License. from __future__ import annotations -from ...utils import ExistingDirectory, assert_that_is_instance, ExistingFile, non_empty_str, get_relative_path_segments +from ...utils import non_empty_str, get_relative_path_segments from ...exceptions import PythonArgumentStructureError, InvalidNamespaceError from .namespaces import PythonModuleNamespace, PythonPackageNamespace from .definitions import PythonDefinition, PythonFunctionDefinition, PythonAsyncFunctionDefinition, PythonClassDefinition @@ -742,11 +742,11 @@ class PythonModuleDocumentationGenerator: A documentation generator for a Python module. :param module_path: The path of the module file. - :type module_path: ExistingFile + :type module_path: pathlib.Path :param src_directory: The directory of the source code. It is optional and only used for making the module identifiers in the documentation relative. - :type src_directory: Union[ExistingDirectory, None] + :type src_directory: Union[pathlib.Path, None] :param include_sections: The sections that will be included. :type include_sections: PythonDefinitionDocumentationIncludeSections :param kwargs: The options for ``PythonDefinitionDocumentationGenerator``. @@ -754,15 +754,11 @@ class PythonModuleDocumentationGenerator: def __init__( self, - module_path: ExistingFile, - src_directory: Union[ExistingDirectory, None] = None, + module_path: pathlib.Path, + src_directory: Union[pathlib.Path, None] = None, include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), **kwargs ) -> None: - - assert_that_is_instance(module_path, ExistingFile) - assert_that_is_instance(src_directory, (ExistingDirectory, types.NoneType)) - if module_path.name.count('.') > 1: raise InvalidNamespaceError('namespace identifier cannot contain more than one dot', namespace_identifier = module_path.name) @@ -877,8 +873,8 @@ def _generate_package_namespace_documentation( documentation_directory / (sub_namespace.name + '.md') ).write_text( PythonModuleDocumentationGenerator( - ExistingFile(str(dir / (sub_namespace.name + '.py'))), - ExistingDirectory(str(root_directory)), + pathlib.Path(str(dir / (sub_namespace.name + '.py'))), + pathlib.Path(str(root_directory)), include_sections, **kwargs ).generate_documentation() @@ -889,9 +885,9 @@ class PythonDocumentationGenerator: The class for the documentation generator. :param project_directory: The directory of the project. - :type project_directory: ExistingDirectory + :type project_directory: pathlib.Path :param docs_directory: The directory for the generated documentation. - :type docs_directory: ExistingDirectory + :type docs_directory: pathlib.Path :param include_sections: The sections to be included in the documentation. :type include_sections: PythonDefinitionDocumentationIncludeSections :param kwargs: The options for ``PythonDefinitionDocumentationGenerator``. @@ -899,14 +895,11 @@ class PythonDocumentationGenerator: def __init__( self, - project_directory: ExistingDirectory, - docs_directory: ExistingDirectory, + project_directory: pathlib.Path, + docs_directory: pathlib.Path, include_sections: PythonDefinitionDocumentationIncludeSections = PythonDefinitionDocumentationIncludeSections(), **kwargs ) -> None: - assert_that_is_instance(project_directory, ExistingDirectory) - assert_that_is_instance(docs_directory, ExistingDirectory) - self.project_directory = pathlib.Path(str(project_directory)) self.docs_directory = pathlib.Path(str(docs_directory)) self.include_sections = include_sections From 78e3e020be6b13e9fb3995831f94afb7206e960f Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Sat, 5 Sep 2026 14:41:58 +0200 Subject: [PATCH 4/4] fix(cli): check whether project directory exists and is a directory --- src/jcloud_docsgen/cli.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/jcloud_docsgen/cli.py b/src/jcloud_docsgen/cli.py index 0ff0a18..b153c0a 100644 --- a/src/jcloud_docsgen/cli.py +++ b/src/jcloud_docsgen/cli.py @@ -24,10 +24,12 @@ import argparse import pathlib def existing_directory(path: str) -> pathlib.Path: - try: - return pathlib.Path(path) - except (FileNotFoundError, NotADirectoryError) as e: - raise argparse.ArgumentTypeError(f'{e.filename}{": " + e.args[1] if len(e.args) > 1 else ""}') + _path = path + path = pathlib.Path(_path) + if not path.exists(): + raise argparse.ArgumentTypeError(f'{_path}: no such file or directory') + if not path.is_dir(): + raise argparse.ArgumentTypeError(f'{_path}: not a directory') def main() -> int: argument_parser = argparse.ArgumentParser()