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,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating the SimpleCLI class from fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
This example shows how to create a command-line interface using SimpleCLI.
|
|
5
|
+
Commands are registered via decorators and executed based on command-line
|
|
6
|
+
arguments. Supports --help flag and graceful error handling.
|
|
7
|
+
|
|
8
|
+
Run this file with various commands to see SimpleCLI in action:
|
|
9
|
+
python cli_example.py greet Alice
|
|
10
|
+
python cli_example.py add 5 3
|
|
11
|
+
python cli_example.py --help
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from fishertools.patterns import SimpleCLI
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
"""Create and run a CLI application."""
|
|
19
|
+
|
|
20
|
+
# Create a CLI instance
|
|
21
|
+
cli = SimpleCLI("calculator", "A simple calculator application")
|
|
22
|
+
|
|
23
|
+
# Register commands using decorators
|
|
24
|
+
|
|
25
|
+
@cli.command("greet", "Greet someone with a message")
|
|
26
|
+
def greet(name="World"):
|
|
27
|
+
"""Greet a person by name."""
|
|
28
|
+
print(f"Hello, {name}! Welcome to the calculator app.")
|
|
29
|
+
|
|
30
|
+
@cli.command("add", "Add two numbers")
|
|
31
|
+
def add(a, b):
|
|
32
|
+
"""Add two numbers and display the result."""
|
|
33
|
+
try:
|
|
34
|
+
result = float(a) + float(b)
|
|
35
|
+
print(f"{a} + {b} = {result}")
|
|
36
|
+
except ValueError:
|
|
37
|
+
print(f"Error: '{a}' and '{b}' must be valid numbers")
|
|
38
|
+
|
|
39
|
+
@cli.command("subtract", "Subtract two numbers")
|
|
40
|
+
def subtract(a, b):
|
|
41
|
+
"""Subtract b from a and display the result."""
|
|
42
|
+
try:
|
|
43
|
+
result = float(a) - float(b)
|
|
44
|
+
print(f"{a} - {b} = {result}")
|
|
45
|
+
except ValueError:
|
|
46
|
+
print(f"Error: '{a}' and '{b}' must be valid numbers")
|
|
47
|
+
|
|
48
|
+
@cli.command("multiply", "Multiply two numbers")
|
|
49
|
+
def multiply(a, b):
|
|
50
|
+
"""Multiply two numbers and display the result."""
|
|
51
|
+
try:
|
|
52
|
+
result = float(a) * float(b)
|
|
53
|
+
print(f"{a} * {b} = {result}")
|
|
54
|
+
except ValueError:
|
|
55
|
+
print(f"Error: '{a}' and '{b}' must be valid numbers")
|
|
56
|
+
|
|
57
|
+
@cli.command("divide", "Divide two numbers")
|
|
58
|
+
def divide(a, b):
|
|
59
|
+
"""Divide a by b and display the result."""
|
|
60
|
+
try:
|
|
61
|
+
a_float = float(a)
|
|
62
|
+
b_float = float(b)
|
|
63
|
+
if b_float == 0:
|
|
64
|
+
print("Error: Cannot divide by zero")
|
|
65
|
+
else:
|
|
66
|
+
result = a_float / b_float
|
|
67
|
+
print(f"{a} / {b} = {result}")
|
|
68
|
+
except ValueError:
|
|
69
|
+
print(f"Error: '{a}' and '{b}' must be valid numbers")
|
|
70
|
+
|
|
71
|
+
@cli.command("power", "Raise a number to a power")
|
|
72
|
+
def power(base, exponent):
|
|
73
|
+
"""Raise base to the power of exponent."""
|
|
74
|
+
try:
|
|
75
|
+
result = float(base) ** float(exponent)
|
|
76
|
+
print(f"{base} ^ {exponent} = {result}")
|
|
77
|
+
except ValueError:
|
|
78
|
+
print(f"Error: '{base}' and '{exponent}' must be valid numbers")
|
|
79
|
+
|
|
80
|
+
@cli.command("square", "Calculate the square of a number")
|
|
81
|
+
def square(number):
|
|
82
|
+
"""Calculate the square of a number."""
|
|
83
|
+
try:
|
|
84
|
+
result = float(number) ** 2
|
|
85
|
+
print(f"{number}² = {result}")
|
|
86
|
+
except ValueError:
|
|
87
|
+
print(f"Error: '{number}' must be a valid number")
|
|
88
|
+
|
|
89
|
+
@cli.command("sqrt", "Calculate the square root of a number")
|
|
90
|
+
def sqrt(number):
|
|
91
|
+
"""Calculate the square root of a number."""
|
|
92
|
+
try:
|
|
93
|
+
num = float(number)
|
|
94
|
+
if num < 0:
|
|
95
|
+
print("Error: Cannot calculate square root of negative number")
|
|
96
|
+
else:
|
|
97
|
+
result = num ** 0.5
|
|
98
|
+
print(f"√{number} = {result}")
|
|
99
|
+
except ValueError:
|
|
100
|
+
print(f"Error: '{number}' must be a valid number")
|
|
101
|
+
|
|
102
|
+
@cli.command("info", "Show information about this application")
|
|
103
|
+
def show_info():
|
|
104
|
+
"""Display information about the calculator."""
|
|
105
|
+
print("\n" + "=" * 60)
|
|
106
|
+
print("Calculator Application - SimpleCLI Demo")
|
|
107
|
+
print("=" * 60)
|
|
108
|
+
print("\nThis is a demonstration of the SimpleCLI pattern from")
|
|
109
|
+
print("fishertools.patterns module.")
|
|
110
|
+
print("\nFeatures:")
|
|
111
|
+
print(" • Command registration via decorators")
|
|
112
|
+
print(" • Automatic help generation")
|
|
113
|
+
print(" • Graceful error handling")
|
|
114
|
+
print(" • Support for multiple arguments")
|
|
115
|
+
print("\nUsage examples:")
|
|
116
|
+
print(" python cli_example.py greet Alice")
|
|
117
|
+
print(" python cli_example.py add 10 5")
|
|
118
|
+
print(" python cli_example.py multiply 3.5 2")
|
|
119
|
+
print(" python cli_example.py divide 20 4")
|
|
120
|
+
print(" python cli_example.py power 2 8")
|
|
121
|
+
print(" python cli_example.py square 7")
|
|
122
|
+
print(" python cli_example.py sqrt 16")
|
|
123
|
+
print(" python cli_example.py --help")
|
|
124
|
+
print("=" * 60 + "\n")
|
|
125
|
+
|
|
126
|
+
@cli.command("demo", "Run a demonstration of all commands")
|
|
127
|
+
def run_demo():
|
|
128
|
+
"""Run a demonstration of all calculator functions."""
|
|
129
|
+
print("\n" + "=" * 60)
|
|
130
|
+
print("Calculator Demo - Running all operations")
|
|
131
|
+
print("=" * 60 + "\n")
|
|
132
|
+
|
|
133
|
+
print("1. Greeting:")
|
|
134
|
+
greet("Demo User")
|
|
135
|
+
|
|
136
|
+
print("\n2. Basic arithmetic:")
|
|
137
|
+
add("10", "5")
|
|
138
|
+
subtract("10", "5")
|
|
139
|
+
multiply("10", "5")
|
|
140
|
+
divide("10", "5")
|
|
141
|
+
|
|
142
|
+
print("\n3. Power operations:")
|
|
143
|
+
power("2", "8")
|
|
144
|
+
square("7")
|
|
145
|
+
sqrt("16")
|
|
146
|
+
|
|
147
|
+
print("\n" + "=" * 60)
|
|
148
|
+
print("Demo complete!")
|
|
149
|
+
print("=" * 60 + "\n")
|
|
150
|
+
|
|
151
|
+
# Run the CLI
|
|
152
|
+
cli.run()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating the explain() function from fishertools.learn.
|
|
3
|
+
|
|
4
|
+
This example shows how to use the explain() function to get structured
|
|
5
|
+
explanations for Python topics, including descriptions, usage guidance,
|
|
6
|
+
and code examples.
|
|
7
|
+
|
|
8
|
+
Run this file to see explanations for various Python topics.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from fishertools.learn import explain
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
"""Demonstrate the explain() function with various topics."""
|
|
16
|
+
|
|
17
|
+
print("=" * 70)
|
|
18
|
+
print("fishertools Learning Tools - explain() Function Demo")
|
|
19
|
+
print("=" * 70)
|
|
20
|
+
|
|
21
|
+
# List of topics to demonstrate
|
|
22
|
+
topics = ["list", "for", "lambda", "try", "with"]
|
|
23
|
+
|
|
24
|
+
for topic in topics:
|
|
25
|
+
try:
|
|
26
|
+
print(f"\n{'─' * 70}")
|
|
27
|
+
print(f"Topic: {topic.upper()}")
|
|
28
|
+
print(f"{'─' * 70}")
|
|
29
|
+
|
|
30
|
+
# Get the explanation
|
|
31
|
+
explanation = explain(topic)
|
|
32
|
+
|
|
33
|
+
# Display the explanation
|
|
34
|
+
print(f"\n📖 Description:")
|
|
35
|
+
print(f" {explanation['description']}")
|
|
36
|
+
|
|
37
|
+
print(f"\n💡 When to use:")
|
|
38
|
+
print(f" {explanation['when_to_use']}")
|
|
39
|
+
|
|
40
|
+
print(f"\n💻 Example:")
|
|
41
|
+
print(" " + "\n ".join(explanation['example'].split("\n")))
|
|
42
|
+
|
|
43
|
+
except ValueError as e:
|
|
44
|
+
print(f"❌ Error: {e}")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(f"❌ Unexpected error: {e}")
|
|
47
|
+
|
|
48
|
+
# Demonstrate error handling with invalid topic
|
|
49
|
+
print(f"\n{'─' * 70}")
|
|
50
|
+
print("Demonstrating error handling with invalid topic:")
|
|
51
|
+
print(f"{'─' * 70}")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
explain("invalid_topic_xyz")
|
|
55
|
+
except ValueError as e:
|
|
56
|
+
print(f"✓ Caught expected error:")
|
|
57
|
+
print(f" {e}")
|
|
58
|
+
|
|
59
|
+
print("\n" + "=" * 70)
|
|
60
|
+
print("Demo complete!")
|
|
61
|
+
print("=" * 70)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
main()
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating the SimpleLogger class from fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
This example shows how to use SimpleLogger to add logging to applications
|
|
5
|
+
without complex configuration. Demonstrates info, warning, and error logging
|
|
6
|
+
with automatic timestamps and log levels.
|
|
7
|
+
|
|
8
|
+
Run this file to see SimpleLogger in action.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import time
|
|
13
|
+
from fishertools.patterns import SimpleLogger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
"""Demonstrate SimpleLogger functionality."""
|
|
18
|
+
|
|
19
|
+
print("=" * 70)
|
|
20
|
+
print("fishertools Patterns - SimpleLogger Demo")
|
|
21
|
+
print("=" * 70)
|
|
22
|
+
|
|
23
|
+
# Create a logger instance
|
|
24
|
+
log_file = "demo_app.log"
|
|
25
|
+
logger = SimpleLogger(log_file)
|
|
26
|
+
|
|
27
|
+
# Clean up any existing log file from previous runs
|
|
28
|
+
if os.path.exists(log_file):
|
|
29
|
+
os.remove(log_file)
|
|
30
|
+
print(f"\n✓ Cleaned up existing {log_file}")
|
|
31
|
+
|
|
32
|
+
# Example 1: Basic logging
|
|
33
|
+
print("\n" + "─" * 70)
|
|
34
|
+
print("Example 1: Basic logging with different levels")
|
|
35
|
+
print("─" * 70)
|
|
36
|
+
|
|
37
|
+
print("\nLogging messages...")
|
|
38
|
+
logger.info("Application started")
|
|
39
|
+
logger.info("Configuration loaded successfully")
|
|
40
|
+
logger.warning("Deprecated API endpoint used")
|
|
41
|
+
logger.error("Failed to connect to database")
|
|
42
|
+
|
|
43
|
+
print("✓ Messages logged to", log_file)
|
|
44
|
+
|
|
45
|
+
# Example 2: Simulating application workflow
|
|
46
|
+
print("\n" + "─" * 70)
|
|
47
|
+
print("Example 2: Simulating application workflow")
|
|
48
|
+
print("─" * 70)
|
|
49
|
+
|
|
50
|
+
print("\nSimulating user registration workflow...")
|
|
51
|
+
|
|
52
|
+
logger.info("User registration started")
|
|
53
|
+
logger.info("Validating email: user@example.com")
|
|
54
|
+
|
|
55
|
+
time.sleep(0.5) # Simulate processing
|
|
56
|
+
|
|
57
|
+
logger.info("Email validation passed")
|
|
58
|
+
logger.info("Checking username availability")
|
|
59
|
+
|
|
60
|
+
time.sleep(0.5) # Simulate processing
|
|
61
|
+
|
|
62
|
+
logger.info("Username 'john_doe' is available")
|
|
63
|
+
logger.info("Creating user account")
|
|
64
|
+
|
|
65
|
+
time.sleep(0.5) # Simulate processing
|
|
66
|
+
|
|
67
|
+
logger.info("User account created successfully")
|
|
68
|
+
logger.info("Sending welcome email")
|
|
69
|
+
logger.info("User registration completed")
|
|
70
|
+
|
|
71
|
+
print("✓ Workflow logged successfully")
|
|
72
|
+
|
|
73
|
+
# Example 3: Error handling and warnings
|
|
74
|
+
print("\n" + "─" * 70)
|
|
75
|
+
print("Example 3: Error handling and warnings")
|
|
76
|
+
print("─" * 70)
|
|
77
|
+
|
|
78
|
+
print("\nSimulating error scenarios...")
|
|
79
|
+
|
|
80
|
+
logger.warning("Memory usage above 80%")
|
|
81
|
+
logger.warning("Cache hit rate below threshold")
|
|
82
|
+
logger.error("Connection timeout after 30 seconds")
|
|
83
|
+
logger.error("Failed to save user preferences")
|
|
84
|
+
logger.info("Attempting automatic recovery")
|
|
85
|
+
logger.info("Recovery successful")
|
|
86
|
+
|
|
87
|
+
print("✓ Error scenarios logged")
|
|
88
|
+
|
|
89
|
+
# Example 4: Nested directory logging
|
|
90
|
+
print("\n" + "─" * 70)
|
|
91
|
+
print("Example 4: Logging to nested directories")
|
|
92
|
+
print("─" * 70)
|
|
93
|
+
|
|
94
|
+
nested_log_file = "logs/2024/01/application.log"
|
|
95
|
+
nested_logger = SimpleLogger(nested_log_file)
|
|
96
|
+
|
|
97
|
+
print(f"\nCreating logger for nested path: {nested_log_file}")
|
|
98
|
+
nested_logger.info("Nested logging initialized")
|
|
99
|
+
nested_logger.info("This log is in a nested directory structure")
|
|
100
|
+
|
|
101
|
+
print(f"✓ Nested directories created automatically")
|
|
102
|
+
print(f"✓ Logging to {nested_log_file}")
|
|
103
|
+
|
|
104
|
+
# Example 5: Display the log file contents
|
|
105
|
+
print("\n" + "─" * 70)
|
|
106
|
+
print("Example 5: Reading log file contents")
|
|
107
|
+
print("─" * 70)
|
|
108
|
+
|
|
109
|
+
print(f"\nContents of {log_file}:")
|
|
110
|
+
print("─" * 70)
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
with open(log_file, 'r', encoding='utf-8') as f:
|
|
114
|
+
log_contents = f.read()
|
|
115
|
+
print(log_contents)
|
|
116
|
+
except FileNotFoundError:
|
|
117
|
+
print(f"✗ Log file {log_file} not found")
|
|
118
|
+
|
|
119
|
+
print("─" * 70)
|
|
120
|
+
|
|
121
|
+
# Example 6: Demonstrating log levels
|
|
122
|
+
print("\n" + "─" * 70)
|
|
123
|
+
print("Example 6: Different log levels")
|
|
124
|
+
print("─" * 70)
|
|
125
|
+
|
|
126
|
+
info_logger = SimpleLogger("info_demo.log")
|
|
127
|
+
|
|
128
|
+
print("\nLogging different severity levels...")
|
|
129
|
+
info_logger.info("This is an informational message")
|
|
130
|
+
info_logger.warning("This is a warning message")
|
|
131
|
+
info_logger.error("This is an error message")
|
|
132
|
+
|
|
133
|
+
print("✓ Different log levels demonstrated")
|
|
134
|
+
|
|
135
|
+
# Display the info demo log
|
|
136
|
+
print(f"\nContents of info_demo.log:")
|
|
137
|
+
print("─" * 70)
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
with open("info_demo.log", 'r', encoding='utf-8') as f:
|
|
141
|
+
print(f.read())
|
|
142
|
+
except FileNotFoundError:
|
|
143
|
+
print("✗ Log file not found")
|
|
144
|
+
|
|
145
|
+
print("─" * 70)
|
|
146
|
+
|
|
147
|
+
# Cleanup
|
|
148
|
+
print("\n" + "─" * 70)
|
|
149
|
+
print("Cleanup")
|
|
150
|
+
print("─" * 70)
|
|
151
|
+
|
|
152
|
+
files_to_remove = [log_file, "info_demo.log", nested_log_file]
|
|
153
|
+
|
|
154
|
+
for file_to_remove in files_to_remove:
|
|
155
|
+
if os.path.exists(file_to_remove):
|
|
156
|
+
os.remove(file_to_remove)
|
|
157
|
+
print(f"✓ Removed {file_to_remove}")
|
|
158
|
+
|
|
159
|
+
# Remove empty directories
|
|
160
|
+
dirs_to_remove = ["logs/2024/01", "logs/2024", "logs"]
|
|
161
|
+
|
|
162
|
+
for dir_to_remove in dirs_to_remove:
|
|
163
|
+
if os.path.exists(dir_to_remove):
|
|
164
|
+
try:
|
|
165
|
+
os.rmdir(dir_to_remove)
|
|
166
|
+
print(f"✓ Removed directory {dir_to_remove}")
|
|
167
|
+
except OSError:
|
|
168
|
+
pass
|
|
169
|
+
|
|
170
|
+
print("\n" + "=" * 70)
|
|
171
|
+
print("SimpleLogger demo complete!")
|
|
172
|
+
print("=" * 70)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
main()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating the simple_menu() function from fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
This example shows how to create an interactive console menu using the
|
|
5
|
+
simple_menu() function. The menu allows users to select options and execute
|
|
6
|
+
corresponding functions.
|
|
7
|
+
|
|
8
|
+
Run this file to interact with the menu.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from fishertools.patterns import simple_menu
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def show_greeting():
|
|
15
|
+
"""Display a greeting message."""
|
|
16
|
+
name = input("What's your name? ")
|
|
17
|
+
print(f"Hello, {name}! Welcome to the menu demo.")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def show_calculator():
|
|
21
|
+
"""Simple calculator menu."""
|
|
22
|
+
print("\n--- Simple Calculator ---")
|
|
23
|
+
try:
|
|
24
|
+
a = float(input("Enter first number: "))
|
|
25
|
+
b = float(input("Enter second number: "))
|
|
26
|
+
|
|
27
|
+
print(f"\nResults:")
|
|
28
|
+
print(f" {a} + {b} = {a + b}")
|
|
29
|
+
print(f" {a} - {b} = {a - b}")
|
|
30
|
+
print(f" {a} * {b} = {a * b}")
|
|
31
|
+
if b != 0:
|
|
32
|
+
print(f" {a} / {b} = {a / b}")
|
|
33
|
+
else:
|
|
34
|
+
print(f" {a} / {b} = Cannot divide by zero")
|
|
35
|
+
except ValueError:
|
|
36
|
+
print("❌ Invalid input. Please enter numbers.")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def show_about():
|
|
40
|
+
"""Display information about the demo."""
|
|
41
|
+
print("\n--- About This Demo ---")
|
|
42
|
+
print("This is a demonstration of the simple_menu() function")
|
|
43
|
+
print("from fishertools.patterns module.")
|
|
44
|
+
print("\nFeatures:")
|
|
45
|
+
print(" • Interactive menu with numbered options")
|
|
46
|
+
print(" • Easy function execution")
|
|
47
|
+
print(" • Graceful error handling")
|
|
48
|
+
print(" • Exit with 'quit' or 'exit' command")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def list_fruits():
|
|
52
|
+
"""Display a list of fruits."""
|
|
53
|
+
fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry"]
|
|
54
|
+
print("\n--- Available Fruits ---")
|
|
55
|
+
for i, fruit in enumerate(fruits, 1):
|
|
56
|
+
print(f" {i}. {fruit}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def show_help():
|
|
60
|
+
"""Display help information."""
|
|
61
|
+
print("\n--- Help ---")
|
|
62
|
+
print("This menu demonstrates the simple_menu() function.")
|
|
63
|
+
print("\nHow to use:")
|
|
64
|
+
print(" 1. Select an option by entering its number")
|
|
65
|
+
print(" 2. Follow the prompts for each option")
|
|
66
|
+
print(" 3. Type 'quit' or 'exit' to leave the menu")
|
|
67
|
+
print("\nAvailable options:")
|
|
68
|
+
print(" • Greeting: Get a personalized greeting")
|
|
69
|
+
print(" • Calculator: Perform basic math operations")
|
|
70
|
+
print(" • Fruits: See a list of fruits")
|
|
71
|
+
print(" • About: Learn about this demo")
|
|
72
|
+
print(" • Help: Show this help message")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main():
|
|
76
|
+
"""Run the menu demo."""
|
|
77
|
+
print("=" * 60)
|
|
78
|
+
print("fishertools Patterns - simple_menu() Demo")
|
|
79
|
+
print("=" * 60)
|
|
80
|
+
print("\nWelcome! This demo shows how to use simple_menu().")
|
|
81
|
+
print("Type 'quit' or 'exit' at any time to leave.\n")
|
|
82
|
+
|
|
83
|
+
# Create the menu with options
|
|
84
|
+
menu_options = {
|
|
85
|
+
"Greeting": show_greeting,
|
|
86
|
+
"Calculator": show_calculator,
|
|
87
|
+
"Fruits": list_fruits,
|
|
88
|
+
"About": show_about,
|
|
89
|
+
"Help": show_help,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Run the menu
|
|
93
|
+
simple_menu(menu_options)
|
|
94
|
+
|
|
95
|
+
print("\n" + "=" * 60)
|
|
96
|
+
print("Thank you for using the menu demo!")
|
|
97
|
+
print("=" * 60)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example demonstrating the JSONStorage class from fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
This example shows how to use JSONStorage to persist and retrieve data
|
|
5
|
+
in JSON format without writing file handling code. Demonstrates saving,
|
|
6
|
+
loading, and checking file existence.
|
|
7
|
+
|
|
8
|
+
Run this file to see JSONStorage in action.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from fishertools.patterns import JSONStorage
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
"""Demonstrate JSONStorage functionality."""
|
|
17
|
+
|
|
18
|
+
print("=" * 70)
|
|
19
|
+
print("fishertools Patterns - JSONStorage Demo")
|
|
20
|
+
print("=" * 70)
|
|
21
|
+
|
|
22
|
+
# Create a storage instance
|
|
23
|
+
storage_path = "demo_data.json"
|
|
24
|
+
storage = JSONStorage(storage_path)
|
|
25
|
+
|
|
26
|
+
# Clean up any existing file from previous runs
|
|
27
|
+
if os.path.exists(storage_path):
|
|
28
|
+
os.remove(storage_path)
|
|
29
|
+
print(f"\n✓ Cleaned up existing {storage_path}")
|
|
30
|
+
|
|
31
|
+
# Example 1: Save data
|
|
32
|
+
print("\n" + "─" * 70)
|
|
33
|
+
print("Example 1: Saving data")
|
|
34
|
+
print("─" * 70)
|
|
35
|
+
|
|
36
|
+
user_data = {
|
|
37
|
+
"name": "Alice Johnson",
|
|
38
|
+
"age": 28,
|
|
39
|
+
"email": "alice@example.com",
|
|
40
|
+
"skills": ["Python", "JavaScript", "SQL"],
|
|
41
|
+
"is_active": True,
|
|
42
|
+
"metadata": {
|
|
43
|
+
"created": "2024-01-15",
|
|
44
|
+
"last_login": "2024-01-26"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
print(f"\nSaving user data to {storage_path}:")
|
|
49
|
+
print(f" {user_data}")
|
|
50
|
+
|
|
51
|
+
storage.save(user_data)
|
|
52
|
+
print("✓ Data saved successfully!")
|
|
53
|
+
|
|
54
|
+
# Example 2: Check if file exists
|
|
55
|
+
print("\n" + "─" * 70)
|
|
56
|
+
print("Example 2: Checking file existence")
|
|
57
|
+
print("─" * 70)
|
|
58
|
+
|
|
59
|
+
if storage.exists():
|
|
60
|
+
print(f"✓ File {storage_path} exists")
|
|
61
|
+
else:
|
|
62
|
+
print(f"✗ File {storage_path} does not exist")
|
|
63
|
+
|
|
64
|
+
# Example 3: Load data
|
|
65
|
+
print("\n" + "─" * 70)
|
|
66
|
+
print("Example 3: Loading data")
|
|
67
|
+
print("─" * 70)
|
|
68
|
+
|
|
69
|
+
loaded_data = storage.load()
|
|
70
|
+
print(f"\nLoaded data from {storage_path}:")
|
|
71
|
+
print(f" Name: {loaded_data['name']}")
|
|
72
|
+
print(f" Age: {loaded_data['age']}")
|
|
73
|
+
print(f" Email: {loaded_data['email']}")
|
|
74
|
+
print(f" Skills: {', '.join(loaded_data['skills'])}")
|
|
75
|
+
print(f" Active: {loaded_data['is_active']}")
|
|
76
|
+
print(f" Created: {loaded_data['metadata']['created']}")
|
|
77
|
+
|
|
78
|
+
# Example 4: Update data
|
|
79
|
+
print("\n" + "─" * 70)
|
|
80
|
+
print("Example 4: Updating data")
|
|
81
|
+
print("─" * 70)
|
|
82
|
+
|
|
83
|
+
loaded_data['age'] = 29
|
|
84
|
+
loaded_data['skills'].append("Docker")
|
|
85
|
+
loaded_data['metadata']['last_login'] = "2024-01-26"
|
|
86
|
+
|
|
87
|
+
print(f"\nUpdating user data:")
|
|
88
|
+
print(f" Age: 28 → {loaded_data['age']}")
|
|
89
|
+
print(f" Skills: Added 'Docker'")
|
|
90
|
+
print(f" Last login: Updated to 2024-01-26")
|
|
91
|
+
|
|
92
|
+
storage.save(loaded_data)
|
|
93
|
+
print("✓ Updated data saved!")
|
|
94
|
+
|
|
95
|
+
# Example 5: Working with nested directories
|
|
96
|
+
print("\n" + "─" * 70)
|
|
97
|
+
print("Example 5: Creating nested directories")
|
|
98
|
+
print("─" * 70)
|
|
99
|
+
|
|
100
|
+
nested_storage_path = "data/users/profiles/alice.json"
|
|
101
|
+
nested_storage = JSONStorage(nested_storage_path)
|
|
102
|
+
|
|
103
|
+
profile_data = {
|
|
104
|
+
"username": "alice_dev",
|
|
105
|
+
"bio": "Python developer and open source enthusiast",
|
|
106
|
+
"followers": 150,
|
|
107
|
+
"following": 75
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
print(f"\nSaving profile to nested path: {nested_storage_path}")
|
|
111
|
+
nested_storage.save(profile_data)
|
|
112
|
+
print("✓ Nested directories created automatically!")
|
|
113
|
+
print(f"✓ Profile saved to {nested_storage_path}")
|
|
114
|
+
|
|
115
|
+
# Verify the nested file was created
|
|
116
|
+
if nested_storage.exists():
|
|
117
|
+
print(f"✓ Verified: {nested_storage_path} exists")
|
|
118
|
+
|
|
119
|
+
# Example 6: Round-trip verification
|
|
120
|
+
print("\n" + "─" * 70)
|
|
121
|
+
print("Example 6: Round-trip verification")
|
|
122
|
+
print("─" * 70)
|
|
123
|
+
|
|
124
|
+
original_data = {
|
|
125
|
+
"items": [1, 2, 3, 4, 5],
|
|
126
|
+
"config": {
|
|
127
|
+
"debug": True,
|
|
128
|
+
"timeout": 30,
|
|
129
|
+
"retries": 3
|
|
130
|
+
},
|
|
131
|
+
"tags": ["important", "urgent", "review"]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
roundtrip_storage = JSONStorage("roundtrip_test.json")
|
|
135
|
+
|
|
136
|
+
print(f"\nOriginal data:")
|
|
137
|
+
print(f" {original_data}")
|
|
138
|
+
|
|
139
|
+
roundtrip_storage.save(original_data)
|
|
140
|
+
loaded_roundtrip = roundtrip_storage.load()
|
|
141
|
+
|
|
142
|
+
print(f"\nLoaded data:")
|
|
143
|
+
print(f" {loaded_roundtrip}")
|
|
144
|
+
|
|
145
|
+
if original_data == loaded_roundtrip:
|
|
146
|
+
print("✓ Round-trip successful: Data matches perfectly!")
|
|
147
|
+
else:
|
|
148
|
+
print("✗ Round-trip failed: Data mismatch")
|
|
149
|
+
|
|
150
|
+
# Cleanup
|
|
151
|
+
print("\n" + "─" * 70)
|
|
152
|
+
print("Cleanup")
|
|
153
|
+
print("─" * 70)
|
|
154
|
+
|
|
155
|
+
for file_to_remove in [storage_path, nested_storage_path, "roundtrip_test.json"]:
|
|
156
|
+
if os.path.exists(file_to_remove):
|
|
157
|
+
os.remove(file_to_remove)
|
|
158
|
+
print(f"✓ Removed {file_to_remove}")
|
|
159
|
+
|
|
160
|
+
# Remove empty directories
|
|
161
|
+
for dir_to_remove in ["data/users/profiles", "data/users", "data"]:
|
|
162
|
+
if os.path.exists(dir_to_remove):
|
|
163
|
+
try:
|
|
164
|
+
os.rmdir(dir_to_remove)
|
|
165
|
+
print(f"✓ Removed directory {dir_to_remove}")
|
|
166
|
+
except OSError:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
print("\n" + "=" * 70)
|
|
170
|
+
print("JSONStorage demo complete!")
|
|
171
|
+
print("=" * 70)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
main()
|