edsl 0.1.49__py3-none-any.whl → 0.1.51__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 (257) hide show
  1. edsl/__init__.py +124 -53
  2. edsl/__version__.py +1 -1
  3. edsl/agents/agent.py +21 -21
  4. edsl/agents/agent_list.py +2 -5
  5. edsl/agents/exceptions.py +119 -5
  6. edsl/base/__init__.py +10 -35
  7. edsl/base/base_class.py +71 -36
  8. edsl/base/base_exception.py +204 -0
  9. edsl/base/data_transfer_models.py +1 -1
  10. edsl/base/exceptions.py +94 -0
  11. edsl/buckets/__init__.py +15 -1
  12. edsl/buckets/bucket_collection.py +3 -4
  13. edsl/buckets/exceptions.py +107 -0
  14. edsl/buckets/model_buckets.py +1 -2
  15. edsl/buckets/token_bucket.py +11 -6
  16. edsl/buckets/token_bucket_api.py +27 -12
  17. edsl/buckets/token_bucket_client.py +9 -7
  18. edsl/caching/cache.py +12 -4
  19. edsl/caching/cache_entry.py +10 -9
  20. edsl/caching/exceptions.py +113 -7
  21. edsl/caching/remote_cache_sync.py +6 -7
  22. edsl/caching/sql_dict.py +20 -14
  23. edsl/cli.py +43 -0
  24. edsl/config/__init__.py +1 -1
  25. edsl/config/config_class.py +32 -6
  26. edsl/conversation/Conversation.py +8 -4
  27. edsl/conversation/car_buying.py +1 -3
  28. edsl/conversation/exceptions.py +58 -0
  29. edsl/conversation/mug_negotiation.py +2 -8
  30. edsl/coop/__init__.py +28 -6
  31. edsl/coop/coop.py +120 -29
  32. edsl/coop/coop_functions.py +1 -1
  33. edsl/coop/ep_key_handling.py +1 -1
  34. edsl/coop/exceptions.py +188 -9
  35. edsl/coop/price_fetcher.py +5 -8
  36. edsl/coop/utils.py +4 -6
  37. edsl/dataset/__init__.py +5 -4
  38. edsl/dataset/dataset.py +177 -86
  39. edsl/dataset/dataset_operations_mixin.py +98 -76
  40. edsl/dataset/dataset_tree.py +11 -7
  41. edsl/dataset/display/table_display.py +0 -2
  42. edsl/dataset/display/table_renderers.py +6 -4
  43. edsl/dataset/exceptions.py +125 -0
  44. edsl/dataset/file_exports.py +18 -11
  45. edsl/dataset/r/ggplot.py +13 -6
  46. edsl/display/__init__.py +27 -0
  47. edsl/display/core.py +147 -0
  48. edsl/display/plugin.py +189 -0
  49. edsl/display/utils.py +52 -0
  50. edsl/inference_services/__init__.py +9 -1
  51. edsl/inference_services/available_model_cache_handler.py +1 -1
  52. edsl/inference_services/available_model_fetcher.py +5 -6
  53. edsl/inference_services/data_structures.py +10 -7
  54. edsl/inference_services/exceptions.py +132 -1
  55. edsl/inference_services/inference_service_abc.py +2 -2
  56. edsl/inference_services/inference_services_collection.py +2 -6
  57. edsl/inference_services/registry.py +4 -3
  58. edsl/inference_services/service_availability.py +4 -3
  59. edsl/inference_services/services/anthropic_service.py +4 -1
  60. edsl/inference_services/services/aws_bedrock.py +13 -12
  61. edsl/inference_services/services/azure_ai.py +12 -10
  62. edsl/inference_services/services/deep_infra_service.py +1 -4
  63. edsl/inference_services/services/deep_seek_service.py +1 -5
  64. edsl/inference_services/services/google_service.py +7 -3
  65. edsl/inference_services/services/groq_service.py +1 -1
  66. edsl/inference_services/services/mistral_ai_service.py +4 -2
  67. edsl/inference_services/services/ollama_service.py +1 -1
  68. edsl/inference_services/services/open_ai_service.py +7 -5
  69. edsl/inference_services/services/perplexity_service.py +6 -2
  70. edsl/inference_services/services/test_service.py +8 -7
  71. edsl/inference_services/services/together_ai_service.py +2 -3
  72. edsl/inference_services/services/xai_service.py +1 -1
  73. edsl/instructions/__init__.py +1 -1
  74. edsl/instructions/change_instruction.py +7 -5
  75. edsl/instructions/exceptions.py +61 -0
  76. edsl/instructions/instruction.py +6 -2
  77. edsl/instructions/instruction_collection.py +6 -4
  78. edsl/instructions/instruction_handler.py +12 -15
  79. edsl/interviews/ReportErrors.py +0 -3
  80. edsl/interviews/__init__.py +9 -2
  81. edsl/interviews/answering_function.py +11 -13
  82. edsl/interviews/exception_tracking.py +15 -8
  83. edsl/interviews/exceptions.py +79 -0
  84. edsl/interviews/interview.py +33 -30
  85. edsl/interviews/interview_status_dictionary.py +4 -2
  86. edsl/interviews/interview_status_log.py +2 -1
  87. edsl/interviews/interview_task_manager.py +5 -5
  88. edsl/interviews/request_token_estimator.py +5 -2
  89. edsl/interviews/statistics.py +3 -4
  90. edsl/invigilators/__init__.py +7 -1
  91. edsl/invigilators/exceptions.py +79 -0
  92. edsl/invigilators/invigilator_base.py +0 -1
  93. edsl/invigilators/invigilators.py +9 -13
  94. edsl/invigilators/prompt_constructor.py +1 -5
  95. edsl/invigilators/prompt_helpers.py +8 -4
  96. edsl/invigilators/question_instructions_prompt_builder.py +1 -1
  97. edsl/invigilators/question_option_processor.py +9 -5
  98. edsl/invigilators/question_template_replacements_builder.py +3 -2
  99. edsl/jobs/__init__.py +42 -5
  100. edsl/jobs/async_interview_runner.py +25 -23
  101. edsl/jobs/check_survey_scenario_compatibility.py +11 -10
  102. edsl/jobs/data_structures.py +8 -5
  103. edsl/jobs/exceptions.py +177 -8
  104. edsl/jobs/fetch_invigilator.py +1 -1
  105. edsl/jobs/jobs.py +74 -69
  106. edsl/jobs/jobs_checks.py +6 -7
  107. edsl/jobs/jobs_component_constructor.py +4 -4
  108. edsl/jobs/jobs_pricing_estimation.py +4 -3
  109. edsl/jobs/jobs_remote_inference_logger.py +5 -4
  110. edsl/jobs/jobs_runner_asyncio.py +3 -4
  111. edsl/jobs/jobs_runner_status.py +8 -9
  112. edsl/jobs/remote_inference.py +27 -24
  113. edsl/jobs/results_exceptions_handler.py +10 -7
  114. edsl/key_management/__init__.py +3 -1
  115. edsl/key_management/exceptions.py +62 -0
  116. edsl/key_management/key_lookup.py +1 -1
  117. edsl/key_management/key_lookup_builder.py +37 -14
  118. edsl/key_management/key_lookup_collection.py +2 -0
  119. edsl/language_models/__init__.py +1 -1
  120. edsl/language_models/exceptions.py +302 -14
  121. edsl/language_models/language_model.py +9 -8
  122. edsl/language_models/model.py +4 -4
  123. edsl/language_models/model_list.py +1 -1
  124. edsl/language_models/price_manager.py +1 -1
  125. edsl/language_models/raw_response_handler.py +14 -9
  126. edsl/language_models/registry.py +17 -21
  127. edsl/language_models/repair.py +0 -6
  128. edsl/language_models/unused/fake_openai_service.py +0 -1
  129. edsl/load_plugins.py +69 -0
  130. edsl/logger.py +146 -0
  131. edsl/notebooks/__init__.py +24 -1
  132. edsl/notebooks/exceptions.py +82 -0
  133. edsl/notebooks/notebook.py +7 -3
  134. edsl/notebooks/notebook_to_latex.py +1 -2
  135. edsl/plugins/__init__.py +63 -0
  136. edsl/plugins/built_in/export_example.py +50 -0
  137. edsl/plugins/built_in/pig_latin.py +67 -0
  138. edsl/plugins/cli.py +372 -0
  139. edsl/plugins/cli_typer.py +283 -0
  140. edsl/plugins/exceptions.py +31 -0
  141. edsl/plugins/hookspec.py +51 -0
  142. edsl/plugins/plugin_host.py +128 -0
  143. edsl/plugins/plugin_manager.py +633 -0
  144. edsl/plugins/plugins_registry.py +168 -0
  145. edsl/prompts/__init__.py +24 -1
  146. edsl/prompts/exceptions.py +107 -5
  147. edsl/prompts/prompt.py +15 -7
  148. edsl/questions/HTMLQuestion.py +5 -11
  149. edsl/questions/Quick.py +0 -1
  150. edsl/questions/__init__.py +6 -4
  151. edsl/questions/answer_validator_mixin.py +318 -323
  152. edsl/questions/compose_questions.py +3 -3
  153. edsl/questions/descriptors.py +11 -50
  154. edsl/questions/exceptions.py +278 -22
  155. edsl/questions/loop_processor.py +7 -5
  156. edsl/questions/prompt_templates/question_list.jinja +3 -0
  157. edsl/questions/question_base.py +46 -19
  158. edsl/questions/question_base_gen_mixin.py +2 -2
  159. edsl/questions/question_base_prompts_mixin.py +13 -7
  160. edsl/questions/question_budget.py +503 -98
  161. edsl/questions/question_check_box.py +660 -160
  162. edsl/questions/question_dict.py +345 -194
  163. edsl/questions/question_extract.py +401 -61
  164. edsl/questions/question_free_text.py +80 -14
  165. edsl/questions/question_functional.py +119 -9
  166. edsl/questions/{derived/question_likert_five.py → question_likert_five.py} +2 -2
  167. edsl/questions/{derived/question_linear_scale.py → question_linear_scale.py} +3 -4
  168. edsl/questions/question_list.py +275 -28
  169. edsl/questions/question_matrix.py +643 -96
  170. edsl/questions/question_multiple_choice.py +219 -51
  171. edsl/questions/question_numerical.py +361 -32
  172. edsl/questions/question_rank.py +401 -124
  173. edsl/questions/question_registry.py +7 -5
  174. edsl/questions/{derived/question_top_k.py → question_top_k.py} +3 -3
  175. edsl/questions/{derived/question_yes_no.py → question_yes_no.py} +3 -4
  176. edsl/questions/register_questions_meta.py +2 -2
  177. edsl/questions/response_validator_abc.py +13 -15
  178. edsl/questions/response_validator_factory.py +10 -12
  179. edsl/questions/templates/dict/answering_instructions.jinja +1 -0
  180. edsl/questions/templates/rank/question_presentation.jinja +1 -1
  181. edsl/results/__init__.py +1 -1
  182. edsl/results/exceptions.py +141 -7
  183. edsl/results/report.py +1 -2
  184. edsl/results/result.py +11 -9
  185. edsl/results/results.py +480 -321
  186. edsl/results/results_selector.py +8 -4
  187. edsl/scenarios/PdfExtractor.py +2 -2
  188. edsl/scenarios/construct_download_link.py +69 -35
  189. edsl/scenarios/directory_scanner.py +33 -14
  190. edsl/scenarios/document_chunker.py +1 -1
  191. edsl/scenarios/exceptions.py +238 -14
  192. edsl/scenarios/file_methods.py +1 -1
  193. edsl/scenarios/file_store.py +7 -3
  194. edsl/scenarios/handlers/__init__.py +17 -0
  195. edsl/scenarios/handlers/docx_file_store.py +0 -5
  196. edsl/scenarios/handlers/pdf_file_store.py +0 -1
  197. edsl/scenarios/handlers/pptx_file_store.py +0 -5
  198. edsl/scenarios/handlers/py_file_store.py +0 -1
  199. edsl/scenarios/handlers/sql_file_store.py +1 -4
  200. edsl/scenarios/handlers/sqlite_file_store.py +0 -1
  201. edsl/scenarios/handlers/txt_file_store.py +1 -1
  202. edsl/scenarios/scenario.py +1 -3
  203. edsl/scenarios/scenario_list.py +179 -27
  204. edsl/scenarios/scenario_list_pdf_tools.py +1 -0
  205. edsl/scenarios/scenario_selector.py +0 -1
  206. edsl/surveys/__init__.py +3 -4
  207. edsl/surveys/dag/__init__.py +4 -2
  208. edsl/surveys/descriptors.py +1 -1
  209. edsl/surveys/edit_survey.py +1 -0
  210. edsl/surveys/exceptions.py +165 -9
  211. edsl/surveys/memory/__init__.py +5 -3
  212. edsl/surveys/memory/memory_management.py +1 -0
  213. edsl/surveys/memory/memory_plan.py +6 -15
  214. edsl/surveys/rules/__init__.py +5 -3
  215. edsl/surveys/rules/rule.py +1 -2
  216. edsl/surveys/rules/rule_collection.py +1 -1
  217. edsl/surveys/survey.py +12 -24
  218. edsl/surveys/survey_css.py +3 -3
  219. edsl/surveys/survey_export.py +6 -3
  220. edsl/surveys/survey_flow_visualization.py +10 -1
  221. edsl/surveys/survey_simulator.py +2 -1
  222. edsl/tasks/__init__.py +23 -1
  223. edsl/tasks/exceptions.py +72 -0
  224. edsl/tasks/question_task_creator.py +3 -3
  225. edsl/tasks/task_creators.py +1 -3
  226. edsl/tasks/task_history.py +8 -10
  227. edsl/tasks/task_status_log.py +1 -2
  228. edsl/tokens/__init__.py +29 -1
  229. edsl/tokens/exceptions.py +37 -0
  230. edsl/tokens/interview_token_usage.py +3 -2
  231. edsl/tokens/token_usage.py +4 -3
  232. edsl/utilities/__init__.py +21 -1
  233. edsl/utilities/decorators.py +1 -2
  234. edsl/utilities/markdown_to_docx.py +2 -2
  235. edsl/utilities/markdown_to_pdf.py +1 -1
  236. edsl/utilities/repair_functions.py +0 -1
  237. edsl/utilities/restricted_python.py +0 -1
  238. edsl/utilities/template_loader.py +2 -3
  239. edsl/utilities/utilities.py +8 -29
  240. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/METADATA +32 -2
  241. edsl-0.1.51.dist-info/RECORD +365 -0
  242. edsl-0.1.51.dist-info/entry_points.txt +3 -0
  243. edsl/dataset/smart_objects.py +0 -96
  244. edsl/exceptions/BaseException.py +0 -21
  245. edsl/exceptions/__init__.py +0 -54
  246. edsl/exceptions/configuration.py +0 -16
  247. edsl/exceptions/general.py +0 -34
  248. edsl/questions/derived/__init__.py +0 -0
  249. edsl/study/ObjectEntry.py +0 -173
  250. edsl/study/ProofOfWork.py +0 -113
  251. edsl/study/SnapShot.py +0 -80
  252. edsl/study/Study.py +0 -520
  253. edsl/study/__init__.py +0 -6
  254. edsl/utilities/interface.py +0 -135
  255. edsl-0.1.49.dist-info/RECORD +0 -347
  256. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/LICENSE +0 -0
  257. {edsl-0.1.49.dist-info → edsl-0.1.51.dist-info}/WHEEL +0 -0
