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,185 @@
1
+ """
2
+ Safe input collection module for fishertools.
3
+
4
+ This module provides validated input collection functions with automatic type checking
5
+ and range validation. It helps beginners collect user input safely without writing
6
+ repetitive validation code.
7
+
8
+ Functions:
9
+ ask_int() - Prompt user for an integer with optional range validation
10
+ ask_float() - Prompt user for a float with optional range validation
11
+ ask_str() - Prompt user for a string with optional length validation
12
+ ask_choice() - Prompt user to choose from a list of options
13
+ """
14
+
15
+ from typing import List, Optional, Any
16
+
17
+
18
+ def ask_int(prompt: str, min: Optional[int] = None, max: Optional[int] = None) -> int:
19
+ """
20
+ Prompt user for an integer with optional range validation.
21
+
22
+ Args:
23
+ prompt: The prompt to display to the user
24
+ min: Minimum allowed value (inclusive), optional
25
+ max: Maximum allowed value (inclusive), optional
26
+
27
+ Returns:
28
+ Validated integer from user input
29
+
30
+ Raises:
31
+ EOFError: If user provides EOF (Ctrl+D)
32
+
33
+ Example:
34
+ >>> age = ask_int("How old are you? ", min=0, max=150)
35
+ >>> score = ask_int("Enter your score: ")
36
+ """
37
+ while True:
38
+ try:
39
+ user_input = input(prompt)
40
+ value = int(user_input)
41
+
42
+ # Check min constraint
43
+ if min is not None and value < min:
44
+ print(f"Error: Value must be at least {min}")
45
+ continue
46
+
47
+ # Check max constraint
48
+ if max is not None and value > max:
49
+ print(f"Error: Value must be at most {max}")
50
+ continue
51
+
52
+ return value
53
+ except ValueError:
54
+ print(f"Error: Please enter a valid integer")
55
+ except EOFError:
56
+ raise
57
+
58
+
59
+ def ask_float(prompt: str, min: Optional[float] = None, max: Optional[float] = None) -> float:
60
+ """
61
+ Prompt user for a float with optional range validation.
62
+
63
+ Args:
64
+ prompt: The prompt to display to the user
65
+ min: Minimum allowed value (inclusive), optional
66
+ max: Maximum allowed value (inclusive), optional
67
+
68
+ Returns:
69
+ Validated float from user input
70
+
71
+ Raises:
72
+ EOFError: If user provides EOF (Ctrl+D)
73
+
74
+ Example:
75
+ >>> temperature = ask_float("Enter temperature (C): ", min=-273.15)
76
+ >>> price = ask_float("Enter price: ", min=0)
77
+ """
78
+ while True:
79
+ try:
80
+ user_input = input(prompt)
81
+ value = float(user_input)
82
+
83
+ # Check min constraint
84
+ if min is not None and value < min:
85
+ print(f"Error: Value must be at least {min}")
86
+ continue
87
+
88
+ # Check max constraint
89
+ if max is not None and value > max:
90
+ print(f"Error: Value must be at most {max}")
91
+ continue
92
+
93
+ return value
94
+ except ValueError:
95
+ print(f"Error: Please enter a valid number")
96
+ except EOFError:
97
+ raise
98
+
99
+
100
+ def ask_str(prompt: str, min_length: Optional[int] = None, max_length: Optional[int] = None) -> str:
101
+ """
102
+ Prompt user for a string with optional length validation.
103
+
104
+ Args:
105
+ prompt: The prompt to display to the user
106
+ min_length: Minimum string length, optional
107
+ max_length: Maximum string length, optional
108
+
109
+ Returns:
110
+ Validated string from user input (whitespace stripped)
111
+
112
+ Raises:
113
+ EOFError: If user provides EOF (Ctrl+D)
114
+
115
+ Example:
116
+ >>> name = ask_str("Enter your name: ", min_length=1, max_length=50)
117
+ >>> password = ask_str("Enter password: ", min_length=8)
118
+ """
119
+ while True:
120
+ try:
121
+ user_input = input(prompt)
122
+ value = user_input.strip()
123
+
124
+ # Check min_length constraint
125
+ if min_length is not None and len(value) < min_length:
126
+ print(f"Error: String must be at least {min_length} characters long")
127
+ continue
128
+
129
+ # Check max_length constraint
130
+ if max_length is not None and len(value) > max_length:
131
+ print(f"Error: String must be at most {max_length} characters long")
132
+ continue
133
+
134
+ return value
135
+ except EOFError:
136
+ raise
137
+
138
+
139
+ def ask_choice(prompt: str, options: List[str]) -> str:
140
+ """
141
+ Prompt user to choose from a list of options.
142
+
143
+ Args:
144
+ prompt: The prompt to display to the user
145
+ options: List of available choices
146
+
147
+ Returns:
148
+ The selected option (exact string from options list)
149
+
150
+ Raises:
151
+ EOFError: If user provides EOF (Ctrl+D)
152
+ ValueError: If options list is empty
153
+
154
+ Example:
155
+ >>> color = ask_choice("Choose a color: ", ["red", "green", "blue"])
156
+ >>> choice = ask_choice("Select: ", ["Yes", "No", "Maybe"])
157
+ """
158
+ if not options:
159
+ raise ValueError("Options list cannot be empty")
160
+
161
+ while True:
162
+ try:
163
+ # Display options
164
+ for i, option in enumerate(options, 1):
165
+ print(f" {i}. {option}")
166
+
167
+ user_input = input(prompt).strip()
168
+
169
+ # Try numeric selection first
170
+ try:
171
+ choice_index = int(user_input) - 1
172
+ if 0 <= choice_index < len(options):
173
+ return options[choice_index]
174
+ else:
175
+ print(f"Error: Please enter a number between 1 and {len(options)}")
176
+ continue
177
+ except ValueError:
178
+ # Try direct text matching
179
+ if user_input in options:
180
+ return user_input
181
+ else:
182
+ print(f"Error: '{user_input}' is not a valid option")
183
+ continue
184
+ except EOFError:
185
+ raise
@@ -5,14 +5,31 @@ This module provides educational utilities that help beginners
5
5
  learn Python best practices and concepts.
