fishertools 0.2.1__py3-none-any.whl → 0.4.0__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 +16 -5
- fishertools/errors/__init__.py +11 -3
- fishertools/errors/exception_types.py +282 -0
- fishertools/errors/explainer.py +87 -1
- fishertools/errors/models.py +73 -1
- fishertools/errors/patterns.py +40 -0
- fishertools/examples/cli_example.py +156 -0
- fishertools/examples/learn_example.py +65 -0
- fishertools/examples/logger_example.py +176 -0
- fishertools/examples/menu_example.py +101 -0
- fishertools/examples/storage_example.py +175 -0
- fishertools/input_utils.py +185 -0
- fishertools/learn/__init__.py +19 -2
- fishertools/learn/examples.py +88 -1
- fishertools/learn/knowledge_engine.py +321 -0
- fishertools/learn/repl/__init__.py +19 -0
- fishertools/learn/repl/cli.py +31 -0
- fishertools/learn/repl/code_sandbox.py +229 -0
- fishertools/learn/repl/command_handler.py +544 -0
- fishertools/learn/repl/command_parser.py +165 -0
- fishertools/learn/repl/engine.py +479 -0
- fishertools/learn/repl/models.py +121 -0
- fishertools/learn/repl/session_manager.py +284 -0
- fishertools/learn/repl/test_code_sandbox.py +261 -0
- fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
- fishertools/learn/repl/test_command_handler.py +224 -0
- fishertools/learn/repl/test_command_handler_pbt.py +189 -0
- fishertools/learn/repl/test_command_parser.py +160 -0
- fishertools/learn/repl/test_command_parser_pbt.py +100 -0
- fishertools/learn/repl/test_engine.py +190 -0
- fishertools/learn/repl/test_session_manager.py +310 -0
- fishertools/learn/repl/test_session_manager_pbt.py +182 -0
- fishertools/learn/test_knowledge_engine.py +241 -0
- fishertools/learn/test_knowledge_engine_pbt.py +180 -0
- fishertools/patterns/__init__.py +46 -0
- fishertools/patterns/cli.py +175 -0
- fishertools/patterns/logger.py +140 -0
- fishertools/patterns/menu.py +99 -0
- fishertools/patterns/storage.py +127 -0
- fishertools/readme_transformer.py +631 -0
- fishertools/safe/__init__.py +6 -1
- fishertools/safe/files.py +329 -1
- fishertools/transform_readme.py +105 -0
- fishertools-0.4.0.dist-info/METADATA +104 -0
- fishertools-0.4.0.dist-info/RECORD +131 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
- tests/test_documentation_properties.py +329 -0
- tests/test_documentation_structure.py +349 -0
- tests/test_errors/test_exception_types.py +446 -0
- tests/test_errors/test_exception_types_pbt.py +333 -0
- tests/test_errors/test_patterns.py +52 -0
- tests/test_input_utils/__init__.py +1 -0
- tests/test_input_utils/test_input_utils.py +65 -0
- tests/test_learn/test_examples.py +179 -1
- tests/test_learn/test_explain_properties.py +307 -0
- tests/test_patterns_cli.py +611 -0
- tests/test_patterns_docstrings.py +473 -0
- tests/test_patterns_logger.py +465 -0
- tests/test_patterns_menu.py +440 -0
- tests/test_patterns_storage.py +447 -0
- tests/test_readme_enhancements_v0_3_1.py +2036 -0
- tests/test_readme_transformer/__init__.py +1 -0
- tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
- tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
- tests/test_safe/test_files.py +726 -1
- fishertools-0.2.1.dist-info/METADATA +0 -256
- fishertools-0.2.1.dist-info/RECORD +0 -81
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SimpleLogger pattern for file-based logging.
|
|
3
|
+
|
|
4
|
+
This module provides the SimpleLogger class for adding logging to applications
|
|
5
|
+
without complex configuration. Messages are written to a file with timestamps
|
|
6
|
+
and log levels.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
logger = SimpleLogger("app.log")
|
|
10
|
+
logger.info("Application started")
|
|
11
|
+
logger.warning("Low memory detected")
|
|
12
|
+
logger.error("Connection failed")
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SimpleLogger:
|
|
21
|
+
"""
|
|
22
|
+
Simple file-based logging with timestamps and levels.
|
|
23
|
+
|
|
24
|
+
This class provides a straightforward way to log messages to a file with
|
|
25
|
+
automatic timestamps and log level indicators. No configuration is required.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
file_path (str): Path to the log file. Created automatically if it
|
|
29
|
+
doesn't exist. Parent directories are created as needed.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
file_path (str): The path to the log file.
|
|
33
|
+
|
|
34
|
+
Methods:
|
|
35
|
+
info(message): Log an info-level message.
|
|
36
|
+
warning(message): Log a warning-level message.
|
|
37
|
+
error(message): Log an error-level message.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
IOError: If file operations fail.
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
logger = SimpleLogger("logs/app.log")
|
|
44
|
+
logger.info("User logged in")
|
|
45
|
+
logger.warning("Deprecated function used")
|
|
46
|
+
logger.error("Database connection failed")
|
|
47
|
+
|
|
48
|
+
Note:
|
|
49
|
+
- Log format: [TIMESTAMP] [LEVEL] message
|
|
50
|
+
- Timestamp format: YYYY-MM-DD HH:MM:SS
|
|
51
|
+
- Messages are appended to the file
|
|
52
|
+
- File is created automatically on first write
|
|
53
|
+
- Parent directories are created automatically
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, file_path):
|
|
57
|
+
"""
|
|
58
|
+
Initialize SimpleLogger with a file path.
|
|
59
|
+
|
|
60
|
+
Parameters:
|
|
61
|
+
file_path (str): Path to the log file.
|
|
62
|
+
"""
|
|
63
|
+
self.file_path = file_path
|
|
64
|
+
|
|
65
|
+
def info(self, message):
|
|
66
|
+
"""
|
|
67
|
+
Log an info-level message.
|
|
68
|
+
|
|
69
|
+
Parameters:
|
|
70
|
+
message (str): The message to log.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
None
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
IOError: If file write fails.
|
|
77
|
+
"""
|
|
78
|
+
self._log("INFO", message)
|
|
79
|
+
|
|
80
|
+
def warning(self, message):
|
|
81
|
+
"""
|
|
82
|
+
Log a warning-level message.
|
|
83
|
+
|
|
84
|
+
Parameters:
|
|
85
|
+
message (str): The message to log.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
None
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
IOError: If file write fails.
|
|
92
|
+
"""
|
|
93
|
+
self._log("WARNING", message)
|
|
94
|
+
|
|
95
|
+
def error(self, message):
|
|
96
|
+
"""
|
|
97
|
+
Log an error-level message.
|
|
98
|
+
|
|
99
|
+
Parameters:
|
|
100
|
+
message (str): The message to log.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
None
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
IOError: If file write fails.
|
|
107
|
+
"""
|
|
108
|
+
self._log("ERROR", message)
|
|
109
|
+
|
|
110
|
+
def _log(self, level, message):
|
|
111
|
+
"""
|
|
112
|
+
Internal method to write a log message.
|
|
113
|
+
|
|
114
|
+
Parameters:
|
|
115
|
+
level (str): The log level (INFO, WARNING, ERROR).
|
|
116
|
+
message (str): The message to log.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
None
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
IOError: If file write fails.
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
# Create parent directories if they don't exist
|
|
126
|
+
parent_dir = os.path.dirname(self.file_path)
|
|
127
|
+
if parent_dir:
|
|
128
|
+
Path(parent_dir).mkdir(parents=True, exist_ok=True)
|
|
129
|
+
|
|
130
|
+
# Get current timestamp
|
|
131
|
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
132
|
+
|
|
133
|
+
# Format log message
|
|
134
|
+
log_entry = f"[{timestamp}] [{level}] {message}\n"
|
|
135
|
+
|
|
136
|
+
# Append to log file with UTF-8 encoding
|
|
137
|
+
with open(self.file_path, 'a', encoding='utf-8') as f:
|
|
138
|
+
f.write(log_entry)
|
|
139
|
+
except IOError as e:
|
|
140
|
+
raise IOError(f"Failed to write to {self.file_path}: {e}")
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple menu pattern for interactive console menus.
|
|
3
|
+
|
|
4
|
+
This module provides the simple_menu() function for creating interactive
|
|
5
|
+
console-based menus without complex configuration.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
def greet():
|
|
9
|
+
print("Hello!")
|
|
10
|
+
|
|
11
|
+
def goodbye():
|
|
12
|
+
print("Goodbye!")
|
|
13
|
+
|
|
14
|
+
simple_menu({
|
|
15
|
+
"Greet": greet,
|
|
16
|
+
"Say goodbye": goodbye
|
|
17
|
+
})
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def simple_menu(options):
|
|
22
|
+
"""
|
|
23
|
+
Create an interactive console menu.
|
|
24
|
+
|
|
25
|
+
Displays a numbered menu of options and prompts the user to select one.
|
|
26
|
+
The selected option's corresponding function is executed. Invalid selections
|
|
27
|
+
trigger an error message and re-prompt. The menu exits on "quit" or "exit".
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
options (dict): Dictionary where keys are display names (str) and
|
|
31
|
+
values are callable functions to execute when selected.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
None
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
TypeError: If options is not a dictionary or values are not callable.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
def save_data():
|
|
41
|
+
print("Data saved!")
|
|
42
|
+
|
|
43
|
+
def load_data():
|
|
44
|
+
print("Data loaded!")
|
|
45
|
+
|
|
46
|
+
simple_menu({
|
|
47
|
+
"Save": save_data,
|
|
48
|
+
"Load": load_data
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
Note:
|
|
52
|
+
- Menu options are displayed with numbers starting from 1
|
|
53
|
+
- User can type "quit" or "exit" to exit the menu
|
|
54
|
+
- Invalid selections display an error and re-prompt
|
|
55
|
+
- Each selected function is called with no arguments
|
|
56
|
+
"""
|
|
57
|
+
# Validate input
|
|
58
|
+
if not isinstance(options, dict):
|
|
59
|
+
raise TypeError("options must be a dictionary")
|
|
60
|
+
|
|
61
|
+
if not options:
|
|
62
|
+
raise ValueError("options dictionary cannot be empty")
|
|
63
|
+
|
|
64
|
+
# Validate that all values are callable
|
|
65
|
+
for key, value in options.items():
|
|
66
|
+
if not callable(value):
|
|
67
|
+
raise TypeError(f"Value for key '{key}' must be callable")
|
|
68
|
+
|
|
69
|
+
# Convert options to a list to maintain order and allow indexing
|
|
70
|
+
option_list = list(options.items())
|
|
71
|
+
|
|
72
|
+
while True:
|
|
73
|
+
# Display menu
|
|
74
|
+
print("\n" + "=" * 40)
|
|
75
|
+
for i, (name, _) in enumerate(option_list, 1):
|
|
76
|
+
print(f"{i}. {name}")
|
|
77
|
+
print("=" * 40)
|
|
78
|
+
|
|
79
|
+
# Get user input
|
|
80
|
+
user_input = input("Enter your choice (or 'quit'/'exit' to exit): ").strip().lower()
|
|
81
|
+
|
|
82
|
+
# Check for exit commands
|
|
83
|
+
if user_input in ("quit", "exit"):
|
|
84
|
+
print("Exiting menu.")
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
# Try to parse as a number
|
|
88
|
+
try:
|
|
89
|
+
choice = int(user_input)
|
|
90
|
+
|
|
91
|
+
# Check if choice is valid
|
|
92
|
+
if 1 <= choice <= len(option_list):
|
|
93
|
+
# Execute the selected function
|
|
94
|
+
_, func = option_list[choice - 1]
|
|
95
|
+
func()
|
|
96
|
+
else:
|
|
97
|
+
print(f"Error: Please enter a number between 1 and {len(option_list)}.")
|
|
98
|
+
except ValueError:
|
|
99
|
+
print("Error: Invalid input. Please enter a number or 'quit'/'exit'.")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSONStorage pattern for persistent data storage.
|
|
3
|
+
|
|
4
|
+
This module provides the JSONStorage class for saving and loading data
|
|
5
|
+
in JSON format without requiring manual file handling code.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
storage = JSONStorage("data.json")
|
|
9
|
+
storage.save({"name": "Alice", "age": 30})
|
|
10
|
+
data = storage.load()
|
|
11
|
+
print(data) # {"name": "Alice", "age": 30}
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JSONStorage:
|
|
20
|
+
"""
|
|
21
|
+
Persist and retrieve data in JSON format.
|
|
22
|
+
|
|
23
|
+
This class handles all file operations for JSON data storage, including
|
|
24
|
+
automatic directory creation and error handling. Data is stored in a
|
|
25
|
+
human-readable JSON format.
|
|
26
|
+
|
|
27
|
+
Parameters:
|
|
28
|
+
file_path (str): Path to the JSON file for storage. Parent directories
|
|
29
|
+
are created automatically if they don't exist.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
file_path (str): The path to the JSON storage file.
|
|
33
|
+
|
|
34
|
+
Methods:
|
|
35
|
+
save(data): Write data to the JSON file.
|
|
36
|
+
load(): Read data from the JSON file.
|
|
37
|
+
exists(): Check if the storage file exists.
|
|
38
|
+
|
|
39
|
+
Raises:
|
|
40
|
+
IOError: If file operations fail.
|
|
41
|
+
json.JSONDecodeError: If the JSON file is corrupted.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
storage = JSONStorage("config/app.json")
|
|
45
|
+
storage.save({"theme": "dark", "language": "en"})
|
|
46
|
+
config = storage.load()
|
|
47
|
+
if storage.exists():
|
|
48
|
+
print("Config file exists")
|
|
49
|
+
|
|
50
|
+
Note:
|
|
51
|
+
- Parent directories are created automatically
|
|
52
|
+
- File is created on first save if it doesn't exist
|
|
53
|
+
- Existing files are overwritten when saving
|
|
54
|
+
- Load returns an empty dict if file doesn't exist
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, file_path):
|
|
58
|
+
"""
|
|
59
|
+
Initialize JSONStorage with a file path.
|
|
60
|
+
|
|
61
|
+
Parameters:
|
|
62
|
+
file_path (str): Path to the JSON storage file.
|
|
63
|
+
"""
|
|
64
|
+
self.file_path = file_path
|
|
65
|
+
|
|
66
|
+
def save(self, data):
|
|
67
|
+
"""
|
|
68
|
+
Save data to the JSON file.
|
|
69
|
+
|
|
70
|
+
Parameters:
|
|
71
|
+
data (dict): Data to save. Must be JSON-serializable.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
None
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
IOError: If file write fails.
|
|
78
|
+
TypeError: If data is not JSON-serializable.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
# Create parent directories if they don't exist
|
|
82
|
+
parent_dir = os.path.dirname(self.file_path)
|
|
83
|
+
if parent_dir:
|
|
84
|
+
Path(parent_dir).mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
# Write data to JSON file
|
|
87
|
+
with open(self.file_path, 'w') as f:
|
|
88
|
+
json.dump(data, f, indent=2)
|
|
89
|
+
except TypeError as e:
|
|
90
|
+
raise TypeError(f"Data is not JSON-serializable: {e}")
|
|
91
|
+
except IOError as e:
|
|
92
|
+
raise IOError(f"Failed to write to {self.file_path}: {e}")
|
|
93
|
+
|
|
94
|
+
def load(self):
|
|
95
|
+
"""
|
|
96
|
+
Load data from the JSON file.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
dict: The loaded data, or empty dict if file doesn't exist.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
IOError: If file read fails.
|
|
103
|
+
json.JSONDecodeError: If the JSON file is corrupted.
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
if not os.path.exists(self.file_path):
|
|
107
|
+
return {}
|
|
108
|
+
|
|
109
|
+
with open(self.file_path, 'r') as f:
|
|
110
|
+
return json.load(f)
|
|
111
|
+
except json.JSONDecodeError as e:
|
|
112
|
+
raise json.JSONDecodeError(
|
|
113
|
+
f"Failed to parse JSON from {self.file_path}: {e.msg}",
|
|
114
|
+
e.doc,
|
|
115
|
+
e.pos
|
|
116
|
+
)
|
|
117
|
+
except IOError as e:
|
|
118
|
+
raise IOError(f"Failed to read from {self.file_path}: {e}")
|
|
119
|
+
|
|
120
|
+
def exists(self):
|
|
121
|
+
"""
|
|
122
|
+
Check if the storage file exists.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
bool: True if the file exists, False otherwise.
|
|
126
|
+
"""
|
|
127
|
+
return os.path.exists(self.file_path)
|