Skip to content

Commit 4b745a3

Browse files
authored
fix: comprehensive tests and bug fixes for decorators and processor (#60)
* fix: comprehensive tests and bug fixes for decorators and processor - Create missing src/reqstool_python_decorators/decorators/__init__.py - Fix broken import paths in test resources and README (src.reqstool_decorators → reqstool_python_decorators) - Fix state accumulation bug: reset req_svc_results at start of process_decorated_data() to prevent results doubling on repeated calls - Fix fragile AST elementKind extraction: replace CPython repr parsing str(type(node)).split(".")[-1][:-5] with node.__class__.__name__[:-3] - Add str | os.PathLike to all path/file parameters - Add test_decorators.py with 7 tests covering Requirements/SVCs decorators - Expand test_processors.py from 5 to 32 tests covering all previously untested methods; standardise fixtures on tmp_path * style: apply black formatting to decorator_processor
1 parent 2802a79 commit 4b745a3

7 files changed

Lines changed: 332 additions & 33 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ reqstool-python-decorators = "<version>"
4545
Import decorators:
4646

4747
```
48-
from reqstool-decorators.decorators.decorators import Requirements, SVCs
48+
from reqstool_python_decorators.decorators.decorators import Requirements, SVCs
4949
```
5050

5151
Example usage of the decorators:
@@ -65,7 +65,7 @@ def test_somefunction():
6565
Import processor:
6666

6767
```
68-
from reqstool.processors.decorator_processor import DecoratorProcessor
68+
from reqstool_python_decorators.processors.decorator_processor import DecoratorProcessor
6969
```
7070

7171
Main function to collect decorators data and generate yaml file:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Copyright © LFV

‎src/reqstool_python_decorators/processors/decorator_processor.py‎

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ def __init__(self, *args, **kwargs):
4949
super().__init__(*args, **kwargs)
5050
self.req_svc_results = []
5151

52-
def find_python_files(self, directory):
52+
def find_python_files(self, directory: str | os.PathLike) -> list[str]:
5353
"""
5454
Find Python files in the given directory.
5555
5656
Parameters:
57-
- `directory` (str): The directory to search for Python files.
57+
- `directory` (str | PathLike): The directory to search for Python files.
5858
5959
Returns:
6060
- `python_files` (list): List of Python files found in the directory.
@@ -66,17 +66,14 @@ def find_python_files(self, directory):
6666
python_files.append(os.path.join(root, file))
6767
return python_files
6868

69-
def get_functions_and_classes(self, file_path, decorator_names):
69+
def get_functions_and_classes(self, file_path: str | os.PathLike, decorator_names: list[str]) -> None:
7070
"""
7171
Get information about functions and classes, if annotated with "Requirements" or "SVCs":
7272
decorator filepath, elementKind, name and decorators is saved to list that is returned.
7373
7474
Parameters:
75-
- `file_path` (str): The path to the Python file.
76-
- `decorator_names` (list): List of decorator names to search for.
77-
78-
Returns:
79-
- `results` (list): List of dictionaries containing information about functions and classes.
75+
- `file_path` (str | PathLike): The path to the Python file.
76+
- `decorator_names` (list[str]): List of decorator names to search for.
8077
8178
Each dictionary includes:
8279
- `fullyQualifiedName` (str): The fully qualified name of the file.
@@ -85,7 +82,7 @@ def get_functions_and_classes(self, file_path, decorator_names):
8582
- `decorators` (list): List of dictionaries with decorator info including name and arguments e.g. "REQ_001".
8683
"""
8784
with open(file_path, "r") as file:
88-
tree = ast.parse(file.read(), filename=file_path)
85+
tree = ast.parse(file.read(), filename=str(file_path))
8986

9087
for node in ast.walk(tree):
9188
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
@@ -100,18 +97,18 @@ def get_functions_and_classes(self, file_path, decorator_names):
10097
self.req_svc_results.append(
10198
{
10299
"fullyQualifiedName": str(file_path).replace("/", "."),
103-
"elementKind": str(type(node)).split(".")[-1][:-5].upper(),
100+
"elementKind": node.__class__.__name__[:-3].upper(),
104101
"name": node.name,
105102
"decorators": decorators_info,
106103
}
107104
)
108105

109-
def write_to_yaml(self, output_file, formatted_data):
106+
def write_to_yaml(self, output_file: str | os.PathLike, formatted_data) -> None:
110107
"""
111108
Write formatted data to a YAML file.
112109
113110
Parameters:
114-
- `output_file` (str): The path to the output YAML file.
111+
- `output_file` (str | PathLike): The path to the output YAML file.
115112
- `formatted_data` (dict): The formatted data to be written to the YAML file.
116113
117114
Writes the formatted data to the specified YAML file.
@@ -135,7 +132,7 @@ def map_type(self, input_str) -> str:
135132
mapping = {item.from_value: item.to_value for item in DECORATOR_TYPES}
136133
return mapping.get(input_str, input_str)
137134

138-
def format_results(self, results):
135+
def format_results(self, results) -> dict:
139136
"""
140137
Format the collected results into a structured data format for YAML.
141138
@@ -174,31 +171,35 @@ def format_results(self, results):
174171

175172
return formatted_data
176173

177-
def create_dir_from_path(self, filepath: str) -> None:
174+
def create_dir_from_path(self, filepath: str | os.PathLike) -> None:
178175
"""
179176
Creates directory of provided filepath if it does not exists
180177
181178
Parameters:
182-
- `filepath` (str): Filepath to check and create directory from.
179+
- `filepath` (str | PathLike): Filepath to check and create directory from.
183180
"""
184181
directory = os.path.dirname(filepath)
185182
if not os.path.exists(directory):
186183
os.makedirs(directory)
187184

188185
def process_decorated_data(
189-
self, path_to_python_files: str, output_file: str = "build/reqstool/annotations.yml"
186+
self,
187+
path_to_python_files: list[str | os.PathLike],
188+
output_file: str | os.PathLike = "build/reqstool/annotations.yml",
190189
) -> None:
191190
"""
192191
"Main" function, runs all functions resulting in a yaml file containing decorated data.
193192
194193
Parameters:
195194
- `path_to_python_files` (list): List of directories containing Python files.
196-
- `output_file` (str): Set path for output file, defaults to build/annotations.yml
195+
- `output_file` (str | PathLike): Set path for output file, defaults to build/annotations.yml
197196
198197
This method takes a list of directories containing Python files, collects decorated data from these files,
199198
formats the collected data, and writes the formatted results to YAML file for Requirements and SVCs annotations.
200199
"""
201200

201+
self.req_svc_results = []
202+
202203
for path in path_to_python_files:
203204
python_files = self.find_python_files(directory=path)
204205
for file_path in python_files:

‎tests/resources/test_decorators/requirements_decorators.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from src.reqstool_decorators.decorators.decorators import Requirements
1+
from reqstool_python_decorators.decorators.decorators import Requirements
22

33

44
@Requirements("REQ_001", "REQ_222")

‎tests/resources/test_decorators/svc_decorators.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from src.reqstool_decorators.decorators.decorators import SVCs
1+
from reqstool_python_decorators.decorators.decorators import SVCs
22

33

44
@SVCs("SVC_999")

0 commit comments

Comments
 (0)