6
6
  """
7
7
 
8
- from .examples import generate_example, list_available_concepts, get_concept_info
8
+ from .examples import generate_example, list_available_concepts, get_concept_info, explain
9
9
  from .tips import show_best_practice, list_available_topics, get_topic_summary
10
+ from .knowledge_engine import (
11
+ KnowledgeEngine,
12
+ get_topic,
13
+ list_topics,
14
+ search_topics,
15
+ get_random_topic,
16
+ get_learning_path,
17
+ get_engine
18
+ )
10
19
 
11
20
  __all__ = [
12
21
  "generate_example",
13
22
  "list_available_concepts",
14
23
  "get_concept_info",
24
+ "explain",
15
25
  "show_best_practice",
16
26
  "list_available_topics",
17
- "get_topic_summary"
27
+ "get_topic_summary",
28
+ "KnowledgeEngine",
29
+ "get_topic",
30
+ "list_topics",
31
+ "search_topics",
32
+ "get_random_topic",
33
+ "get_learning_path",
34
+ "get_engine"
18
35
  ]
@@ -5,6 +5,8 @@ This module contains functions to generate educational code examples
5
5
  for common Python concepts that beginners need to learn.
6
6
  """
7
7
 
8
+ import json
9
+ import os
8
10
  from typing import Dict, Optional
9
11
 
10
12
 
