edsl 0.1.33.dev3__py3-none-any.whl → 0.1.34__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 (44) hide show
  1. edsl/Base.py +15 -11
  2. edsl/__version__.py +1 -1
  3. edsl/agents/Invigilator.py +22 -3
  4. edsl/agents/PromptConstructor.py +80 -184
  5. edsl/agents/prompt_helpers.py +129 -0
  6. edsl/coop/coop.py +3 -2
  7. edsl/data_transfer_models.py +0 -1
  8. edsl/inference_services/AnthropicService.py +5 -2
  9. edsl/inference_services/AwsBedrock.py +5 -2
  10. edsl/inference_services/AzureAI.py +5 -2
  11. edsl/inference_services/GoogleService.py +108 -33
  12. edsl/inference_services/MistralAIService.py +5 -2
  13. edsl/inference_services/OpenAIService.py +3 -2
  14. edsl/inference_services/TestService.py +11 -2
  15. edsl/inference_services/TogetherAIService.py +1 -1
  16. edsl/jobs/Jobs.py +91 -10
  17. edsl/jobs/interviews/Interview.py +15 -2
  18. edsl/jobs/runners/JobsRunnerAsyncio.py +46 -25
  19. edsl/jobs/runners/JobsRunnerStatus.py +4 -3
  20. edsl/jobs/tasks/QuestionTaskCreator.py +1 -13
  21. edsl/language_models/LanguageModel.py +12 -9
  22. edsl/language_models/utilities.py +5 -2
  23. edsl/questions/QuestionBase.py +13 -3
  24. edsl/questions/QuestionBaseGenMixin.py +28 -0
  25. edsl/questions/QuestionCheckBox.py +1 -1
  26. edsl/questions/QuestionMultipleChoice.py +8 -4
  27. edsl/questions/ResponseValidatorABC.py +5 -1
  28. edsl/questions/descriptors.py +12 -11
  29. edsl/questions/templates/numerical/answering_instructions.jinja +0 -1
  30. edsl/questions/templates/yes_no/answering_instructions.jinja +2 -2
  31. edsl/scenarios/FileStore.py +159 -76
  32. edsl/scenarios/Scenario.py +23 -49
  33. edsl/scenarios/ScenarioList.py +6 -2
  34. edsl/surveys/DAG.py +62 -0
  35. edsl/surveys/MemoryPlan.py +26 -0
  36. edsl/surveys/Rule.py +24 -0
  37. edsl/surveys/RuleCollection.py +36 -2
  38. edsl/surveys/Survey.py +182 -10
  39. edsl/surveys/base.py +4 -0
  40. {edsl-0.1.33.dev3.dist-info → edsl-0.1.34.dist-info}/METADATA +2 -1
  41. {edsl-0.1.33.dev3.dist-info → edsl-0.1.34.dist-info}/RECORD +43 -43
  42. edsl/scenarios/ScenarioImageMixin.py +0 -100
  43. {edsl-0.1.33.dev3.dist-info → edsl-0.1.34.dist-info}/LICENSE +0 -0
  44. {edsl-0.1.33.dev3.dist-info → edsl-0.1.34.dist-info}/WHEEL +0 -0
@@ -440,7 +440,7 @@ class LanguageModel(
440
440
  system_prompt: str,
441
441
  cache: "Cache",
442
442
  iteration: int = 0,
443
- encoded_image=None,
443
+ files_list=None,
444
444
  ) -> ModelResponse:
445
445
  """Handle caching of responses.
446
446
 
@@ -462,16 +462,18 @@ class LanguageModel(
462
462
  >>> m._get_intended_model_call_outcome(user_prompt = "Hello", system_prompt = "hello", cache = Cache())
463
463
  ModelResponse(...)"""
464
464
 
