scratchattach 2.1.15b0__py3-none-any.whl → 3.0.0b0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. scratchattach/__init__.py +14 -6
  2. scratchattach/__main__.py +93 -0
  3. {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/METADATA +7 -11
  4. scratchattach-3.0.0b0.dist-info/RECORD +8 -0
  5. {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/WHEEL +1 -1
  6. scratchattach-3.0.0b0.dist-info/entry_points.txt +2 -0
  7. scratchattach/cloud/__init__.py +0 -2
  8. scratchattach/cloud/_base.py +0 -458
  9. scratchattach/cloud/cloud.py +0 -183
  10. scratchattach/editor/__init__.py +0 -21
  11. scratchattach/editor/asset.py +0 -253
  12. scratchattach/editor/backpack_json.py +0 -117
  13. scratchattach/editor/base.py +0 -193
  14. scratchattach/editor/block.py +0 -579
  15. scratchattach/editor/blockshape.py +0 -357
  16. scratchattach/editor/build_defaulting.py +0 -51
  17. scratchattach/editor/code_translation/__init__.py +0 -0
  18. scratchattach/editor/code_translation/parse.py +0 -177
  19. scratchattach/editor/comment.py +0 -80
  20. scratchattach/editor/commons.py +0 -273
  21. scratchattach/editor/extension.py +0 -50
  22. scratchattach/editor/field.py +0 -99
  23. scratchattach/editor/inputs.py +0 -135
  24. scratchattach/editor/meta.py +0 -114
  25. scratchattach/editor/monitor.py +0 -183
  26. scratchattach/editor/mutation.py +0 -324
  27. scratchattach/editor/pallete.py +0 -90
  28. scratchattach/editor/prim.py +0 -170
  29. scratchattach/editor/project.py +0 -279
  30. scratchattach/editor/sprite.py +0 -599
  31. scratchattach/editor/twconfig.py +0 -114
  32. scratchattach/editor/vlb.py +0 -134
  33. scratchattach/eventhandlers/__init__.py +0 -0
  34. scratchattach/eventhandlers/_base.py +0 -100
  35. scratchattach/eventhandlers/cloud_events.py +0 -110
  36. scratchattach/eventhandlers/cloud_recorder.py +0 -26
  37. scratchattach/eventhandlers/cloud_requests.py +0 -459
  38. scratchattach/eventhandlers/cloud_server.py +0 -246
  39. scratchattach/eventhandlers/cloud_storage.py +0 -136
  40. scratchattach/eventhandlers/combine.py +0 -30
  41. scratchattach/eventhandlers/filterbot.py +0 -161
  42. scratchattach/eventhandlers/message_events.py +0 -42
  43. scratchattach/other/__init__.py +0 -0
  44. scratchattach/other/other_apis.py +0 -284
  45. scratchattach/other/project_json_capabilities.py +0 -475
  46. scratchattach/site/__init__.py +0 -0
  47. scratchattach/site/_base.py +0 -66
  48. scratchattach/site/activity.py +0 -382
  49. scratchattach/site/alert.py +0 -227
  50. scratchattach/site/backpack_asset.py +0 -118
  51. scratchattach/site/browser_cookie3_stub.py +0 -17
  52. scratchattach/site/browser_cookies.py +0 -61
  53. scratchattach/site/classroom.py +0 -447
  54. scratchattach/site/cloud_activity.py +0 -107
  55. scratchattach/site/comment.py +0 -242
  56. scratchattach/site/forum.py +0 -432
  57. scratchattach/site/project.py +0 -826
  58. scratchattach/site/session.py +0 -1238
  59. scratchattach/site/studio.py +0 -611
  60. scratchattach/site/user.py +0 -956
  61. scratchattach/utils/__init__.py +0 -0
  62. scratchattach/utils/commons.py +0 -255
  63. scratchattach/utils/encoder.py +0 -158
  64. scratchattach/utils/enums.py +0 -236
  65. scratchattach/utils/exceptions.py +0 -243
  66. scratchattach/utils/requests.py +0 -93
  67. scratchattach-2.1.15b0.dist-info/RECORD +0 -66
  68. {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/licenses/LICENSE +0 -0
  69. {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b0.dist-info}/top_level.txt +0 -0
@@ -1,579 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import warnings
4
- from typing import Optional, Iterable, Union
5
- from typing_extensions import Self
6
-
7
- from . import base, sprite, mutation, field, inputs, commons, vlb, blockshape, prim, comment, build_defaulting
8
- from scratchattach.utils import exceptions
9
-
10
-
11
- class Block(base.SpriteSubComponent):
12
- """
13
- Represents a block in the scratch editor, as a subcomponent of a sprite.
14
- """
15
- _id: Optional[str] = None
16
- def __init__(self, _opcode: str, _shadow: bool = False, _top_level: Optional[bool] = None,
17
- _mutation: Optional[mutation.Mutation] = None, _fields: Optional[dict[str, field.Field]] = None,
18
- _inputs: Optional[dict[str, inputs.Input]] = None, x: int = 0, y: int = 0, pos: Optional[tuple[int, int]] = None,
19
-
20
- _next: Optional[Block] = None, _parent: Optional[Block] = None,
21
- *, _next_id: Optional[str] = None, _parent_id: Optional[str] = None, _sprite: commons.SpriteInput = build_defaulting.SPRITE_DEFAULT):
22
- # Defaulting for args
23
- if _fields is None:
24
- _fields = {}
25
- if _inputs is None:
26
- _inputs = {}
27
-
28
- if pos is not None:
29
- x, y = pos
30
-
31
- self.opcode = _opcode
32
- self.is_shadow = _shadow
33
- self.is_top_level = _top_level
34
-
35
- self.x, self.y = x, y
36
-
37
- self.mutation = _mutation
38
- self.fields = _fields
39
- self.inputs = _inputs
40
-
41
- # Temporarily stores id of next block. Will be used later during project instantiation to find the next block object
42
- self._next_id = _next_id
43
-
44
- # Temporarily stores id of parent block. Will be used later during project instantiation to find the parent block object
45
- self._parent_id = _parent_id
46
-
47
- self.next = _next
48
- self.parent = _parent
49
-
50
- self.check_toplevel()
51
-
52
- super().__init__(_sprite)
53
- self.link_subcomponents()
54
-
55
- def __repr__(self):
56
- return f"Block<{self.opcode!r}>"
57
-
58
- def link_subcomponents(self):
59
- """
60
- Iterate through subcomponents and assign the 'block' attribute
61
- """
62
- if self.mutation:
63
- self.mutation.block = self
64
-
65
- for iterable in (self.fields.values(), self.inputs.values()):
66
- for subcomponent in iterable:
67
- subcomponent.block = self
68
-
69
- def add_input(self, name: str, _input: inputs.Input) -> Self:
70
- """
71
- Add an input to the block.
72
- """ # not sure what else to say
73
- self.inputs[name] = _input
74
- for val in (_input.value, _input.obscurer):
75
- if isinstance(val, Block):
76
- val.parent = self
77
- return self
78
-
79
- def add_field(self, name: str, _field: field.Field) -> Self:
80
- """
81
- Add a field to the block.
82
- """ # not sure what else to sa
83
- self.fields[name] = _field
84
- return self
85
-
86
- def set_mutation(self, _mutation: mutation.Mutation) -> Self:
87
- """
88
- Attach a mutation object and call mutation.link_arguments()
89
- """ # this comment explains *what* this does, not *why*
90
- self.mutation = _mutation
91
- _mutation.block = self
92
- _mutation.link_arguments()
93
- return self
94
-
95
- def set_comment(self, _comment: comment.Comment) -> Self:
96
- """
97
- Attach a comment and add it to the sprite.
98
- """
99
- _comment.block = self
100
- self.sprite.add_comment(_comment)
101
-
102
- return self
103
-
104
- def check_toplevel(self):
105
- """
106
- Edit the toplevel, x, and y attributes based on whether the parent attribute is None
107
- """
108
- self.is_top_level = self.parent is None
109
-
110
- if not self.is_top_level:
111
- self.x, self.y = None, None
112
-
113
- @property
114
- def target(self):
115
- """
116
- Alias for sprite
117
- """ # remove this?
118
- return self.sprite
119
-
120
- @property
121
- def block_shape(self) -> blockshape.BlockShape:
122
- """
123
- Search for the blockshape stored in blockshape.py
124
- :return: The block's block shape (by opcode)
125
- """
126
- _shape = blockshape.BlockShapes.find(self.opcode, "opcode")
127
- if _shape is None:
128
- warnings.warn(f"No blockshape {self.opcode!r} exists! Defaulting to {blockshape.BlockShapes.UNDEFINED}")
129
- _shape = blockshape.BlockShapes.UNDEFINED
130
- assert isinstance(_shape, blockshape.BlockShape)
131
- return _shape
132
-
133
- @property
134
- def can_next(self):
135
- """
136
- :return: Whether the block *can* have a next block (basically checks if it's not a cap block, also considering the behaviour of control_stop)
137
- """
138
- _shape = self.block_shape
139
- if _shape.is_cap is not blockshape.MUTATION_DEPENDENT:
140
- return _shape.is_attachable
141
- else:
142
- if self.mutation is None:
143
- # If there's no mutation, let's just assume yes
144
- warnings.warn(f"{self} has no mutation! Assuming we can add block ;-;")
145
- return True
146
-
147
- return self.mutation.has_next
148
-
149
- @property
150
- def id(self) -> str:
151
- """
152
- Work out the id of this block by searching through the sprite dictionary
153
- """
154
- if self._id:
155
- return self.id
156
- # warnings.warn(f"Using block IDs can cause consistency issues and is not recommended")
157
- # This property is used when converting comments to JSON (we don't want random warning when exporting a project)
158
- for _block_id, _block in self.sprite.blocks.items():
159
- if _block is self:
160
- self._id = _block_id
161
- return self.id
162
-
163
- # Let's just automatically assign ourselves an id
164
- self.sprite.add_block(self)
165
- return self.id
166
-
167
- @id.setter
168
- def id(self, value: str) -> None:
169
- self._id = value
170
-
171
- @property
172
- def parent_id(self):
173
- """
174
- Get the id of the parent block, if applicable
175
- """
176
- if self.parent is not None:
177
- return self.parent.id
178
- else:
179
- return None
180
-
181
- @property
182
- def next_id(self):
183
- """
184
- Get the id of the next block, if applicable
185
- """
186
- if self.next is not None:
187
- return self.next.id
188
- else:
189
- return None
190
-
191
- @property
192
- def relatives(self) -> list[Block]:
193
- """
194
- :return: A list of blocks which are related to this block (e.g. parent, next, inputs)
195
- """
196
- _ret = []
197
-
198
- def yield_block(_block: Block | None):
199
- if isinstance(_block, Block):
200
- _ret.append(_block)
201
-
202
- yield_block(self.next)
203
- yield_block(self.parent)
204
-
205
- return _ret
206
-
207
- @property
208
- def children(self) -> list[Block | prim.Prim]:
209
- """
210
- :return: A list of blocks that are inside of this block, **NOT INCLUDING THE ATTACHED BLOCK**
211
- """
212
- _children = []
213
- for _input in self.inputs.values():
214
- if isinstance(_input.value, Block) or isinstance(_input.value, prim.Prim):
215
- _children.append(_input.value)
216
-
217
- if _input.obscurer is not None:
218
- _children.append(_input.obscurer)
219
- return _children
220
-
221
- @property
222
- def previous_chain(self):
223
- """
224
- Recursive getter method to get all previous blocks in the blockchain (until hitting a top-level block)
225
- """
226
- if self.parent is None: # todo: use is_top_level?
227
- return [self]
228
-
229
- return [self] + self.parent.previous_chain
230
-
231
- @property
232
- def attached_chain(self):
233
- """
234
- Recursive getter method to get all next blocks in the blockchain (until hitting a bottom-levell block)
235
- """
236
- if self.next is None:
237
- return [self]
238
-
239
- return [self] + self.next.attached_chain
240
-
241
- @property
242
- def complete_chain(self):
243
- """
244
- Attach previous and attached chains from this block
245
- """
246
- return self.previous_chain[:1:-1] + self.attached_chain
247
-
248
- @property
249
- def top_level_block(self):
250
- """
251
- Get the first block in the block stack that this block is part of
252
- same as the old stack_parent property from sbedtior v1
253
- """
254
- return self.previous_chain[-1]
255
-
256
- @property
257
- def bottom_level_block(self):
258
- """
259
- Get the last block in the block stack that this block is part of
260
- """
261
- return self.attached_chain[-1]
262
-
263
- @property
264
- def stack_tree(self):
265
- """
266
- Useful for showing a block stack in the console, using pprint
267
- :return: A tree-like nested list structure representing the stack of blocks, including inputs, starting at this block
268
- """
269
- _tree = [self]
270
- for child in self.children:
271
- if isinstance(child, prim.Prim):
272
- _tree.append(child)
273
- elif isinstance(child, Block):
274
- _tree.append(child.stack_tree)
275
-
276
- if self.next:
277
- _tree += self.next.stack_tree
278
-
279
- return _tree
280
-
281
- @property
282
- def category(self):
283
- """
284
- Works out what category of block this is using the opcode. Does not perform validation
285
- """
286
- return self.opcode.split('_')[0]
287
-
288
- @property
289
- def is_input(self):
290
- """
291
- :return: Whether this block is an input obscurer or value
292
- """
293
- return self.parent_input is not None
294
-
295
- @property
296
- def is_next_block(self):
297
- """
298
- :return: Whether this block is attached (as next block) to a previous block and not an input
299
- """
300
- return self.parent and not self.is_input
301
-
302
- @property
303
- def parent_input(self):
304
- """
305
- Fetch an input that this block is placed inside of (if applicable)
306
- """
307
- if not self.parent:
308
- return None
309
-
310
- for _input in self.parent.inputs.values():
311
- if _input.obscurer is self or _input.value is self:
312
- return _input
313
- return None
314
-
315
- @property
316
- def new_id(self):
317
- return self.sprite.new_id
318
-
319
- @property
320
- def comment(self) -> comment.Comment | None:
321
- """
322
- Fetch an associated comment (if applicable) by searching the associated sprite
323
- """
324
- for _comment in self.sprite.comments:
325
- if _comment.block is self:
326
- return _comment
327
- return None
328
-
329
- @property
330
- def turbowarp_block_opcode(self):
331
- """
332
- :return: The 'opcode' if this is a turbowarp block: e.g.
333
- - log
334
- - breakpoint
335
- - error
336
- - warn
337
- - is compiled?
338
- - is turbowarp?
339
- - is forkphorus?
340
- If it's not one, just returns None
341
- """
342
- if self.opcode == "procedures_call":
343
- if self.mutation:
344
- if self.mutation.proc_code:
345
- # \u200B is a zero-width space
346
- if self.mutation.proc_code == "\u200B\u200Bbreakpoint\u200B\u200B":
347
- return "breakpoint"
348
- elif self.mutation.proc_code == "\u200B\u200Blog\u200B\u200B %s":
349
- return "log"
350
- elif self.mutation.proc_code == "\u200B\u200Berror\u200B\u200B %s":
351
- return "error"
352
- elif self.mutation.proc_code == "\u200B\u200Bwarn\u200B\u200B %s":
353
- return "warn"
354
-
355
- elif self.opcode == "argument_reporter_boolean":
356
- arg = self.fields.get("VALUE")
357
-
358
- if arg is not None:
359
- arg = arg.value
360
- if isinstance(arg, str):
361
- arg = arg.lower()
362
-
363
- if arg == "is turbowarp?":
364
- return "is_turbowarp?"
365
-
366
- elif arg == "is compiled?":
367
- return "is_compiled?"
368
-
369
- elif arg == "is forkphorus?":
370
- return "is_forkphorus?"
371
-
372
- return None
373
-
374
- @property
375
- def is_turbowarp_block(self):
376
- """
377
- Return whether this block is actually a turbowarp debugger/boolean block, based on mutation
378
- """
379
- return self.turbowarp_block_opcode is not None
380
-
381
- @staticmethod
382
- def from_json(data: dict) -> Block:
383
- """
384
- Load a block from the JSON dictionary.
385
- :param data: a dictionary (not list)
386
- :return: The new Block object
387
- """
388
- _opcode = data["opcode"]
389
-
390
- _x, _y = data.get("x"), data.get("y")
391
-
392
- _next_id = data.get("next")
393
- _parent_id = data.get("parent")
394
-
395
- _shadow = data.get("shadow", False)
396
- _top_level = data.get("topLevel", _parent_id is None)
397
-
398
- _inputs = {}
399
- for _input_code, _input_data in data.get("inputs", {}).items():
400
- _inputs[_input_code] = inputs.Input.from_json(_input_data)
401
-
402
- _fields = {}
403
- for _field_code, _field_data in data.get("fields", {}).items():
404
- _fields[_field_code] = field.Field.from_json(_field_data)
405
-
406
- if "mutation" in data:
407
- _mutation = mutation.Mutation.from_json(data["mutation"])
408
- else:
409
- _mutation = None
410
-
411
- return Block(_opcode, _shadow, _top_level, _mutation, _fields, _inputs, _x, _y, _next_id=_next_id,
412
- _parent_id=_parent_id)
413
-
414
- def to_json(self) -> dict:
415
- """
416
- Convert a block to the project.json format
417
- """
418
- self.check_toplevel()
419
-
420
- _json = {
421
- "opcode": self.opcode,
422
- "next": self.next_id,
423
- "parent": self.parent_id,
424
- "inputs": {_id: _input.to_json() for _id, _input in self.inputs.items()},
425
- "fields": {_id: _field.to_json() for _id, _field in self.fields.items()},
426
- "shadow": self.is_shadow,
427
- "topLevel": self.is_top_level,
428
- }
429
- _comment = self.comment
430
- if _comment:
431
- commons.noneless_update(_json, {
432
- "comment": _comment.id
433
- })
434
-
435
- if self.is_top_level:
436
- commons.noneless_update(_json, {
437
- "x": self.x,
438
- "y": self.y,
439
- })
440
-
441
- if self.mutation is not None:
442
- commons.noneless_update(_json, {
443
- "mutation": self.mutation.to_json(),
444
- })
445
-
446
- return _json
447
-
448
- def link_using_sprite(self, link_subs: bool = True):
449
- """
450
- Link this block to various other blocks once the sprite has been assigned
451
- """
452
- if link_subs:
453
- self.link_subcomponents()
454
-
455
- if self.mutation:
456
- self.mutation.link_arguments()
457
-
458
- if self._parent_id is not None:
459
- self.parent = self.sprite.find_block(self._parent_id, "id")
460
- if self.parent is not None:
461
- self._parent_id = None
462
-
463
- if self._next_id is not None:
464
- self.next = self.sprite.find_block(self._next_id, "id")
465
- if self.next is not None:
466
- self._next_id = None
467
-
468
- for _block in self.relatives:
469
- _block.sprite = self.sprite
470
-
471
- for _field in self.fields.values():
472
- if _field.id is not None:
473
- new_value = self.sprite.find_vlb(_field.id, "id")
474
- if new_value is None:
475
- # We probably need to add a local global variable
476
- _type = _field.type
477
-
478
- if _type == field.Types.VARIABLE:
479
- # Create a new variable
480
- new_value = vlb.Variable(commons.gen_id(),
481
- _field.value)
482
- elif _type == field.Types.LIST:
483
- # Create a list
484
- new_value = vlb.List(commons.gen_id(),
485
- _field.value)
486
- elif _type == field.Types.BROADCAST:
487
- # Create a broadcast
488
- new_value = vlb.Broadcast(commons.gen_id(),
489
- _field.value)
490
- else:
491
- # Something probably went wrong
492
- warnings.warn(
493
- f"Could not find {_field.id!r} in {self.sprite}. Can't create a new {_type} so we gave a warning")
494
-
495
- if new_value is not None:
496
- self.sprite.add_local_global(new_value)
497
-
498
- # Check again since there may have been a newly created VLB
499
- if new_value is not None:
500
- _field.value = new_value
501
- _field.id = None
502
-
503
- for _input in self.inputs.values():
504
- _input.link_using_block()
505
-
506
- # Adding/removing block
507
- def attach_block(self, new: Block) -> Block:
508
- """
509
- Connect another block onto the boottom of this block (not necessarily bottom of chain)
510
- """
511
- if not self.can_next:
512
- raise exceptions.BadBlockShape(f"{self.block_shape} cannot be stacked onto")
513
- elif new.block_shape.is_hat or not new.block_shape.is_stack:
514
- raise exceptions.BadBlockShape(f"{new.block_shape} is not stackable")
515
-
516
- new.parent = self
517
- new.next = self.next
518
-
519
- self.next = new
520
-
521
- new.check_toplevel()
522
- self.sprite.add_block(new)
523
-
524
- return new
525
-
526
- def duplicate_single_block(self) -> Block:
527
- return self.attach_block(self.dcopy())
528
-
529
- def attach_chain(self, *chain: Iterable[Block]) -> Block:
530
- attaching_block = self
531
- for _block in chain:
532
- attaching_block = attaching_block.attach_block(_block)
533
-
534
- return attaching_block
535
-
536
- def duplicate_chain(self) -> Block:
537
- return self.bottom_level_block.attach_chain(
538
- *map(Block.dcopy, self.attached_chain)
539
- )
540
-
541
- def slot_above(self, new: Block) -> Block:
542
- """
543
- Place a single block directly above this block
544
- """
545
- if not new.can_next:
546
- raise exceptions.BadBlockShape(f"{new.block_shape} cannot be stacked onto")
547
-
548
- elif self.block_shape.is_hat or not self.block_shape.is_stack:
549
- raise exceptions.BadBlockShape(f"{self.block_shape} is not stackable")
550
-
551
- new.parent, new.next = self.parent, self
552
-
553
- self.parent = new
554
-
555
- if new.parent:
556
- new.parent.next = new
557
-
558
- return self.sprite.add_block(new)
559
-
560
- def delete_single_block(self):
561
- if self.is_next_block:
562
- self.parent.next = self.next
563
-
564
- if self.next:
565
- self.next.parent = self.parent
566
-
567
- if self.is_top_level:
568
- self.next.is_top_level = True
569
- self.next.x, self.next.y = self.next.x, self.next.y
570
-
571
- self.sprite.remove_block(self)
572
-
573
- def delete_chain(self):
574
- """
575
- Delete all blocks in the attached blockchain (and self)
576
- """
577
- for _block in self.attached_chain:
578
- _block.delete_single_block()
579
-