scratchattach 3.0.0b0__py3-none-any.whl → 3.0.0b2__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 (80) hide show
  1. scratchattach/cli/__about__.py +1 -0
  2. scratchattach/cli/__init__.py +26 -0
  3. scratchattach/cli/cmd/__init__.py +4 -0
  4. scratchattach/cli/cmd/group.py +127 -0
  5. scratchattach/cli/cmd/login.py +60 -0
  6. scratchattach/cli/cmd/profile.py +7 -0
  7. scratchattach/cli/cmd/sessions.py +5 -0
  8. scratchattach/cli/context.py +142 -0
  9. scratchattach/cli/db.py +66 -0
  10. scratchattach/cli/namespace.py +14 -0
  11. scratchattach/cloud/__init__.py +2 -0
  12. scratchattach/cloud/_base.py +483 -0
  13. scratchattach/cloud/cloud.py +183 -0
  14. scratchattach/editor/__init__.py +22 -0
  15. scratchattach/editor/asset.py +265 -0
  16. scratchattach/editor/backpack_json.py +115 -0
  17. scratchattach/editor/base.py +191 -0
  18. scratchattach/editor/block.py +584 -0
  19. scratchattach/editor/blockshape.py +357 -0
  20. scratchattach/editor/build_defaulting.py +51 -0
  21. scratchattach/editor/code_translation/__init__.py +0 -0
  22. scratchattach/editor/code_translation/parse.py +177 -0
  23. scratchattach/editor/comment.py +80 -0
  24. scratchattach/editor/commons.py +145 -0
  25. scratchattach/editor/extension.py +50 -0
  26. scratchattach/editor/field.py +99 -0
  27. scratchattach/editor/inputs.py +138 -0
  28. scratchattach/editor/meta.py +117 -0
  29. scratchattach/editor/monitor.py +185 -0
  30. scratchattach/editor/mutation.py +381 -0
  31. scratchattach/editor/pallete.py +88 -0
  32. scratchattach/editor/prim.py +174 -0
  33. scratchattach/editor/project.py +381 -0
  34. scratchattach/editor/sprite.py +609 -0
  35. scratchattach/editor/twconfig.py +114 -0
  36. scratchattach/editor/vlb.py +134 -0
  37. scratchattach/eventhandlers/__init__.py +0 -0
  38. scratchattach/eventhandlers/_base.py +101 -0
  39. scratchattach/eventhandlers/cloud_events.py +130 -0
  40. scratchattach/eventhandlers/cloud_recorder.py +26 -0
  41. scratchattach/eventhandlers/cloud_requests.py +544 -0
  42. scratchattach/eventhandlers/cloud_server.py +249 -0
  43. scratchattach/eventhandlers/cloud_storage.py +135 -0
  44. scratchattach/eventhandlers/combine.py +30 -0
  45. scratchattach/eventhandlers/filterbot.py +163 -0
  46. scratchattach/eventhandlers/message_events.py +42 -0
  47. scratchattach/other/__init__.py +0 -0
  48. scratchattach/other/other_apis.py +598 -0
  49. scratchattach/other/project_json_capabilities.py +475 -0
  50. scratchattach/site/__init__.py +0 -0
  51. scratchattach/site/_base.py +93 -0
  52. scratchattach/site/activity.py +426 -0
  53. scratchattach/site/alert.py +226 -0
  54. scratchattach/site/backpack_asset.py +119 -0
  55. scratchattach/site/browser_cookie3_stub.py +17 -0
  56. scratchattach/site/browser_cookies.py +61 -0
  57. scratchattach/site/classroom.py +454 -0
  58. scratchattach/site/cloud_activity.py +121 -0
  59. scratchattach/site/comment.py +228 -0
  60. scratchattach/site/forum.py +436 -0
  61. scratchattach/site/placeholder.py +132 -0
  62. scratchattach/site/project.py +932 -0
  63. scratchattach/site/session.py +1323 -0
  64. scratchattach/site/studio.py +704 -0
  65. scratchattach/site/typed_dicts.py +151 -0
  66. scratchattach/site/user.py +1252 -0
  67. scratchattach/utils/__init__.py +0 -0
  68. scratchattach/utils/commons.py +263 -0
  69. scratchattach/utils/encoder.py +161 -0
  70. scratchattach/utils/enums.py +237 -0
  71. scratchattach/utils/exceptions.py +277 -0
  72. scratchattach/utils/optional_async.py +154 -0
  73. scratchattach/utils/requests.py +306 -0
  74. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b2.dist-info}/METADATA +1 -1
  75. scratchattach-3.0.0b2.dist-info/RECORD +81 -0
  76. scratchattach-3.0.0b0.dist-info/RECORD +0 -8
  77. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b2.dist-info}/WHEEL +0 -0
  78. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b2.dist-info}/entry_points.txt +0 -0
  79. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
  80. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,185 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, TYPE_CHECKING