edsl/load_plugins.py ADDED
@@ -0,0 +1,69 @@
1
+ import logging
2
+
3
+ def load_plugins():
4
+ """
5
+ Load plugins for EDSL.
6
+
7
+ This function handles the discovery and loading of plugins via the pluggy library.
8
+ It searches for entry points registered under the "edsl" namespace.
9
+ """
10
+ logger = logging.getLogger("edsl")
11
+
12
+ try:
13
+ logger.info("Loading plugins")
14
+ import pluggy
15
+ import pkg_resources
16
+
17
+ logger.info("Available edsl entrypoints: %s", [ep for ep in pkg_resources.iter_entry_points("edsl")])
18
+
19
+ # Import hook specs from the plugin system
20
+ from edsl.plugins.hookspec import EDSLPluginSpec
21
+
22
+ # Create plugin manager and register specs
23
+ pm = pluggy.PluginManager("edsl")
24
+ pm.add_hookspecs(EDSLPluginSpec)
25
+
26
+ # Load all plugins
27
+ logger.info("Loading setuptools entrypoints...")
28
+ pm.load_setuptools_entrypoints("edsl")
29
+
30
+ # Get registered plugins
31
+ registered_plugins = [
32
+ plugin_name
33
+ for plugin_name, _ in pm.list_name_plugin()
34
+ if plugin_name != "EDSLHookSpecs"
35
+ ]
36
+ logger.info("Registered plugins: %s", registered_plugins)
37
+
38
+ # Get plugins and add to __all__
39
+ logger.info("Calling edsl_plugin hook...")
40
+ try:
41
+ results = pm.hook.edsl_plugin()
42
+ logger.info("Results: %s", results)
43
+ plugins = {}
44
+
45
+ if results:
46
+ for plugin in results:
47
+ if hasattr(plugin, "__name__"):
48
+ plugin_name = plugin.__name__
49
+ elif hasattr(plugin, "__class__"):
50
+ plugin_name = plugin.__class__.__name__
51
+ else:
52
+ plugin_name = f"Plugin_{len(plugins)}"
53
+
54
+ logger.info(f"Loaded plugin: {plugin_name}")
55
+ plugins[plugin_name] = plugin
56
+
57
+ return plugins
58
+ except Exception as e:
59
+ logger.error("Error calling edsl_plugin hook: %s", e)
60
+ except ImportError as e:
61
+ # pluggy not available
62
+ logger.info("pluggy not available, skipping plugin loading: %s", e)
63
+ logger.debug("pluggy not available, skipping plugin loading: %s", e)
64
+ except Exception as e:
65
+ # Error loading plugins
66
+ logger.error("Error loading plugins: %s", e)
67
+ logger.debug("Error loading plugins: %s", e)
68
+
69
+ return {}
edsl/logger.py ADDED
@@ -0,0 +1,146 @@
1
+ """
2
+ Logger module for EDSL.
3
+
4
+ This module provides a centralized logging configuration for the EDSL package.
5
+ It configures console and file logging with appropriate formatting.
6
+ """
7
+
8
+ import logging
9
+ import os
10
+ import sys
11
+ from logging.handlers import RotatingFileHandler
12
+ from pathlib import Path
13
+
14
+ # Create the logger
15
+ logger = logging.getLogger("edsl")
16
+ logger.setLevel(logging.ERROR) # Default level
17
+
18
+ # Avoid adding handlers multiple times when imported in different modules
19
+ if not logger.handlers:
20
+ # Console handler removed - logs only go to file now
21
+
22
+ # File handler - create logs directory if it doesn't exist
23
+ try:
24
+ log_dir = Path.home() / ".edsl" / "logs"
25
+ os.makedirs(log_dir, exist_ok=True)
26
+
27
+ file_handler = RotatingFileHandler(
28
+ log_dir / "edsl.log", maxBytes=5 * 1024 * 1024, backupCount=3 # 5 MB
29
+ )
30
+ file_handler.setLevel(logging.ERROR)
31
+ file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
32
+ file_handler.setFormatter(file_formatter)
33
+ logger.addHandler(file_handler)
34
+ except Exception as e:
35
+ # Don't fail if file logging can't be set up
36
+ # No console handler to adjust
37
+ print(f"WARNING: Could not set up file logging: {e}")
38
+
39
+
40
+ def get_logger(name):
41
+ """
42
+ Get a logger for a specific module.
43
+
44
+ Args:
45
+ name: Usually __name__ of the module
46
+
47
+ Returns:
48
+ A Logger instance configured with the EDSL settings
49
+ """
50
+ return logging.getLogger(f"edsl.{name}")
51
+
52
+
53
+ def set_level(level):
54
+ """
55
+ Set the logging level for the EDSL logger.
56
+
57
+ Args:
58
+ level: A logging level (e.g., logging.DEBUG, logging.INFO, etc.)
59
+ """
60
+ logger.setLevel(level)
61
+ for handler in logger.handlers:
62
+ handler.setLevel(level)
63
+
64
+ # Update child loggers
65
+ for logger_name in logging.root.manager.loggerDict:
66
+ if logger_name.startswith("edsl."):
67
+ logging.getLogger(logger_name).setLevel(level)
68
+
69
+
70
+ # Convenience function to avoid importing logging in every file
71
+ def debug(msg, *args, **kwargs):
72
+ logger.debug(msg, *args, **kwargs)
73
+
74
+
75
+ def info(msg, *args, **kwargs):
76
+ logger.info(msg, *args, **kwargs)
77
+
78
+
79
+ def warning(msg, *args, **kwargs):
80
+ logger.warning(msg, *args, **kwargs)
81
+
82
+
83
+ def error(msg, *args, **kwargs):
84
+ logger.error(msg, *args, **kwargs)
85
+
86
+
87
+ def critical(msg, *args, **kwargs):
88
+ logger.critical(msg, *args, **kwargs)
89
+
90
+
91
+ def exception(msg, *args, **kwargs):
92
+ """Log an exception with traceback at the ERROR level"""
93
+ logger.exception(msg, *args, **kwargs)
94
+
95
+
96
+ def configure_from_config():
97
+ """
98
+ Configure logging based on EDSL_LOG_LEVEL environment variable or config.
99
+
100
+ This function looks for the EDSL_LOG_LEVEL setting in the config and sets
101
+ the logging level accordingly. Valid values are:
102
+ - DEBUG
103
+ - INFO
104
+ - WARNING
105
+ - ERROR
106
+ - CRITICAL
107
+ If no configuration is found, the default level (ERROR) is maintained.
108
+
109
+ """
110
+ try:
111
+ import os
112
+
113
+ # First check environment variable
114
+ log_level = os.environ.get("EDSL_LOG_LEVEL")
115
+
116
+ # If not in environment, try to get from config
117
+ if not log_level:
118
+ try:
119
+ from edsl.config import CONFIG
120
+
121
+ log_level = CONFIG.EDSL_LOG_LEVEL
122
+ except (ImportError, AttributeError):
123
+ # Config might not be available or doesn't have EDSL_LOG_LEVEL
124
+ pass
125
+
126
+ if log_level:
127
+ # Convert to uppercase to match logging constants
128
+ log_level = log_level.upper()
129
+
130
+ # Map string to logging level
131
+ level_map = {
132
+ "DEBUG": logging.DEBUG,
133
+ "INFO": logging.INFO,
134
+ "WARNING": logging.WARNING,
135
+ "ERROR": logging.ERROR,
136
+ "CRITICAL": logging.CRITICAL,
137
+ }
138
+
139
+ if log_level in level_map:
140
+ set_level(level_map[log_level])
141
+ info(f"Log level set to {log_level} from configuration")
142
+ else:
143
+ warning(f"Invalid log level in configuration: {log_level}")
144
+ except Exception as e:
145
+ # Catch any exceptions to ensure logging configuration doesn't break the application
146
+ warning(f"Error configuring logging from config: {e}")
@@ -1,3 +1,26 @@
1
+ """
2
+ The notebooks module provides tools for working with Jupyter notebooks.
3
+
4
+ It includes classes for notebook creation, manipulation, and conversion
5
+ to other formats such as LaTeX.
6
+ """
7
+
1
8
  from .notebook import Notebook
