edsl 0.1.47__py3-none-any.whl → 0.1.49__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 (314) hide show
  1. edsl/__init__.py +44 -39
  2. edsl/__version__.py +1 -1
  3. edsl/agents/__init__.py +4 -2
  4. edsl/agents/{Agent.py → agent.py} +442 -152
  5. edsl/agents/{AgentList.py → agent_list.py} +220 -162
  6. edsl/agents/descriptors.py +46 -7
  7. edsl/{exceptions/agents.py → agents/exceptions.py} +3 -12
  8. edsl/base/__init__.py +75 -0
  9. edsl/base/base_class.py +1303 -0
  10. edsl/base/data_transfer_models.py +114 -0
  11. edsl/base/enums.py +215 -0
  12. edsl/base.py +8 -0
  13. edsl/buckets/__init__.py +25 -0
  14. edsl/buckets/bucket_collection.py +324 -0
  15. edsl/buckets/model_buckets.py +206 -0
  16. edsl/buckets/token_bucket.py +502 -0
  17. edsl/{jobs/buckets/TokenBucketAPI.py → buckets/token_bucket_api.py} +1 -1
  18. edsl/buckets/token_bucket_client.py +509 -0
  19. edsl/caching/__init__.py +20 -0
  20. edsl/caching/cache.py +814 -0
  21. edsl/caching/cache_entry.py +427 -0
  22. edsl/{data/CacheHandler.py → caching/cache_handler.py} +14 -15
  23. edsl/caching/exceptions.py +24 -0
  24. edsl/caching/orm.py +30 -0
  25. edsl/{data/RemoteCacheSync.py → caching/remote_cache_sync.py} +3 -3
  26. edsl/caching/sql_dict.py +441 -0
  27. edsl/config/__init__.py +8 -0
  28. edsl/config/config_class.py +177 -0
  29. edsl/config.py +4 -176
  30. edsl/conversation/Conversation.py +7 -7
  31. edsl/conversation/car_buying.py +4 -4
  32. edsl/conversation/chips.py +6 -6
  33. edsl/coop/__init__.py +25 -2
  34. edsl/coop/coop.py +311 -75
  35. edsl/coop/{ExpectedParrotKeyHandler.py → ep_key_handling.py} +86 -10
  36. edsl/coop/exceptions.py +62 -0
  37. edsl/coop/price_fetcher.py +126 -0
  38. edsl/coop/utils.py +89 -24
  39. edsl/data_transfer_models.py +5 -72
  40. edsl/dataset/__init__.py +10 -0
  41. edsl/{results/Dataset.py → dataset/dataset.py} +116 -36
  42. edsl/{results/DatasetExportMixin.py → dataset/dataset_operations_mixin.py} +606 -122
  43. edsl/{results/DatasetTree.py → dataset/dataset_tree.py} +156 -75
  44. edsl/{results/TableDisplay.py → dataset/display/table_display.py} +18 -7
  45. edsl/{results → dataset/display}/table_renderers.py +58 -2
  46. edsl/{results → dataset}/file_exports.py +4 -5
  47. edsl/{results → dataset}/smart_objects.py +2 -2
  48. edsl/enums.py +5 -205
  49. edsl/inference_services/__init__.py +5 -0
  50. edsl/inference_services/{AvailableModelCacheHandler.py → available_model_cache_handler.py} +2 -3
  51. edsl/inference_services/{AvailableModelFetcher.py → available_model_fetcher.py} +8 -14
  52. edsl/inference_services/data_structures.py +3 -2
  53. edsl/{exceptions/inference_services.py → inference_services/exceptions.py} +1 -1
  54. edsl/inference_services/{InferenceServiceABC.py → inference_service_abc.py} +1 -1
  55. edsl/inference_services/{InferenceServicesCollection.py → inference_services_collection.py} +8 -7
  56. edsl/inference_services/registry.py +4 -41
  57. edsl/inference_services/{ServiceAvailability.py → service_availability.py} +5 -25
  58. edsl/inference_services/services/__init__.py +31 -0
  59. edsl/inference_services/{AnthropicService.py → services/anthropic_service.py} +3 -3
  60. edsl/inference_services/{AwsBedrock.py → services/aws_bedrock.py} +2 -2
  61. edsl/inference_services/{AzureAI.py → services/azure_ai.py} +2 -2
  62. edsl/inference_services/{DeepInfraService.py → services/deep_infra_service.py} +1 -3
  63. edsl/inference_services/{DeepSeekService.py → services/deep_seek_service.py} +2 -4
  64. edsl/inference_services/{GoogleService.py → services/google_service.py} +5 -4
  65. edsl/inference_services/{GroqService.py → services/groq_service.py} +1 -1
  66. edsl/inference_services/{MistralAIService.py → services/mistral_ai_service.py} +3 -3
  67. edsl/inference_services/{OllamaService.py → services/ollama_service.py} +1 -7
  68. edsl/inference_services/{OpenAIService.py → services/open_ai_service.py} +5 -6
  69. edsl/inference_services/{PerplexityService.py → services/perplexity_service.py} +3 -7
  70. edsl/inference_services/{TestService.py → services/test_service.py} +7 -6
  71. edsl/inference_services/{TogetherAIService.py → services/together_ai_service.py} +2 -6
  72. edsl/inference_services/{XAIService.py → services/xai_service.py} +1 -1
  73. edsl/inference_services/write_available.py +1 -2
  74. edsl/instructions/__init__.py +6 -0
  75. edsl/{surveys/instructions/Instruction.py → instructions/instruction.py} +11 -6
  76. edsl/{surveys/instructions/InstructionCollection.py → instructions/instruction_collection.py} +10 -5
  77. edsl/{surveys/InstructionHandler.py → instructions/instruction_handler.py} +3 -3
  78. edsl/{jobs/interviews → interviews}/ReportErrors.py +2 -2
  79. edsl/interviews/__init__.py +4 -0
  80. edsl/{jobs/AnswerQuestionFunctionConstructor.py → interviews/answering_function.py} +45 -18
  81. edsl/{jobs/interviews/InterviewExceptionEntry.py → interviews/exception_tracking.py} +107 -22
  82. edsl/interviews/interview.py +638 -0
  83. edsl/{jobs/interviews/InterviewStatusDictionary.py → interviews/interview_status_dictionary.py} +21 -12
  84. edsl/{jobs/interviews/InterviewStatusLog.py → interviews/interview_status_log.py} +16 -7
  85. edsl/{jobs/InterviewTaskManager.py → interviews/interview_task_manager.py} +12 -7
  86. edsl/{jobs/RequestTokenEstimator.py → interviews/request_token_estimator.py} +8 -3
  87. edsl/{jobs/interviews/InterviewStatistic.py → interviews/statistics.py} +36 -10
  88. edsl/invigilators/__init__.py +38 -0
  89. edsl/invigilators/invigilator_base.py +477 -0
  90. edsl/{agents/Invigilator.py → invigilators/invigilators.py} +263 -10
  91. edsl/invigilators/prompt_constructor.py +476 -0
  92. edsl/{agents → invigilators}/prompt_helpers.py +2 -1
  93. edsl/{agents/QuestionInstructionPromptBuilder.py → invigilators/question_instructions_prompt_builder.py} +18 -13
  94. edsl/{agents → invigilators}/question_option_processor.py +96 -21
  95. edsl/{agents/QuestionTemplateReplacementsBuilder.py → invigilators/question_template_replacements_builder.py} +64 -12
  96. edsl/jobs/__init__.py +7 -1
  97. edsl/jobs/async_interview_runner.py +99 -35
  98. edsl/jobs/check_survey_scenario_compatibility.py +7 -5
  99. edsl/jobs/data_structures.py +153 -22
  100. edsl/{exceptions/jobs.py → jobs/exceptions.py} +2 -1
  101. edsl/jobs/{FetchInvigilator.py → fetch_invigilator.py} +4 -4
  102. edsl/jobs/{loggers/HTMLTableJobLogger.py → html_table_job_logger.py} +6 -2
  103. edsl/jobs/{Jobs.py → jobs.py} +313 -167
  104. edsl/jobs/{JobsChecks.py → jobs_checks.py} +15 -7
  105. edsl/jobs/{JobsComponentConstructor.py → jobs_component_constructor.py} +19 -17
  106. edsl/jobs/{InterviewsConstructor.py → jobs_interview_constructor.py} +10 -5
  107. edsl/jobs/jobs_pricing_estimation.py +347 -0
  108. edsl/jobs/{JobsRemoteInferenceLogger.py → jobs_remote_inference_logger.py} +4 -3
  109. edsl/jobs/jobs_runner_asyncio.py +282 -0
  110. edsl/jobs/{JobsRemoteInferenceHandler.py → remote_inference.py} +19 -22
  111. edsl/jobs/results_exceptions_handler.py +2 -2
  112. edsl/key_management/__init__.py +28 -0
  113. edsl/key_management/key_lookup.py +161 -0
  114. edsl/{language_models/key_management/KeyLookupBuilder.py → key_management/key_lookup_builder.py} +118 -47
  115. edsl/key_management/key_lookup_collection.py +82 -0
  116. edsl/key_management/models.py +218 -0
  117. edsl/language_models/__init__.py +7 -2
  118. edsl/language_models/{ComputeCost.py → compute_cost.py} +18 -3
  119. edsl/{exceptions/language_models.py → language_models/exceptions.py} +2 -1
  120. edsl/language_models/language_model.py +1080 -0
  121. edsl/language_models/model.py +10 -25
  122. edsl/language_models/{ModelList.py → model_list.py} +9 -14
  123. edsl/language_models/{RawResponseHandler.py → raw_response_handler.py} +1 -1
  124. edsl/language_models/{RegisterLanguageModelsMeta.py → registry.py} +1 -1
  125. edsl/language_models/repair.py +4 -4
  126. edsl/language_models/utilities.py +4 -4
  127. edsl/notebooks/__init__.py +3 -1
  128. edsl/notebooks/{Notebook.py → notebook.py} +7 -8
  129. edsl/prompts/__init__.py +1 -1
  130. edsl/{exceptions/prompts.py → prompts/exceptions.py} +3 -1
  131. edsl/prompts/{Prompt.py → prompt.py} +101 -95
  132. edsl/questions/HTMLQuestion.py +1 -1
  133. edsl/questions/__init__.py +154 -25
  134. edsl/questions/answer_validator_mixin.py +1 -1
  135. edsl/questions/compose_questions.py +4 -3
  136. edsl/questions/derived/question_likert_five.py +166 -0
  137. edsl/questions/derived/{QuestionLinearScale.py → question_linear_scale.py} +4 -4
  138. edsl/questions/derived/{QuestionTopK.py → question_top_k.py} +4 -4
  139. edsl/questions/derived/{QuestionYesNo.py → question_yes_no.py} +4 -5
  140. edsl/questions/descriptors.py +24 -30
  141. edsl/questions/loop_processor.py +65 -19
  142. edsl/questions/question_base.py +881 -0
  143. edsl/questions/question_base_gen_mixin.py +15 -16
  144. edsl/questions/{QuestionBasePromptsMixin.py → question_base_prompts_mixin.py} +2 -2
  145. edsl/questions/{QuestionBudget.py → question_budget.py} +3 -4
  146. edsl/questions/{QuestionCheckBox.py → question_check_box.py} +16 -16
  147. edsl/questions/{QuestionDict.py → question_dict.py} +39 -5
  148. edsl/questions/{QuestionExtract.py → question_extract.py} +9 -9
  149. edsl/questions/question_free_text.py +282 -0
  150. edsl/questions/{QuestionFunctional.py → question_functional.py} +6 -5
  151. edsl/questions/{QuestionList.py → question_list.py} +6 -7
  152. edsl/questions/{QuestionMatrix.py → question_matrix.py} +6 -5
  153. edsl/questions/{QuestionMultipleChoice.py → question_multiple_choice.py} +126 -21
  154. edsl/questions/{QuestionNumerical.py → question_numerical.py} +5 -5
  155. edsl/questions/{QuestionRank.py → question_rank.py} +6 -6
  156. edsl/questions/question_registry.py +4 -9
  157. edsl/questions/register_questions_meta.py +8 -4
  158. edsl/questions/response_validator_abc.py +17 -16
  159. edsl/results/__init__.py +4 -1
  160. edsl/{exceptions/results.py → results/exceptions.py} +1 -1
  161. edsl/results/report.py +197 -0
  162. edsl/results/{Result.py → result.py} +131 -45
  163. edsl/results/{Results.py → results.py} +365 -220
  164. edsl/results/results_selector.py +344 -25
  165. edsl/scenarios/__init__.py +30 -3
  166. edsl/scenarios/{ConstructDownloadLink.py → construct_download_link.py} +7 -0
  167. edsl/scenarios/directory_scanner.py +156 -13
  168. edsl/scenarios/document_chunker.py +186 -0
  169. edsl/scenarios/exceptions.py +101 -0
  170. edsl/scenarios/file_methods.py +2 -3
  171. edsl/scenarios/{FileStore.py → file_store.py} +275 -189
  172. edsl/scenarios/handlers/__init__.py +14 -14
  173. edsl/scenarios/handlers/{csv.py → csv_file_store.py} +1 -2
  174. edsl/scenarios/handlers/{docx.py → docx_file_store.py} +8 -7
  175. edsl/scenarios/handlers/{html.py → html_file_store.py} +1 -2
  176. edsl/scenarios/handlers/{jpeg.py → jpeg_file_store.py} +1 -1
  177. edsl/scenarios/handlers/{json.py → json_file_store.py} +1 -1
  178. edsl/scenarios/handlers/latex_file_store.py +5 -0
  179. edsl/scenarios/handlers/{md.py → md_file_store.py} +1 -1
  180. edsl/scenarios/handlers/{pdf.py → pdf_file_store.py} +2 -2
  181. edsl/scenarios/handlers/{png.py → png_file_store.py} +1 -1
  182. edsl/scenarios/handlers/{pptx.py → pptx_file_store.py} +8 -7
  183. edsl/scenarios/handlers/{py.py → py_file_store.py} +1 -3
  184. edsl/scenarios/handlers/{sql.py → sql_file_store.py} +2 -1
  185. edsl/scenarios/handlers/{sqlite.py → sqlite_file_store.py} +2 -3
  186. edsl/scenarios/handlers/{txt.py → txt_file_store.py} +1 -1
  187. edsl/scenarios/scenario.py +928 -0
  188. edsl/scenarios/scenario_join.py +18 -5
  189. edsl/scenarios/{ScenarioList.py → scenario_list.py} +294 -106
  190. edsl/scenarios/{ScenarioListPdfMixin.py → scenario_list_pdf_tools.py} +16 -15
  191. edsl/scenarios/scenario_selector.py +5 -1
  192. edsl/study/ObjectEntry.py +2 -2
  193. edsl/study/SnapShot.py +5 -5
  194. edsl/study/Study.py +18 -19
  195. edsl/study/__init__.py +6 -4
  196. edsl/surveys/__init__.py +7 -4
  197. edsl/surveys/dag/__init__.py +2 -0
  198. edsl/surveys/{ConstructDAG.py → dag/construct_dag.py} +3 -3
  199. edsl/surveys/{DAG.py → dag/dag.py} +13 -10
  200. edsl/surveys/descriptors.py +1 -1
  201. edsl/surveys/{EditSurvey.py → edit_survey.py} +9 -9
  202. edsl/{exceptions/surveys.py → surveys/exceptions.py} +1 -2
  203. edsl/surveys/memory/__init__.py +3 -0
  204. edsl/surveys/{MemoryPlan.py → memory/memory_plan.py} +10 -9
  205. edsl/surveys/rules/__init__.py +3 -0
  206. edsl/surveys/{Rule.py → rules/rule.py} +103 -43
  207. edsl/surveys/{RuleCollection.py → rules/rule_collection.py} +21 -30
  208. edsl/surveys/{RuleManager.py → rules/rule_manager.py} +19 -13
  209. edsl/surveys/survey.py +1743 -0
  210. edsl/surveys/{SurveyExportMixin.py → survey_export.py} +22 -27
  211. edsl/surveys/{SurveyFlowVisualization.py → survey_flow_visualization.py} +11 -2
  212. edsl/surveys/{Simulator.py → survey_simulator.py} +10 -3
  213. edsl/tasks/__init__.py +32 -0
  214. edsl/{jobs/tasks/QuestionTaskCreator.py → tasks/question_task_creator.py} +115 -57
  215. edsl/tasks/task_creators.py +135 -0
  216. edsl/{jobs/tasks/TaskHistory.py → tasks/task_history.py} +86 -47
  217. edsl/{jobs/tasks → tasks}/task_status_enum.py +91 -7
  218. edsl/tasks/task_status_log.py +85 -0
  219. edsl/tokens/__init__.py +2 -0
  220. edsl/tokens/interview_token_usage.py +53 -0
  221. edsl/utilities/PrettyList.py +1 -1
  222. edsl/utilities/SystemInfo.py +25 -22
  223. edsl/utilities/__init__.py +29 -21
  224. edsl/utilities/gcp_bucket/__init__.py +2 -0
  225. edsl/utilities/gcp_bucket/cloud_storage.py +99 -96
  226. edsl/utilities/interface.py +44 -536
  227. edsl/{results/MarkdownToPDF.py → utilities/markdown_to_pdf.py} +13 -5
  228. edsl/utilities/repair_functions.py +1 -1
  229. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/METADATA +1 -1
  230. edsl-0.1.49.dist-info/RECORD +347 -0
  231. edsl/Base.py +0 -493
  232. edsl/BaseDiff.py +0 -260
  233. edsl/agents/InvigilatorBase.py +0 -260
  234. edsl/agents/PromptConstructor.py +0 -318
  235. edsl/coop/PriceFetcher.py +0 -54
  236. edsl/data/Cache.py +0 -582
  237. edsl/data/CacheEntry.py +0 -238
  238. edsl/data/SQLiteDict.py +0 -292
  239. edsl/data/__init__.py +0 -5
  240. edsl/data/orm.py +0 -10
  241. edsl/exceptions/cache.py +0 -5
  242. edsl/exceptions/coop.py +0 -14
  243. edsl/exceptions/data.py +0 -14
  244. edsl/exceptions/scenarios.py +0 -29
  245. edsl/jobs/Answers.py +0 -43
  246. edsl/jobs/JobsPrompts.py +0 -354
  247. edsl/jobs/buckets/BucketCollection.py +0 -134
  248. edsl/jobs/buckets/ModelBuckets.py +0 -65
  249. edsl/jobs/buckets/TokenBucket.py +0 -283
  250. edsl/jobs/buckets/TokenBucketClient.py +0 -191
  251. edsl/jobs/interviews/Interview.py +0 -395
  252. edsl/jobs/interviews/InterviewExceptionCollection.py +0 -99
  253. edsl/jobs/interviews/InterviewStatisticsCollection.py +0 -25
  254. edsl/jobs/runners/JobsRunnerAsyncio.py +0 -163
  255. edsl/jobs/runners/JobsRunnerStatusData.py +0 -0
  256. edsl/jobs/tasks/TaskCreators.py +0 -64
  257. edsl/jobs/tasks/TaskStatusLog.py +0 -23
  258. edsl/jobs/tokens/InterviewTokenUsage.py +0 -27
  259. edsl/language_models/LanguageModel.py +0 -635
  260. edsl/language_models/ServiceDataSources.py +0 -0
  261. edsl/language_models/key_management/KeyLookup.py +0 -63
  262. edsl/language_models/key_management/KeyLookupCollection.py +0 -38
  263. edsl/language_models/key_management/models.py +0 -137
  264. edsl/questions/QuestionBase.py +0 -544
  265. edsl/questions/QuestionFreeText.py +0 -130
  266. edsl/questions/derived/QuestionLikertFive.py +0 -76
  267. edsl/results/ResultsExportMixin.py +0 -45
  268. edsl/results/TextEditor.py +0 -50
  269. edsl/results/results_fetch_mixin.py +0 -33
  270. edsl/results/results_tools_mixin.py +0 -98
  271. edsl/scenarios/DocumentChunker.py +0 -104
  272. edsl/scenarios/Scenario.py +0 -548
  273. edsl/scenarios/ScenarioHtmlMixin.py +0 -65
  274. edsl/scenarios/ScenarioListExportMixin.py +0 -45
  275. edsl/scenarios/handlers/latex.py +0 -5
  276. edsl/shared.py +0 -1
  277. edsl/surveys/Survey.py +0 -1301
  278. edsl/surveys/SurveyQualtricsImport.py +0 -284
  279. edsl/surveys/SurveyToApp.py +0 -141
  280. edsl/surveys/instructions/__init__.py +0 -0
  281. edsl/tools/__init__.py +0 -1
  282. edsl/tools/clusters.py +0 -192
  283. edsl/tools/embeddings.py +0 -27
  284. edsl/tools/embeddings_plotting.py +0 -118
  285. edsl/tools/plotting.py +0 -112
  286. edsl/tools/summarize.py +0 -18
  287. edsl/utilities/data/Registry.py +0 -6
  288. edsl/utilities/data/__init__.py +0 -1
  289. edsl/utilities/data/scooter_results.json +0 -1
  290. edsl-0.1.47.dist-info/RECORD +0 -354
  291. /edsl/coop/{CoopFunctionsMixin.py → coop_functions.py} +0 -0
  292. /edsl/{results → dataset/display}/CSSParameterizer.py +0 -0
  293. /edsl/{language_models/key_management → dataset/display}/__init__.py +0 -0
  294. /edsl/{results → dataset/display}/table_data_class.py +0 -0
  295. /edsl/{results → dataset/display}/table_display.css +0 -0
  296. /edsl/{results/ResultsGGMixin.py → dataset/r/ggplot.py} +0 -0
  297. /edsl/{results → dataset}/tree_explore.py +0 -0
  298. /edsl/{surveys/instructions/ChangeInstruction.py → instructions/change_instruction.py} +0 -0
  299. /edsl/{jobs/interviews → interviews}/interview_status_enum.py +0 -0
  300. /edsl/jobs/{runners/JobsRunnerStatus.py → jobs_runner_status.py} +0 -0
  301. /edsl/language_models/{PriceManager.py → price_manager.py} +0 -0
  302. /edsl/language_models/{fake_openai_call.py → unused/fake_openai_call.py} +0 -0
  303. /edsl/language_models/{fake_openai_service.py → unused/fake_openai_service.py} +0 -0
  304. /edsl/notebooks/{NotebookToLaTeX.py → notebook_to_latex.py} +0 -0
  305. /edsl/{exceptions/questions.py → questions/exceptions.py} +0 -0
  306. /edsl/questions/{SimpleAskMixin.py → simple_ask_mixin.py} +0 -0
  307. /edsl/surveys/{Memory.py → memory/memory.py} +0 -0
  308. /edsl/surveys/{MemoryManagement.py → memory/memory_management.py} +0 -0
  309. /edsl/surveys/{SurveyCSS.py → survey_css.py} +0 -0
  310. /edsl/{jobs/tokens/TokenUsage.py → tokens/token_usage.py} +0 -0
  311. /edsl/{results/MarkdownToDocx.py → utilities/markdown_to_docx.py} +0 -0
  312. /edsl/{TemplateLoader.py → utilities/template_loader.py} +0 -0
  313. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/LICENSE +0 -0
  314. {edsl-0.1.47.dist-info → edsl-0.1.49.dist-info}/WHEEL +0 -0
