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
utils/exceptions.py ADDED
@@ -0,0 +1,277 @@
1
+ # Authentication / Authorization:
2
+ from __future__ import annotations
3
+
4
+ class Unauthenticated(Exception):
5
+ """
6
+ Raised when a method that requires a login / session is called on an object that wasn't created with a session.
7
+
8
+ If you create Project, Studio, or User objects using :meth:`scratchattach.get_project`, :meth:`scratchattach.get_studio`, or :meth:`scratchattach.get_user`, they cannot be used for actions that require authentication. Instead, use the following methods to ensure the objects are connected to an authenticated session:
9
+
10
+ - :meth:`scratchattach.Session.connect_project`
11
+
12
+ - :meth:`scratchattach.Session.connect_user`
13
+
14
+ - :meth:`scratchattach.Session.connect_studio`
15
+
16
+ This also applies to cloud variables, forum topics, and forum posts.
17
+ """
18
+
19
+ def __init__(self, message=""):
20
+ self.message = "No login / session connected.\n\nThe object on which the method was called was created using scratchattach.get_xyz()\nUse session.connect_xyz() instead (xyz is a placeholder for user / project / cloud / ...).\n\nMore information: https://scratchattach.readthedocs.io/en/latest/scratchattach.html#scratchattach.utils.exceptions.Unauthenticated"
21
+ super().__init__(self.message)
22
+
23
+
24
+ class Unauthorized(Exception):
25
+ """
26
+ Raised when an action is performed that the user associated with the session that the object was created with is not allowed to do.
27
+
28
+ Example: Changing the "about me" of other users will raise this error.
29
+ """
30
+
31
+ def __init__(self, message=""):
32
+ self.message = (
33
+ f"The user corresponding to the connected login / session is not allowed to perform this action. "
34
+ f"{message}")
35
+ super().__init__(self.message)
36
+
37
+
38
+ class XTokenError(Exception):
39
+ """
40
+ Raised when an action can't be performed because there is no XToken available.
41
+
42
+ This error can occur if the xtoken couldn't be fetched when the session was created. Some actions (like loving projects) require providing this token.
43
+ """
44
+
45
+
46
+ # Not found errors:
47
+
48
+ class UserNotFound(Exception):
49
+ """
50
+ Raised when a non-existent user is requested.
51
+ """
52
+
53
+
54
+ class ProjectNotFound(Exception):
55
+ """
56
+ Raised when a non-existent project is requested.
57
+ """
58
+
59
+ class ClassroomNotFound(Exception):
60
+ """
61
+ Raised when a non-existent Classroom is requested.
62
+ """
63
+
64
+
65
+ class StudioNotFound(Exception):
66
+ """
67
+ Raised when a non-existent studio is requested.
68
+ """
69
+
70
+
71
+ class ForumContentNotFound(Exception):
72
+ """
73
+ Raised when a non-existent forum topic / post is requested.
74
+ """
75
+
76
+
77
+ class CommentNotFound(Exception):
78
+ pass
79
+
80
+
81
+ # Invalid inputs
82
+ class InvalidLanguage(Exception):
83
+ """
84
+ Raised when an invalid language/language code/language object is provided, for TTS or Translate
85
+ """
86
+
87
+
88
+ class InvalidTTSGender(Exception):
89
+ """
90
+ Raised when an invalid TTS gender is provided.
91
+ """
92
+
93
+ # API errors:
94
+
95
+ class LoginFailure(Exception):
96
+ """
97
+ Raised when the Scratch server doesn't respond with a session id.
98
+
99
+ This could be caused by an invalid username / password. Another cause could be that your IP address was banned from logging in to Scratch. If you're using an online IDE (like replit), try running the code on your computer.
100
+ """
101
+
102
+
103
+ class FetchError(Exception):
104
+ """
105
+ Raised when getting information from the Scratch API fails. This can have various reasons. Make sure all provided arguments are valid.
106
+ """
107
+
108
+
109
+ class BadRequest(Exception):
110
+ """
111
+ Raised when the Scratch API responds with a "Bad Request" error message. This can have various reasons. Make sure all provided arguments are valid.
112
+ """
113
+
114
+ class RateLimitedError(Exception):
115
+ """
116
+ Indicates a ratelimit enforced by scratchattach
117
+ """
118
+
119
+ class Response429(Exception):
120
+ """
121
+ Raised when the Scratch API responds with a 429 error. This means that your network was ratelimited or blocked by Scratch. If you're using an online IDE (like replit.com), try running the code on your computer.
122
+ """
123
+
124
+
125
+ class CommentPostFailure(Exception):
126
+ """
127
+ Raised when a comment fails to post. This can have various reasons.
128
+ """
129
+
130
+
131
+ class APIError(Exception):
132
+ """
133
+ For API errors that can't be classified into one of the above errors
134
+ """
135
+
136
+
137
+ class ScrapeError(Exception):
138
+ """
139
+ Raised when something goes wrong while web-scraping a page with bs4.
140
+ """
141
+
142
+
143
+
144
+ # Cloud / encoding errors:
145
+
146
+ class CloudConnectionError(Exception):
147
+ """
148
+ Raised when connecting to Scratch's cloud server fails. This can have various reasons.
149
+ """
150
+
151
+
152
+
153
+ class InvalidCloudValue(Exception):
154
+ """
155
+ Raised when a cloud variable is set to an invalid value.
156
+ """
157
+
158
+
159
+
160
+ class InvalidDecodeInput(Exception):
161
+ """
162
+ Raised when the built-in decoder :meth:`scratchattach.encoder.Encoding.decode` receives an invalid input.
163
+ """
164
+
165
+
166
+
167
+ # Cloud Requests errors:
168
+
169
+ class RequestNotFound(Exception):
170
+ """
171
+ Cloud Requests: Raised when a non-existent cloud request is edited using :meth:`scratchattach.cloud_requests.CloudRequests.edit_request`.
172
+ """
173
+
174
+
175
+
176
+ # Websocket server errors:
177
+
178
+ class WebsocketServerError(Exception):
179
+ """
180
+ Raised when the self-hosted cloud websocket server fails to start.
181
+ """
182
+
183
+
184
+
185
+ # Editor errors:
186
+
187
+ class UnclosedJSONError(Exception):
188
+ """
189
+ Raised when a JSON string is never closed.
190
+ """
191
+
192
+
193
+ class BadVLBPrimitiveError(Exception):
194
+ """
195
+ Raised when a Primitive claiming to be a variable/list/broadcast actually isn't
196
+ """
197
+
198
+
199
+ class UnlinkedVLB(Exception):
200
+ """
201
+ Raised when a Primitive cannot be linked to variable/list/broadcast because the provided ID does not have an associated variable/list/broadcast
202
+ """
203
+
204
+
205
+ class InvalidStageCount(Exception):
206
+ """
207
+ Raised when a project has too many or too few Stage sprites
208
+ """
209
+
210
+
211
+ class InvalidVLBName(Exception):
212
+ """
213
+ Raised when an invalid VLB name is provided (not variable, list or broadcast)
214
+ """
215
+
216
+
217
+ class BadBlockShape(Exception):
218
+ """
219
+ Raised when the block shape cannot allow for the operation
220
+ """
221
+
222
+
223
+ class BadScript(Exception):
224
+ """
225
+ Raised when the block script cannot allow for the operation
226
+ """
227
+
228
+ # Warnings
229
+
230
+ class LoginDataWarning(UserWarning):
231
+ """
232
+ Warns you not to accidentally share your login data.
233
+ """
234
+
235
+ class InvalidUpdateWarning(UserWarning):
236
+ """
237
+ Warns you that something cannot be updated.
238
+ """
239
+
240
+ class GetAuthenticationWarning(UserWarning):
241
+ """
242
+ All authentication warnings.
243
+ """
244
+
245
+ class UserAuthenticationWarning(GetAuthenticationWarning):
246
+ """
247
+ Warns you to use session.connect_user instead of user.get_user
248
+ for actions that require authentication.
249
+ """
250
+
251
+ class ProjectAuthenticationWarning(GetAuthenticationWarning):
252
+ """
253
+ Warns you to use session.connect_project instead of project.get_project
254
+ for actions that require authentication.
255
+ """
256
+
257
+ class StudioAuthenticationWarning(GetAuthenticationWarning):
258
+ """
259
+ Warns you to use session.connect_studio instead of studio.get_studio
260
+ for actions that require authentication.
261
+ """
262
+
263
+ class ClassroomAuthenticationWarning(GetAuthenticationWarning):
264
+ """
265
+ Warns you to use session.connect_classroom or session.connect_classroom_from_token instead of classroom.get_classroom
266
+ for actions that require authentication.
267
+ """
268
+
269
+ class CloudAuthenticationWarning(GetAuthenticationWarning):
270
+ """
271
+ Warns you about usage of
272
+ """
273
+
274
+ class UnexpectedWebsocketEventWarning(RuntimeWarning):
275
+ """
276
+ Warns about an unexpected occurrence with a websocket.
277
+ """
@@ -0,0 +1,154 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from collections.abc import Awaitable, Generator, Callable
5
+ from typing import Generic, TypeVar, ParamSpec, Optional, Union, Any
6
+ from functools import wraps
7
+ import asyncio
8
+ import time
9
+
10
+ from . import requests
11
+
12
+ P = ParamSpec("P")
13
+ R = TypeVar("R")
14
+
15
+ class CallableAwaitable(Generic[R], ABC, Awaitable[R]):
16
+ result: R
17
+
18
+ @abstractmethod
19
+ def sync_impl(self) -> R:
20
+ pass
21
+
22
+ def __pos__(self) -> R:
23
+ return self.sync_impl()
24
+
25
+ @abstractmethod
26
+ async def async_impl(self) -> R:
27
+ pass
28
+
29
+ def __await__(self) -> Generator[None, None, R]:
30
+ return self.async_impl().__await__()
31
+
32
+ class OptionallyAsync(Generic[P, R], ABC):
33
+ @abstractmethod
34
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> CallableAwaitable[R]:
35
+ pass
36
+
37
+ def optionally_async(func: Callable[P, Generator[CallableAwaitable, None, R]]) -> OptionallyAsync[P, R]:
38
+ class Wrapped(OptionallyAsync[P, R]):
39
+ def __call__(self, *args: P.args, **kwargs: P.kwargs) -> CallableAwaitable[R]:
40
+ class Implementation(CallableAwaitable[R]):
41
+ def sync_impl(self) -> R:
42
+ i = func(*args, **kwargs)
43
+ try:
44
+ while True:
45
+ c = next(i)
46
+ c.result = +c
47
+ except StopIteration as excp:
48
+ return excp.value
49
+
50
+ async def async_impl(self) -> R:
51
+ i = func(*args, **kwargs)
52
+ try:
53
+ while True:
54
+ c = next(i)
55
+ c.result = await c
56
+ except StopIteration as excp:
57
+ return excp.value
58
+
59
+ return Implementation()
60
+ return Wrapped()
61
+
62
+ def make_async(func: Callable[P, Generator[CallableAwaitable, None, R]]) -> Callable[P, Awaitable[R]]:
63
+ @wraps(func)
64
+ async def async_impl(*args: P.args, **kwargs: P.kwargs) -> R:
65
+ i = func(*args, **kwargs)
66
+ try:
67
+ while True:
68
+ c = next(i)
69
+ c.result = await c
70
+ except StopIteration as excp:
71
+ return excp.value
72
+ return async_impl
73
+
74
+ def make_sync(func: Callable[P, Generator[CallableAwaitable, None, R]]) -> Callable[P, R]:
75
+ @wraps(func)
76
+ def sync_impl(*args: P.args, **kwargs: P.kwargs) -> R:
77
+ i = func(*args, **kwargs)
78
+ try:
79
+ while True:
80
+ c = next(i)
81
+ c.result = +c
82
+ except StopIteration as excp:
83
+ return excp.value
84
+ return sync_impl
85
+
86
+ class CASleep(CallableAwaitable[bool]):
87
+ amount: float
88
+
89
+ def __init__(self, amount: float) -> None:
90
+ self.amount = amount
91
+
92
+ def sync_impl(self):
93
+ time.sleep(self.amount)
94
+ return True
95
+
96
+ async def async_impl(self):
97
+ await asyncio.sleep(self.amount)
98
+ return True
99
+
100
+ def oa_sleep(amount: float):
101
+ return CASleep(amount)
102
+
103
+ class CARequest(CallableAwaitable["requests.AnyHTTPResponse"]):
104
+ requests_session: requests.OAHTTPSession
105
+ method: requests.HTTPMethod
106
+ url: str
107
+ cookies: Optional[dict[str, str]]
108
+ headers: Optional[dict[str, str]]
109
+ params: Optional[dict[str, str]]
110
+ data: Optional[Union[dict[str, str], str]]
111
+ json: Optional[dict[str, str]]
112
+
113
+ def __init__(
114
+ self,
115
+ requests_session: requests.OAHTTPSession,
116
+ method: requests.HTTPMethod,
117
+ url: str,
118
+ *,
119
+ cookies: Optional[dict[str, str]] = None,
120
+ headers: Optional[dict[str, str]] = None,
121
+ params: Optional[dict[str, str]] = None,
122
+ data: Optional[Union[dict[str, str], str]] = None,
123
+ json: Optional[Any] = None
124
+ ) -> None:
125
+ self.requests_session = requests_session
126
+ self.method = method
127
+ self.url = url
128
+ self.cookies = cookies
129
+ self.headers = headers
130
+ self.params = params
131
+ self.data = data
132
+ self.json = json
133
+
134
+ def sync_impl(self):
135
+ return self.requests_session.sync_request(
136
+ method = self.method,
137
+ url = self.url,
138
+ cookies = self.cookies,
139
+ headers = self.headers,
140
+ params = self.params,
141
+ data = self.data,
142
+ json = self.json
143
+ )
144
+
145
+ async def async_impl(self):
146
+ return await self.requests_session.async_request(
147
+ method = self.method,
148
+ url = self.url,
149
+ cookies = self.cookies,
150
+ headers = self.headers,
151
+ params = self.params,
152
+ data = self.data,
153
+ json = self.json
154
+ )