9
+ from .notebook_to_latex import NotebookToLaTeX
10
+ from .exceptions import (
11
+ NotebookError,
12
+ NotebookValueError,
13
+ NotebookFormatError,
14
+ NotebookConversionError,
15
+ NotebookEnvironmentError,
16
+ )
2
17
 
3
- __all__ = ["Notebook"]
18
+ __all__ = [
19
+ "Notebook",
20
+ "NotebookToLaTeX",
21
+ "NotebookError",
22
+ "NotebookValueError",
23
+ "NotebookFormatError",
24
+ "NotebookConversionError",
25
+ "NotebookEnvironmentError",
26
+ ]
@@ -0,0 +1,82 @@
1
+ """
2
+ Custom exceptions for the notebooks module.
3
+ """
4
+
5
+ from ..base import BaseException
6
+
7
+
8
+ class NotebookError(BaseException):
9
+ """
10
+ Base exception class for all notebook-related errors.
11
+
12
+ This is the parent class for all exceptions related to notebook
13
+ operations, including creation, validation, and conversion.
14
+ """
15
+ relevant_doc = "https://docs.expectedparrot.com/"
16
+
17
+
18
+ class NotebookValueError(NotebookError):
19
+ """
20
+ Exception raised when an invalid value is provided to a notebook method.
21
+
22
+ This exception occurs when attempting to create or modify a notebook
23
+ with invalid values, such as:
24
+ - Invalid data format
25
+ - Incompatible notebook contents
26
+
27
+ Examples:
28
+ ```python
29
+ # Attempting to create a notebook with invalid data
30
+ notebook = Notebook(data=invalid_data) # Raises NotebookValueError
31
+ ```
32
+ """
33
+ relevant_doc = "https://docs.expectedparrot.com/"
34
+
35
+
36
+ class NotebookFormatError(NotebookError):
37
+ """
38
+ Exception raised when a notebook's format is invalid.
39
+
40
+ This exception occurs when the notebook structure does not conform
41
+ to the expected Jupyter Notebook format.
42
+
43
+ Examples:
44
+ ```python
45
+ # Attempting to load a notebook with invalid format
46
+ notebook = Notebook.from_dict(invalid_dict) # Raises NotebookFormatError
47
+ ```
48
+ """
49
+ relevant_doc = "https://docs.expectedparrot.com/"
50
+
51
+
52
+ class NotebookConversionError(NotebookError):
53
+ """
54
+ Exception raised when a notebook conversion fails.
55
+
56
+ This exception occurs when attempting to convert a notebook to another
57
+ format (like LaTeX) and the conversion process fails.
58
+
59
+ Examples:
60
+ ```python
61
+ # Attempting to convert a notebook to LaTeX with invalid options
62
+ notebook.to_latex(invalid_options) # Raises NotebookConversionError
63
+ ```
64
+ """
65
+ relevant_doc = "https://docs.expectedparrot.com/"
66
+
67
+
68
+ class NotebookEnvironmentError(NotebookError):
69
+ """
70
+ Exception raised when the notebook environment is not supportable.
71
+
72
+ This exception occurs when attempting to create a notebook in an
73
+ environment that doesn't provide required context, such as when
74
+ trying to create a notebook from within itself outside of VS Code.
75
+
76
+ Examples:
77
+ ```python
78
+ # Attempting to create a notebook from within itself in an unsupported IDE
79
+ notebook = Notebook() # Raises NotebookEnvironmentError
80
+ ```
81
+ """
82
+ relevant_doc = "https://docs.expectedparrot.com/"
@@ -2,7 +2,10 @@
2
2
 
