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.
- scratchattach/__init__.py +28 -25
- scratchattach/cloud/__init__.py +2 -0
- scratchattach/cloud/_base.py +454 -282
- scratchattach/cloud/cloud.py +171 -168
- scratchattach/editor/__init__.py +21 -0
- scratchattach/editor/asset.py +199 -0
- scratchattach/editor/backpack_json.py +117 -0
- scratchattach/editor/base.py +142 -0
- scratchattach/editor/block.py +507 -0
- scratchattach/editor/blockshape.py +353 -0
- scratchattach/editor/build_defaulting.py +47 -0
- scratchattach/editor/comment.py +74 -0
- scratchattach/editor/commons.py +243 -0
- scratchattach/editor/extension.py +43 -0
- scratchattach/editor/field.py +90 -0
- scratchattach/editor/inputs.py +132 -0
- scratchattach/editor/meta.py +106 -0
- scratchattach/editor/monitor.py +175 -0
- scratchattach/editor/mutation.py +317 -0
- scratchattach/editor/pallete.py +91 -0
- scratchattach/editor/prim.py +170 -0
- scratchattach/editor/project.py +273 -0
- scratchattach/editor/sbuild.py +2837 -0
- scratchattach/editor/sprite.py +586 -0
- scratchattach/editor/twconfig.py +113 -0
- scratchattach/editor/vlb.py +134 -0
- scratchattach/eventhandlers/_base.py +99 -92
- scratchattach/eventhandlers/cloud_events.py +110 -103
- scratchattach/eventhandlers/cloud_recorder.py +26 -21
- scratchattach/eventhandlers/cloud_requests.py +460 -452
- scratchattach/eventhandlers/cloud_server.py +246 -244
- scratchattach/eventhandlers/cloud_storage.py +135 -134
- scratchattach/eventhandlers/combine.py +29 -27
- scratchattach/eventhandlers/filterbot.py +160 -159
- scratchattach/eventhandlers/message_events.py +41 -40
- scratchattach/other/other_apis.py +284 -212
- scratchattach/other/project_json_capabilities.py +475 -546
- scratchattach/site/_base.py +64 -46
- scratchattach/site/activity.py +414 -122
- scratchattach/site/backpack_asset.py +118 -84
- scratchattach/site/classroom.py +430 -142
- scratchattach/site/cloud_activity.py +107 -103
- scratchattach/site/comment.py +220 -190
- scratchattach/site/forum.py +400 -399
- scratchattach/site/project.py +806 -787
- scratchattach/site/session.py +1134 -867
- scratchattach/site/studio.py +611 -609
- scratchattach/site/user.py +835 -837
- scratchattach/utils/commons.py +243 -148
- scratchattach/utils/encoder.py +157 -156
- scratchattach/utils/enums.py +197 -190
- scratchattach/utils/exceptions.py +233 -206
- scratchattach/utils/requests.py +67 -59
- {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/LICENSE +21 -21
- {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/METADATA +154 -146
- scratchattach-2.1.10a0.dist-info/RECORD +62 -0
- {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/WHEEL +1 -1
- scratchattach-2.1.8.dist-info/RECORD +0 -40
- {scratchattach-2.1.8.dist-info → scratchattach-2.1.10a0.dist-info}/top_level.txt +0 -0
|
@@ -1,135 +1,136 @@
|
|
|
1
|
-
"""CloudStorage class"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.
|
|
17
|
-
self.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
self.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
self.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
self.
|
|
89
|
-
self.request(self.
|
|
90
|
-
self.request(self.
|
|
91
|
-
self.request(self.
|
|
92
|
-
self.request(self.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
"""CloudStorage class"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .cloud_requests import CloudRequests
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from threading import Thread
|
|
8
|
+
|
|
9
|
+
class Database:
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
A Database is a simple key-value storage that stores data in a JSON file saved locally (other database services like MongoDB can be implemented)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, name, *, json_file_path, save_interval=30):
|
|
16
|
+
self.save_event_function = None
|
|
17
|
+
self.set_event_function = None
|
|
18
|
+
self.name = name
|
|
19
|
+
|
|
20
|
+
# Import from JSON file
|
|
21
|
+
if not json_file_path.endswith(".json"):
|
|
22
|
+
json_file_path = json_file_path+".json"
|
|
23
|
+
self.json_file_path = json_file_path
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
with open(json_file_path, 'r') as json_file:
|
|
27
|
+
self.data = json.load(json_file)
|
|
28
|
+
except FileNotFoundError:
|
|
29
|
+
print(f"Creating file {json_file_path}. Your database {name} will be stored there.")
|
|
30
|
+
self.data = {}
|
|
31
|
+
self.save_to_json()
|
|
32
|
+
|
|
33
|
+
if isinstance(self.data , list):
|
|
34
|
+
raise ValueError(
|
|
35
|
+
"Invalid JSON file content: Top-level object must be a dict, not a list"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Start autosaving
|
|
39
|
+
self.save_interval = save_interval
|
|
40
|
+
if self.save_interval is not None:
|
|
41
|
+
Thread(target=self._autosaver).start()
|
|
42
|
+
|
|
43
|
+
def save_to_json(self):
|
|
44
|
+
with open(self.json_file_path, 'w') as json_file:
|
|
45
|
+
json.dump(self.data, json_file, indent=4)
|
|
46
|
+
|
|
47
|
+
if self.save_event_function is not None:
|
|
48
|
+
self.save_event_function()
|
|
49
|
+
|
|
50
|
+
def keys(self) -> list:
|
|
51
|
+
return list(self.data.keys())
|
|
52
|
+
|
|
53
|
+
def get(self, key) -> str:
|
|
54
|
+
if not key in self.data:
|
|
55
|
+
return None
|
|
56
|
+
return self.data[key]
|
|
57
|
+
|
|
58
|
+
def set(self, key, value):
|
|
59
|
+
self.data[key] = value
|
|
60
|
+
|
|
61
|
+
if self.set_event_function is not None:
|
|
62
|
+
self.set_event_function(key, value)
|
|
63
|
+
|
|
64
|
+
def event(self, event_function):
|
|
65
|
+
# Decorator function for adding the on_save event that is called when a save is performed
|
|
66
|
+
if event_function.__name__ == "on_save":
|
|
67
|
+
self.save_event_function = event_function
|
|
68
|
+
if event_function.__name__ == "on_set":
|
|
69
|
+
self.set_event_function = event_function
|
|
70
|
+
|
|
71
|
+
def _autosaver(self):
|
|
72
|
+
# Task autosaving the db. save interval specified in .save_interval attribute
|
|
73
|
+
while True:
|
|
74
|
+
time.sleep(self.save_interval)
|
|
75
|
+
self.save_to_json()
|
|
76
|
+
|
|
77
|
+
class CloudStorage(CloudRequests):
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
A CloudStorage object saves multiple databases and allows the connected Scratch project to access and modify the data of these databases through cloud requests
|
|
81
|
+
|
|
82
|
+
The CloudStorage class is built upon CloudRequests
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, cloud, used_cloud_vars=["1", "2", "3", "4", "5", "6", "7", "8", "9"], no_packet_loss=False):
|
|
86
|
+
super().__init__(cloud, used_cloud_vars=used_cloud_vars, no_packet_loss=no_packet_loss)
|
|
87
|
+
# Setup
|
|
88
|
+
self._databases = {}
|
|
89
|
+
self.request(self.get, thread=False)
|
|
90
|
+
self.request(self.set, thread=False)
|
|
91
|
+
self.request(self.keys, thread=False)
|
|
92
|
+
self.request(self.database_names, thread=False)
|
|
93
|
+
self.request(self.ping, thread=False)
|
|
94
|
+
|
|
95
|
+
def ping(self):
|
|
96
|
+
return "Database backend is running"
|
|
97
|
+
|
|
98
|
+
def get(self, db_name, key) -> str:
|
|
99
|
+
try:
|
|
100
|
+
return self.get_database(db_name).get(key)
|
|
101
|
+
except Exception:
|
|
102
|
+
if self.get_database(db_name) is None:
|
|
103
|
+
return f"Error: Database {db_name} doesn't exist"
|
|
104
|
+
else:
|
|
105
|
+
return f"Error: Key {key} doesn't exist in database {db_name}"
|
|
106
|
+
|
|
107
|
+
def set(self, db_name, key, value):
|
|
108
|
+
print(db_name, key, value, self._databases)
|
|
109
|
+
return self.get_database(db_name).set(key, value)
|
|
110
|
+
|
|
111
|
+
def keys(self, db_name) -> list:
|
|
112
|
+
try:
|
|
113
|
+
return self.get_database(db_name).keys()
|
|
114
|
+
except Exception:
|
|
115
|
+
return f"Error: Database {db_name} doesn't exist"
|
|
116
|
+
|
|
117
|
+
def databases(self) -> list:
|
|
118
|
+
return list(self._databases.values())
|
|
119
|
+
|
|
120
|
+
def database_names(self) -> list:
|
|
121
|
+
return list(self._databases.keys())
|
|
122
|
+
|
|
123
|
+
def add_database(self, database:Database):
|
|
124
|
+
self._databases[database.name] = database
|
|
125
|
+
|
|
126
|
+
def get_database(self, name) -> Database:
|
|
127
|
+
if name in self._databases:
|
|
128
|
+
return self._databases[name]
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
def save(self):
|
|
132
|
+
"""
|
|
133
|
+
Saves the data in the JSON files for all databases in self._databases
|
|
134
|
+
"""
|
|
135
|
+
for dbname in self._databases:
|
|
135
136
|
self._databases[dbname].save_to_json()
|
|
@@ -1,28 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
class MultiEventHandler:
|
|
4
|
+
|
|
5
|
+
def __init__(self, *handlers):
|
|
6
|
+
self.handlers = handlers
|
|
7
|
+
|
|
8
|
+
def request(self, function, *args, **kwargs):
|
|
9
|
+
for handler in self.handlers:
|
|
10
|
+
handler.request(function, *args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def event(self, function, *args, **kwargs):
|
|
13
|
+
for handler in self.handlers:
|
|
14
|
+
handler.request(function, *args, **kwargs)
|
|
15
|
+
|
|
16
|
+
def start(self, *args, **kwargs):
|
|
17
|
+
for handler in self.handlers:
|
|
18
|
+
handler.start(*args, **kwargs)
|
|
19
|
+
|
|
20
|
+
def stop(self, *args, **kwargs):
|
|
21
|
+
for handler in self.handlers:
|
|
22
|
+
handler.stop(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
def pause(self, *args, **kwargs):
|
|
25
|
+
for handler in self.handlers:
|
|
26
|
+
handler.pause(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
def resume(self, *args, **kwargs):
|
|
29
|
+
for handler in self.handlers:
|
|
28
30
|
handler.resume(*args, **kwargs)
|