scratchattach 2.1.8__py3-none-any.whl → 2.1.10a0__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.8.dist-info → scratchattach-2.1.10a0.dist-info}/LICENSE +21 -21
  55. {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/METADATA +154 -146
  56. scratchattach-2.1.10a0.dist-info/RECORD +62 -0
  57. {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/WHEEL +1 -1
  58. scratchattach-2.1.8.dist-info/RECORD +0 -40
  59. {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/top_level.txt +0 -0
@@ -1,206 +1,233 @@
1
- # Authentication / Authorization:
2
-
3
- class Unauthenticated(Exception):
4
- """
5
- Raised when a method that requires a login / session is called on an object that wasn't created with a session.
6
-
7
- 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:
8
-
9
- - :meth:`scratchattach.Session.connect_project`
10
-
11
- - :meth:`scratchattach.Session.connect_user`
12
-
13
- - :meth:`scratchattach.Session.connect_studio`
14
-
15
- This also applies to cloud variables, forum topics, and forum posts.
16
- """
17
-
18
- def __init__(self, message=""):
19
- 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"
20
- super().__init__(self.message)
21
-
22
-
23
- class Unauthorized(Exception):
24
- """
25
- Raised when an action is performed that the user associated with the session that the object was created with is not allowed to do.
26
-
27
- Example: Changing the "about me" of other users will raise this error.
28
- """
29
-
30
- def __init__(self, message=""):
31
- self.message = "The user corresponding to the connected login / session is not allowed to perform this action."
32
- super().__init__(self.message)
33
-
34
-
35
- class XTokenError(Exception):
36
- """
37
- Raised when an action can't be performed because there is no XToken available.
38
-
39
- 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.
40
- """
41
-
42
- pass
43
-
44
-
45
- # Not found errors:
46
-
47
- class UserNotFound(Exception):
48
- """
49
- Raised when a non-existent user is requested.
50
- """
51
-
52
- pass
53
-
54
-
55
- class ProjectNotFound(Exception):
56
- """
57
- Raised when a non-existent project is requested.
58
- """
59
-
60
- pass
61
-
62
-
63
- class ClassroomNotFound(Exception):
64
- """
65
- Raised when a non-existent Classroom is requested.
66
- """
67
-
68
- pass
69
-
70
-
71
- class StudioNotFound(Exception):
72
- """
73
- Raised when a non-existent studio is requested.
74
- """
75
-
76
- pass
77
-
78
-
79
- class ForumContentNotFound(Exception):
80
- """
81
- Raised when a non-existent forum topic / post is requested.
82
- """
83
- pass
84
-
85
-
86
- class CommentNotFound(Exception):
87
- pass
88
-
89
-
90
- # Invalid inputs
91
- class InvalidLanguage(Exception):
92
- """
93
- Raised when an invalid language/language code/language object is provided, for TTS or Translate
94
- """
95
- pass
96
-
97
-
98
- class InvalidTTSGender(Exception):
99
- """
100
- Raised when an invalid TTS gender is provided.
101
- """
102
- pass
103
-
104
- # API errors:
105
-
106
- class LoginFailure(Exception):
107
- """
108
- Raised when the Scratch server doesn't respond with a session id.
109
-
110
- 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.
111
- """
112
-
113
- pass
114
-
115
-
116
- class FetchError(Exception):
117
- """
118
- Raised when getting information from the Scratch API fails. This can have various reasons. Make sure all provided arguments are valid.
119
- """
120
-
121
- pass
122
-
123
-
124
- class BadRequest(Exception):
125
- """
126
- Raised when the Scratch API responds with a "Bad Request" error message. This can have various reasons. Make sure all provided arguments are valid.
127
- """
128
-
129
- pass
130
-
131
-
132
- class Response429(Exception):
133
- """
134
- 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.
135
- """
136
-
137
- pass
138
-
139
-
140
- class CommentPostFailure(Exception):
141
- """
142
- Raised when a comment fails to post. This can have various reasons.
143
- """
144
-
145
- pass
146
-
147
-
148
- class APIError(Exception):
149
- """
150
- For API errors that can't be classified into one of the above errors
151
- """
152
- pass
153
-
154
-
155
- class ScrapeError(Exception):
156
- """
157
- Raised when something goes wrong while web-scraping a page with bs4.
158
- """
159
-
160
- pass
161
-
162
-
163
- # Cloud / encoding errors:
164
-
165
- class CloudConnectionError(Exception):
166
- """
167
- Raised when connecting to Scratch's cloud server fails. This can have various reasons.
168
- """
169
-
170
- pass
171
-
172
-
173
- class InvalidCloudValue(Exception):
174
- """
175
- Raised when a cloud variable is set to an invalid value.
176
- """
177
-
178
- pass
179
-
180
-
181
- class InvalidDecodeInput(Exception):
182
- """
183
- Raised when the built-in decoder :meth:`scratchattach.encoder.Encoding.decode` receives an invalid input.
184
- """
185
-
186
- pass
187
-
188
-
189
- # Cloud Requests errors:
190
-
191
- class RequestNotFound(Exception):
192
- """
193
- Cloud Requests: Raised when a non-existent cloud request is edited using :meth:`scratchattach.cloud_requests.CloudRequests.edit_request`.
194
- """
195
-
196
- pass
197
-
198
-
199
- # Websocket server errors:
200
-
201
- class WebsocketServerError(Exception):
202
- """
203
- Raised when the self-hosted cloud websocket server fails to start.
204
- """
205
-
206
- pass
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
+ """
@@ -1,59 +1,67 @@
1
- import requests
2
- from . import exceptions
3
-
4
- proxies = None
5
-
6
- class Requests:
7
- """
8
- Centralized HTTP request handler (for better error handling and proxies)
9
- """
10
-
11
- @staticmethod
12
- def check_response(r : requests.Response):
13
- if r.status_code == 403 or r.status_code == 401:
14
- raise exceptions.Unauthorized
15
- if r.status_code == 500:
16
- raise exceptions.APIError("Internal Scratch server error")
17
- if r.status_code == 429:
18
- raise exceptions.Response429("You are being rate-limited (or blocked) by Scratch")
19
- if r.text == '{"code":"BadRequest","message":""}':
20
- raise exceptions.BadRequest("Make sure all provided arguments are valid")
21
- if r.text == '{"code":"BadRequest","message":""}':
22
- raise exceptions.BadRequest("Make sure all provided arguments are valid")
23
-
24
- @staticmethod
25
- def get(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
26
- try:
27
- r = requests.get(url, data=data, json=json, headers=headers, cookies=cookies, params=params, timeout=timeout, proxies=proxies)
28
- except Exception as e:
29
- raise exceptions.FetchError(e)
30
- Requests.check_response(r)
31
- return r
32
-
33
- @staticmethod
34
- def post(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None, errorhandling=True):
35
- try:
36
- r = requests.post(url, data=data, json=json, headers=headers, cookies=cookies, params=params, timeout=timeout, proxies=proxies)
37
- except Exception as e:
38
- raise exceptions.FetchError(e)
39
- if errorhandling:
40
- Requests.check_response(r)
41
- return r
42
-
43
- @staticmethod
44
- def delete(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
45
- try:
46
- r = requests.delete(url, data=data, json=json, headers=headers, cookies=cookies, params=params, timeout=timeout, proxies=proxies)
47
- except Exception as e:
48
- raise exceptions.FetchError(e)
49
- Requests.check_response(r)
50
- return r
51
-
52
- @staticmethod
53
- def put(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
54
- try:
55
- r = requests.put(url, data=data, json=json, headers=headers, cookies=cookies, params=params, timeout=timeout, proxies=proxies)
56
- except Exception as e:
57
- raise exceptions.FetchError(e)
58
- Requests.check_response(r)
59
- return r
1
+ from __future__ import annotations
2
+
3
+ import requests
4
+ from . import exceptions
5
+
6
+ proxies = None
7
+
8
+
9
+ class Requests:
10
+ """
11
+ Centralized HTTP request handler (for better error handling and proxies)
12
+ """
13
+
14
+ @staticmethod
15
+ def check_response(r: requests.Response):
16
+ if r.status_code == 403 or r.status_code == 401:
17
+ raise exceptions.Unauthorized(f"Request content: {r.content}")
18
+ if r.status_code == 500:
19
+ raise exceptions.APIError("Internal Scratch server error")
20
+ if r.status_code == 429:
21
+ raise exceptions.Response429("You are being rate-limited (or blocked) by Scratch")
22
+ if r.text == '{"code":"BadRequest","message":""}':
23
+ raise exceptions.BadRequest("Make sure all provided arguments are valid")
24
+ if r.text == '{"code":"BadRequest","message":""}':
25
+ raise exceptions.BadRequest("Make sure all provided arguments are valid")
26
+
27
+ @staticmethod
28
+ def get(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
29
+ try:
30
+ r = requests.get(url, data=data, json=json, headers=headers, cookies=cookies, params=params,
31
+ timeout=timeout, proxies=proxies)
32
+ except Exception as e:
33
+ raise exceptions.FetchError(e)
34
+ Requests.check_response(r)
35
+ return r
36
+
37
+ @staticmethod
38
+ def post(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None, files=None, errorhandling=True, ):
39
+ try:
40
+ r = requests.post(url, data=data, json=json, headers=headers, cookies=cookies, params=params,
41
+ timeout=timeout, proxies=proxies, files=files)
42
+ except Exception as e:
43
+ raise exceptions.FetchError(e)
44
+ if errorhandling:
45
+ Requests.check_response(r)
46
+ return r
47
+
48
+ @staticmethod
49
+ def delete(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
50
+ try:
51
+ r = requests.delete(url, data=data, json=json, headers=headers, cookies=cookies, params=params,
52
+ timeout=timeout, proxies=proxies)
53
+ except Exception as e:
54
+ raise exceptions.FetchError(e)
55
+ Requests.check_response(r)
56
+ return r
57
+
58
+ @staticmethod
59
+ def put(url, *, data=None, json=None, headers=None, cookies=None, timeout=None, params=None):
60
+ try:
61
+ r = requests.put(url, data=data, json=json, headers=headers, cookies=cookies, params=params,
62
+ timeout=timeout, proxies=proxies)
63
+ except Exception as e:
64
+ raise exceptions.FetchError(e)
65
+ Requests.check_response(r)
66
+ return r
67
+
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 TimMcCool (github user)
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2022 TimMcCool (github user)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.