3
3
  from __future__ import annotations
4
4
  import json
5
- from typing import Dict, List, Optional, Union
5
+ from typing import Dict, List, Optional, TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from rich.table import Table
6
9
  from uuid import uuid4
7
10
 
8
11
  from ..base import Base
@@ -47,7 +50,8 @@ class Notebook(Base):
47
50
  self.data = json.loads(json.dumps(data))
48
51
  else:
49
52
  # TODO: Support for IDEs other than VSCode
50
- raise NotImplementedError(
53
+ from .exceptions import NotebookEnvironmentError
54
+ raise NotebookEnvironmentError(
51
55
  "Cannot create a notebook from within itself in this development environment"
52
56
  )
53
57
 
@@ -251,7 +255,7 @@ class Notebook(Base):
251
255
 
252
256
  :param filename: Name of the output folder and main tex file (without extension)
253
257
  """
254
- from .NotebookToLaTeX import NotebookToLaTeX
258
+ from .notebook_to_latex import NotebookToLaTeX
255
259
 
256
260
  NotebookToLaTeX(self).convert(filename)
257
261
 
@@ -2,7 +2,6 @@ from typing import Optional, Dict
2
2
  import os
3
3
  import nbformat
4
4
  from nbconvert.exporters import LatexExporter
5
- from nbconvert.writers import FilesWriter
6
5
 
7
6
 
8
7
  class NotebookToLaTeX:
@@ -123,7 +122,7 @@ To clean up build files:
123
122
 
124
123
  # Example usage:
125
124
  if __name__ == "__main__":
126
- from edsl import Notebook
125
+ from .. import Notebook
127
126
 
128
127
  # Create or load a notebook
129
128
  notebook = Notebook.example()
@@ -0,0 +1,63 @@
1
+ """EDSL Plugins module for extending functionality with custom plugins."""
2
+
3
+ # Import for public API
4
+ from .plugin_host import PluginHost, get_plugin_manager
5
+ from .exceptions import (
6
+ PluginException,
7
+ PluginNotFoundError,
8
+ PluginInstallationError,
9
+ GitHubRepoError,
10
+ InvalidPluginError,
11
+ PluginMethodError,
12
+ PluginDependencyError
13
+ )
14
+ from .cli import PluginCLI
15
+ # Import the Typer CLI
16
+ from .cli_typer import app as typer_app
17
+
18
+ # Public API functions
19
+ def install_from_github(github_url, branch=None):
20
+ """Install a plugin from a GitHub repository."""
21
+ return PluginHost.install_from_github(github_url, branch)
22
+
23
+ def uninstall_plugin(plugin_name):
24
+ """Uninstall a plugin by name."""
25
+ return PluginHost.uninstall_plugin(plugin_name)
26
+
27
+ def list_plugins():
28
+ """List all installed plugins."""
29
+ return PluginHost.list_plugins()
30
+
31
+ def get_exports():
32
+ """Get objects exported to the global namespace by plugins."""
33
+ return PluginHost.get_exports()
34
+
35
+ def cli():
36
+ """Run the plugin CLI (legacy version)."""
37
+ from .cli import main
38
+ main()
39
+
40
+ def cli_typer():
41
+ """Run the Typer-based plugin CLI."""
42
+ from .cli_typer import run
43
+ run()
44
+
45
+ __all__ = [
46
+ 'PluginHost',
47
+ 'get_plugin_manager',
48
+ 'install_from_github',
49
+ 'uninstall_plugin',
50
+ 'list_plugins',
51
+ 'get_exports',
52
+ 'cli',
53
+ 'cli_typer',
54
+ 'PluginCLI',
55
+ 'typer_app',
56
+ 'PluginException',
57
+ 'PluginNotFoundError',
58
+ 'PluginInstallationError',
59
+ 'GitHubRepoError',
60
+ 'InvalidPluginError',
61
+ 'PluginMethodError',
62
+ 'PluginDependencyError'
63
+ ]
@@ -0,0 +1,50 @@
1
+ # Example plugin that demonstrates exporting objects to the global namespace
2
+ import pluggy
3
+ from typing import Dict, Any, Optional
4
+
5
+ # Define a hook implementation marker
6
+ hookimpl = pluggy.HookimplMarker("edsl")
7
+
8
+ # Create a class that will be exported to the global namespace
9
+ class ExportedClass:
10
+ """An example class exported from a plugin to the global namespace."""
11
+
12
+ def __init__(self, name="Example"):
13
+ self.name = name
14
+
15
+ def greet(self):
16
+ """Return a greeting message."""
17
+ return f"Hello from {self.name}!"
18
+
19
+ # A function to be exported to the global namespace
20
+ def exported_function(text):
21
+ """An example function exported from a plugin to the global namespace."""
22
+ return f"Processed: {text}"
23
+
24
+ class ExportExample:
25
+ """Example plugin that exports objects to the global namespace."""
26
+
27
+ @hookimpl
28
+ def plugin_name(self):
29
+ return "ExportExample"
30
+
31
+ @hookimpl
32
+ def plugin_description(self):
33
+ return "Demonstrates how to export objects to the global namespace."
34
+
35
+ @hookimpl
36
+ def edsl_plugin(self, plugin_name=None):
37
+ if plugin_name is None or plugin_name == "ExportExample":
38
+ return self
39
+
40
+ @hookimpl
41
+ def get_plugin_methods(self):
42
+ return {}
43
+
44
+ @hookimpl
45
+ def exports_to_namespace(self) -> Dict[str, Any]:
46
+ """Export objects to the global namespace."""
47
+ return {
48
+ "ExportedClass": ExportedClass,
49
+ "exported_function": exported_function
50
+ }
@@ -0,0 +1,67 @@
1
+ # edsl/plugins/text_plugins.py
2
+ import pluggy
3
+ from typing import Dict, Any, Optional
4
+
5
+ # Define a hook implementation marker
6
+ hookimpl = pluggy.HookimplMarker("edsl")
7
+
8
+ # Create an exportable class for demonstration
9
+ class PigLatinTranslator:
10
+ """A class that translates text to Pig Latin."""
11
+
12
+ @staticmethod
13
+ def translate(text):
14
+ """Translate text to Pig Latin."""
15
+ words = text.split()
16
+ result = []
17
+ for word in words:
18
+ # Simple Pig Latin rule: move first letter to end and add "ay"
19
+ if len(word) > 1:
20
+ pig_latin = word[1:] + word[0] + "ay"
21
+ result.append(pig_latin)
22
+ else:
23
+ result.append(word + "ay")
24
+ return " ".join(result)
25
+
26
+ class PigLatin:
27
+ """Text processing plugin."""
28
+
29
+ @hookimpl
30
+ def plugin_name(self):
31
+ return "PigLatin"
32
+
33
+ @hookimpl
34
+ def plugin_description(self):
35
+ return "This plugin translates text to Pig Latin."
36
+
37
+ @hookimpl
38
+ def edsl_plugin(self, plugin_name=None):
39
+ if plugin_name is None or plugin_name == "PigLatin":
40
+ return self
41
+
42
+ @hookimpl
43
+ def get_plugin_methods(self):
44
+ return {
45
+ "pig_latin": self.pig_latin
46
+ }
47
+
48
+ @hookimpl
49
+ def exports_to_namespace(self) -> Dict[str, Any]:
50
+ """Export objects to the global namespace."""
51
+ return {
52
+ "PigLatinTranslator": PigLatinTranslator
53
+ }
54
+
55
+ def pig_latin(self, survey, *args, **kwargs):
56
+ """Get pig latin translation of survey questions."""
57
+ # A simple Pig Latin translator without using the model
58
+ # This avoids needing API keys for testing
59
+
60
+ #print(f"Processing survey: {survey}")
61
+
62
+ # Translate each question text
63
+ translations = []
64
+ for question in survey.questions:
65
+ translations.append(PigLatinTranslator.translate(question.question_text))
66
+
67
+ return translations