scratchattach 3.0.0b0__py3-none-any.whl → 3.0.0b1__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 (83) hide show
  1. cli/__about__.py +1 -0
  2. cli/__init__.py +26 -0
  3. cli/cmd/__init__.py +4 -0
  4. cli/cmd/group.py +127 -0
  5. cli/cmd/login.py +60 -0
  6. cli/cmd/profile.py +7 -0
  7. cli/cmd/sessions.py +5 -0
  8. cli/context.py +142 -0
  9. cli/db.py +66 -0
  10. cli/namespace.py +14 -0
  11. cloud/__init__.py +2 -0
  12. cloud/_base.py +483 -0
  13. cloud/cloud.py +183 -0
  14. editor/__init__.py +22 -0
  15. editor/asset.py +265 -0
  16. editor/backpack_json.py +115 -0
  17. editor/base.py +191 -0
  18. editor/block.py +584 -0
  19. editor/blockshape.py +357 -0
  20. editor/build_defaulting.py +51 -0
  21. editor/code_translation/__init__.py +0 -0
  22. editor/code_translation/parse.py +177 -0
  23. editor/comment.py +80 -0
  24. editor/commons.py +145 -0
  25. editor/extension.py +50 -0
  26. editor/field.py +99 -0
  27. editor/inputs.py +138 -0
  28. editor/meta.py +117 -0
  29. editor/monitor.py +185 -0
  30. editor/mutation.py +381 -0
  31. editor/pallete.py +88 -0
  32. editor/prim.py +174 -0
  33. editor/project.py +381 -0
  34. editor/sprite.py +609 -0
  35. editor/twconfig.py +114 -0
  36. editor/vlb.py +134 -0
  37. eventhandlers/__init__.py +0 -0
  38. eventhandlers/_base.py +101 -0
  39. eventhandlers/cloud_events.py +130 -0
  40. eventhandlers/cloud_recorder.py +26 -0
  41. eventhandlers/cloud_requests.py +544 -0
  42. eventhandlers/cloud_server.py +249 -0
  43. eventhandlers/cloud_storage.py +135 -0
  44. eventhandlers/combine.py +30 -0
  45. eventhandlers/filterbot.py +163 -0
  46. eventhandlers/message_events.py +42 -0
  47. other/__init__.py +0 -0
  48. other/other_apis.py +598 -0
  49. other/project_json_capabilities.py +475 -0
  50. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b1.dist-info}/METADATA +1 -1
  51. scratchattach-3.0.0b1.dist-info/RECORD +79 -0
  52. scratchattach-3.0.0b1.dist-info/top_level.txt +7 -0
  53. site/__init__.py +0 -0
  54. site/_base.py +93 -0
  55. site/activity.py +426 -0
  56. site/alert.py +226 -0
  57. site/backpack_asset.py +119 -0
  58. site/browser_cookie3_stub.py +17 -0
  59. site/browser_cookies.py +61 -0
  60. site/classroom.py +454 -0
  61. site/cloud_activity.py +121 -0
  62. site/comment.py +228 -0
  63. site/forum.py +436 -0
  64. site/placeholder.py +132 -0
  65. site/project.py +932 -0
  66. site/session.py +1323 -0
  67. site/studio.py +704 -0
  68. site/typed_dicts.py +151 -0
  69. site/user.py +1252 -0
  70. utils/__init__.py +0 -0
  71. utils/commons.py +263 -0
  72. utils/encoder.py +161 -0
  73. utils/enums.py +237 -0
  74. utils/exceptions.py +277 -0
  75. utils/optional_async.py +154 -0
  76. utils/requests.py +306 -0
  77. scratchattach/__init__.py +0 -37
  78. scratchattach/__main__.py +0 -93
  79. scratchattach-3.0.0b0.dist-info/RECORD +0 -8
  80. scratchattach-3.0.0b0.dist-info/top_level.txt +0 -1
  81. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b1.dist-info}/WHEEL +0 -0
  82. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b1.dist-info}/entry_points.txt +0 -0
  83. {scratchattach-3.0.0b0.dist-info → scratchattach-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
editor/prim.py ADDED
@@ -0,0 +1,174 @@
1
+ """
2
+ The PrimType(s) and Prim classes
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import warnings
8
+ from dataclasses import dataclass
9
+ from typing import Optional, Callable, Final
10
+
11
+ from . import base, sprite, vlb, commons, build_defaulting
12
+ from scratchattach.utils import enums, exceptions
13
+
14
+
15
+ @dataclass
16
+ class PrimType(base.JSONSerializable):
17
+ code: int
18
+ name: str
19
+ attrs: list = None
20
+ opcode: str = None
21
+
22
+ def __eq__(self, other):
23
+ if isinstance(other, str):
24
+ return self.name == other
25
+ elif isinstance(other, enums._EnumWrapper):
26
+ other = other.value
27
+ return super().__eq__(other)
28
+
29
+ @staticmethod
30
+ def from_json(data: int):
31
+ return PrimTypes.find(data, "code")
32
+
33
+ def to_json(self) -> int:
34
+ return self.code
35
+
36
+
37
+ BASIC_ATTRS: Final[tuple[str]] = ("value",)
38
+ VLB_ATTRS: Final[tuple[str]] = ("name", "id", "x", "y")
39
+
40
+
41
+ class PrimTypes(enums._EnumWrapper):
42
+ # Yeah, they actually do have opcodes
43
+ NUMBER = PrimType(4, "number", BASIC_ATTRS, "math_number")
44
+ POSITIVE_NUMBER = PrimType(5, "positive number", BASIC_ATTRS, "math_positive_number")
45
+ POSITIVE_INTEGER = PrimType(6, "positive integer", BASIC_ATTRS, "math_whole_number")
46
+ INTEGER = PrimType(7, "integer", BASIC_ATTRS, "math_integer")
47
+ ANGLE = PrimType(8, "angle", BASIC_ATTRS, "math_angle")
48
+ COLOR = PrimType(9, "color", BASIC_ATTRS, "colour_picker")
49
+ STRING = PrimType(10, "string", BASIC_ATTRS, "text")
50
+ BROADCAST = PrimType(11, "broadcast", VLB_ATTRS, "event_broadcast_menu")
51
+ VARIABLE = PrimType(12, "variable", VLB_ATTRS, "data_variable")
52
+ LIST = PrimType(13, "list", VLB_ATTRS, "data_listcontents")
53
+
54
+ @classmethod
55
+ def find(cls, value, by: str, apply_func: Optional[Callable] = None) -> PrimType:
56
+ return super().find(value, by, apply_func=apply_func)
57
+
58
+
59
+ def is_prim_opcode(opcode: str):
60
+ return opcode in PrimTypes.all_of("opcode") and opcode is not None
61
+
62
+
63
+ class Prim(base.SpriteSubComponent):
64
+ def __init__(self, _primtype: PrimType | PrimTypes, _value: Optional[str | vlb.Variable | vlb.List | vlb.Broadcast] = None,
65
+ _name: Optional[str] = None, _id: Optional[str] = None, _x: Optional[int] = None,
66
+ _y: Optional[int] = None, _sprite: sprite.Sprite = build_defaulting.SPRITE_DEFAULT):
67
+ """
68
+ Class representing a Scratch string, number, angle, variable etc.
69
+ Technically blocks but behave differently
70
+ https://en.scratch-wiki.info/wiki/Scratch_File_Format#Targets:~:text=A%20few%20blocks,13
71
+ """
72
+ if isinstance(_primtype, PrimTypes):
73
+ _primtype = _primtype.value
74
+
75
+ self.type = _primtype
76
+
77
+ self.value = _value
78
+
79
+ self.name = _name
80
+ """
81
+ Once you get the object associated with this primitive (sprite.link_prims()),
82
+ the name will be removed and the value will be changed from ``None``
83
+ """
84
+ self.value_id = _id
85
+ """
86
+ It's not an object accessed by id, but it may reference an object with an id.
87
+
88
+ ----
89
+
90
+ Once you get the object associated with it (sprite.link_prims()),
91
+ the id will be removed and the value will be changed from ``None``
92
+ """
93
+
94
+ self.x = _x
95
+ self.y = _y
96
+
97
+ super().__init__(_sprite)
98
+
99
+ def __repr__(self):
100
+ if self.is_basic:
101
+ return f"Prim<{self.type.name}: {self.value}>"
102
+ elif self.is_vlb:
103
+ return f"Prim<{self.type.name}: {self.value}>"
104
+ else:
105
+ return f"Prim<{self.type.name}>"
106
+
107
+ @property
108
+ def is_vlb(self):
109
+ return self.type.attrs == VLB_ATTRS
110
+
111
+ @property
112
+ def is_basic(self):
113
+ return self.type.attrs == BASIC_ATTRS
114
+
115
+ @staticmethod
116
+ def from_json(data: list):
117
+ assert isinstance(data, list)
118
+
119
+ _type_idx = data[0]
120
+ _prim_type = PrimTypes.find(_type_idx, "code")
121
+
122
+ _value, _name, _value_id, _x, _y = (None,) * 5
123
+ if _prim_type.attrs == BASIC_ATTRS:
124
+ assert len(data) == 2
125
+ _value = data[1]
126
+
127
+ elif _prim_type.attrs == VLB_ATTRS:
128
+ assert len(data) in (3, 5)
129
+ _name, _value_id = data[1:3]
130
+
131
+ if len(data) == 5:
132
+ _x, _y = data[3:]
133
+
134
+ return Prim(_prim_type, _value, _name, _value_id, _x, _y)
135
+
136
+ def to_json(self) -> list:
137
+ if self.type.attrs == BASIC_ATTRS:
138
+ return [self.type.code, self.value]
139
+ else:
140
+ return commons.trim_final_nones([self.type.code, self.value.name, self.value.id, self.x, self.y])
141
+
142
+ def link_using_sprite(self):
143
+ # Link prim to var/list/broadcast
144
+ if self.is_vlb:
145
+ if self.type.name == "variable":
146
+ self.value = self.sprite.find_variable(self.value_id, "id")
147
+
148
+ elif self.type.name == "list":
149
+ self.value = self.sprite.find_list(self.value_id, "id")
150
+
151
+ elif self.type.name == "broadcast":
152
+ self.value = self.sprite.find_broadcast(self.value_id, "id")
153
+ else:
154
+ # This should never happen
155
+ raise exceptions.BadVLBPrimitiveError(f"{self} claims to be VLB, but is {self.type.name}")
156
+
157
+ if self.value is None:
158
+ if not self.project:
159
+ new_vlb = vlb.construct(self.type.name.lower(), self.value_id, self.name)
160
+ self.sprite.add_local_global(new_vlb)
161
+ self.value = new_vlb
162
+
163
+ else:
164
+ new_vlb = vlb.construct(self.type.name.lower(), self.value_id, self.name)
165
+ self.sprite.stage.add_vlb(new_vlb)
166
+
167
+ warnings.warn(
168
+ f"Prim<name={self.name!r}, id={self.name!r}> has unknown {self.type.name} id; adding as global variable")
169
+ self.name = None
170
+ self.value_id = None
171
+
172
+ @property
173
+ def can_next(self):
174
+ return False
editor/project.py ADDED
@@ -0,0 +1,381 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import string
6
+ import warnings
7
+ from io import BytesIO, TextIOWrapper
8
+ from typing import Optional, Iterable, Generator, BinaryIO
9
+ from typing_extensions import deprecated
10
+ from zipfile import ZipFile
11
+
12
+ from . import base, meta, extension, monitor, sprite, asset, vlb, twconfig, comment, commons, mutation
13
+ from scratchattach.site import session
14
+ from scratchattach.utils import exceptions
15
+
16
+
17
+ class Project(base.JSONExtractable):
18
+ """
19
+ A Project (body). Represents the editor contents of a scratch project
20
+ """
21
+ def __init__(self, _name: Optional[str] = None, _meta: Optional[meta.Meta] = None, _extensions: Iterable[extension.Extension] = (),
22
+ _monitors: Iterable[monitor.Monitor] = (), _sprites: Iterable[sprite.Sprite] = (), *,
23
+ _asset_data: Optional[list[asset.AssetFile]] = None, _session: Optional[session.Session] = None):
24
+ # Defaulting for list parameters
25
+ if _meta is None:
26
+ _meta = meta.Meta()
27
+ if _asset_data is None:
28
+ _asset_data = []
29
+
30
+ self._session = _session
31
+
32
+ self.name = _name
33
+
34
+ self.meta = _meta
35
+ self.extensions: list[extension.Extension] = list(_extensions)
36
+ self.monitors = list(_monitors)
37
+ self.sprites = list(_sprites)
38
+
39
+ self.asset_data = _asset_data
40
+
41
+ self._tw_config_comment = None
42
+
43
+ # Link subcomponents
44
+ for iterable in (self.monitors, self.sprites):
45
+ for _subcomponent in iterable:
46
+ _subcomponent.project = self
47
+
48
+ # Link sprites
49
+ _stage_count = 0
50
+
51
+ for _sprite in self.sprites:
52
+ if _sprite.is_stage:
53
+ _stage_count += 1
54
+
55
+ _sprite.link_subcomponents()
56
+
57
+ # Link monitors
58
+ for _monitor in self.monitors:
59
+ _monitor.link_using_project()
60
+
61
+ if _stage_count != 1:
62
+ raise exceptions.InvalidStageCount(f"Project {self}")
63
+
64
+ def __repr__(self):
65
+ _ret = "Project<"
66
+ if self.name is not None:
67
+ _ret += f"name={self.name}, "
68
+ _ret += f"meta={self.meta}"
69
+ _ret += '>'
70
+ return _ret
71
+
72
+ @property
73
+ def stage(self) -> Optional[sprite.Sprite]:
74
+ for _sprite in self.sprites:
75
+ if _sprite.is_stage:
76
+ return _sprite
77
+ warnings.warn(f"Could not find stage for {self.name}")
78
+ return None
79
+
80
+ def to_json(self) -> dict:
81
+ _json = {
82
+ "targets": [_sprite.to_json() for _sprite in self.sprites],
83
+ "monitors": [_monitor.to_json() for _monitor in self.monitors],
84
+ "extensions": [_extension.to_json() for _extension in self.extensions],
85
+ "meta": self.meta.to_json(),
86
+ }
87
+
88
+ return _json
89
+
90
+ @property
91
+ def assets(self) -> Generator[asset.Asset, None, None]:
92
+ for _sprite in self.sprites:
93
+ for _asset in _sprite.assets:
94
+ yield _asset
95
+
96
+ @property
97
+ def tw_config_comment(self) -> comment.Comment | None:
98
+ for _comment in self.stage.comments:
99
+ if twconfig.is_valid_twconfig(_comment.text):
100
+ return _comment
101
+ return None
102
+
103
+ @property
104
+ def tw_config(self) -> twconfig.TWConfig | None:
105
+ _comment = self.tw_config_comment
106
+ if _comment:
107
+ return twconfig.TWConfig.from_str(_comment.text)
108
+ return None
109
+
110
+ @property
111
+ def all_ids(self):
112
+ _ret = []
113
+ for _sprite in self.sprites:
114
+ _ret += _sprite.all_ids
115
+ return _ret
116
+
117
+ @property
118
+ def new_id(self):
119
+ return commons.gen_id()
120
+
121
+ @staticmethod
122
+ def from_json(data: dict):
123
+ assert isinstance(data, dict)
124
+
125
+ # Load metadata
126
+ _meta = meta.Meta.from_json(data.get("meta"))
127
+
128
+ # Load extensions
129
+ _extensions = []
130
+ for _extension_data in data.get("extensions", []):
131
+ _extensions.append(extension.Extension.from_json(_extension_data))
132
+
133
+ # Load monitors
134
+ _monitors = []
135
+ for _monitor_data in data.get("monitors", []):
136
+ _monitors.append(monitor.Monitor.from_json(_monitor_data))
137
+
138
+ # Load sprites (targets)
139
+ _sprites = []
140
+ for _sprite_data in data.get("targets", []):
141
+ _sprites.append(sprite.Sprite.from_json(_sprite_data))
142
+
143
+ return Project(None, _meta, _extensions, _monitors, _sprites)
144
+
145
+ @staticmethod
146
+ def load_json(data: str | bytes | TextIOWrapper | BinaryIO, load_assets: bool = True, _name: Optional[str] = None):
147
+ """
148
+ Load project JSON and assets from an .sb3 file/bytes/file path
149
+ :return: Project name, asset data, json string
150
+ """
151
+ _dir_for_name = None
152
+
153
+ if _name is None:
154
+ if hasattr(data, "name"):
155
+ _dir_for_name = data.name
156
+
157
+ if not isinstance(_name, str) and _name is not None:
158
+ _name = str(_name)
159
+
160
+ if isinstance(data, bytes):
161
+ data = BytesIO(data)
162
+
163
+ elif isinstance(data, str):
164
+ _dir_for_name = data
165
+ data = open(data, "rb")
166
+
167
+ if _name is None and _dir_for_name is not None:
168
+ # Remove any directory names and the file extension
169
+ _name = _dir_for_name.split('/')[-1]
170
+ _name = '.'.join(_name.split('.')[:-1])
171
+
172
+ asset_data = []
173
+ with data:
174
+ # For if the sb3 is just JSON (e.g. if it's exported from scratchattach)
175
+ if commons.is_valid_json(data):
176
+ json_str = data
177
+
178
+ else:
179
+ with ZipFile(data) as archive:
180
+ json_str = archive.read("project.json")
181
+
182
+ # Also load assets
183
+ if load_assets:
184
+ for filename in archive.namelist():
185
+ if filename != "project.json":
186
+ md5_hash = filename.split('.')[0]
187
+
188
+ asset_data.append(
189
+ asset.AssetFile(filename, archive.read(filename), md5_hash)
190
+ )
191
+
192
+ else:
193
+ warnings.warn(
194
+ "Loading sb3 without loading assets. When exporting the project, there may be errors due to assets not being uploaded to the Scratch website")
195
+
196
+ return _name, asset_data, json_str
197
+
198
+ @classmethod
199
+ def from_sb3(cls, data: str | bytes | TextIOWrapper | BinaryIO, load_assets: bool = True, _name: Optional[str] = None):
200
+ """
201
+ Load a project from an .sb3 file/bytes/file path
202
+ """
203
+ _name, asset_data, json_str = cls.load_json(data, load_assets, _name)
204
+ data = json.loads(json_str)
205
+
206
+ project = cls.from_json(data)
207
+ project.name = _name
208
+ project.asset_data = asset_data
209
+
210
+ return project
211
+
212
+ @staticmethod
213
+ @deprecated("Use get_project(id).body() instead")
214
+ def from_id(project_id: int, _name: Optional[str] = None):
215
+ raise Exception("This method is deprecated")
216
+ # _proj = get_project(project_id)
217
+ # data = json.loads(_proj.get_json())
218
+
219
+ # if _name is None:
220
+ # _name = _proj.title
221
+ # _name = str(_name)
222
+
223
+ # _proj = Project.from_json(data)
224
+ # _proj.name = _name
225
+ # return _proj
226
+
227
+ def find_vlb(self, value: str | None, by: str = "name",
228
+ multiple: bool = False) -> Optional[vlb.Variable | vlb.List | vlb.Broadcast | list[
229
+ vlb.Variable | vlb.List | vlb.Broadcast]]:
230
+
231
+ _ret: list[vlb.Variable | vlb.List | vlb.Broadcast] = []
232
+ for _sprite in self.sprites:
233
+ val = _sprite.find_vlb(value, by, multiple)
234
+ if multiple:
235
+ _ret += val
236
+ else:
237
+ if val is not None:
238
+ return val
239
+
240
+ if multiple:
241
+ return _ret
242
+
243
+ return None
244
+
245
+ def find_sprite(self, value: str | None, by: str = "name",
246
+ multiple: bool = False) -> sprite.Sprite | list[sprite.Sprite]:
247
+ _ret = []
248
+ for _sprite in self.sprites:
249
+ if by == "name":
250
+ _val = _sprite.name
251
+ else:
252
+ _val = getattr(_sprite, by)
253
+
254
+ if _val == value:
255
+ if multiple:
256
+ _ret.append(_sprite)
257
+ else:
258
+ return _sprite
259
+
260
+ if multiple:
261
+ return _ret
262
+
263
+ def export(self, fp: str, *, auto_open: bool = False, export_as_zip: bool = True):
264
+ data = self.to_json()
265
+
266
+ if export_as_zip:
267
+ with ZipFile(fp, "w") as archive:
268
+ for _asset in self.assets:
269
+ asset_file = _asset.asset_file
270
+ if asset_file.filename not in archive.namelist():
271
+ archive.writestr(asset_file.filename, asset_file.data)
272
+
273
+ archive.writestr("project.json", json.dumps(data))
274
+ else:
275
+ with open(fp, "w") as json_file:
276
+ json.dump(data, json_file)
277
+
278
+ if auto_open:
279
+ os.system(f"explorer.exe \"{fp}\"")
280
+
281
+ def add_monitor(self, _monitor: monitor.Monitor) -> monitor.Monitor:
282
+ """
283
+ Bind a monitor to this project. Doing these manually can lead to interesting results.
284
+ """
285
+ _monitor.project = self
286
+ _monitor.reporter_id = self.new_id
287
+ self.monitors.append(_monitor)
288
+ return _monitor
289
+
290
+ def obfuscate(self, *, goto_origin: bool=True) -> None:
291
+ """
292
+ Randomly set all the variable names etc. Do not upload this project to the scratch website, as it is
293
+ against the community guidelines.
294
+ """
295
+ # this code is an embarrassing mess. If certain features are added to sa.editor, then it could become a lot cleaner
296
+ chars = string.ascii_letters + string.digits + string.punctuation
297
+
298
+ def b10_to_cbase(b10: int | float):
299
+ ret = ''
300
+ new_base = len(chars)
301
+ while b10 >= 1:
302
+ ret = chars[int(b10 % new_base)] + ret
303
+ b10 /= new_base
304
+
305
+ return ret
306
+
307
+ used = 0
308
+
309
+ def rand():
310
+ nonlocal used
311
+ used += 1
312
+
313
+ return b10_to_cbase(used)
314
+
315
+ for _sprite in self.sprites:
316
+ procedure_mappings: dict[str, str] = {}
317
+ argument_mappings: dict[str, str] = {}
318
+ done_args: list[mutation.Argument] = []
319
+
320
+ for _variable in _sprite.variables:
321
+ _variable.name = rand()
322
+ for _list in _sprite.lists:
323
+ _list.name = rand()
324
+ # don't rename broadcasts as these can be dynamically called
325
+
326
+ def arg_get(name: str) -> str:
327
+ if name not in argument_mappings:
328
+ argument_mappings[name] = rand()
329
+ return argument_mappings[name]
330
+
331
+ for _block in _sprite.blocks.values():
332
+ if goto_origin:
333
+ _block.x, _block.y = 0, 0
334
+
335
+ # TODO: Add special handling for turbowarp debugger blocks
336
+ if _block.opcode in ("procedures_call", "procedures_prototype", "procedures_definition"):
337
+ if _block.mutation is None:
338
+ continue
339
+
340
+ proccode = _block.mutation.proc_code
341
+ if proccode is None:
342
+ continue
343
+
344
+ if proccode not in procedure_mappings:
345
+ parsed_ppc = _block.mutation.parsed_proc_code
346
+
347
+ if parsed_ppc is None:
348
+ continue
349
+
350
+ new: list[str | mutation.ArgumentType] = []
351
+ for item in parsed_ppc:
352
+ if isinstance(item, str):
353
+ item = rand()
354
+
355
+ new.append(item)
356
+
357
+ new_proccode = mutation.construct_proccode(*new)
358
+ procedure_mappings[proccode] = new_proccode
359
+
360
+ _block.mutation.proc_code = procedure_mappings[proccode]
361
+
362
+ assert _block.mutation.arguments is not None
363
+ for arg in _block.mutation.arguments:
364
+ if arg in done_args:
365
+ continue
366
+ done_args.append(arg)
367
+
368
+ arg.name = arg_get(arg.name)
369
+
370
+ # print(_block, _block.mutation)
371
+ elif _block.opcode in ("argument_reporter_string_number", "argument_reporter_boolean"):
372
+ arg_name = _block.fields["VALUE"].value
373
+ assert isinstance(arg_name, str)
374
+ _block.fields["VALUE"].value = arg_get(arg_name)
375
+
376
+
377
+ # print(argument_mappings)
378
+
379
+ if goto_origin:
380
+ for _comment in _sprite.comments:
381
+ _comment.x, _comment.y = 0, 0