Skip to content

Commit 2942a1d

Browse files
committed
2 parents 0d1bcc5 + 8bda7ba commit 2942a1d

File tree

11 files changed

+169
-18
lines changed

11 files changed

+169
-18
lines changed

.github/workflows/python-app.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
# This workflow will upload a Python Package using Twine when a release is created
5+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
6+
7+
# This workflow uses actions that are not certified by GitHub.
8+
# They are provided by a third-party and are governed by
9+
# separate terms of service, privacy policy, and support
10+
# documentation.
11+
12+
13+
name: Python application
14+
15+
16+
on:
17+
release:
18+
types: [published]
19+
workflow_dispatch:
20+
21+
permissions:
22+
contents: read
23+
24+
jobs:
25+
build:
26+
27+
runs-on: ubuntu-latest
28+
environment: release
29+
permissions:
30+
id-token: write
31+
steps:
32+
- uses: actions/checkout@v4
33+
- name: Set up Python 3.12
34+
uses: actions/setup-python@v3
35+
with:
36+
python-version: "3.12"
37+
- name: Install dependencies
38+
run: |
39+
python -m pip install --upgrade pip
40+
pip install flake8 pytest
41+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
42+
- name: Lint with flake8
43+
run: |
44+
# stop the build if there are Python syntax errors or undefined names
45+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
46+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
47+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
48+
- name: Test with pytest
49+
run: |
50+
pytest tests/
51+
- run: pip install -U wheel build
52+
- name: Build a binary wheel and a source tarball
53+
run: python -m build
54+
- name: Publish package distributions to PyPI
55+
uses: pypa/gh-action-pypi-publish@release/v1

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
websocket-client
2+
requests
3+
bs4
4+
SimpleWebSocketServer

scratchattach/cloud/_base.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ def events(self) -> CloudEvents:
9797
return CloudEvents(self)
9898

9999
def requests(self, *, no_packet_loss: bool = False, used_cloud_vars: list[str] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
100-
respond_order="receive") -> CloudRequests:
100+
respond_order="receive", debug: bool = False) -> CloudRequests:
101101
return CloudRequests(self, used_cloud_vars=used_cloud_vars, no_packet_loss=no_packet_loss,
102-
respond_order=respond_order)
102+
respond_order=respond_order, debug=debug)
103103

104104
def storage(self, *, no_packet_loss: bool = False, used_cloud_vars: list[str] = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]) -> CloudStorage:
105105
return CloudStorage(self, used_cloud_vars=used_cloud_vars, no_packet_loss=no_packet_loss)
@@ -365,6 +365,7 @@ def set_var(self, variable, value):
365365
self._assert_valid_value(value)
366366
if not isinstance(variable, str):
367367
raise ValueError("cloud var name must be a string")
368+
variable = variable.removeprefix("☁ ")
368369
if not self.active_connection:
369370
self.connect()
370371
self._enforce_ratelimit(n=1)
@@ -401,6 +402,7 @@ def set_vars(self, var_value_dict, *, intelligent_waits=True):
401402
packet_list = []
402403
for variable in var_value_dict:
403404
value = var_value_dict[variable]
405+
variable = variable.removeprefix("☁ ")
404406
self._assert_valid_value(value)
405407
if not isinstance(variable, str):
406408
raise ValueError("cloud var name must be a string")
@@ -416,6 +418,7 @@ def set_vars(self, var_value_dict, *, intelligent_waits=True):
416418
self.last_var_set = time.time()
417419

418420
def get_var(self, var, *, recorder_initial_values={}):
421+
var = "☁ "+var.removeprefix("☁ ")
419422
if self.recorder is None:
420423
self.recorder = cloud_recorder.CloudRecorder(self, initial_values=recorder_initial_values)
421424
self.recorder.start()

