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.
Files changed (69) hide show
  1. fishertools/__init__.py +16 -5
  2. fishertools/errors/__init__.py +11 -3
  3. fishertools/errors/exception_types.py +282 -0
  4. fishertools/errors/explainer.py +87 -1
  5. fishertools/errors/models.py +73 -1
  6. fishertools/errors/patterns.py +40 -0
  7. fishertools/examples/cli_example.py +156 -0
  8. fishertools/examples/learn_example.py +65 -0
  9. fishertools/examples/logger_example.py +176 -0
  10. fishertools/examples/menu_example.py +101 -0
  11. fishertools/examples/storage_example.py +175 -0
  12. fishertools/input_utils.py +185 -0
  13. fishertools/learn/__init__.py +19 -2
  14. fishertools/learn/examples.py +88 -1
  15. fishertools/learn/knowledge_engine.py +321 -0
  16. fishertools/learn/repl/__init__.py +19 -0
  17. fishertools/learn/repl/cli.py +31 -0
  18. fishertools/learn/repl/code_sandbox.py +229 -0
  19. fishertools/learn/repl/command_handler.py +544 -0
  20. fishertools/learn/repl/command_parser.py +165 -0
  21. fishertools/learn/repl/engine.py +479 -0
  22. fishertools/learn/repl/models.py +121 -0
  23. fishertools/learn/repl/session_manager.py +284 -0
  24. fishertools/learn/repl/test_code_sandbox.py +261 -0
  25. fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
  26. fishertools/learn/repl/test_command_handler.py +224 -0
  27. fishertools/learn/repl/test_command_handler_pbt.py +189 -0
  28. fishertools/learn/repl/test_command_parser.py +160 -0
  29. fishertools/learn/repl/test_command_parser_pbt.py +100 -0
  30. fishertools/learn/repl/test_engine.py +190 -0
  31. fishertools/learn/repl/test_session_manager.py +310 -0
  32. fishertools/learn/repl/test_session_manager_pbt.py +182 -0
  33. fishertools/learn/test_knowledge_engine.py +241 -0
  34. fishertools/learn/test_knowledge_engine_pbt.py +180 -0
  35. fishertools/patterns/__init__.py +46 -0
  36. fishertools/patterns/cli.py +175 -0
  37. fishertools/patterns/logger.py +140 -0
  38. fishertools/patterns/menu.py +99 -0
  39. fishertools/patterns/storage.py +127 -0
  40. fishertools/readme_transformer.py +631 -0
  41. fishertools/safe/__init__.py +6 -1
  42. fishertools/safe/files.py +329 -1
  43. fishertools/transform_readme.py +105 -0
  44. fishertools-0.4.0.dist-info/METADATA +104 -0
  45. fishertools-0.4.0.dist-info/RECORD +131 -0
  46. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
  47. tests/test_documentation_properties.py +329 -0
  48. tests/test_documentation_structure.py +349 -0
  49. tests/test_errors/test_exception_types.py +446 -0
  50. tests/test_errors/test_exception_types_pbt.py +333 -0
  51. tests/test_errors/test_patterns.py +52 -0
  52. tests/test_input_utils/__init__.py +1 -0
  53. tests/test_input_utils/test_input_utils.py +65 -0
  54. tests/test_learn/test_examples.py +179 -1
  55. tests/test_learn/test_explain_properties.py +307 -0
  56. tests/test_patterns_cli.py +611 -0
  57. tests/test_patterns_docstrings.py +473 -0
  58. tests/test_patterns_logger.py +465 -0
  59. tests/test_patterns_menu.py +440 -0
  60. tests/test_patterns_storage.py +447 -0
  61. tests/test_readme_enhancements_v0_3_1.py +2036 -0
  62. tests/test_readme_transformer/__init__.py +1 -0
  63. tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
  64. tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
  65. tests/test_safe/test_files.py +726 -1
  66. fishertools-0.2.1.dist-info/METADATA +0 -256
  67. fishertools-0.2.1.dist-info/RECORD +0 -81
  68. {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
  69. {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)