fishertools 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for documentation generation completeness.
|
|
3
|
+
|
|
4
|
+
Feature: fishertools-enhancements
|
|
5
|
+
Property 2: Documentation Generation Completeness
|
|
6
|
+
Validates: Requirements 2.1, 2.2, 2.3, 2.4
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import tempfile
|
|
11
|
+
import os
|
|
12
|
+
from hypothesis import given, strategies as st, assume
|
|
13
|
+
from fishertools.documentation import DocumentationGenerator, APIGenerator
|
|
14
|
+
from fishertools.documentation.models import APIInfo, FunctionInfo
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Test data generators
|
|
18
|
+
@st.composite
|
|
19
|
+
def generate_function_info(draw):
|
|
20
|
+
"""Generate valid FunctionInfo for testing."""
|
|
21
|
+
name = draw(st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd'), blacklist_characters='_')))
|
|
22
|
+
assume(name.isidentifier() and not name.startswith('_'))
|
|
23
|
+
|
|
24
|
+
docstring = draw(st.one_of(
|
|
25
|
+
st.none(),
|
|
26
|
+
st.text(min_size=10, max_size=200)
|
|
27
|
+
))
|
|
28
|
+
|
|
29
|
+
# Generate parameters with type annotations
|
|
30
|
+
param_count = draw(st.integers(min_value=0, max_value=5))
|
|
31
|
+
parameters = {}
|
|
32
|
+
for i in range(param_count):
|
|
33
|
+
param_name = f"param_{i}"
|
|
34
|
+
param_type = draw(st.sampled_from(['str', 'int', 'float', 'bool', 'List[str]', 'Dict[str, Any]', 'Optional[str]']))
|
|
35
|
+
parameters[param_name] = param_type
|
|
36
|
+
|
|
37
|
+
return_type = draw(st.one_of(
|
|
38
|
+
st.none(),
|
|
39
|
+
st.sampled_from(['str', 'int', 'bool', 'List[str]', 'None'])
|
|
40
|
+
))
|
|
41
|
+
|
|
42
|
+
return FunctionInfo(
|
|
43
|
+
name=name,
|
|
44
|
+
docstring=docstring,
|
|
45
|
+
parameters=parameters,
|
|
46
|
+
return_type=return_type,
|
|
47
|
+
module_path="/fake/path/module.py",
|
|
48
|
+
line_number=draw(st.integers(min_value=1, max_value=1000))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@st.composite
|
|
53
|
+
def generate_api_info(draw):
|
|
54
|
+
"""Generate valid APIInfo for testing."""
|
|
55
|
+
module_name = draw(st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd'), blacklist_characters='_')))
|
|
56
|
+
assume(module_name.isidentifier())
|
|
57
|
+
|
|
58
|
+
functions = draw(st.lists(generate_function_info(), min_size=1, max_size=10))
|
|
59
|
+
|
|
60
|
+
classes = draw(st.lists(st.fixed_dictionaries({
|
|
61
|
+
'name': st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll'), blacklist_characters='_')),
|
|
62
|
+
'docstring': st.one_of(st.none(), st.text(min_size=10, max_size=100)),
|
|
63
|
+
'methods': st.lists(generate_function_info(), max_size=3),
|
|
64
|
+
'line_number': st.integers(min_value=1, max_value=1000)
|
|
65
|
+
}), max_size=3))
|
|
66
|
+
|
|
67
|
+
constants = draw(st.dictionaries(
|
|
68
|
+
st.text(min_size=1, max_size=15, alphabet=st.characters(whitelist_categories=('Lu', 'Nd'), blacklist_characters='_')),
|
|
69
|
+
st.text(min_size=1, max_size=50),
|
|
70
|
+
max_size=5
|
|
71
|
+
))
|
|
72
|
+
|
|
73
|
+
imports = draw(st.lists(
|
|
74
|
+
st.text(min_size=5, max_size=30),
|
|
75
|
+
max_size=10
|
|
76
|
+
))
|
|
77
|
+
|
|
78
|
+
docstring = draw(st.one_of(
|
|
79
|
+
st.none(),
|
|
80
|
+
st.text(min_size=20, max_size=200)
|
|
81
|
+
))
|
|
82
|
+
|
|
83
|
+
return APIInfo(
|
|
84
|
+
module_name=module_name,
|
|
85
|
+
functions=functions,
|
|
86
|
+
classes=classes,
|
|
87
|
+
constants=constants,
|
|
88
|
+
imports=imports,
|
|
89
|
+
docstring=docstring
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TestDocumentationGenerationProperties:
|
|
94
|
+
"""Property-based tests for documentation generation completeness."""
|
|
95
|
+
|
|
96
|
+
@given(generate_api_info())
|
|
97
|
+
def test_documentation_generation_completeness(self, api_info):
|
|
98
|
+
"""
|
|
99
|
+
Property 2: Documentation Generation Completeness
|
|
100
|
+
|
|
101
|
+
For any code change in the fishertools library, the Documentation_Generator
|
|
102
|
+
should automatically extract all docstrings, parameter types, and generate
|
|
103
|
+
structured navigation with usage examples for each function.
|
|
104
|
+
|
|
105
|
+
**Validates: Requirements 2.1, 2.2, 2.3, 2.4**
|
|
106
|
+
"""
|
|
107
|
+
generator = DocumentationGenerator("test_project")
|
|
108
|
+
|
|
109
|
+
# Test requirement 2.1: Extract all docstrings and parameter types
|
|
110
|
+
sphinx_docs = generator.generate_sphinx_docs(api_info)
|
|
111
|
+
|
|
112
|
+
# Verify all docstrings are extracted (2.1)
|
|
113
|
+
rst_content = sphinx_docs.source_files[f"{api_info.module_name}.rst"]
|
|
114
|
+
|
|
115
|
+
# Check that module docstring is included if present
|
|
116
|
+
if api_info.docstring:
|
|
117
|
+
assert api_info.module_name in rst_content
|
|
118
|
+
|
|
119
|
+
# Check that all functions are documented (2.2)
|
|
120
|
+
for function in api_info.functions:
|
|
121
|
+
assert f"autofunction:: {api_info.module_name}.{function.name}" in rst_content
|
|
122
|
+
|
|
123
|
+
# Check that all classes are documented (2.2)
|
|
124
|
+
for cls in api_info.classes:
|
|
125
|
+
assert f"autoclass:: {api_info.module_name}.{cls['name']}" in rst_content
|
|
126
|
+
|
|
127
|
+
# Check that all constants are documented (2.2)
|
|
128
|
+
for constant_name in api_info.constants.keys():
|
|
129
|
+
assert f"autodata:: {api_info.module_name}.{constant_name}" in rst_content
|
|
130
|
+
|
|
131
|
+
# Test requirement 2.3: Generate structured navigation
|
|
132
|
+
navigation = sphinx_docs.navigation
|
|
133
|
+
assert navigation is not None
|
|
134
|
+
assert navigation.name == "test_project"
|
|
135
|
+
assert len(navigation.children) >= 1
|
|
136
|
+
|
|
137
|
+
# Verify navigation includes the module
|
|
138
|
+
module_found = any(child.name == api_info.module_name for child in navigation.children)
|
|
139
|
+
assert module_found, f"Module {api_info.module_name} not found in navigation"
|
|
140
|
+
|
|
141
|
+
# Test requirement 2.4: Generate usage examples for each function
|
|
142
|
+
for function in api_info.functions:
|
|
143
|
+
examples = generator.add_usage_examples(function)
|
|
144
|
+
assert len(examples) >= 1, f"No examples generated for function {function.name}"
|
|
145
|
+
|
|
146
|
+
# Verify each example has required components
|
|
147
|
+
for example in examples:
|
|
148
|
+
assert example.code is not None and len(example.code.strip()) > 0
|
|
149
|
+
assert example.description is not None and len(example.description.strip()) > 0
|
|
150
|
+
assert function.name in example.code, f"Function {function.name} not found in example code"
|
|
151
|
+
|
|
152
|
+
@given(st.lists(generate_api_info(), min_size=1, max_size=5))
|
|
153
|
+
def test_multiple_modules_documentation_completeness(self, api_infos):
|
|
154
|
+
"""
|
|
155
|
+
Test documentation generation completeness for multiple modules.
|
|
156
|
+
|
|
157
|
+
Validates that the generator can handle multiple modules and create
|
|
158
|
+
proper navigation structure for all of them.
|
|
159
|
+
"""
|
|
160
|
+
generator = DocumentationGenerator("multi_module_project")
|
|
161
|
+
|
|
162
|
+
# Create temporary module files for testing
|
|
163
|
+
module_paths = []
|
|
164
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
165
|
+
for i, api_info in enumerate(api_infos):
|
|
166
|
+
module_path = os.path.join(temp_dir, f"{api_info.module_name}.py")
|
|
167
|
+
# Create a minimal Python file
|
|
168
|
+
with open(module_path, 'w') as f:
|
|
169
|
+
f.write(f'"""{api_info.docstring or "Module docstring"}"""\n')
|
|
170
|
+
for func in api_info.functions:
|
|
171
|
+
f.write(f'def {func.name}():\n """{func.docstring or "Function docstring"}"""\n pass\n\n')
|
|
172
|
+
module_paths.append(module_path)
|
|
173
|
+
|
|
174
|
+
# Test building documentation for multiple modules
|
|
175
|
+
try:
|
|
176
|
+
sphinx_docs = generator.build_documentation(module_paths)
|
|
177
|
+
|
|
178
|
+
# Verify all modules are included
|
|
179
|
+
for api_info in api_infos:
|
|
180
|
+
rst_file = f"{api_info.module_name}.rst"
|
|
181
|
+
assert rst_file in sphinx_docs.source_files
|
|
182
|
+
|
|
183
|
+
# Verify navigation includes all modules
|
|
184
|
+
assert len(sphinx_docs.navigation.children) == len(api_infos)
|
|
185
|
+
|
|
186
|
+
# Verify index.rst includes all modules
|
|
187
|
+
index_content = sphinx_docs.source_files["index.rst"]
|
|
188
|
+
for api_info in api_infos:
|
|
189
|
+
assert api_info.module_name in index_content
|
|
190
|
+
|
|
191
|
+
except Exception as e:
|
|
192
|
+
# If parsing fails due to invalid generated code, that's acceptable
|
|
193
|
+
# as we're testing with synthetic data
|
|
194
|
+
pytest.skip(f"Skipping due to synthetic data parsing issue: {e}")
|
|
195
|
+
|
|
196
|
+
@given(generate_function_info())
|
|
197
|
+
def test_usage_examples_generation_completeness(self, function_info):
|
|
198
|
+
"""
|
|
199
|
+
Test that usage examples are generated for any function.
|
|
200
|
+
|
|
201
|
+
Validates requirement 2.4: Generate usage examples for each function.
|
|
202
|
+
"""
|
|
203
|
+
generator = DocumentationGenerator("test_project")
|
|
204
|
+
|
|
205
|
+
examples = generator.add_usage_examples(function_info)
|
|
206
|
+
|
|
207
|
+
# Must generate at least one example
|
|
208
|
+
assert len(examples) >= 1
|
|
209
|
+
|
|
210
|
+
# Each example must have valid structure
|
|
211
|
+
for example in examples:
|
|
212
|
+
assert example.code is not None
|
|
213
|
+
assert len(example.code.strip()) > 0
|
|
214
|
+
assert example.description is not None
|
|
215
|
+
assert len(example.description.strip()) > 0
|
|
216
|
+
assert example.language == "python"
|
|
217
|
+
|
|
218
|
+
# Function name should appear in the example code
|
|
219
|
+
assert function_info.name in example.code
|
|
220
|
+
|
|
221
|
+
# Code should be syntactically valid Python (basic check)
|
|
222
|
+
assert "=" in example.code or function_info.name + "(" in example.code
|
|
223
|
+
|
|
224
|
+
def test_sphinx_configuration_completeness(self):
|
|
225
|
+
"""
|
|
226
|
+
Test that Sphinx configuration includes all required extensions and settings.
|
|
227
|
+
|
|
228
|
+
Validates that the generated Sphinx configuration supports autodoc,
|
|
229
|
+
viewcode, napoleon, and other required extensions.
|
|
230
|
+
"""
|
|
231
|
+
generator = DocumentationGenerator("test_project")
|
|
232
|
+
config = generator._generate_sphinx_config()
|
|
233
|
+
|
|
234
|
+
# Required configuration keys
|
|
235
|
+
required_keys = ['project', 'extensions', 'html_theme']
|
|
236
|
+
for key in required_keys:
|
|
237
|
+
assert key in config
|
|
238
|
+
|
|
239
|
+
# Required extensions for API documentation
|
|
240
|
+
required_extensions = [
|
|
241
|
+
'sphinx.ext.autodoc',
|
|
242
|
+
'sphinx.ext.viewcode',
|
|
243
|
+
'sphinx.ext.napoleon'
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
for ext in required_extensions:
|
|
247
|
+
assert ext in config['extensions']
|
|
248
|
+
|
|
249
|
+
# Autodoc configuration should be present
|
|
250
|
+
assert 'autodoc_default_options' in config
|
|
251
|
+
autodoc_options = config['autodoc_default_options']
|
|
252
|
+
assert autodoc_options.get('members') is True
|
|
253
|
+
assert autodoc_options.get('undoc-members') is True
|