scratchattach 2.1.9__py3-none-any.whl → 2.1.10a1__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 (59) hide show
  1. scratchattach/__init__.py +28 -25
  2. scratchattach/cloud/__init__.py +2 -0
  3. scratchattach/cloud/_base.py +454 -282
  4. scratchattach/cloud/cloud.py +171 -168
  5. scratchattach/editor/__init__.py +21 -0
  6. scratchattach/editor/asset.py +199 -0
  7. scratchattach/editor/backpack_json.py +117 -0
  8. scratchattach/editor/base.py +142 -0
  9. scratchattach/editor/block.py +507 -0
  10. scratchattach/editor/blockshape.py +353 -0
  11. scratchattach/editor/build_defaulting.py +47 -0
  12. scratchattach/editor/comment.py +74 -0
  13. scratchattach/editor/commons.py +243 -0
  14. scratchattach/editor/extension.py +43 -0
  15. scratchattach/editor/field.py +90 -0
  16. scratchattach/editor/inputs.py +132 -0
  17. scratchattach/editor/meta.py +106 -0
  18. scratchattach/editor/monitor.py +175 -0
  19. scratchattach/editor/mutation.py +317 -0
  20. scratchattach/editor/pallete.py +91 -0
  21. scratchattach/editor/prim.py +170 -0
  22. scratchattach/editor/project.py +273 -0
  23. scratchattach/editor/sbuild.py +2837 -0
  24. scratchattach/editor/sprite.py +586 -0
  25. scratchattach/editor/twconfig.py +113 -0
  26. scratchattach/editor/vlb.py +134 -0
  27. scratchattach/eventhandlers/_base.py +99 -92
  28. scratchattach/eventhandlers/cloud_events.py +110 -103
  29. scratchattach/eventhandlers/cloud_recorder.py +26 -21
  30. scratchattach/eventhandlers/cloud_requests.py +460 -452
  31. scratchattach/eventhandlers/cloud_server.py +246 -244
  32. scratchattach/eventhandlers/cloud_storage.py +135 -134
  33. scratchattach/eventhandlers/combine.py +29 -27
  34. scratchattach/eventhandlers/filterbot.py +160 -159
  35. scratchattach/eventhandlers/message_events.py +41 -40
  36. scratchattach/other/other_apis.py +284 -212
  37. scratchattach/other/project_json_capabilities.py +475 -546
  38. scratchattach/site/_base.py +64 -46
  39. scratchattach/site/activity.py +414 -122
  40. scratchattach/site/backpack_asset.py +118 -84
  41. scratchattach/site/classroom.py +430 -142
  42. scratchattach/site/cloud_activity.py +107 -103
  43. scratchattach/site/comment.py +220 -190
  44. scratchattach/site/forum.py +400 -399
  45. scratchattach/site/project.py +806 -787
  46. scratchattach/site/session.py +1134 -867
  47. scratchattach/site/studio.py +611 -609
  48. scratchattach/site/user.py +835 -837
  49. scratchattach/utils/commons.py +243 -148
  50. scratchattach/utils/encoder.py +157 -156
  51. scratchattach/utils/enums.py +197 -190
  52. scratchattach/utils/exceptions.py +233 -206
  53. scratchattach/utils/requests.py +67 -59
  54. {scratchattach-2.1.9.dist-info → scratchattach-2.1.10a1.dist-info}/METADATA +155 -146
  55. scratchattach-2.1.10a1.dist-info/RECORD +62 -0
  56. {scratchattach-2.1.9.dist-info → scratchattach-2.1.10a1.dist-info}/WHEEL +1 -1
  57. {scratchattach-2.1.9.dist-info → scratchattach-2.1.10a1.dist-info/licenses}/LICENSE +21 -21
  58. scratchattach-2.1.9.dist-info/RECORD +0 -40
  59. {scratchattach-2.1.9.dist-info → scratchattach-2.1.10a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,117 @@
1
+ """
2
+ Module to deal with the backpack's weird JSON format, by overriding with new load methods
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from . import block, prim, field, inputs, mutation, sprite
7
+
8
+
9
+ def parse_prim_fields(_fields: dict[str]) -> tuple[str | None, str | None, str | None]:
10
+ """
11
+ Function for reading the fields in a backpack **primitive**
12
+ """
13
+ for key, value in _fields.items():
14
+ key: str
15
+ value: dict[str, str]
16
+ prim_value, prim_name, prim_id = (None,) * 3
17
+ if key == "NUM":
18
+ prim_value = value.get("value")
19
+ else:
20
+ prim_name = value.get("value")
21
+ prim_id = value.get("id")
22
+
23
+ # There really should only be 1 item, and this function can only return for that item
24
+ return prim_value, prim_name, prim_id
25
+ return (None,) * 3
26
+
27
+
28
+ class BpField(field.Field):
29
+ """
30
+ A normal field but with a different load method
31
+ """
32
+
33
+ @staticmethod
34
+ def from_json(data: dict[str, str]) -> field.Field:
35
+ # We can very simply convert it to the regular format
36
+ data = [data.get("value"), data.get("id")]
37
+ return field.Field.from_json(data)
38
+
39
+
40
+ class BpInput(inputs.Input):
41
+ """
42
+ A normal input but with a different load method
43
+ """
44
+
45
+ @staticmethod
46
+ def from_json(data: dict[str, str]) -> inputs.Input:
47
+ # The actual data is stored in a separate prim block
48
+ _id = data.get("shadow")
49
+ _obscurer_id = data.get("block")
50
+
51
+ if _obscurer_id == _id:
52
+ # If both the shadow and obscurer are the same, then there is no actual obscurer
53
+ _obscurer_id = None
54
+ # We cannot work out the shadow status yet since that is located in the primitive
55
+ return inputs.Input(None, _id=_id, _obscurer_id=_obscurer_id)
56
+
57
+
58
+ class BpBlock(block.Block):
59
+ """
60
+ A normal block but with a different load method
61
+ """
62
+
63
+ @staticmethod
64
+ def from_json(data: dict) -> prim.Prim | block.Block:
65
+ """
66
+ Load a block in the **backpack** JSON format
67
+ :param data: A dictionary (not list)
68
+ :return: A new block/prim object
69
+ """
70
+ _opcode = data["opcode"]
71
+
72
+ _x, _y = data.get("x"), data.get("y")
73
+ if prim.is_prim_opcode(_opcode):
74
+ # This is actually a prim
75
+ prim_value, prim_name, prim_id = parse_prim_fields(data["fields"])
76
+ return prim.Prim(prim.PrimTypes.find(_opcode, "opcode"),
77
+ prim_value, prim_name, prim_id)
78
+
79
+ _next_id = data.get("next")
80
+ _parent_id = data.get("parent")
81
+
82
+ _shadow = data.get("shadow", False)
83
+ _top_level = data.get("topLevel", _parent_id is None)
84
+
85
+ _inputs = {}
86
+ for _input_code, _input_data in data.get("inputs", {}).items():
87
+ _inputs[_input_code] = BpInput.from_json(_input_data)
88
+
89
+ _fields = {}
90
+ for _field_code, _field_data in data.get("fields", {}).items():
91
+ _fields[_field_code] = BpField.from_json(_field_data)
92
+
93
+ if "mutation" in data:
94
+ _mutation = mutation.Mutation.from_json(data["mutation"])
95
+ else:
96
+ _mutation = None
97
+
98
+ return block.Block(_opcode, _shadow, _top_level, _mutation, _fields, _inputs, _x, _y, _next_id=_next_id,
99
+ _parent_id=_parent_id)
100
+
101
+
102
+ def load_script(_script_data: list[dict]) -> sprite.Sprite:
103
+ """
104
+ Loads a script into a sprite from the backpack JSON format
105
+ :param _script_data: Backpack script JSON data
106
+ :return: a blockchain object containing the script
107
+ """
108
+ # Using a sprite since it simplifies things, e.g. local global loading
109
+ _blockchain = sprite.Sprite()
110
+
111
+ for _block_data in _script_data:
112
+ _block = BpBlock.from_json(_block_data)
113
+ _block.sprite = _blockchain
114
+ _blockchain.blocks[_block_data["id"]] = _block
115
+
116
+ _blockchain.link_subcomponents()
117
+ return _blockchain
@@ -0,0 +1,142 @@
1
+ """
2
+ Editor base classes
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import copy
8
+ import json
9
+ from abc import ABC, abstractmethod
10
+ from io import TextIOWrapper
11
+ from typing import Optional, Any, TYPE_CHECKING, BinaryIO
12
+
13
+ if TYPE_CHECKING:
14
+ from . import project, sprite, block, mutation, asset
15
+
16
+ from . import build_defaulting
17
+
18
+
19
+ class Base(ABC):
20
+ def dcopy(self):
21
+ """
22
+ :return: A **deep** copy of self
23
+ """
24
+ return copy.deepcopy(self)
25
+
26
+ def copy(self):
27
+ """
28
+ :return: A **shallow** copy of self
29
+ """
30
+ return copy.copy(self)
31
+
32
+
33
+ class JSONSerializable(Base, ABC):
34
+ @staticmethod
35
+ @abstractmethod
36
+ def from_json(data: dict | list | Any):
37
+ pass
38
+
39
+ @abstractmethod
40
+ def to_json(self) -> dict | list | Any:
41
+ pass
42
+
43
+ def save_json(self, name: str = ''):
44
+ data = self.to_json()
45
+ with open(f"{self.__class__.__name__.lower()}{name}.json", "w") as f:
46
+ json.dump(data, f)
47
+
48
+
49
+ class JSONExtractable(JSONSerializable, ABC):
50
+ @staticmethod
51
+ @abstractmethod
52
+ def load_json(data: str | bytes | TextIOWrapper | BinaryIO, load_assets: bool = True, _name: Optional[str] = None) -> tuple[
53
+ str, list[asset.AssetFile], str]:
54
+ """
55
+ Automatically extracts the JSON data as a string, as well as providing auto naming
56
+ :param data: Either a string of JSON, sb3 file as bytes or as a file object
57
+ :param load_assets: Whether to extract assets as well (if applicable)
58
+ :param _name: Any provided name (will automatically find one otherwise)
59
+ :return: tuple of the name, asset data & json as a string
60
+ """
61
+ ...
62
+
63
+
64
+ class ProjectSubcomponent(JSONSerializable, ABC):
65
+ def __init__(self, _project: Optional[project.Project] = None):
66
+ self.project = _project
67
+
68
+
69
+ class SpriteSubComponent(JSONSerializable, ABC):
70
+ def __init__(self, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
71
+ if _sprite is build_defaulting.SPRITE_DEFAULT:
72
+ _sprite = build_defaulting.current_sprite()
73
+
74
+ self.sprite = _sprite
75
+
76
+ # @property
77
+ # def sprite(self):
78
+ # if self._sprite is None:
79
+ # print("ok, ", build_defaulting.current_sprite())
80
+ # return build_defaulting.current_sprite()
81
+ # else:
82
+ # return self._sprite
83
+
84
+ # @sprite.setter
85
+ # def sprite(self, value):
86
+ # self._sprite = value
87
+
88
+ @property
89
+ def project(self) -> project.Project:
90
+ return self.sprite.project
91
+
92
+
93
+ class IDComponent(SpriteSubComponent, ABC):
94
+ def __init__(self, _id: str, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
95
+ self.id = _id
96
+ super().__init__(_sprite)
97
+
98
+ def __repr__(self):
99
+ return f"<{self.__class__.__name__}: {self.id}>"
100
+
101
+
102
+ class NamedIDComponent(IDComponent, ABC):
103
+ """
104
+ Base class for Variables, Lists and Broadcasts (Name + ID + sprite)
105
+ """
106
+
107
+ def __init__(self, _id: str, name: str, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
108
+ self.name = name
109
+ super().__init__(_id, _sprite)
110
+
111
+ def __repr__(self):
112
+ return f"<{self.__class__.__name__} '{self.name}'>"
113
+
114
+
115
+ class BlockSubComponent(JSONSerializable, ABC):
116
+ def __init__(self, _block: Optional[block.Block] = None):
117
+ self.block = _block
118
+
119
+ @property
120
+ def sprite(self) -> sprite.Sprite:
121
+ return self.block.sprite
122
+
123
+ @property
124
+ def project(self) -> project.Project:
125
+ return self.sprite.project
126
+
127
+
128
+ class MutationSubComponent(JSONSerializable, ABC):
129
+ def __init__(self, _mutation: Optional[mutation.Mutation] = None):
130
+ self.mutation = _mutation
131
+
132
+ @property
133
+ def block(self) -> block.Block:
134
+ return self.mutation.block
135
+
136
+ @property
137
+ def sprite(self) -> sprite.Sprite:
138
+ return self.block.sprite
139
+
140
+ @property
141
+ def project(self) -> project.Project:
142
+ return self.sprite.project