scratchattach/cloud/cloud.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,35 +49,35 @@ def logs(self, *, filter_by_var_named=None, limit=100, offset=0) -> list[cloud_a
4949
try:
5050
data = requests.get(f"https://clouddata.scratch.mit.edu/logs?projectid={self.project_id}&limit={limit}&offset={offset}", timeout=10).json()
5151
if filter_by_var_named is not None:
52+
filter_by_var_named = filter_by_var_named.removeprefix("☁ ")
5253
data = list(filter(lambda k: k["name"] == "☁ "+filter_by_var_named, data))
5354
for x in data:
5455
x["cloud"] = self
55-
x["name"] = x["name"][2:]
5656
return commons.parse_object_list(data, cloud_activity.CloudActivity, self._session, "name")
5757
except Exception as e:
5858
raise exceptions.FetchError(str(e))
5959

6060
def get_var(self, var, *, use_logs=False):
61+
var = var.removeprefix("☁ ")
6162
if self._session is None or use_logs:
62-
logs = self.logs(limit=100)
63-
filtered = list(filter(lambda k: k.name == "☁ "+var, logs))
63+
filtered = self.logs(limit=100, filter_by_var_named="☁ "+var)
6464
if len(filtered) == 0:
6565
return None
6666
return filtered[0].value
6767
else:
6868
if self.recorder is None:
6969
initial_values = self.get_all_vars(use_logs=True)
70-
return super().get_var(var, recorder_initial_values=initial_values)
70+
return super().get_var("☁ "+var, recorder_initial_values=initial_values)
7171
else:
72-
return super().get_var(var)
72+
return super().get_var("☁ "+var)
7373

7474
def get_all_vars(self, *, use_logs=False):
7575
if self._session is None or use_logs:
7676
logs = self.logs(limit=100)
7777
logs.reverse()
7878
clouddata = {}
7979
for activity in logs:
80-
clouddata[activity.name.replace("☁ ", "")] = activity.value
80+
clouddata[activity.name] = activity.value
8181
return clouddata
8282
else:
8383
if self.recorder is None:

scratchattach/eventhandlers/cloud_requests.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ class Request:
1616
Saves a request added to the request handler
1717
"""
1818

19-
def __init__(self, request_name, *, on_call, cloud_requests, thread=True, enabled=True, response_priority=0):
19+
def __init__(self, request_name, *, on_call, cloud_requests, thread=True, enabled=True, response_priority=0, debug=False):
2020
self.name = request_name
2121
self.on_call = on_call
2222
self.thread = thread
2323
self.enabled = enabled
2424
self.response_priority = response_priority
2525
self.cloud_requests = cloud_requests # the corresponding CloudRequests object
26+
self.debug = debug
2627

2728
def __call__(self, received_request):
2829
if not self.enabled:
@@ -44,7 +45,13 @@ def __call__(self, received_request):
4445
else:
4546
print(f"Exception in request '{self.name}':")
4647
raise(e)
47-
self.cloud_requests.request_outputs.append({"receive":received_request.timestamp, "request_id":received_request.request_id, "output":[f"Error in request {self.name}","Check the Python console"], "priority":self.response_priority})
48+
if self.debug:
49+
traceback_full = traceback.format_exc().splitlines()
50+
output = [f"Error in request {self.name}", "Traceback: "]
51+
output.extend(traceback_full)
52+
self.cloud_requests.request_outputs.append({"receive":received_request.timestamp, "request_id":received_request.request_id, "output":output, "priority":self.response_priority})
53+
else:
54+
self.cloud_requests.request_outputs.append({"receive":received_request.timestamp, "request_id":received_request.request_id, "output":[f"Error in request {self.name}","Check the Python console"], "priority":self.response_priority})
4855
self.cloud_requests.responder_event.set() # Activate the .cloud_requests._responder process so it sends back the data to Scratch
4956

5057
class ReceivedRequest:

scratchattach/site/project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def create_remix(self, *, title=None, project_json=None): # not working
145145
title = " remix"
146146
if project_json is None:
147147
if "title" in self.__dict__:
148-
project_json = self.get_raw_json()
148+
project_json = self.raw_json()
149149
else:
150150
project_json = empty_project_json
151151

scratchattach/site/session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ def login(username, password, *, timeout=10) -> Session:
10921092
session_id = str(re.search('"(.*)"', request.headers["Set-Cookie"]).group())
10931093
except (AttributeError, Exception):
10941094
raise exceptions.LoginFailure(
1095-
"Either the provided authentication data is wrong or your network is banned from Scratch.\n\nIf you're using an online IDE (like replit.com) Scratch possibly banned its IP adress. In this case, try logging in with your session id: https://github.com/TimMcCool/scratchattach/wiki#logging-in")
1095+
"Either the provided authentication data is wrong or your network is banned from Scratch.\n\nIf you're using an online IDE (like replit.com) Scratch possibly banned its IP address. In this case, try logging in with your session id: https://github.com/TimMcCool/scratchattach/wiki#logging-in")
10961096

10971097
# Create session object:
10981098
with suppress_login_warning():
@@ -1141,4 +1141,4 @@ def login_from_browser(browser: Browser = ANY):
11411141
cookies = cookies_from_browser(browser)
11421142
if "scratchsessionsid" in cookies:
11431143
return login_by_id(cookies["scratchsessionsid"])
1144-
raise ValueError("Not enough data to log in.")
1144+
raise ValueError("Not enough data to log in.")

scratchattach/site/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ def report_comment(self, *, comment_id):
635635
Args:
636636
comment_id: The id of the comment that should be reported
637637
"""
638-
self._assert_permission()
638+
self._assert_auth()
639639
return requests.post(
640640
f"https://scratch.mit.edu/site-api/comments/user/{self.username}/rep/",
641641
headers = headers,

tests/test_import.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import sys
2+
def test_import():
3+
sys.path.insert(0, ".")
4+
import scratchattach

website/source/css/style.css

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
@media (max-width: 768px) {
1+
2+
3+
@media only screen and (max-width: 800px) {
24
.nav-buttons {
35
display: none; /* Hides the navigation buttons */
46
}
7+
#menu-button {
8+
display: block!important;
9+
}
10+
.nav-buttons {
11+
display: none!important; /* Hides the navigation buttons */
12+
}
513

614
/* You can also hide the logo if needed */
715
.logo {
8-
display: none; /* Hides the logo */
16+
display: block; /* Hides the logo */
17+
}
18+
19+
.logo img {
20+
width: 100px!important;
921
}
1022

1123
/* Stack columns in section 1 */
@@ -19,19 +31,31 @@
1931
width: 80%; /* Make image responsive */
2032
margin-bottom: 20px; /* Add some space below the image */
2133
}
34+
35+
.expand-box {
36+
width: 90%!important;
37+
}
2238
}
2339

2440
/* General Reset */
2541
* {
2642
margin: 0;
2743
padding: 0;
2844
box-sizing: border-box;
45+
/* overflow-x: hidden; */
46+
}
47+
48+
#menu-button {
49+
background-color: rgba(107, 0, 0, 0);
50+
color: white;
51+
border: none;
2952
}
3053

3154
body {
3255
font-family: 'IBM Plex Sans', sans-serif;
3356
background-color: #121212;
3457
color: #ffffff;
58+
overflow-x: hidden;
3559
}
3660

3761
/* Sticky Header */
@@ -44,8 +68,9 @@ header {
4468
border-bottom: 1px solid #333;
4569
position: sticky;
4670
top: 0;
47-
z-index: 1000;
71+
z-index: 10000000000000000000000000000000000000;
4872
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); /* Added shadow for the header */
73+
height: 65px;
4974
}
5075

5176
.logo-section {
@@ -467,4 +492,20 @@ a {
467492
font-size: 14px; /* Font size for author */
468493
color: #aaa; /* Slightly lighter color for author */
469494
margin: 0;
470-
}
495+
}
496+
497+
.menu {
498+
top: 65px;
499+
position: absolute;
500+
width: 100vw;
501+
height: 100vh;
502+
background-color: rgba(0, 0, 0, 0.767);
503+
backdrop-filter: blur(10px);
504+
margin: 0;
505+
left: 0;
506+
display: flex;
507+
flex-direction: column;
508+
align-items: center;
509+
justify-content: center;
510+
gap: 10%;
511+
}

0 commit comments

Comments
 (0)