465
- if encoded_image:
466
- # the image has is appended to the user_prompt for hash-lookup purposes
467
- image_hash = hashlib.md5(encoded_image.encode()).hexdigest()
468
- user_prompt += f" {image_hash}"
465
+ if files_list:
466
+ files_hash = "+".join([str(hash(file)) for file in files_list])
467
+ # print(f"Files hash: {files_hash}")
468
+ user_prompt_with_hashes = user_prompt + f" {files_hash}"
469
+ else:
470
+ user_prompt_with_hashes = user_prompt
469
471
 
470
472
  cache_call_params = {
471
473
  "model": str(self.model),
472
474
  "parameters": self.parameters,
473
475
  "system_prompt": system_prompt,
474
- "user_prompt": user_prompt,
476
+ "user_prompt": user_prompt_with_hashes,
475
477
  "iteration": iteration,
476
478
  }
477
479
  cached_response, cache_key = cache.fetch(**cache_call_params)
@@ -487,7 +489,8 @@ class LanguageModel(
487
489
  params = {
488
490
  "user_prompt": user_prompt,
489
491
  "system_prompt": system_prompt,
490
- **({"encoded_image": encoded_image} if encoded_image else {}),
492
+ "files_list": files_list
493
+ # **({"encoded_image": encoded_image} if encoded_image else {}),
491
494
  }
492
495
  # response = await f(**params)
493
496
  response = await asyncio.wait_for(f(**params), timeout=TIMEOUT)
@@ -531,7 +534,7 @@ class LanguageModel(
531
534
  system_prompt: str,
532
535
  cache: "Cache",
533
536
  iteration: int = 1,
534
- encoded_image=None,
537
+ files_list: Optional[List["File"]] = None,
535
538
  ) -> dict:
536
539
  """Get response, parse, and return as string.
537
540
 
@@ -547,7 +550,7 @@ class LanguageModel(
547
550
  "system_prompt": system_prompt,
548
551
  "iteration": iteration,
549
552
  "cache": cache,
550
- **({"encoded_image": encoded_image} if encoded_image else {}),
553
+ "files_list": files_list,
551
554
  }
552
555
  model_inputs = ModelInputs(user_prompt=user_prompt, system_prompt=system_prompt)
553
556
  model_outputs = await self._async_get_intended_model_call_outcome(**params)
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import Any
2
+ from typing import Any, Optional, List
3
3
  from edsl import Survey
4
4
  from edsl.config import CONFIG
5
5
  from edsl.enums import InferenceServiceType
@@ -40,7 +40,10 @@ def create_language_model(
40
40
  _tpm = 1000000000000
41
41
 
42
42
  async def async_execute_model_call(
43
- self, user_prompt: str, system_prompt: str
43
+ self,
44
+ user_prompt: str,
45
+ system_prompt: str,
46
+ files_list: Optional[List[Any]] = None,
44
47
  ) -> dict[str, Any]:
45
48
  question_number = int(
46
49
  user_prompt.split("XX")[1]
@@ -44,6 +44,13 @@ class QuestionBase(
44
44
  _answering_instructions = None
45
45
  _question_presentation = None
46
46
 
47
+ @property
48
+ def response_model(self) -> type["BaseModel"]:
49
+ if self._response_model is not None:
50
+ return self._response_model
51
+ else:
52
+ return self.create_response_model()
53
+
47
54
  # region: Validation and simulation methods
48
55
  @property
49
56
  def response_validator(self) -> "ResponseValidatorBase":
@@ -75,7 +82,8 @@ class QuestionBase(
75
82
  if not hasattr(self, "_fake_data_factory"):
76
83
  from polyfactory.factories.pydantic_factory import ModelFactory
77
84
 
78
- class FakeData(ModelFactory[self.response_model]): ...
85
+ class FakeData(ModelFactory[self.response_model]):
86
+ ...
79
87
 
80
88
  self._fake_data_factory = FakeData
81
89
  return self._fake_data_factory
@@ -98,7 +106,9 @@ class QuestionBase(
98
106
  comment: Optional[str]
99
107
  generated_tokens: Optional[str]
100
108
 
101
- def _validate_answer(self, answer: dict) -> ValidatedAnswer:
109
+ def _validate_answer(
110
+ self, answer: dict, replacement_dict: dict = None
111
+ ) -> ValidatedAnswer:
102
112
  """Validate the answer.
103
113
  >>> from edsl.exceptions import QuestionAnswerValidationError
104
114
  >>> from edsl import QuestionFreeText as Q
@@ -106,7 +116,7 @@ class QuestionBase(
106
116
  {'answer': 'Hello', 'generated_tokens': 'Hello'}
107
117
  """
108
118
 
109
- return self.response_validator.validate(answer)
119
+ return self.response_validator.validate(answer, replacement_dict)
110
120
 
111
121
  # endregion
112
122
 
@@ -95,6 +95,34 @@ class QuestionBaseGenMixin:
95
95
  questions.append(QuestionBase.from_dict(new_data))
96
96
  return questions
97
97
 
98
+ def render(self, replacement_dict: dict) -> "QuestionBase":
99
+ """Render the question components as jinja2 templates with the replacement dictionary."""
100
+ from jinja2 import Environment
101
+ from edsl import Scenario
102
+
103
+ strings_only_replacement_dict = {
104
+ k: v for k, v in replacement_dict.items() if not isinstance(v, Scenario)
105
+ }
106
+
107
+ def render_string(value: str) -> str:
108
+ if value is None or not isinstance(value, str):
109
+ return value
110
+ else:
111
+ try:
112
+ return (
113
+ Environment()
114
+ .from_string(value)
115
+ .render(strings_only_replacement_dict)
116
+ )
117
+ except Exception as e:
118
+ import warnings
119
+
120
+ warnings.warn("Failed to render string: " + value)
121
+ # breakpoint()
122
+ return value
123
+
124
+ return self.apply_function(render_string)
125
+
98
126
  def apply_function(self, func: Callable, exclude_components=None) -> QuestionBase:
99
127
  """Apply a function to the question parts
100
128
 
@@ -245,7 +245,7 @@ class QuestionCheckBox(QuestionBase):
245
245
 
246
246
  scenario = scenario or Scenario()
247
247
  translated_options = [
248
- Template(option).render(scenario) for option in self.question_options
248
+ Template(str(option)).render(scenario) for option in self.question_options
249
249
  ]
250
250
  translated_codes = []
251
251
  for answer_code in answer_codes:
@@ -120,9 +120,9 @@ class QuestionMultipleChoice(QuestionBase):
120
120
 
121
121
  question_type = "multiple_choice"
122
122
  purpose = "When options are known and limited"
123
- question_options: Union[list[str], list[list], list[float], list[int]] = (
124
- QuestionOptionsDescriptor()
125
- )
123
+ question_options: Union[
124
+ list[str], list[list], list[float], list[int]
125
+ ] = QuestionOptionsDescriptor()
126
126
  _response_model = None
127
127
  response_validator_class = MultipleChoiceResponseValidator
128
128
 
@@ -163,7 +163,11 @@ class QuestionMultipleChoice(QuestionBase):
163
163
  # Answer methods
164
164
  ################
165
165
 
166
- def create_response_model(self):
166
+ def create_response_model(self, replacement_dict: dict = None):
167
+ if replacement_dict is None:
168
+ replacement_dict = {}
169
+ # The replacement dict that could be from scenario, current answers, etc. to populate the response model
170
+
167
171
  if self.use_code:
168
172
  return create_response_model(
169
173
  list(range(len(self.question_options))), self.permissive
@@ -92,7 +92,11 @@ class ResponseValidatorABC(ABC):
92
92
  generated_tokens: Optional[str]
93
93
 
94
94
  def validate(
95
- self, raw_edsl_answer_dict: RawEdslAnswerDict, fix=False, verbose=False
95
+ self,
96
+ raw_edsl_answer_dict: RawEdslAnswerDict,
97
+ fix=False,
98
+ verbose=False,
99
+ replacement_dict: dict = None,
96
100
  ) -> EdslAnswerDict:
97
101
  """This is the main validation function.
98
102
 
@@ -303,7 +303,7 @@ class QuestionOptionsDescriptor(BaseDescriptor):
303
303
  return None
304
304
  else:
305
305
  raise QuestionCreationValidationError(
306
- f"Dynamic question options must have jina2 braces - instead received: {value}."
306
+ f"Dynamic question options must have jinja2 braces - instead received: {value}."
307
307
  )
308
308
  if not isinstance(value, list):
309
309
  raise QuestionCreationValidationError(
@@ -325,14 +325,15 @@ class QuestionOptionsDescriptor(BaseDescriptor):
325
325
  )
326
326
  if not self.linear_scale:
327
327
  if not self.q_budget:
328
- if not (
329
- value
330
- and all(type(x) == type(value[0]) for x in value)
331
- and isinstance(value[0], (str, list, int, float))
332
- ):
333
- raise QuestionCreationValidationError(
334
- f"Question options must be all same type (got {value}).)"
335
- )
328
+ pass
329
+ # if not (
330
+ # value
331
+ # and all(type(x) == type(value[0]) for x in value)
332
+ # and isinstance(value[0], (str, list, int, float))
333
+ # ):
334
+ # raise QuestionCreationValidationError(
335
+ # f"Question options must be all same type (got {value}).)"
336
+ # )
336
337
  else:
337
338
  if not all(isinstance(x, (str)) for x in value):
338
339
  raise QuestionCreationValidationError(
@@ -390,8 +391,8 @@ class QuestionTextDescriptor(BaseDescriptor):
390
391
 
391
392
  def validate(self, value, instance):
392
393
  """Validate the value is a string."""
393
- if len(value) > Settings.MAX_QUESTION_LENGTH:
394
- raise Exception("Question is too long!")
394
+ # if len(value) > Settings.MAX_QUESTION_LENGTH:
395
+ # raise Exception("Question is too long!")
395
396
  if len(value) < 1:
396
397
  raise Exception("Question is too short!")
397
398
  if not isinstance(value, str):
@@ -1,7 +1,6 @@
1
1
  This question requires a numerical response in the form of an integer or decimal (e.g., -12, 0, 1, 2, 3.45, ...).
2
2
  Respond with just your number on a single line.
3
3
  If your response is equivalent to zero, report '0'
4
- If you cannot determine the answer, report 'None'
5
4
 
6
5
  {% if include_comment %}
7
6
  After the answer, put a comment explaining your choice on the next line.
@@ -1,6 +1,6 @@
1
1
  {# Answering Instructions #}
2
- Please reponse with just your answer.
2
+ Please respond with just your answer.
3
3
 
4
4
  {% if include_comment %}
5
- After the answer, you can put a comment explaining your reponse.
5
+ After the answer, you can put a comment explaining your response.
6
6
  {% endif %}
@@ -1,41 +1,101 @@
1
- from edsl import Scenario
2
1
  import base64
3
2
  import io
4
3
  import tempfile
5
- from typing import Optional
4
+ import mimetypes
5
+ import os
6
+ from typing import Dict, Any, IO, Optional
7
+ import requests
8
+ from urllib.parse import urlparse
9
+
10
+ import google.generativeai as genai
11
+
12
+ from edsl import Scenario
13
+ from edsl.utilities.decorators import add_edsl_version, remove_edsl_version
14
+ from edsl.utilities.utilities import is_notebook
15
+
16
+
17
+ def view_pdf(pdf_path):
18
+ import os
19
+ import subprocess
20
+
21
+ if is_notebook():
22
+ from IPython.display import IFrame
23
+ from IPython.display import display, HTML
24
+
25
+ # Replace 'path/to/your/file.pdf' with the actual path to your PDF file
26
+ IFrame(pdf_path, width=700, height=600)
27
+ display(HTML(f'<a href="{pdf_path}" target="_blank">Open PDF</a>'))
28
+ return
29
+
30
+ if os.path.exists(pdf_path):
31
+ try:
32
+ if (os_name := os.name) == "posix":
33
+ # for cool kids
34
+ subprocess.run(["open", pdf_path], check=True) # macOS
35
+ elif os_name == "nt":
36
+ os.startfile(pdf_path) # Windows
37
+ else:
38
+ subprocess.run(["xdg-open", pdf_path], check=True) # Linux
39
+ except Exception as e:
40
+ print(f"Error opening PDF: {e}")
41
+ else:
42
+ print("PDF file was not created successfully.")
6
43
 
7
44
 
8
45
  class FileStore(Scenario):
9
46
  def __init__(
10
47
  self,
11
- filename: str,
48
+ path: Optional[str] = None,
49
+ mime_type: Optional[str] = None,
12
50
  binary: Optional[bool] = None,
13
51
  suffix: Optional[str] = None,
14
52
  base64_string: Optional[str] = None,
53
+ external_locations: Optional[Dict[str, str]] = None,
54
+ **kwargs,
15
55
  ):
16
- self.filename = filename
17
- self.suffix = suffix or "." + filename.split(".")[-1]
56
+ if path is None and "filename" in kwargs:
57
+ path = kwargs["filename"]
58
+ self.path = path
59
+ self.suffix = suffix or path.split(".")[-1]
18
60
  self.binary = binary or False
19
- self.base64_string = base64_string or self.encode_file_to_base64_string(
20
- filename
61
+ self.mime_type = (
62
+ mime_type or mimetypes.guess_type(path)[0] or "application/octet-stream"
21
63
  )
64
+ self.base64_string = base64_string or self.encode_file_to_base64_string(path)
65
+ self.external_locations = external_locations or {}
22
66
  super().__init__(
23
67
  {
24
- "filename": self.filename,
68
+ "path": self.path,
25
69
  "base64_string": self.base64_string,
26
70
  "binary": self.binary,
27
71
  "suffix": self.suffix,
72
+ "mime_type": self.mime_type,
73
+ "external_locations": self.external_locations,
28
74
  }
29
75
  )
30
76
 
77
+ def __str__(self):
78
+ return "FileStore: self.path"
79
+
80
+ @property
81
+ def size(self) -> int:
82
+ return os.path.getsize(self.path)
83
+
84
+ def upload_google(self, refresh: bool = False) -> None:
85
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
86
+ google_info = genai.upload_file(self.path, mime_type=self.mime_type)
87
+ self.external_locations["google"] = google_info.to_dict()
88
+
31
89
  @classmethod
90
+ @remove_edsl_version
32
91
  def from_dict(cls, d):
33
- return cls(d["filename"], d["binary"], d["suffix"], d["base64_string"])
92
+ # return cls(d["filename"], d["binary"], d["suffix"], d["base64_string"])
93
+ return cls(**d)
34
94
 
35
95
  def __repr__(self):
36
- return f"FileStore(filename='{self.filename}', binary='{self.binary}', 'suffix'={self.suffix})"
96
+ return f"FileStore({self.path})"
37
97
 
38
- def encode_file_to_base64_string(self, file_path):
98
+ def encode_file_to_base64_string(self, file_path: str):
39
99
  try:
40
100
  # Attempt to open the file in text mode
41
101
  with open(file_path, "r") as text_file:
@@ -56,14 +116,14 @@ class FileStore(Scenario):
56
116
 
57
117
  return base64_string
58
118
 
59
- def open(self):
119
+ def open(self) -> "IO":
60
120
  if self.binary:
61
121
  return self.base64_to_file(self["base64_string"], is_binary=True)
62
122
  else:
63
123
  return self.base64_to_text_file(self["base64_string"])
64
124
 
65
125
  @staticmethod
66
- def base64_to_text_file(base64_string):
126
+ def base64_to_text_file(base64_string) -> "IO":
67
127
  # Decode the base64 string to bytes
68
128
  text_data_bytes = base64.b64decode(base64_string)
69
129
 
@@ -101,7 +161,9 @@ class FileStore(Scenario):
101
161
 
102
162
  # Create a named temporary file
103
163
  mode = "wb" if self.binary else "w"
104
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, mode=mode)
164
+ temp_file = tempfile.NamedTemporaryFile(
165
+ delete=False, suffix="." + suffix, mode=mode
166
+ )
105
167
 
106
168
  if self.binary:
107
169
  temp_file.write(file_like_object.read())
@@ -112,31 +174,95 @@ class FileStore(Scenario):
112
174
 
113
175
  return temp_file.name
114
176
 
115
- def push(self, description=None):
177
+ def view(self, max_size: int = 300) -> None:
178
+ if self.suffix == "pdf":
179
+ view_pdf(self.path)
180
+
181
+ if self.suffix == "png" or self.suffix == "jpg" or self.suffix == "jpeg":
182
+ if is_notebook():
183
+ from IPython.display import Image
184
+ from PIL import Image as PILImage
185
+
186
+ if max_size:
187
+ # Open the image using Pillow
188
+ with PILImage.open(self.path) as img:
189
+ # Get original width and height
190
+ original_width, original_height = img.size
191
+
192
+ # Calculate the scaling factor
193
+ scale = min(
194
+ max_size / original_width, max_size / original_height
195
+ )
196
+
197
+ # Calculate new dimensions
198
+ new_width = int(original_width * scale)
199
+ new_height = int(original_height * scale)
200
+
201
+ return Image(self.path, width=new_width, height=new_height)
202
+ else:
203
+ return Image(self.path)
204
+
205
+ def push(
206
+ self, description: Optional[str] = None, visibility: str = "unlisted"
207
+ ) -> dict:
208
+ """
209
+ Push the object to Coop.
210
+ :param description: The description of the object to push.
211
+ :param visibility: The visibility of the object to push.
212
+ """
116
213
  scenario_version = Scenario.from_dict(self.to_dict())
117
214
  if description is None:
118
- description = "File: " + self["filename"]
119
- info = scenario_version.push(description=description)
215
+ description = "File: " + self.path
216
+ info = scenario_version.push(description=description, visibility=visibility)
120
217
  return info
121
218
 
122
219
  @classmethod
123
- def pull(cls, uuid, expected_parrot_url: Optional[str] = None):
220
+ def pull(cls, uuid: str, expected_parrot_url: Optional[str] = None) -> "FileStore":
221
+ """
222
+ :param uuid: The UUID of the object to pull.
223
+ :param expected_parrot_url: The URL of the Parrot server to use.
224
+ :return: The object pulled from the Parrot server.
225
+ """
124
226
  scenario_version = Scenario.pull(uuid, expected_parrot_url=expected_parrot_url)
125
227
  return cls.from_dict(scenario_version.to_dict())
126
228
 
229
+ @classmethod
230
+ def from_url(
231
+ cls,
232
+ url: str,
233
+ download_path: Optional[str] = None,
234
+ mime_type: Optional[str] = None,
235
+ ) -> "FileStore":
236
+ """
237
+ :param url: The URL of the file to download.
238
+ :param download_path: The path to save the downloaded file.
239
+ :param mime_type: The MIME type of the file. If None, it will be guessed from the file extension.
240
+ """
241
+
242
+ response = requests.get(url, stream=True)
243
+ response.raise_for_status() # Raises an HTTPError for bad responses
244
+
245
+ # Get the filename from the URL if download_path is not provided
246
+ if download_path is None:
247
+ filename = os.path.basename(urlparse(url).path)
248
+ if not filename:
249
+ filename = "downloaded_file"
250
+ # download_path = filename
251
+ download_path = os.path.join(os.getcwd(), filename)
252
+
253
+ # Ensure the directory exists
254
+ os.makedirs(os.path.dirname(download_path), exist_ok=True)
255
+
256
+ # Write the file
257
+ with open(download_path, "wb") as file:
258
+ for chunk in response.iter_content(chunk_size=8192):
259
+ file.write(chunk)
260
+
261
+ # Create and return a new File instance
262
+ return cls(download_path, mime_type=mime_type)
127
263
 
128
- class CSVFileStore(FileStore):
129
- def __init__(
130
- self,
131
- filename,
132
- binary: Optional[bool] = None,
133
- suffix: Optional[str] = None,
134
- base64_string: Optional[str] = None,
135
- ):
136
- super().__init__(
137
- filename, binary=binary, base64_string=base64_string, suffix=".csv"
138
- )
139
264
 
265
+ class CSVFileStore(FileStore):
140
266
  @classmethod
141
267
  def example(cls):
142
268
  from edsl.results.Results import Results
@@ -155,17 +281,6 @@ class CSVFileStore(FileStore):
155
281
 
156
282
 
157
283
  class PDFFileStore(FileStore):
158
- def __init__(
159
- self,
160
- filename,
161
- binary: Optional[bool] = None,
162
- suffix: Optional[str] = None,
163
- base64_string: Optional[str] = None,
164
- ):
165
- super().__init__(
166
- filename, binary=binary, base64_string=base64_string, suffix=".pdf"
167
- )
168
-
169
284
  def view(self):
170
285
  pdf_path = self.to_tempfile()
171
286
  print(f"PDF path: {pdf_path}") # Print the path to ensure it exists
@@ -241,17 +356,6 @@ class PDFFileStore(FileStore):
241
356
 
242
357
 
243
358
  class PNGFileStore(FileStore):
244
- def __init__(
245
- self,
246
- filename,
247
- binary: Optional[bool] = None,
248
- suffix: Optional[str] = None,
249
- base64_string: Optional[str] = None,
250
- ):
251
- super().__init__(
252
- filename, binary=binary, base64_string=base64_string, suffix=".png"
253
- )
254
-
255
359
  @classmethod
256
360
  def example(cls):
257
361
  import textwrap
@@ -275,17 +379,6 @@ class PNGFileStore(FileStore):
275
379
 
276
380
 
277
381
  class SQLiteFileStore(FileStore):
278
- def __init__(
279
- self,
280
- filename,
281
- binary: Optional[bool] = None,
282
- suffix: Optional[str] = None,
283
- base64_string: Optional[str] = None,
284
- ):
285
- super().__init__(
286
- filename, binary=binary, base64_string=base64_string, suffix=".sqlite"
287
- )
288
-
289
382
  @classmethod
290
383
  def example(cls):
291
384
  import sqlite3
@@ -308,17 +401,6 @@ class SQLiteFileStore(FileStore):
308
401
 
309
402
 
310
403
  class HTMLFileStore(FileStore):
311
- def __init__(
312
- self,
313
- filename,
314
- binary: Optional[bool] = None,
315
- suffix: Optional[str] = None,
316
- base64_string: Optional[str] = None,
317
- ):
318
- super().__init__(
319
- filename, binary=binary, base64_string=base64_string, suffix=".html"
320
- )
321
-
322
404
  @classmethod
323
405
  def example(cls):
324
406
  import tempfile
@@ -350,9 +432,10 @@ if __name__ == "__main__":
350
432
  # fs = PDFFileStore("paper.pdf")
351
433
  # fs.view()
352
434
  # from edsl import Conjure
353
-
354
- fs = PNGFileStore("robot.png")
355
- fs.view()
435
+ pass
436
+ # fs = PNGFileStore("logo.png")
437
+ # fs.view()
438
+ # fs.upload_google()
356
439
 
357
440
  # c = Conjure(datafile_name=fs.to_tempfile())
358
441
  # f = PDFFileStore("paper.pdf")