@@ -547,4 +549,89 @@ def get_concept_info(concept: str) -> Optional[Dict[str, str]]:
547
549
  return {
548
550
  "title": CODE_EXAMPLES[concept_lower]["title"],
549
551
  "description": CODE_EXAMPLES[concept_lower]["description"]
550
- }
552
+ }
553
+
554
+
555
+
556
+ def explain(topic: str) -> Dict[str, str]:
557
+ """
558
+ Get a structured explanation for a Python topic.
559
+
560
+ This function loads explanations from the explanations.json file and returns
561
+ a dictionary containing a description, usage guidance, and code example for
562
+ the requested topic.
563
+
564
+ Parameters
565
+ ----------
566
+ topic : str
567
+ The name of the Python topic to explain (e.g., 'list', 'for', 'lambda').
568
+ Topic names are case-insensitive.
569
+
570
+ Returns
571
+ -------
572
+ dict
573
+ A dictionary with the following keys:
574
+ - 'description' (str): A clear, concise explanation of what the topic is
575
+ - 'when_to_use' (str): Practical guidance on when to use this topic
576
+ - 'example' (str): Valid, runnable Python code demonstrating the topic
577
+
578
+ Raises
579
+ ------
580
+ ValueError
581
+ If the topic is not found in the explanations database. The error message
582
+ includes a helpful list of all available topics.
583
+ FileNotFoundError
584
+ If the explanations.json file cannot be found.
585
+ json.JSONDecodeError
586
+ If the explanations.json file is corrupted or invalid.
587
+
588
+ Examples
589
+ --------
590
+ >>> explanation = explain('list')
591
+ >>> print(explanation['description'])
592
+ Ordered collection of items that can be of different types...
593
+
594
+ >>> explanation = explain('lambda')
595
+ >>> print(explanation['example'])
596
+ square = lambda x: x ** 2
597
+ print(square(5))
598
+
599
+ >>> try:
600
+ ... explain('invalid_topic')
601
+ ... except ValueError as e:
602
+ ... print(str(e))
603
+ Topic 'invalid_topic' not found. Available topics: int, float, str, ...
604
+ """
605
+ # Normalize the topic name
606
+ topic_normalized = topic.strip().lower()
607
+
608
+ # Get the path to the explanations.json file
609
+ current_dir = os.path.dirname(os.path.abspath(__file__))
610
+ explanations_path = os.path.join(current_dir, 'explanations.json')
611
+
612
+ # Load the explanations from JSON file
613
+ try:
614
+ with open(explanations_path, 'r', encoding='utf-8') as f:
615
+ explanations = json.load(f)
616
+ except FileNotFoundError:
617
+ raise FileNotFoundError(
618
+ f"Explanations file not found at {explanations_path}. "
619
+ "Please ensure explanations.json is in the fishertools/learn/ directory."
620
+ )
621
+ except json.JSONDecodeError as e:
622
+ raise json.JSONDecodeError(
623
+ f"Failed to parse explanations.json: {e.msg}",
624
+ e.doc,
625
+ e.pos
626
+ )
627
+
628
+ # Check if the topic exists
629
+ if topic_normalized not in explanations:
630
+ available_topics = sorted(explanations.keys())
631
+ topics_str = ", ".join(available_topics)
632
+ raise ValueError(
633
+ f"Topic '{topic}' not found. Available topics: {topics_str}"
634
+ )
635
+
636
+ # Return the explanation dictionary
637
+ return explanations[topic_normalized]
@@ -0,0 +1,321 @@
1
+ """
2
+ Knowledge Engine for fishertools - Educational system for Python concepts.
3
+
4
+ This module provides a structured knowledge base of Python concepts for beginners,
5
+ with explanations, examples, common mistakes, and related topics.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import random
11
+ from typing import Dict, List, Optional
12
+
13
+
14
+ class KnowledgeEngine:
15
+ """
16
+ Knowledge Engine for managing and accessing educational content about Python concepts.
17
+
18
+ The engine loads topics from a JSON file and provides methods to search, filter,
19
+ and retrieve information about Python concepts for beginners.
20
+
21
+ Attributes:
22
+ topics (Dict[str, Dict]): Dictionary of all loaded topics indexed by name
23
+ categories (Dict[str, List[str]]): Dictionary mapping categories to topic names
24
+
25
+ Example:
26
+ >>> engine = KnowledgeEngine()
27
+ >>> topic = engine.get_topic("Lists")
28
+ >>> print(topic["description"])
29
+ >>> all_topics = engine.list_topics()
30
+ >>> related = engine.get_related_topics("Lists")
31
+ """
32
+
33
+ def __init__(self, topics_file: Optional[str] = None):
34
+ """
35
+ Initialize the Knowledge Engine by loading topics from a JSON file.
36
+
37
+ Args:
38
+ topics_file: Path to the JSON file containing topics. If None, uses default location.
39
+
40
+ Raises:
41
+ FileNotFoundError: If the topics file is not found
42
+ ValueError: If the JSON file is invalid or corrupted
43
+
44
+ Example:
45
+ >>> engine = KnowledgeEngine()
46
+ >>> engine = KnowledgeEngine("custom_topics.json")
47
+ """
48
+ if topics_file is None:
49
+ # Use default location relative to this file
50
+ topics_file = os.path.join(os.path.dirname(__file__), "topics.json")
51
+
52
+ if not os.path.exists(topics_file):
53
+ raise FileNotFoundError(f"Topics file not found: {topics_file}")
54
+
55
+ try:
56
+ with open(topics_file, 'r', encoding='utf-8') as f:
57
+ topics_list = json.load(f)
58
+ except json.JSONDecodeError as e:
59
+ raise ValueError(f"Invalid JSON in topics file: {e}")
60
+
61
+ # Index topics by name for fast lookup
62
+ self.topics: Dict[str, Dict] = {}
63
+ self.categories: Dict[str, List[str]] = {}
64
+
65
+ for topic in topics_list:
66
+ topic_name = topic.get("topic")
67
+ if not topic_name:
68
+ raise ValueError("Topic missing 'topic' field")
69
+
70
+ self.topics[topic_name] = topic
71
+
72
+ # Index by category
73
+ category = topic.get("category", "Uncategorized")
74
+ if category not in self.categories:
75
+ self.categories[category] = []
76
+ self.categories[category].append(topic_name)
77
+
78
+ def get_topic(self, name: str) -> Optional[Dict]:
79
+ """
80
+ Get a topic by name.
81
+
82
+ Args:
83
+ name: The name of the topic to retrieve
84
+
85
+ Returns:
86
+ Dictionary containing the topic information, or None if not found
87
+
88
+ Example:
89
+ >>> engine = KnowledgeEngine()
90
+ >>> topic = engine.get_topic("Lists")
91
+ >>> if topic:
92
+ ... print(topic["description"])
93
+ """
94
+ return self.topics.get(name)
95
+
96
+ def list_topics(self) -> List[str]:
97
+ """
98
+ Get a list of all available topics.
99
+
100
+ Returns:
101
+ Sorted list of all topic names
102
+
103
+ Example:
104
+ >>> engine = KnowledgeEngine()
105
+ >>> topics = engine.list_topics()
106
+ >>> print(f"Available topics: {len(topics)}")
107
+ """
108
+ return sorted(self.topics.keys())
109
+
110
+ def search_topics(self, keyword: str) -> List[str]:
111
+ """
112
+ Search for topics containing a keyword in name or description.
113
+
114
+ The search is case-insensitive and matches partial words.
115
+
116
+ Args:
117
+ keyword: The keyword to search for
118
+
119
+ Returns:
120
+ List of topic names matching the keyword
121
+
122
+ Example:
123
+ >>> engine = KnowledgeEngine()
124
+ >>> results = engine.search_topics("list")
125
+ >>> print(f"Found {len(results)} topics about lists")
126
+ """
127
+ keyword_lower = keyword.lower()
128
+ results = []
129
+
130
+ for name, topic in self.topics.items():
131
+ # Search in topic name
132
+ if keyword_lower in name.lower():
133
+ results.append(name)
134
+ continue
135
+
136
+ # Search in description
137
+ description = topic.get("description", "").lower()
138
+ if keyword_lower in description:
139
+ results.append(name)
140
+ continue
141
+
142
+ # Search in when_to_use
143
+ when_to_use = topic.get("when_to_use", "").lower()
144
+ if keyword_lower in when_to_use:
145
+ results.append(name)
146
+
147
+ return sorted(results)
148
+
149
+ def get_random_topic(self) -> Dict:
150
+ """
151
+ Get a random topic for learning.
152
+
153
+ Returns:
154
+ A randomly selected topic dictionary
155
+
156
+ Example:
157
+ >>> engine = KnowledgeEngine()
158
+ >>> topic = engine.get_random_topic()
159
+ >>> print(f"Today's topic: {topic['topic']}")
160
+ """
161
+ topic_name = random.choice(list(self.topics.keys()))
162
+ return self.topics[topic_name]
163
+
164
+ def get_related_topics(self, topic_name: str) -> List[str]:
165
+ """
166
+ Get topics related to a given topic.
167
+
168
+ Args:
169
+ topic_name: The name of the topic
170
+
171
+ Returns:
172
+ List of related topic names that exist in the knowledge base
173
+
174
+ Example:
175
+ >>> engine = KnowledgeEngine()
176
+ >>> related = engine.get_related_topics("Lists")
177
+ >>> print(f"Related topics: {related}")
178
+ """
179
+ topic = self.get_topic(topic_name)
180
+ if not topic:
181
+ return []
182
+
183
+ related = topic.get("related_topics", [])
184
+ # Filter to only include topics that exist in the knowledge base
185
+ return [t for t in related if t in self.topics]
186
+
187
+ def get_topics_by_category(self, category: str) -> List[str]:
188
+ """
189
+ Get all topics in a specific category.
190
+
191
+ Args:
192
+ category: The category name
193
+
194
+ Returns:
195
+ Sorted list of topic names in the category
196
+
197
+ Example:
198
+ >>> engine = KnowledgeEngine()
199
+ >>> basic_types = engine.get_topics_by_category("Basic Types")
200
+ >>> print(f"Topics in Basic Types: {basic_types}")
201
+ """
202
+ return sorted(self.categories.get(category, []))
203
+
204
+ def get_learning_path(self) -> List[str]:
205
+ """
206
+ Get the recommended learning path from simple to complex topics.
207
+
208
+ Returns:
209
+ List of topic names ordered by difficulty
210
+
211
+ Example:
212
+ >>> engine = KnowledgeEngine()
213
+ >>> path = engine.get_learning_path()
214
+ >>> for topic_name in path:
215
+ ... print(topic_name)
216
+ """
217
+ # Sort topics by order field, then by difficulty
218
+ sorted_topics = sorted(
219
+ self.topics.items(),
220
+ key=lambda x: (x[1].get("order", 999), x[1].get("difficulty", ""))
221
+ )
222
+ return [name for name, _ in sorted_topics]
223
+
224
+
225
+ # Global engine instance
226
+ _engine: Optional[KnowledgeEngine] = None
227
+
228
+
229
+ def get_engine() -> KnowledgeEngine:
230
+ """
231
+ Get the global Knowledge Engine instance.
232
+
233
+ Returns:
234
+ The global KnowledgeEngine instance
235
+
236
+ Example:
237
+ >>> engine = get_engine()
238
+ >>> topics = engine.list_topics()
239
+ """
240
+ global _engine
241
+ if _engine is None:
242
+ _engine = KnowledgeEngine()
243
+ return _engine
244
+
245
+
246
+ def get_topic(name: str) -> Optional[Dict]:
247
+ """
248
+ Get a topic by name using the global engine.
249
+
250
+ Args:
251
+ name: The name of the topic
252
+
253
+ Returns:
254
+ Topic dictionary or None if not found
255
+
256
+ Example:
257
+ >>> topic = get_topic("Lists")
258
+ >>> if topic:
259
+ ... print(topic["description"])
260
+ """
261
+ return get_engine().get_topic(name)
262
+
263
+
264
+ def list_topics() -> List[str]:
265
+ """
266
+ Get a list of all available topics using the global engine.
267
+
268
+ Returns:
269
+ Sorted list of all topic names
270
+
271
+ Example:
272
+ >>> topics = list_topics()
273
+ >>> print(f"Total topics: {len(topics)}")
274
+ """
275
+ return get_engine().list_topics()
276
+
277
+
278
+ def search_topics(keyword: str) -> List[str]:
279
+ """
280
+ Search for topics by keyword using the global engine.
281
+
282
+ Args:
283
+ keyword: The keyword to search for
284
+
285
+ Returns:
286
+ List of matching topic names
287
+
288
+ Example:
289
+ >>> results = search_topics("loop")
290
+ >>> print(f"Found {len(results)} topics about loops")
291
+ """
292
+ return get_engine().search_topics(keyword)
293
+
294
+
295
+ def get_random_topic() -> Dict:
296
+ """
297
+ Get a random topic using the global engine.
298
+
299
+ Returns:
300
+ A randomly selected topic dictionary
301
+
302
+ Example:
303
+ >>> topic = get_random_topic()
304
+ >>> print(f"Random topic: {topic['topic']}")
305
+ """
306
+ return get_engine().get_random_topic()
307
+
308
+
309
+ def get_learning_path() -> List[str]:
310
+ """
311
+ Get the recommended learning path using the global engine.
312
+
313
+ Returns:
314
+ List of topic names ordered by difficulty
315
+
316
+ Example:
317
+ >>> path = get_learning_path()
318
+ >>> for topic_name in path[:5]:
319
+ ... print(topic_name)
320
+ """
321
+ return get_engine().get_learning_path()
@@ -0,0 +1,19 @@
1
+ """
2
+ Knowledge Engine Interactive REPL - An interactive command-line interface for exploring Python topics.
3
+
4
+ This package provides a beginner-friendly REPL for the Knowledge Engine that enables:
5
+ - Topic browsing and discovery
6
+ - Code execution in a safe sandbox
7
+ - Learning progress tracking
8
+ - Contextual hints and guidance
9
+ """
10
+
11
+ def get_repl_engine():
12
+ """Get a REPL engine instance."""
13
+ from fishertools.learn.repl.engine import REPLEngine
14
+ return REPLEngine()
15
+
16
+
17
+ __all__ = [
18
+ "get_repl_engine",
19
+ ]
@@ -0,0 +1,31 @@
1
+ """
2
+ Command-line interface entry point for the Knowledge Engine REPL.
3
+
4
+ This module provides the main entry point for running the REPL from the command line.
5
+ """
6
+
7
+ import sys
8
+ from fishertools.learn.repl.engine import REPLEngine
9
+
10
+
11
+ def main() -> int:
12
+ """
13
+ Main entry point for the REPL.
14
+
15
+ Returns:
16
+ Exit code (0 for success, 1 for error)
17
+ """
18
+ try:
19
+ engine = REPLEngine()
20
+ engine.start()
21
+ return 0
22
+ except KeyboardInterrupt:
23
+ print("\n\n👋 Goodbye!")
24
+ return 0
25
+ except Exception as e:
26
+ print(f"❌ Error: {e}", file=sys.stderr)
27
+ return 1
28
+
29
+
30
+ if __name__ == "__main__":
31
+ sys.exit(main())