4
+
5
+ from typing_extensions import deprecated
6
+ import warnings
7
+
8
+ if TYPE_CHECKING:
9
+ from . import project
10
+
11
+ from . import base, block
12
+
13
+
14
+ class Monitor(base.ProjectSubcomponent):
15
+ def __init__(self, reporter: Optional[base.NamedIDComponent] = None,
16
+ mode: str = "default",
17
+ opcode: str = "data_variable",
18
+ params: Optional[dict] = None,
19
+ sprite_name: Optional[str] = None,
20
+ value=0,
21
+ width: int | float = 0,
22
+ height: int | float = 0,
23
+ x: int | float = 5,
24
+ y: int | float = 5,
25
+ visible: bool = False,
26
+ slider_min: int | float = 0,
27
+ slider_max: int | float = 100,
28
+ is_discrete: bool = True, *, reporter_id: Optional[str] = None, _project: Optional[project.Project] = None):
29
+ """
30
+ Represents a variable/list monitor
31
+ https://en.scratch-wiki.info/wiki/Scratch_File_Format#Monitors
32
+
33
+ Instantiating these yourself and attaching these to projects can lead to interesting results!
34
+ """
35
+ assert isinstance(reporter, base.SpriteSubComponent) or reporter is None
36
+
37
+ if sprite_name is None and hasattr(reporter, 'sprite'):
38
+ sprite_name = None if reporter.sprite.is_stage else reporter.sprite.name
39
+
40
+ self.reporter_id = reporter_id
41
+ """
42
+ ID referencing the VLB being referenced. Replaced with None during project instantiation, where the reporter attribute is updated
43
+ """
44
+
45
+ self.reporter = reporter
46
+ if params is None:
47
+ params = {}
48
+
49
+ self.mode = mode
50
+
51
+ self.opcode = opcode
52
+ self.params = params
53
+
54
+ self.sprite_name = sprite_name
55
+
56
+ self.value = value
57
+
58
+ self.width, self.height = width, height
59
+ self.x, self.y = x, y
60
+
61
+ self.visible = visible
62
+
63
+ self.slider_min, self.slider_max = slider_min, slider_max
64
+ self.is_discrete = is_discrete
65
+
66
+ super().__init__(_project)
67
+
68
+ def __repr__(self):
69
+ return f"Monitor<{self.opcode}>"
70
+
71
+ @property
72
+ def id(self):
73
+ if self.reporter is not None:
74
+ return self.reporter.id
75
+ # if isinstance(self.reporter, str):
76
+ # return self.reporter
77
+ # else:
78
+ # return self.reporter.id
79
+ else:
80
+ return self.reporter_id
81
+
82
+ @staticmethod
83
+ def from_json(data: dict):
84
+ _id = data["id"]
85
+ # ^^ NEED TO FIND REPORTER OBJECT
86
+
87
+ mode = data["mode"]
88
+
89
+ opcode = data["opcode"]
90
+ params: dict = data["params"]
91
+
92
+ sprite_name = data["spriteName"]
93
+
94
+ value = data["value"]
95
+
96
+ width, height = data["width"], data["height"]
97
+ x, y = data["x"], data["y"]
98
+
99
+ visible = data["visible"]
100
+
101
+ if "isDiscrete" in data.keys():
102
+ slider_min, slider_max = data["sliderMin"], data["sliderMax"]
103
+ is_discrete = data["isDiscrete"]
104
+ else:
105
+ slider_min, slider_max, is_discrete = None, None, None
106
+
107
+ return Monitor(None, mode, opcode, params, sprite_name, value, width, height, x, y, visible, slider_min,
108
+ slider_max, is_discrete, reporter_id=_id)
109
+
110
+ def to_json(self):
111
+ _json = {
112
+ "id": self.id,
113
+ "mode": self.mode,
114
+
115
+ "opcode": self.opcode,
116
+ "params": self.params,
117
+
118
+ "spriteName": self.sprite_name,
119
+
120
+ "value": self.value,
121
+
122
+ "width": self.width,
123
+ "height": self.height,
124
+
125
+ "x": self.x,
126
+ "y": self.y,
127
+
128
+ "visible": self.visible
129
+ }
130
+ if self.is_discrete is not None:
131
+ _json["sliderMin"] = self.slider_min
132
+ _json["sliderMax"] = self.slider_max
133
+ _json["isDiscrete"] = self.is_discrete
134
+
135
+ return _json
136
+
137
+ def link_using_project(self):
138
+ assert self.project is not None
139
+
140
+ if self.opcode in ("data_variable", "data_listcontents", "event_broadcast_menu"):
141
+ new_vlb = self.project.find_vlb(self.reporter_id, "id")
142
+ if new_vlb is not None:
143
+ self.reporter = new_vlb
144
+ self.reporter_id = None
145
+
146
+ # todo: consider reimplementing this
147
+ @staticmethod
148
+ @deprecated("This method does not work correctly (This may be fixed in the future)")
149
+ def from_reporter(reporter: block.Block, _id: str = None, mode: str = "default",
150
+ opcode: str = None, sprite_name: str = None, value=0, width: int | float = 0,
151
+ height: int | float = 0,
152
+ x: int | float = 5, y: int | float = 5, visible: bool = False, slider_min: int | float = 0,
153
+ slider_max: int | float = 100, is_discrete: bool = True, params: dict = None):
154
+ if not reporter.block_shape.is_reporter:
155
+ warnings.warn(f"{reporter} is not a reporter block; the monitor will return '0'")
156
+ elif reporter.block_shape.is_menu:
157
+ warnings.warn(f"{reporter} is a menu block; the monitor will return '0'")
158
+ # Maybe add note that length of list doesn't work fsr?? idk
159
+ if _id is None:
160
+ _id = reporter.opcode
161
+ if opcode is None:
162
+ opcode = reporter.opcode # .replace('_', ' ')
163
+
164
+ if params is None:
165
+ params = {}
166
+ for field in reporter.fields:
167
+ if field.value_id is None:
168
+ params[field.id] = field.value
169
+ else:
170
+ params[field.id] = field.value, field.value_id
171
+
172
+ return Monitor(
173
+ reporter,
174
+ mode,
175
+ opcode,
176
+
177
+ params,
178
+ sprite_name,
179
+ value,
180
+
181
+ width, height,
182
+ x, y,
183
+ visible,
184
+ slider_min, slider_max, is_discrete
185
+ )
@@ -0,0 +1,381 @@
1
+ """
2
+ Contains the mutation class and handlers for Arguments/ArgumentTypes/ArgSettings, and utilities for handling proc codes
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import warnings
9
+ from dataclasses import dataclass
10
+ from typing import Optional, TYPE_CHECKING, Iterable, Any
11
+
12
+ from . import base, commons
13
+ from scratchattach.utils import enums
14
+
15
+ if TYPE_CHECKING:
16
+ from . import block
17
+
18
+
19
+ @dataclass
20
+ class ArgumentType(base.Base):
21
+ type: str
22
+ proc_str: str
23
+
24
+ def __eq__(self, other):
25
+ # noinspection PyProtectedMember
26
+ if isinstance(other, enums._EnumWrapper):
27
+ other = other.value
28
+
29
+ assert isinstance(other, ArgumentType)
30
+
31
+ return self.type == other.type
32
+
33
+ def __repr__(self):
34
+ return f"<ArgType {self.type!r}>"
35
+
36
+ @property
37
+ def default(self) -> str | None:
38
+ if self.proc_str == "%b":
39
+ return "false"
40
+ elif self.proc_str == "%s":
41
+ return ''
42
+ else:
43
+ return None
44
+
45
+
46
+ @dataclass
47
+ class ArgSettings(base.Base):
48
+ """
49
+ Contains whether the ids, names, and defaults of arguments in a mutation are None or not - i.e. the configuration of the arguments
50
+ """
51
+ ids: bool
52
+ names: bool
53
+ defaults: bool
54
+
55
+ def __int__(self):
56
+ return (int(self.ids) +
57
+ int(self.names) +
58
+ int(self.defaults))
59
+
60
+ def __eq__(self, other):
61
+ return (self.ids == other.ids and
62
+ self.names == other.names and
63
+ self.defaults == other.defaults)
64
+
65
+ def __gt__(self, other):
66
+ return int(self) > int(other)
67
+
68
+ def __lt__(self, other):
69
+ return int(self) > int(other)
70
+
71
+
72
+ @dataclass
73
+ class Argument(base.MutationSubComponent):
74
+ name: str
75
+ default: str = ''
76
+ _type: Optional[ArgumentType] = None
77
+
78
+ _id: str = None
79
+ """
80
+ Argument ID: Will be used to replace other parameters during block instantiation.
81
+ """
82
+
83
+ def __post_init__(self):
84
+ super().__init__()
85
+
86
+ @property
87
+ def index(self):
88
+ return self.mutation.arguments.index(self)
89
+
90
+ @property
91
+ def type(self) -> Optional[ArgumentType]:
92
+ if not self._type:
93
+ if not self.mutation:
94
+ raise ValueError(f"Cannot infer 'type' of {self} when there is no mutation attached. "
95
+ f"Consider providing a type manually.")
96
+
97
+ i = 0
98
+ goal = self.index
99
+ for token in parse_proc_code(self.mutation.proc_code):
100
+ if isinstance(token, ArgumentType):
101
+ if i == goal:
102
+ self._type = token
103
+ break
104
+ i += 1
105
+
106
+ return self._type
107
+
108
+ @staticmethod
109
+ def from_json(data: dict | list | Any):
110
+ warnings.warn("No from_json method defined for Arguments (yet?)")
111
+
112
+ def to_json(self) -> dict | list | Any:
113
+ warnings.warn("No to_json method defined for Arguments (yet?)")
114
+
115
+ def link_using_mutation(self):
116
+ if self._id is None:
117
+ self._id = self.block.new_id
118
+
119
+
120
+ # noinspection PyProtectedMember
121
+ class ArgTypes(enums._EnumWrapper):
122
+ BOOLEAN = ArgumentType("boolean", "%b")
123
+ NUMBER_OR_TEXT = ArgumentType("number or text", "%s")
124
+
125
+
126
+ def parse_proc_code(_proc_code: str) -> Optional[list[str | ArgumentType]]:
127
+ """
128
+ Parse a proccode (part of a mutation) into argument types and strings
129
+ """
130
+
131
+ if _proc_code is None:
132
+ return None
133
+ token = ''
134
+ tokens = []
135
+
136
+ last_char = ''
137
+ for char in _proc_code:
138
+ if last_char == '%':
139
+ if char in "sb":
140
+ # If we've hit an %s or %b
141
+ token = token[:-1]
142
+ # Clip the % sign off the token
143
+
144
+ if token.endswith(' '):
145
+ # A space is required before params, but this should not be part of the parsed output
146
+ token = token[:-1]
147
+
148
+ if token != '':
149
+ # Make sure not to append an empty token
150
+ tokens.append(token)
151
+
152
+ # Add the parameter token
153
+ token = f"%{char}"
154
+ if token == "%b":
155
+ tokens.append(ArgTypes.BOOLEAN.value.dcopy())
156
+ elif token == "%s":
157
+ tokens.append(ArgTypes.NUMBER_OR_TEXT.value.dcopy())
158
+
159
+ token = ''
160
+ continue
161
+
162
+ token += char
163
+ last_char = char
164
+
165
+ if token != '':
166
+ tokens.append(token)
167
+
168
+ return tokens
169
+
170
+ def construct_proccode(*components: ArgumentType | ArgTypes | Argument | str) -> str:
171
+ """
172
+ Create a proccode from strings/ArgumentType enum members/Argument instances
173
+
174
+ :param components: list of strings/Arguments/ArgumentType instances
175
+ :return: A proccode, e.g. 'move %s steps' or 'say %s for %n seconds'
176
+ """
177
+
178
+ result = ""
179
+
180
+ for comp in components:
181
+ if isinstance(comp, ArgumentType):
182
+ result += comp.proc_str
183
+
184
+ elif isinstance(comp, ArgTypes):
185
+ new: ArgumentType = comp.value
186
+ result += new.proc_str
187
+
188
+ elif isinstance(comp, Argument):
189
+ result += comp.type.proc_str
190
+
191
+ elif isinstance(comp, str):
192
+ result += comp
193
+
194
+ else:
195
+ raise TypeError(f"Unsupported component type: {type(comp)}")
196
+
197
+ result += ' '
198
+
199
+ return result
200
+
201
+
202
+ class Mutation(base.BlockSubComponent):
203
+ def __init__(self, _tag_name: str = "mutation", _children: Optional[list] = None, _proc_code: Optional[str] = None,
204
+ _is_warp: Optional[bool] = None, _arguments: Optional[list[Argument]] = None, _has_next: Optional[bool] = None,
205
+ _argument_settings: Optional[ArgSettings] = None, *,
206
+ _block: Optional[block.Block] = None):
207
+ """
208
+ Mutation for Control:stop block and procedures
209
+ https://en.scratch-wiki.info/wiki/Scratch_File_Format#Mutations
210
+ """
211
+ # Defaulting for args
212
+ if _children is None:
213
+ _children = []
214
+
215
+ if _argument_settings is None:
216
+ if _arguments:
217
+ _argument_settings = ArgSettings(
218
+ _arguments[0]._id is None,
219
+ _arguments[0].name is None,
220
+ _arguments[0].default is None
221
+ )
222
+ else:
223
+ _argument_settings = ArgSettings(False, False, False)
224
+
225
+ self.tag_name = _tag_name
226
+ self.children = _children
227
+
228
+ self.proc_code = _proc_code
229
+ self.is_warp = _is_warp
230
+ self.arguments = _arguments
231
+ self.og_argument_settings = _argument_settings
232
+
233
+ self.has_next = _has_next
234
+
235
+ super().__init__(_block)
236
+
237
+ def __repr__(self):
238
+ if self.arguments is not None:
239
+ return f"Mutation<args={self.arguments}>"
240
+ else:
241
+ return f"Mutation<hasnext={self.has_next}>"
242
+
243
+ @property
244
+ def argument_ids(self):
245
+ if self.arguments is not None:
246
+ return [_arg._id for _arg in self.arguments]
247
+ else:
248
+ return None
249
+
250
+ @property
251
+ def argument_names(self):
252
+ if self.arguments is not None:
253
+ return [_arg.name for _arg in self.arguments]
254
+ else:
255
+ return None
256
+
257
+ @property
258
+ def argument_defaults(self):
259
+ if self.arguments is not None:
260
+ return [_arg.default for _arg in self.arguments]
261
+ else:
262
+ return None
263
+
264
+ @property
265
+ def argument_settings(self) -> ArgSettings:
266
+ return ArgSettings(bool(commons.safe_get(self.argument_ids, 0)),
267
+ bool(commons.safe_get(self.argument_names, 0)),
268
+ bool(commons.safe_get(self.argument_defaults, 0)))
269
+
270
+ @property
271
+ def parsed_proc_code(self) -> list[str | ArgumentType] | None:
272
+ """
273
+ Parse the proc code into arguments & strings
274
+ """
275
+ return parse_proc_code(self.proc_code)
276
+
277
+ @staticmethod
278
+ def from_json(data: dict) -> Mutation:
279
+ assert isinstance(data, dict)
280
+
281
+ _tag_name = data.get("tagName", "mutation")
282
+ _children = data.get("children", [])
283
+
284
+ # procedures_prototype & procedures_call attrs
285
+ _proc_code = data.get("proccode")
286
+ _is_warp = data.get("warp")
287
+ if isinstance(_is_warp, str):
288
+ _is_warp = json.loads(_is_warp)
289
+
290
+ _argument_ids = data.get("argumentids")
291
+ # For some reason these are stored as JSON strings
292
+ if _argument_ids is not None:
293
+ _argument_ids = json.loads(_argument_ids)
294
+
295
+ # procedures_prototype attrs
296
+ _argument_names = data.get("argumentnames")
297
+ _argument_defaults = data.get("argumentdefaults")
298
+ # For some reason these are stored as JSON strings
299
+ if _argument_names is not None:
300
+ assert isinstance(_argument_names, str)
301
+ _argument_names = json.loads(_argument_names)
302
+ if _argument_defaults is not None:
303
+ assert isinstance(_argument_defaults, str)
304
+ _argument_defaults = json.loads(_argument_defaults)
305
+ _argument_settings = ArgSettings(_argument_ids is not None,
306
+ _argument_names is not None,
307
+ _argument_defaults is not None)
308
+
309
+ # control_stop attrs
310
+ _has_next = data.get("hasnext")
311
+ if isinstance(_has_next, str):
312
+ _has_next = json.loads(_has_next)
313
+
314
+ def get(_lst: list | tuple | None, _idx: int):
315
+ if _lst is None:
316
+ return None
317
+
318
+ if len(_lst) <= _idx:
319
+ return None
320
+ else:
321
+ return _lst[_idx]
322
+
323
+ if _argument_ids is None:
324
+ _arguments = None
325
+ else:
326
+ _arguments = []
327
+ for i, _arg_id in enumerate(_argument_ids):
328
+ _arg_name = get(_argument_names, i)
329
+ _arg_default = get(_argument_defaults, i)
330
+
331
+ _arguments.append(Argument(_arg_name, _arg_default, _id=_arg_id))
332
+
333
+ return Mutation(_tag_name, _children, _proc_code, _is_warp, _arguments, _has_next, _argument_settings)
334
+
335
+ def to_json(self) -> dict | None:
336
+ _json = {
337
+ "tagName": self.tag_name,
338
+ "children": self.children,
339
+ }
340
+ commons.noneless_update(_json, {
341
+ "proccode": self.proc_code,
342
+ "warp": commons.dumps_ifnn(self.is_warp),
343
+ "argumentids": commons.dumps_ifnn(self.argument_ids),
344
+ "argumentnames": commons.dumps_ifnn(self.argument_names),
345
+ "argumentdefaults": commons.dumps_ifnn(self.argument_defaults),
346
+
347
+ "hasNext": commons.dumps_ifnn(self.has_next)
348
+ })
349
+
350
+ return _json
351
+
352
+ def link_arguments(self):
353
+ if self.arguments is None:
354
+ return
355
+
356
+ # You only need to fetch argument data if you actually have arguments
357
+ if len(self.arguments) > 0:
358
+ if self.arguments[0].name is None:
359
+ # This requires linking
360
+ _proc_uses = self.sprite.find_block(self.argument_ids, "argument ids", True)
361
+ # Note: Sometimes there may not be any argument ids provided. There will be no way to find out the names
362
+ # Technically, defaults can be found by using the proc code
363
+ for _use in _proc_uses:
364
+ if _use.mutation.argument_settings > self.argument_settings:
365
+ self.arguments = _use.mutation.arguments
366
+ if int(self.argument_settings) == 3:
367
+ # If all of our argument data is filled, we can stop early
368
+ return
369
+
370
+ # We can still work out argument defaults from parsing the proc code
371
+ if self.arguments[0].default is None:
372
+ _parsed = self.parsed_proc_code
373
+ _arg_phs: Iterable[ArgumentType] = filter(lambda tkn: isinstance(tkn, ArgumentType),
374
+ _parsed)
375
+ for i, _arg_ph in enumerate(_arg_phs):
376
+ self.arguments[i].default = _arg_ph.default
377
+
378
+ for _argument in self.arguments:
379
+ _argument.mutation = self
380
+ _argument.link_using_mutation()
381
+
@@ -0,0 +1,88 @@
1
+ """
2
+ Collection of block information, stating input/field names and opcodes
3
+ Not ready for use
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional
9
+
10
+ from . import prim
11
+ from scratchattach.utils.enums import _EnumWrapper
12
+
13
+
14
+ @dataclass
15
+ class FieldUsage:
16
+ name: str
17
+ value_type: Optional[prim.PrimTypes] = None
18
+
19
+
20
+ @dataclass
21
+ class SpecialFieldUsage(FieldUsage):
22
+ name: str
23
+ value_type: None = None # Order cannot be changed
24
+ attrs: list[str] = field(default_factory=list)
25
+
26
+
27
+
28
+ @dataclass
29
+ class InputUsage:
30
+ name: str
31
+ value_type: Optional[prim.PrimTypes] = None
32
+ default_obscurer: Optional[BlockUsage] = None
33
+
34
+
35
+ @dataclass
36
+ class BlockUsage:
37
+ opcode: str
38
+ fields: Optional[list[FieldUsage]] = None
39
+ if fields is None:
40
+ fields = []
41
+
42
+ inputs: Optional[list[InputUsage]] = None
43
+ if inputs is None:
44
+ inputs = []
45
+
46
+
47
+ class BlockUsages(_EnumWrapper):
48
+ # Special Enum blocks
49
+ MATH_NUMBER = BlockUsage(
50
+ "math_number",
51
+ [SpecialFieldUsage("NUM", attrs=["name", "value"])]
52
+ )
53
+ MATH_POSITIVE_NUMBER = BlockUsage(
54
+ "math_positive_number",
55
+ [SpecialFieldUsage("NUM", attrs=["name", "value"])]
56
+ )
57
+ MATH_WHOLE_NUMBER = BlockUsage(
58
+ "math_whole_number",
59
+ [SpecialFieldUsage("NUM", attrs=["name", "value"])]
60
+ )
61
+ MATH_INTEGER = BlockUsage(
62
+ "math_integer",
63
+ [SpecialFieldUsage("NUM", attrs=["name", "value"])]
64
+ )
65
+ MATH_ANGLE = BlockUsage(
66
+ "math_angle",
67
+ [SpecialFieldUsage("NUM", attrs=["name", "value"])]
68
+ )
69
+ COLOUR_PICKER = BlockUsage(
70
+ "colour_picker",
71
+ [SpecialFieldUsage("COLOUR", attrs=["name", "value"])]
72
+ )
73
+ TEXT = BlockUsage(
74
+ "text",
75
+ [SpecialFieldUsage("TEXT", attrs=["name", "value"])]
76
+ )
77
+ EVENT_BROADCAST_MENU = BlockUsage(
78
+ "event_broadcast_menu",
79
+ [SpecialFieldUsage("BROADCAST_OPTION", attrs=["name", "id", "value", "variableType"])]
80
+ )
81
+ DATA_VARIABLE = BlockUsage(
82
+ "data_variable",
83
+ [SpecialFieldUsage("VARIABLE", attrs=["name", "id", "value", "variableType"])]
84
+ )
85
+ DATA_LISTCONTENTS = BlockUsage(
86
+ "data_listcontents",
87
+ [SpecialFieldUsage("LIST", attrs=["name", "id", "value", "variableType"])]
88
+ )