edsl/Base.py DELETED
@@ -1,493 +0,0 @@
1
- """Base class for all classes in the package. It provides rich printing and persistence of objects."""
2
-
3
- from abc import ABC, abstractmethod, ABCMeta
4
- import gzip
5
- import json
6
- from typing import Any, Optional, Union
7
- from uuid import UUID
8
-
9
-
10
- class PersistenceMixin:
11
- """Mixin for saving and loading objects to and from files."""
12
-
13
- def duplicate(self, add_edsl_version=False):
14
- """Return a duplicate of the object."""
15
- return self.from_dict(self.to_dict(add_edsl_version=False))
16
-
17
- def push(
18
- self,
19
- description: Optional[str] = None,
20
- alias: Optional[str] = None,
21
- visibility: Optional[str] = "unlisted",
22
- expected_parrot_url: Optional[str] = None,
23
- ):
24
- """Post the object to coop."""
25
- from edsl.coop import Coop
26
-
27
- c = Coop(url=expected_parrot_url)
28
- return c.create(self, description, alias, visibility)
29
-
30
- def to_yaml(self, add_edsl_version=False, filename: str = None) -> Union[str, None]:
31
- import yaml
32
-
33
- output = yaml.dump(self.to_dict(add_edsl_version=add_edsl_version))
34
- if not filename:
35
- return output
36
-
37
- with open(filename, "w") as f:
38
- f.write(output)
39
-
40
- @classmethod
41
- def from_yaml(cls, yaml_str: Optional[str] = None, filename: Optional[str] = None):
42
- if yaml_str is None and filename is not None:
43
- with open(filename, "r") as f:
44
- yaml_str = f.read()
45
- return cls.from_yaml(yaml_str=yaml_str)
46
- elif yaml_str and filename is None:
47
- import yaml
48
-
49
- d = yaml.load(yaml_str, Loader=yaml.FullLoader)
50
- return cls.from_dict(d)
51
- else:
52
- raise ValueError("Either yaml_str or filename must be provided.")
53
-
54
- def create_download_link(self):
55
- from tempfile import NamedTemporaryFile
56
- from edsl.scenarios.FileStore import FileStore
57
-
58
- with NamedTemporaryFile(suffix=".json.gz") as f:
59
- self.save(f.name)
60
- print(f.name)
61
- fs = FileStore(path=f.name)
62
- return fs.create_link()
63
-
64
- @classmethod
65
- def pull(
66
- cls,
67
- url_or_uuid: Optional[Union[str, UUID]] = None,
68
- ):
69
- """Pull the object from coop.
70
-
71
- Args:
72
- url_or_uuid: Either a UUID string or a URL pointing to the object
73
- """
74
- from edsl.coop import Coop
75
- from edsl.coop.utils import ObjectRegistry
76
-
77
- object_type = ObjectRegistry.get_object_type_by_edsl_class(cls)
78
- coop = Coop()
79
-
80
- return coop.get(url_or_uuid, expected_object_type=object_type)
81
-
82
- @classmethod
83
- def delete(cls, url_or_uuid: Union[str, UUID]) -> None:
84
- """Delete the object from coop."""
85
- from edsl.coop import Coop
86
-
87
- coop = Coop()
88
-
89
- return coop.delete(url_or_uuid)
90
-
91
- @classmethod
92
- def patch_cls(
93
- cls,
94
- url_or_uuid: Union[str, UUID],
95
- description: Optional[str] = None,
96
- value: Optional[Any] = None,
97
- visibility: Optional[str] = None,
98
- ):
99
- """
100
- Patch an uploaded object's attributes (class method version).
101
- - `description` changes the description of the object on Coop
102
- - `value` changes the value of the object on Coop. **has to be an EDSL object**
103
- - `visibility` changes the visibility of the object on Coop
104
- """
105
- from edsl.coop import Coop
106
-
107
- coop = Coop()
108
-
109
- return coop.patch(
110
- url_or_uuid=url_or_uuid,
111
- description=description,
112
- value=value,
113
- visibility=visibility,
114
- )
115
-
116
- class ClassOrInstanceMethod:
117
- """Descriptor that allows a method to be called as both a class method and an instance method."""
118
-
119
- def __init__(self, func):
120
- self.func = func
121
-
122
- def __get__(self, obj, objtype=None):
123
- if obj is None:
124
- # Called as a class method
125
- def wrapper(*args, **kwargs):
126
- return self.func(objtype, *args, **kwargs)
127
-
128
- return wrapper
129
- else:
130
- # Called as an instance method
131
- def wrapper(*args, **kwargs):
132
- return self.func(obj, *args, **kwargs)
133
-
134
- return wrapper
135
-
136
- @ClassOrInstanceMethod
137
- def patch(
138
- self_or_cls,
139
- url_or_uuid: Union[str, UUID],
140
- description: Optional[str] = None,
141
- value: Optional[Any] = None,
142
- visibility: Optional[str] = None,
143
- ):
144
- """
145
- Patch an uploaded object's attributes.
146
-
147
- When called as a class method:
148
- - Requires explicit `value` parameter
149
-
150
- When called as an instance method:
151
- - Uses the instance itself as the `value` parameter
152
-
153
- Parameters:
154
- - `id_or_url`: ID or URL of the object to patch
155
- - `description`: changes the description of the object on Coop
156
- - `value`: changes the value of the object on Coop (required for class method)
157
- - `visibility`: changes the visibility of the object on Coop
158
- """
159
-
160
- # Check if this is being called as a class method
161
- if isinstance(self_or_cls, type):
162
- # This is a class method call
163
- cls = self_or_cls
164
- return cls.patch_cls(
165
- url_or_uuid=url_or_uuid,
166
- description=description,
167
- value=value,
168
- visibility=visibility,
169
- )
170
- else:
171
- # This is an instance method call
172
- instance = self_or_cls
173
- cls_type = instance.__class__
174
-
175
- # Use the instance as the value if not explicitly provided
176
- if value is None:
177
- value = instance
178
- else:
179
- pass
180
-
181
- return cls_type.patch_cls(
182
- url_or_uuid=url_or_uuid,
183
- description=description,
184
- value=value,
185
- visibility=visibility,
186
- )
187
-
188
- @classmethod
189
- def search(cls, query):
190
- """Search for objects on coop."""
191
- from edsl.coop import Coop
192
-
193
- c = Coop()
194
- return c.search(cls, query)
195
-
196
- def store(self, d: dict, key_name: Optional[str] = None):
197
- if key_name is None:
198
- index = len(d)
199
- else:
200
- index = key_name
201
- d[index] = self
202
-
203
- def save(self, filename, compress=True):
204
- """Save the object to a file as zippped JSON.
205
-
206
- >>> obj.save("obj.json.gz")
207
-
208
- """
209
- if filename.endswith("json.gz"):
210
- import warnings
211
-
212
- filename = filename[:-8]
213
- if filename.endswith("json"):
214
- filename = filename[:-5]
215
-
216
- if compress:
217
- full_file_name = filename + ".json.gz"
218
- with gzip.open(full_file_name, "wb") as f:
219
- f.write(json.dumps(self.to_dict()).encode("utf-8"))
220
- else:
221
- full_file_name = filename + ".json"
222
- with open(filename + ".json", "w") as f:
223
- f.write(json.dumps(self.to_dict()))
224
-
225
- print("Saved to", full_file_name)
226
-
227
- @staticmethod
228
- def open_compressed_file(filename):
229
- with gzip.open(filename, "rb") as f:
230
- file_contents = f.read()
231
- file_contents_decoded = file_contents.decode("utf-8")
232
- d = json.loads(file_contents_decoded)
233
- return d
234
-
235
- @staticmethod
236
- def open_regular_file(filename):
237
- with open(filename, "r") as f:
238
- d = json.loads(f.read())
239
- return d
240
-
241
- @classmethod
242
- def load(cls, filename):
243
- """Load the object from a file.
244
-
245
- >>> obj = cls.load("obj.json.gz")
246
-
247
- """
248
-
249
- if filename.endswith("json.gz"):
250
- d = cls.open_compressed_file(filename)
251
- elif filename.endswith("json"):
252
- d = cls.open_regular_file(filename)
253
- else:
254
- try:
255
- d = cls.open_compressed_file(filename + ".json.gz")
256
- except:
257
- d = cls.open_regular_file(filename + ".json")
258
- # finally:
259
- # raise ValueError("File must be a json or json.gz file")
260
-
261
- return cls.from_dict(d)
262
-
263
-
264
- class RegisterSubclassesMeta(ABCMeta):
265
- """Metaclass for registering subclasses."""
266
-
267
- _registry = {}
268
-
269
- def __init__(cls, name, bases, nmspc):
270
- """Register the class in the registry upon creation."""
271
- super(RegisterSubclassesMeta, cls).__init__(name, bases, nmspc)
272
- if cls.__name__ != "Base":
273
- RegisterSubclassesMeta._registry[cls.__name__] = cls
274
-
275
- @staticmethod
276
- def get_registry(exclude_classes: Optional[list] = None):
277
- """Return the registry of subclasses."""
278
- if exclude_classes is None:
279
- exclude_classes = []
280
- return {
281
- k: v
282
- for k, v in dict(RegisterSubclassesMeta._registry).items()
283
- if k not in exclude_classes
284
- }
285
-
286
-
287
- class DiffMethodsMixin:
288
- def __sub__(self, other):
289
- """Return the difference between two objects."""
290
- from edsl.BaseDiff import BaseDiff
291
-
292
- return BaseDiff(self, other)
293
-
294
-
295
- def is_iterable(obj):
296
- try:
297
- iter(obj)
298
- except TypeError:
299
- return False
300
- return True
301
-
302
-
303
- class RepresentationMixin:
304
- def json(self):
305
- return json.loads(json.dumps(self.to_dict(add_edsl_version=False)))
306
-
307
- def to_dataset(self):
308
- from edsl.results.Dataset import Dataset
309
-
310
- return Dataset.from_edsl_object(self)
311
-
312
- def view(self):
313
- "Displays an interactive / perspective view of the object"
314
- return self.to_dataset().view()
315
-
316
- # def print(self, format="rich"):
317
- # return self.to_dataset().table()
318
-
319
- def display_dict(self):
320
- display_dict = {}
321
- d = self.to_dict(add_edsl_version=False)
322
- for key, value in d.items():
323
- if isinstance(value, dict):
324
- for k, v in value.items():
325
- display_dict[f"{key}:{k}"] = v
326
- elif isinstance(value, list):
327
- for i, v in enumerate(value):
328
- display_dict[f"{key}:{i}"] = v
329
- else:
330
- display_dict[key] = value
331
- return display_dict
332
-
333
- def print(self, format="rich"):
334
- from rich.table import Table
335
- from rich.console import Console
336
-
337
- table = Table(title=self.__class__.__name__)
338
- table.add_column("Key", style="bold")
339
- table.add_column("Value", style="bold")
340
-
341
- for key, value in self.display_dict().items():
342
- table.add_row(key, str(value))
343
-
344
- console = Console(record=True)
345
- console.print(table)
346
-
347
- def help(obj):
348
- """
349
- Extract all public instance methods and their docstrings from a class instance.
350
-
351
- Args:
352
- obj: The instance to inspect
353
-
354
- Returns:
355
- dict: A dictionary where keys are method names and values are their docstrings
356
- """
357
- import inspect
358
-
359
- if inspect.isclass(obj):
360
- raise TypeError("Please provide a class instance, not a class")
361
-
362
- methods = {}
363
-
364
- # Get all members of the instance
365
- for name, member in inspect.getmembers(obj):
366
- # Skip private and special methods (those starting with underscore)
367
- if name.startswith("_"):
368
- continue
369
-
370
- # Check if it's specifically an instance method
371
- if inspect.ismethod(member):
372
- # Get the docstring (or empty string if none exists)
373
- docstring = inspect.getdoc(member) or ""
374
- methods[name] = docstring
375
-
376
- from edsl.results.Dataset import Dataset
377
-
378
- d = Dataset(
379
- [
380
- {"method": list(methods.keys())},
381
- {"documentation": list(methods.values())},
382
- ]
383
- )
384
- return d
385
-
386
- def _repr_html_(self):
387
- from edsl.results.TableDisplay import TableDisplay
388
-
389
- if hasattr(self, "_summary"):
390
- summary_dict = self._summary()
391
- summary_line = "".join([f" {k}: {v};" for k, v in summary_dict.items()])
392
- class_name = self.__class__.__name__
393
- docs = getattr(self, "__documentation__", "")
394
- return (
395
- "<p>"
396
- + f"<a href='{docs}'>{class_name}</a>"
397
- + summary_line
398
- + "</p>"
399
- + self.table()._repr_html_()
400
- )
401
- else:
402
- class_name = self.__class__.__name__
403
- documenation = getattr(self, "__documentation__", "")
404
- summary_line = "<p>" + f"<a href='{documenation}'>{class_name}</a>" + "</p>"
405
- display_dict = self.display_dict()
406
- return (
407
- summary_line
408
- + TableDisplay.from_dictionary_wide(display_dict)._repr_html_()
409
- )
410
-
411
- def __str__(self):
412
- return self.__repr__()
413
-
414
-
415
- class HashingMixin:
416
- def __hash__(self) -> int:
417
- """Return a hash of the question."""
418
- from edsl.utilities.utilities import dict_hash
419
-
420
- return dict_hash(self.to_dict(add_edsl_version=False))
421
-
422
- def __eq__(self, other):
423
- """Return whether two objects are equal."""
424
- return hash(self) == hash(other)
425
-
426
-
427
- class Base(
428
- RepresentationMixin,
429
- PersistenceMixin,
430
- DiffMethodsMixin,
431
- HashingMixin,
432
- ABC,
433
- metaclass=RegisterSubclassesMeta,
434
- ):
435
- """Base class for all classes in the package."""
436
-
437
- def keys(self):
438
- """Return the keys of the object."""
439
- _keys = list(self.to_dict().keys())
440
- if "edsl_version" in _keys:
441
- _keys.remove("edsl_version")
442
- if "edsl_class_name" in _keys:
443
- _keys.remove("edsl_class_name")
444
- return _keys
445
-
446
- def values(self):
447
- """Return the values of the object."""
448
- data = self.to_dict()
449
- keys = self.keys()
450
- return {data[key] for key in keys}
451
-
452
- @abstractmethod
453
- def example():
454
- """This method should be implemented by subclasses."""
455
- raise NotImplementedError("This method is not implemented yet.")
456
-
457
- @abstractmethod
458
- def to_dict():
459
- """This method should be implemented by subclasses."""
460
- raise NotImplementedError("This method is not implemented yet.")
461
-
462
- def to_json(self):
463
- return json.dumps(self.to_dict())
464
-
465
- def store(self, d: dict, key_name: Optional[str] = None):
466
- if key_name is None:
467
- index = len(d)
468
- else:
469
- index = key_name
470
- d[index] = self
471
-
472
- @abstractmethod
473
- def from_dict():
474
- """This method should be implemented by subclasses."""
475
- raise NotImplementedError("This method is not implemented yet.")
476
-
477
- @abstractmethod
478
- def code():
479
- """This method should be implemented by subclasses."""
480
- raise NotImplementedError("This method is not implemented yet.")
481
-
482
- def show_methods(self, show_docstrings=True):
483
- """Show the methods of the object."""
484
- public_methods_with_docstrings = [
485
- (method, getattr(self, method).__doc__)
486
- for method in dir(self)
487
- if callable(getattr(self, method)) and not method.startswith("_")
488
- ]
489
- if show_docstrings:
490
- for method, documentation in public_methods_with_docstrings:
491
- print(f"{method}: {documentation}")
492
- else:
493
- return [x[0] for x in public_methods_with_docstrings]