Add Python module documentation generator

This commit is contained in:
2026-04-25 19:34:34 +02:00
parent ecd3f7e76b
commit a17e56fb3d
18 changed files with 1097 additions and 6 deletions
@@ -0,0 +1,964 @@
# Copyright 2026 jCloud Services GbR
# 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.core.python import PythonModuleDocumentationGenerator
from src.jcloud_docsgen.exceptions import InvalidNamespaceError
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'),
])
def test_PythonModuleDocumentationGenerator_exceptions(module_path, src_directory, expected_exception, expected_exception_msg):
with pytest.raises(expected_exception) as exc_info:
PythonModuleDocumentationGenerator(module_path, src_directory)
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`
A test module.
## Function `add`
Adds two numbers.
### Function signature
```python
add(a: int | float, b: int | float) -> int | float
```
### Returns
The sum.
Return type: `int | float`
### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `a` | `int | float` | normal | -- | The first number. |
| `b` | `int | float` | normal | -- | The second number. |
### Docstring
Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float
## Class `Animal`
A class representing an animal.
### Body
#### Constructor (method `__init__`)
##### Method signature
```python
__init__(self, name: str) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
| `name` | `str` | normal | -- | -- |
#### Method `greet`
Returns a greeting.
##### Method signature
```python
greet(self)
```
##### Returns
The greeting.
Return type: `str`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
##### Docstring
Returns a greeting.
:return: The greeting.
:rtype: str
### Docstring
A class representing an animal.
:param name: Its name.
:type name: str
## Class `Dog`
A dog.
### Base classes
- `Animal`
### Body
#### Method `bark`
Barks.
##### Method signature
```python
bark(self) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
##### Docstring
Barks.
### Docstring
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`
A test module.
## Function `add`
Adds two numbers.
### Function signature
```python
add(a: int | float, b: int | float) -> int | float
```
### Returns
The sum.
Return type: `int | float`
### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `a` | `int | float` | normal | -- | The first number. |
| `b` | `int | float` | normal | -- | The second number. |
### Docstring
Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float
## Class `Animal`
A class representing an animal.
### Body
#### Constructor (method `__init__`)
##### Method signature
```python
__init__(self, name: str) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
| `name` | `str` | normal | -- | -- |
#### Method `greet`
Returns a greeting.
##### Method signature
```python
greet(self)
```
##### Returns
The greeting.
Return type: `str`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
##### Docstring
Returns a greeting.
:return: The greeting.
:rtype: str
### Docstring
A class representing an animal.
:param name: Its name.
:type name: str
## Class `Dog`
A dog.
### Base classes
- `Animal`
### Body
#### Method `bark`
Barks.
##### Method signature
```python
bark(self) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
##### Docstring
Barks.
### Docstring
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`
A test module.
## Function `add`
Adds two numbers.
### Function signature
```python
add(a: int | float, b: int | float) -> int | float
```
### Returns
The sum.
Return type: `int | float`
### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `a` | `int | float` | normal | -- | The first number. |
| `b` | `int | float` | normal | -- | The second number. |
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float</details>
## Class `Animal`
A class representing an animal.
### Body
#### Constructor (method `__init__`)
##### Method signature
```python
__init__(self, name: str) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
| `name` | `str` | normal | -- | -- |
#### Method `greet`
Returns a greeting.
##### Method signature
```python
greet(self)
```
##### Returns
The greeting.
Return type: `str`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
<details><summary><h5 style="display:inline">Docstring</h5></summary>Returns a greeting.
:return: The greeting.
:rtype: str</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A class representing an animal.
:param name: Its name.
:type name: str</details>
## Class `Dog`
A dog.
### Base classes
- `Animal`
### Body
#### Method `bark`
Barks.
##### Method signature
```python
bark(self) -> None
```
##### Returns
Return type: `None`
##### Parameters
| Parameter | Type | Kind | Default value | Description |
| --- | --- | --- | --- | --- |
| `self` | -- | normal | -- | -- |
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.</details>'''),
(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`
A test module.
## Function `add`
Adds two numbers.
### Function signature
```python
add(a: int | float, b: int | float) -> int | float
```
### Returns
The sum.
Return type: `int | float`
### Parameters
- Parameter: `a`
Type: `int | float`
Kind: normal
Default value: --
Description: The first number.
- Parameter: `b`
Type: `int | float`
Kind: normal
Default value: --
Description: The second number.
<details><summary><h3 style="display:inline">Docstring</h3></summary>Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float</details>
## Class `Animal`
A class representing an animal.
### Body
#### Constructor (method `__init__`)
##### Method signature
```python
__init__(self, name: str) -> None
```
##### Returns
Return type: `None`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
- Parameter: `name`
Type: `str`
Kind: normal
Default value: --
Description: --
#### Method `greet`
Returns a greeting.
##### Method signature
```python
greet(self)
```
##### Returns
The greeting.
Return type: `str`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
<details><summary><h5 style="display:inline">Docstring</h5></summary>Returns a greeting.
:return: The greeting.
:rtype: str</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A class representing an animal.
:param name: Its name.
:type name: str</details>
## Class `Dog`
A dog.
### Base classes
- `Animal`
### Body
#### Method `bark`
Barks.
##### Method signature
```python
bark(self) -> None
```
##### Returns
Return type: `None`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
<details><summary><h5 style="display:inline">Docstring</h5></summary>Barks.</details>
<details><summary><h3 style="display:inline">Docstring</h3></summary>A dog.</details>'''),
(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`
A test module.
## Function `add`
Adds two numbers.
### Function signature
```python
add(a: int | float, b: int | float) -> int | float
```
### Returns
The sum.
Return type: `int | float`
### Parameters
- Parameter: `a`
Type: `int | float`
Kind: normal
Default value: --
Description: The first number.
- Parameter: `b`
Type: `int | float`
Kind: normal
Default value: --
Description: The second number.
### Docstring
Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float
## Class `Animal`
A class representing an animal.
### Body
#### Constructor (method `__init__`)
##### Method signature
```python
__init__(self, name: str) -> None
```
##### Returns
Return type: `None`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
- Parameter: `name`
Type: `str`
Kind: normal
Default value: --
Description: --
#### Method `greet`
Returns a greeting.
##### Method signature
```python
greet(self)
```
##### Returns
The greeting.
Return type: `str`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
##### Docstring
Returns a greeting.
:return: The greeting.
:rtype: str
### Docstring
A class representing an animal.
:param name: Its name.
:type name: str
## Class `Dog`
A dog.
### Base classes
- `Animal`
### Body
#### Method `bark`
Barks.
##### Method signature
```python
bark(self) -> None
```
##### Returns
Return type: `None`
##### Parameters
- Parameter: `self`
Type: --
Kind: normal
Default value: --
Description: --
##### Docstring
Barks.
### Docstring
A dog.'''),
])
def test_PythonModuleDocumentationGenerator_exceptions(module_documentation_generator, expected):
assert module_documentation_generator.generate_documentation() == expected
@@ -0,0 +1,50 @@
'''
A test module.
'''
def add(a: int | float, b: int | float) -> int | float:
'''
Adds two numbers.
:param a: The first number.
:type a: int | float
:param b: The second number.
:type b: int | float
:return: The sum.
:rtype: int | float
'''
return a + b
class Animal:
'''
A class representing an animal.
:param name: Its name.
:type name: str
'''
def __init__(self, name: str) -> None:
self.name = name
def greet(self):
'''
Returns a greeting.
:return: The greeting.
:rtype: str
'''
return f'Hi there! My name is {self.name}'
class Dog(Animal):
'''
A dog.
'''
def bark(self) -> None:
'''
Barks.
'''
print('Woof!')