Skip to content

Commit 540504f

Browse files
committed
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
1 parent 2802a79 commit 540504f

7 files changed

Lines changed: 334 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: 20 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,16 @@ 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(
70+
self, file_path: str | os.PathLike, decorator_names: list[str]
71+
) -> None:
7072
"""
7173
Get information about functions and classes, if annotated with "Requirements" or "SVCs":
7274
decorator filepath, elementKind, name and decorators is saved to list that is returned.
7375
7476
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.
77+
- `file_path` (str | PathLike): The path to the Python file.
78+
- `decorator_names` (list[str]): List of decorator names to search for.
8079
8180
Each dictionary includes:
8281
- `fullyQualifiedName` (str): The fully qualified name of the file.
@@ -85,7 +84,7 @@ def get_functions_and_classes(self, file_path, decorator_names):
8584
- `decorators` (list): List of dictionaries with decorator info including name and arguments e.g. "REQ_001".
8685
"""
8786
with open(file_path, "r") as file:
88-
tree = ast.parse(file.read(), filename=file_path)
87+
tree = ast.parse(file.read(), filename=str(file_path))
8988

9089
for node in ast.walk(tree):
9190
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
@@ -100,18 +99,18 @@ def get_functions_and_classes(self, file_path, decorator_names):
10099
self.req_svc_results.append(
101100
{
102101
"fullyQualifiedName": str(file_path).replace("/", "."),
103-
"elementKind": str(type(node)).split(".")[-1][:-5].upper(),
102+
"elementKind": node.__class__.__name__[:-3].upper(),
104103
"name": node.name,
105104
"decorators": decorators_info,
106105
}
107106
)
108107

109-
def write_to_yaml(self, output_file, formatted_data):
108+
def write_to_yaml(self, output_file: str | os.PathLike, formatted_data) -> None:
110109
"""
111110
Write formatted data to a YAML file.
112111
113112
Parameters:
114-
- `output_file` (str): The path to the output YAML file.
113+
- `output_file` (str | PathLike): The path to the output YAML file.
115114
- `formatted_data` (dict): The formatted data to be written to the YAML file.
116115
117116
Writes the formatted data to the specified YAML file.
@@ -135,7 +134,7 @@ def map_type(self, input_str) -> str:
135134
mapping = {item.from_value: item.to_value for item in DECORATOR_TYPES}
136135
return mapping.get(input_str, input_str)
137136

138-
def format_results(self, results):
137+
def format_results(self, results) -> dict:
139138
"""
140139
Format the collected results into a structured data format for YAML.
141140
@@ -174,31 +173,35 @@ def format_results(self, results):
174173

175174
return formatted_data
176175

177-
def create_dir_from_path(self, filepath: str) -> None:
176+
def create_dir_from_path(self, filepath: str | os.PathLike) -> None:
178177
"""
179178
Creates directory of provided filepath if it does not exists
180179
181180
Parameters:
182-
- `filepath` (str): Filepath to check and create directory from.
181+
- `filepath` (str | PathLike): Filepath to check and create directory from.
183182
"""
184183
directory = os.path.dirname(filepath)
185184
if not os.path.exists(directory):
186185
os.makedirs(directory)
187186

188187
def process_decorated_data(
189-
self, path_to_python_files: str, output_file: str = "build/reqstool/annotations.yml"
188+
self,
189+
path_to_python_files: list[str | os.PathLike],
190+
output_file: str | os.PathLike = "build/reqstool/annotations.yml",
190191
) -> None:
191192
"""
192193
"Main" function, runs all functions resulting in a yaml file containing decorated data.
193194
194195
Parameters:
195196
- `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
197+
- `output_file` (str | PathLike): Set path for output file, defaults to build/annotations.yml
197198
198199
This method takes a list of directories containing Python files, collects decorated data from these files,
199200
formats the collected data, and writes the formatted results to YAML file for Requirements and SVCs annotations.
200201
"""
201202

203+
self.req_svc_results = []
204+
202205
for path in path_to_python_files:
203206
python_files = self.find_python_files(directory=path)
204207
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)