scratchattach 2.1.15b0__tar.gz → 3.0.0b0__tar.gz

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 (93) hide show
  1. {scratchattach-2.1.15b0/scratchattach.egg-info → scratchattach-3.0.0b0}/PKG-INFO +7 -11
  2. scratchattach-3.0.0b0/pyproject.toml +95 -0
  3. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/scratchattach/__init__.py +14 -6
  4. scratchattach-3.0.0b0/scratchattach/__main__.py +93 -0
  5. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0/scratchattach.egg-info}/PKG-INFO +7 -11
  6. scratchattach-3.0.0b0/scratchattach.egg-info/SOURCES.txt +25 -0
  7. scratchattach-3.0.0b0/scratchattach.egg-info/entry_points.txt +2 -0
  8. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/scratchattach.egg-info/requires.txt +5 -0
  9. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_activity.py +7 -8
  10. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_auth.py +2 -2
  11. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_comment.py +25 -19
  12. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_editor_integration.py +8 -8
  13. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_editor_obfuscation.py +2 -5
  14. scratchattach-3.0.0b0/tests/test_editor_project.py +6 -0
  15. scratchattach-3.0.0b0/tests/test_import.py +5 -0
  16. scratchattach-3.0.0b0/tests/test_memberships.py +36 -0
  17. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_other_apis.py +5 -8
  18. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_project.py +15 -15
  19. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_search.py +7 -5
  20. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_studio.py +17 -12
  21. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_teacher_activity.py +3 -6
  22. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/tests/test_user.py +34 -20
  23. scratchattach-2.1.15b0/MANIFEST.in +0 -1
  24. scratchattach-2.1.15b0/requirements.txt +0 -6
  25. scratchattach-2.1.15b0/scratchattach/cloud/__init__.py +0 -2
  26. scratchattach-2.1.15b0/scratchattach/cloud/_base.py +0 -458
  27. scratchattach-2.1.15b0/scratchattach/cloud/cloud.py +0 -183
  28. scratchattach-2.1.15b0/scratchattach/editor/__init__.py +0 -21
  29. scratchattach-2.1.15b0/scratchattach/editor/asset.py +0 -253
  30. scratchattach-2.1.15b0/scratchattach/editor/backpack_json.py +0 -117
  31. scratchattach-2.1.15b0/scratchattach/editor/base.py +0 -193
  32. scratchattach-2.1.15b0/scratchattach/editor/block.py +0 -579
  33. scratchattach-2.1.15b0/scratchattach/editor/blockshape.py +0 -357
  34. scratchattach-2.1.15b0/scratchattach/editor/build_defaulting.py +0 -51
  35. scratchattach-2.1.15b0/scratchattach/editor/code_translation/__init__.py +0 -0
  36. scratchattach-2.1.15b0/scratchattach/editor/code_translation/parse.py +0 -177
  37. scratchattach-2.1.15b0/scratchattach/editor/comment.py +0 -80
  38. scratchattach-2.1.15b0/scratchattach/editor/commons.py +0 -273
  39. scratchattach-2.1.15b0/scratchattach/editor/extension.py +0 -50
  40. scratchattach-2.1.15b0/scratchattach/editor/field.py +0 -99
  41. scratchattach-2.1.15b0/scratchattach/editor/inputs.py +0 -135
  42. scratchattach-2.1.15b0/scratchattach/editor/meta.py +0 -114
  43. scratchattach-2.1.15b0/scratchattach/editor/monitor.py +0 -183
  44. scratchattach-2.1.15b0/scratchattach/editor/mutation.py +0 -324
  45. scratchattach-2.1.15b0/scratchattach/editor/pallete.py +0 -90
  46. scratchattach-2.1.15b0/scratchattach/editor/prim.py +0 -170
  47. scratchattach-2.1.15b0/scratchattach/editor/project.py +0 -279
  48. scratchattach-2.1.15b0/scratchattach/editor/sprite.py +0 -599
  49. scratchattach-2.1.15b0/scratchattach/editor/twconfig.py +0 -114
  50. scratchattach-2.1.15b0/scratchattach/editor/vlb.py +0 -134
  51. scratchattach-2.1.15b0/scratchattach/eventhandlers/__init__.py +0 -0
  52. scratchattach-2.1.15b0/scratchattach/eventhandlers/_base.py +0 -100
  53. scratchattach-2.1.15b0/scratchattach/eventhandlers/cloud_events.py +0 -110
  54. scratchattach-2.1.15b0/scratchattach/eventhandlers/cloud_recorder.py +0 -26
  55. scratchattach-2.1.15b0/scratchattach/eventhandlers/cloud_requests.py +0 -459
  56. scratchattach-2.1.15b0/scratchattach/eventhandlers/cloud_server.py +0 -246
  57. scratchattach-2.1.15b0/scratchattach/eventhandlers/cloud_storage.py +0 -136
  58. scratchattach-2.1.15b0/scratchattach/eventhandlers/combine.py +0 -30
  59. scratchattach-2.1.15b0/scratchattach/eventhandlers/filterbot.py +0 -161
  60. scratchattach-2.1.15b0/scratchattach/eventhandlers/message_events.py +0 -42
  61. scratchattach-2.1.15b0/scratchattach/other/__init__.py +0 -0
  62. scratchattach-2.1.15b0/scratchattach/other/other_apis.py +0 -284
  63. scratchattach-2.1.15b0/scratchattach/other/project_json_capabilities.py +0 -475
  64. scratchattach-2.1.15b0/scratchattach/site/__init__.py +0 -0
  65. scratchattach-2.1.15b0/scratchattach/site/_base.py +0 -66
  66. scratchattach-2.1.15b0/scratchattach/site/activity.py +0 -382
  67. scratchattach-2.1.15b0/scratchattach/site/alert.py +0 -227
  68. scratchattach-2.1.15b0/scratchattach/site/backpack_asset.py +0 -118
  69. scratchattach-2.1.15b0/scratchattach/site/browser_cookie3_stub.py +0 -17
  70. scratchattach-2.1.15b0/scratchattach/site/browser_cookies.py +0 -61
  71. scratchattach-2.1.15b0/scratchattach/site/classroom.py +0 -447
  72. scratchattach-2.1.15b0/scratchattach/site/cloud_activity.py +0 -107
  73. scratchattach-2.1.15b0/scratchattach/site/comment.py +0 -242
  74. scratchattach-2.1.15b0/scratchattach/site/forum.py +0 -432
  75. scratchattach-2.1.15b0/scratchattach/site/project.py +0 -826
  76. scratchattach-2.1.15b0/scratchattach/site/session.py +0 -1238
  77. scratchattach-2.1.15b0/scratchattach/site/studio.py +0 -611
  78. scratchattach-2.1.15b0/scratchattach/site/user.py +0 -956
  79. scratchattach-2.1.15b0/scratchattach/utils/__init__.py +0 -0
  80. scratchattach-2.1.15b0/scratchattach/utils/commons.py +0 -255
  81. scratchattach-2.1.15b0/scratchattach/utils/encoder.py +0 -158
  82. scratchattach-2.1.15b0/scratchattach/utils/enums.py +0 -236
  83. scratchattach-2.1.15b0/scratchattach/utils/exceptions.py +0 -243
  84. scratchattach-2.1.15b0/scratchattach/utils/requests.py +0 -93
  85. scratchattach-2.1.15b0/scratchattach.egg-info/SOURCES.txt +0 -84
  86. scratchattach-2.1.15b0/setup.py +0 -40
  87. scratchattach-2.1.15b0/tests/test_editor_project.py +0 -9
  88. scratchattach-2.1.15b0/tests/test_import.py +0 -4
  89. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/LICENSE +0 -0
  90. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/README.md +0 -0
  91. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/scratchattach.egg-info/dependency_links.txt +0 -0
  92. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/scratchattach.egg-info/top_level.txt +0 -0
  93. {scratchattach-2.1.15b0 → scratchattach-3.0.0b0}/setup.cfg +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scratchattach
3
- Version: 2.1.15b0
3
+ Version: 3.0.0b0
4
4
  Summary: A Scratch API Wrapper
5
5
  Author: TimMcCool
6
- Author-email:
6
+ License-Expression: MIT
7
7
  Project-URL: Source, https://github.com/timmccool/scratchattach
8
8
  Project-URL: Homepage, https://scratchattach.tim1de.net
9
9
  Keywords: scratch api,scratchattach,scratch api python,scratch python,scratch for python,scratch,scratch cloud,scratch cloud variables,scratch bot
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Operating System :: Unix
14
14
  Classifier: Operating System :: MacOS :: MacOS X
15
15
  Classifier: Operating System :: Microsoft :: Windows
16
+ Requires-Python: >=3.12
16
17
  Description-Content-Type: text/markdown
17
18
  License-File: LICENSE
18
19
  Requires-Dist: websocket-client
@@ -21,18 +22,13 @@ Requires-Dist: bs4
21
22
  Requires-Dist: SimpleWebSocketServer
22
23
  Requires-Dist: typing-extensions
23
24
  Requires-Dist: browser_cookie3
25
+ Requires-Dist: aiohttp
26
+ Requires-Dist: rich
27
+ Provides-Extra: cli
28
+ Requires-Dist: rich-pixels; extra == "cli"
24
29
  Provides-Extra: lark
25
30
  Requires-Dist: lark; extra == "lark"
26
- Dynamic: author
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: keywords
31
31
  Dynamic: license-file
32
- Dynamic: project-url
33
- Dynamic: provides-extra
34
- Dynamic: requires-dist
35
- Dynamic: summary
36
32
 
37
33
  **scratchattach is a Scratch API wrapper with support for almost all site features.** Created by [TimMcCool](https://scratch.mit.edu/users/TimMcCool/).
38
34
 
@@ -0,0 +1,95 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "scratchattach"
7
+ version = "3.0.0b0"
8
+ authors = [{ name = "TimMcCool" }]
9
+ description = "A Scratch API Wrapper"
10
+ requires-python = ">= 3.12"
11
+ dependencies = [
12
+ "websocket-client",
13
+ "requests",
14
+ "bs4",
15
+ "SimpleWebSocketServer",
16
+ "typing-extensions",
17
+ "browser_cookie3",
18
+ "aiohttp",
19
+ "rich",
20
+ ]
21
+ readme = "README.md"
22
+ license = "MIT"
23
+ license-files = ["LICENSE"]
24
+ keywords = [
25
+ "scratch api",
26
+ "scratchattach",
27
+ "scratch api python",
28
+ "scratch python",
29
+ "scratch for python",
30
+ "scratch",
31
+ "scratch cloud",
32
+ "scratch cloud variables",
33
+ "scratch bot",
34
+ ]
35
+ classifiers = [
36
+ "Development Status :: 5 - Production/Stable",
37
+ "Intended Audience :: Developers",
38
+ "Programming Language :: Python :: 3",
39
+ "Operating System :: Unix",
40
+ "Operating System :: MacOS :: MacOS X",
41
+ "Operating System :: Microsoft :: Windows",
42
+ ]
43
+
44
+ [project.urls]
45
+ Source = "https://github.com/timmccool/scratchattach"
46
+ Homepage = "https://scratchattach.tim1de.net"
47
+
48
+ [project.scripts]
49
+ scratch = "scratchattach.__main__:main"
50
+
51
+ [project.optional-dependencies]
52
+ cli = ["rich-pixels"]
53
+ lark = ["lark"]
54
+
55
+ [tool.ruff]
56
+ line-length = 127
57
+ exclude = [
58
+ ".bzr",
59
+ ".direnv",
60
+ ".eggs",
61
+ ".git",
62
+ ".git-rewrite",
63
+ ".hg",
64
+ ".ipynb_checkpoints",
65
+ ".mypy_cache",
66
+ ".nox",
67
+ ".pants.d",
68
+ ".pyenv",
69
+ ".pytest_cache",
70
+ ".pytype",
71
+ ".ruff_cache",
72
+ ".svn",
73
+ ".tox",
74
+ ".venv",
75
+ ".vscode",
76
+ "__pypackages__",
77
+ "_build",
78
+ "buck-out",
79
+ "build",
80
+ "dist",
81
+ "node_modules",
82
+ "site-packages",
83
+ "venv",
84
+ ]
85
+ [tool.ruff.lint]
86
+ select = ["E9", "F63", "F7", "F82"] # , "C901"] # uncomment to re-enable mccabe complexity - see https://github.com/TimMcCool/scratchattach/issues/566
87
+
88
+ [tool.ruff.lint.mccabe]
89
+ # max-complexity = 10
90
+
91
+ [tool.setuptools.packages]
92
+ find = { include = ["scratchattach"] }
93
+
94
+ [dependency-groups]
95
+ dev = ["cryptography>=46.0.3", "pytest>=9.0.2"]
@@ -8,22 +8,30 @@ from .eventhandlers.cloud_storage import Database
8
8
  from .eventhandlers.combine import MultiEventHandler
9
9
 
10
10
  from .other.other_apis import *
11
- from .other.project_json_capabilities import ProjectBody, get_empty_project_pb, get_pb_from_dict, read_sb3_file, download_asset
11
+ # from .other.project_json_capabilities import ProjectBody, get_empty_project_pb, get_pb_from_dict, read_sb3_file, download_asset
12
12
  from .utils.encoder import Encoding
13
13
  from .utils.enums import Languages, TTSVoices
14
- from .utils.exceptions import LoginDataWarning
14
+ from .utils.exceptions import (
15
+ LoginDataWarning,
16
+ GetAuthenticationWarning,
17
+ StudioAuthenticationWarning,
18
+ ClassroomAuthenticationWarning,
19
+ ProjectAuthenticationWarning,
20
+ UserAuthenticationWarning)
15
21
 
16
- from .site.activity import Activity
22
+ from .site.activity import Activity, ActivityTypes
17
23
  from .site.backpack_asset import BackpackAsset
18
- from .site.comment import Comment
24
+ from .site.comment import Comment, CommentSource
19
25
  from .site.cloud_activity import CloudActivity
20
26
  from .site.forum import ForumPost, ForumTopic, get_topic, get_topic_list, youtube_link_to_scratch
21
27
  from .site.project import Project, get_project, search_projects, explore_projects
22
- from .site.session import Session, login, login_by_id, login_by_session_string, login_by_io, login_by_file, login_from_browser
28
+ from .site.session import Session, login, login_by_id, login_by_session_string, login_by_io, login_by_file, \
29
+ login_from_browser
23
30
  from .site.studio import Studio, get_studio, search_studios, explore_studios
24
31
  from .site.classroom import Classroom, get_classroom
25
- from .site.user import User, get_user
32
+ from .site.user import User, get_user, Rank
26
33
  from .site._base import BaseSiteComponent
27
34
  from .site.browser_cookies import Browser, ANY, FIREFOX, CHROME, CHROMIUM, VIVALDI, EDGE, EDGE_DEV, SAFARI
35
+ from .site.placeholder import PlaceholderProject, get_placeholder_project, create_placeholder_project
28
36
 
29
37
  from . import editor
@@ -0,0 +1,93 @@
1
+ """
2
+ Scratchattach CLI. Most source code is in the `cli` directory
3
+ """
4
+
5
+ import argparse
6
+
7
+ from scratchattach import cli
8
+ from scratchattach.cli import db, cmd
9
+ from scratchattach.cli.context import ctx, console
10
+
11
+ import rich.traceback
12
+
13
+ rich.traceback.install()
14
+
15
+
16
+ # noinspection PyUnusedLocal
17
+ def main():
18
+ parser = argparse.ArgumentParser(
19
+ prog="scratch",
20
+ description="Scratchattach CLI",
21
+ epilog=f"Running scratchattach CLI version {cli.VERSION}",
22
+ )
23
+
24
+ # Using walrus operator & ifs for artificial indentation
25
+ if commands := parser.add_subparsers(dest="command"):
26
+ commands.add_parser("profile", help="View your profile")
27
+ commands.add_parser("sessions", help="View session list")
28
+ if login := commands.add_parser("login", help="Login to Scratch"):
29
+ login.add_argument("--sessid", dest="sessid", nargs="?", default=False, const=True,
30
+ help="Login by session ID")
31
+ if group := commands.add_parser("group", help="View current session group"):
32
+ if group_commands := group.add_subparsers(dest="group_command"):
33
+ group_commands.add_parser("list", help="List all session groups")
34
+ group_commands.add_parser("add", help="Add sessions to group")
35
+ group_commands.add_parser("remove", help="Remove sessions from a group")
36
+ group_commands.add_parser("delete", help="Delete current group")
37
+ if group_copy := group_commands.add_parser("copy", help="Copy current group with a new name"):
38
+ group_copy.add_argument("group_name", help="New group name")
39
+ if group_rename := group_commands.add_parser("rename", help="Rename current group"):
40
+ group_rename.add_argument("group_name", help="New group name")
41
+ if group_new := group_commands.add_parser("new", help="Create a new group"):
42
+ group_new.add_argument("group_name")
43
+ if group_switch := group_commands.add_parser("switch", help="Change the current group"):
44
+ group_switch.add_argument("group_name")
45
+
46
+ parser.add_argument("-U", "--username", dest="username", help="Name of user to look at")
47
+ parser.add_argument("-P", "--project", dest="project_id", help="ID of project to look at")
48
+ parser.add_argument("-S", "--studio", dest="studio_id", help="ID of studio to look at")
49
+ parser.add_argument("-L", "--session_name", dest="session_name",
50
+ help="Name of (registered) session/login to look at")
51
+
52
+ args = parser.parse_args(namespace=cli.ArgSpace())
53
+ cli.ctx.args = args
54
+ cli.ctx.parser = parser
55
+
56
+ match args.command:
57
+ case "sessions":
58
+ cmd.sessions()
59
+ case "login":
60
+ cmd.login()
61
+ case "group":
62
+ cmd.group()
63
+ case "profile":
64
+ cmd.profile()
65
+ case None:
66
+ if args.username:
67
+ user = ctx.session.connect_user(args.username)
68
+ console.print(cli.try_get_img(user.icon, (30, 30)))
69
+ console.print(user)
70
+ return
71
+ if args.studio_id:
72
+ studio = ctx.session.connect_studio(args.studio_id)
73
+ console.print(cli.try_get_img(studio.thumbnail, (34, 20)))
74
+ console.print(studio)
75
+ return
76
+ if args.project_id:
77
+ project = ctx.session.connect_project(args.project_id)
78
+ console.print(cli.try_get_img(project.thumbnail, (30, 23)))
79
+ console.print(project)
80
+ return
81
+ if args.session_name:
82
+ if sess := ctx.db_get_sess(args.session_name):
83
+ console.print(sess)
84
+ else:
85
+ raise ValueError(f"No session logged in called {args.session_name!r} "
86
+ f"- try using `scratch sessions` to see available sessions")
87
+ return
88
+
89
+ parser.print_help()
90
+
91
+
92
+ if __name__ == '__main__':
93
+ main()
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scratchattach
3
- Version: 2.1.15b0
3
+ Version: 3.0.0b0
4
4
  Summary: A Scratch API Wrapper
5
5
  Author: TimMcCool
6
- Author-email:
6
+ License-Expression: MIT
7
7
  Project-URL: Source, https://github.com/timmccool/scratchattach
8
8
  Project-URL: Homepage, https://scratchattach.tim1de.net
9
9
  Keywords: scratch api,scratchattach,scratch api python,scratch python,scratch for python,scratch,scratch cloud,scratch cloud variables,scratch bot
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Operating System :: Unix
14
14
  Classifier: Operating System :: MacOS :: MacOS X
15
15
  Classifier: Operating System :: Microsoft :: Windows
16
+ Requires-Python: >=3.12
16
17
  Description-Content-Type: text/markdown
17
18
  License-File: LICENSE
18
19
  Requires-Dist: websocket-client
@@ -21,18 +22,13 @@ Requires-Dist: bs4
21
22
  Requires-Dist: SimpleWebSocketServer
22
23
  Requires-Dist: typing-extensions
23
24
  Requires-Dist: browser_cookie3
25
+ Requires-Dist: aiohttp
26
+ Requires-Dist: rich
27
+ Provides-Extra: cli
28
+ Requires-Dist: rich-pixels; extra == "cli"
24
29
  Provides-Extra: lark
25
30
  Requires-Dist: lark; extra == "lark"
26
- Dynamic: author
27
- Dynamic: classifier
28
- Dynamic: description
29
- Dynamic: description-content-type
30
- Dynamic: keywords
31
31
  Dynamic: license-file
32
- Dynamic: project-url
33
- Dynamic: provides-extra
34
- Dynamic: requires-dist
35
- Dynamic: summary
36
32
 
37
33
  **scratchattach is a Scratch API wrapper with support for almost all site features.** Created by [TimMcCool](https://scratch.mit.edu/users/TimMcCool/).
38
34
 
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ scratchattach/__init__.py
5
+ scratchattach/__main__.py
6
+ scratchattach.egg-info/PKG-INFO
7
+ scratchattach.egg-info/SOURCES.txt
8
+ scratchattach.egg-info/dependency_links.txt
9
+ scratchattach.egg-info/entry_points.txt
10
+ scratchattach.egg-info/requires.txt
11
+ scratchattach.egg-info/top_level.txt
12
+ tests/test_activity.py
13
+ tests/test_auth.py
14
+ tests/test_comment.py
15
+ tests/test_editor_integration.py
16
+ tests/test_editor_obfuscation.py
17
+ tests/test_editor_project.py
18
+ tests/test_import.py
19
+ tests/test_memberships.py
20
+ tests/test_other_apis.py
21
+ tests/test_project.py
22
+ tests/test_search.py
23
+ tests/test_studio.py
24
+ tests/test_teacher_activity.py
25
+ tests/test_user.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ scratch = scratchattach.__main__:main
@@ -4,6 +4,11 @@ bs4
4
4
  SimpleWebSocketServer
5
5
  typing-extensions
6
6
  browser_cookie3
7
+ aiohttp
8
+ rich
9
+
10
+ [cli]
11
+ rich-pixels
7
12
 
8
13
  [lark]
9
14
  lark
@@ -1,23 +1,22 @@
1
- import sys
2
1
  from datetime import datetime, timedelta, timezone
3
2
  import warnings
4
-
3
+ import scratchattach as sa
4
+ from scratchattach.utils import exceptions
5
+ import util
5
6
 
6
7
 
7
8
  def test_activity():
8
- sys.path.insert(0, ".")
9
- import scratchattach as sa
10
- from scratchattach.utils import exceptions
11
- import util
12
9
  if not util.credentials_available():
13
- warnings.warn("Skipped test_activity because there were no credentials available.")
10
+ warnings.warn(
11
+ "Skipped test_activity because there were no credentials available."
12
+ )
14
13
  return
15
14
  sess = util.session()
16
15
 
17
16
  # we cannot do assertions, but we can probe for any errors.
18
17
  messages = sess.messages()
19
18
  for msg in messages:
20
- print(msg, end=' ')
19
+ print(msg, end=" ")
21
20
 
22
21
  try:
23
22
  target = msg.target()
@@ -1,10 +1,10 @@
1
1
  import sys
2
2
  import os
3
3
  import warnings
4
+ import util
5
+
4
6
 
5
7
  def test_auth():
6
- sys.path.insert(0, ".")
7
- import util
8
8
  if not util.credentials_available():
9
9
  warnings.warn("Skipped test_auth because there were no credentials available.")
10
10
  return
@@ -1,13 +1,14 @@
1
- import sys
2
1
  from datetime import datetime, timedelta, timezone
3
2
  import warnings
3
+ import scratchattach as sa
4
+ import util
5
+
4
6
 
5
7
  def test_comment():
6
- sys.path.insert(0, ".")
7
- import scratchattach as sa
8
- import util
9
8
  if not util.credentials_available():
10
- warnings.warn("Skipped test_comment because there were no credentials available.")
9
+ warnings.warn(
10
+ "Skipped test_comment because there were no credentials available."
11
+ )
11
12
  return
12
13
  sess = util.session()
13
14
 
@@ -18,37 +19,39 @@ def test_comment():
18
19
  comment = user.comments(limit=1)[0]
19
20
 
20
21
  assert comment.id == "387076703"
21
- #assert comment.source == sa.CommentSource.USER_PROFILE
22
+ assert comment.source == sa.CommentSource.USER_PROFILE
22
23
  assert comment.source_id == "ScratchAttachV2"
23
24
  assert comment.parent_id is None
24
25
  assert comment.content == "Sample comment"
25
- assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 25, tzinfo=timezone.utc) < timedelta(days=1)
26
+ assert datetime.fromisoformat(comment.datetime_created) - datetime(
27
+ 2025, 8, 25, tzinfo=timezone.utc
28
+ ) < timedelta(days=1)
26
29
  assert comment.reply_count == 0
27
30
  assert comment.text == "Sample comment"
28
31
 
29
32
  comment = proj.comments(limit=1)[0]
30
33
 
31
34
  assert comment.id == 494890468
32
- #assert comment.source == sa.CommentSource.PROJECT
35
+ assert comment.source == sa.CommentSource.PROJECT
33
36
  assert comment.source_id == 1108326850
34
37
  assert comment.parent_id is None
35
- assert comment.content == ("&lt;&amp;;&apos;!\n"
36
- "newline\n"
37
- "testing escaping")
38
- assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 9, 20, tzinfo=timezone.utc) < timedelta(days=1)
38
+ assert comment.content == ("&lt;&amp;;&apos;!\n" "newline\n" "testing escaping")
39
+ assert datetime.fromisoformat(comment.datetime_created) - datetime(
40
+ 2025, 9, 20, tzinfo=timezone.utc
41
+ ) < timedelta(days=1)
39
42
  assert comment.reply_count == 0
40
- assert comment.text == ("<&;'!\n"
41
- "newline\n"
42
- "testing escaping")
43
+ assert comment.text == ("<&;'!\n" "newline\n" "testing escaping")
43
44
 
44
45
  comment = studio.comments(limit=1)[0]
45
46
 
46
47
  assert comment.id == 302129887
47
- #assert comment.source == sa.CommentSource.STUDIO
48
+ assert comment.source == sa.CommentSource.STUDIO
48
49
  assert comment.source_id == 50809872
49
50
  assert comment.parent_id is None
50
51
  assert comment.content == "Sample"
51
- assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 26, tzinfo=timezone.utc) < timedelta(days=1)
52
+ assert datetime.fromisoformat(comment.datetime_created) - datetime(
53
+ 2025, 8, 26, tzinfo=timezone.utc
54
+ ) < timedelta(days=1)
52
55
  assert comment.reply_count == 1
53
56
  assert not comment.written_by_scratchteam
54
57
  assert comment.text == "Sample"
@@ -56,15 +59,18 @@ def test_comment():
56
59
  comment = comment.replies(limit=1)[0]
57
60
 
58
61
  assert comment.id == 302129910
59
- #assert comment.source == sa.CommentSource.STUDIO
62
+ assert comment.source == sa.CommentSource.STUDIO
60
63
  assert comment.source_id == 50809872
61
64
  assert comment.parent_id == 302129887
62
65
  assert comment.commentee_id == 58743127
63
66
  assert comment.content == "text"
64
- assert datetime.fromisoformat(comment.datetime_created) - datetime(2025, 8, 26, tzinfo=timezone.utc) < timedelta(days=1)
67
+ assert datetime.fromisoformat(comment.datetime_created) - datetime(
68
+ 2025, 8, 26, tzinfo=timezone.utc
69
+ ) < timedelta(days=1)
65
70
  assert comment.reply_count == 0
66
71
  assert not comment.written_by_scratchteam
67
72
  assert comment.text == "text"
68
73
 
74
+
69
75
  if __name__ == "__main__":
70
76
  test_comment()
@@ -1,22 +1,22 @@
1
1
  import pprint
2
- import sys
3
2
  import warnings
3
+ import util
4
+ import scratchattach as sa
5
+
4
6
 
5
7
  def test_project():
6
- sys.path.insert(0, ".")
7
- import scratchattach as sa
8
- import util
9
8
  if not util.credentials_available():
10
- warnings.warn("Skipped test_project because there were no credentials available.")
9
+ warnings.warn(
10
+ "Skipped test_project because there were no credentials available."
11
+ )
11
12
  return
12
13
  sess = util.session()
13
14
 
14
-
15
15
  project = sess.connect_project(104)
16
16
  body = project.body()
17
17
  body.to_json() # do nothing with the data, just make sure it works.
18
18
 
19
19
 
20
+ if __name__ == "__main__":
21
+ test_project()
20
22
 
21
- if __name__ == '__main__':
22
- test_project()
@@ -1,12 +1,9 @@
1
1
  import pprint
2
- import sys
3
2
  from pathlib import Path
3
+ import scratchattach as sa
4
4
 
5
5
 
6
6
  def test_project():
7
- return
8
- sys.path.insert(0, ".")
9
- import scratchattach as sa
10
7
 
11
8
  path = Path(__file__).parent.parent / "intro for kelmare (yoda tour) (p2).sb3"
12
9
 
@@ -24,5 +21,5 @@ def test_project():
24
21
  body.export("obfuscated.sb3")
25
22
 
26
23
 
27
- if __name__ == '__main__':
24
+ if __name__ == "__main__":
28
25
  test_project()
@@ -0,0 +1,6 @@
1
+ import scratchattach as sa
2
+
3
+
4
+ def test_editor_project():
5
+ proj = sa.get_project(104)
6
+ body = proj.body()
@@ -0,0 +1,5 @@
1
+ import scratchattach as sa
2
+
3
+
4
+ def test_import():
5
+ pass
@@ -0,0 +1,36 @@
1
+ import scratchattach as sa
2
+ import warnings
3
+
4
+ warnings.filterwarnings("ignore", category=sa.UserAuthenticationWarning)
5
+
6
+
7
+ def test_memberships():
8
+ # unfortunately we don't have amazingly robust test-cases here.
9
+ u1 = sa.get_user("-KittyMax-")
10
+ assert u1.is_member
11
+ assert u1.has_ears
12
+ assert u1.has_badge()
13
+
14
+ u2 = sa.get_user("ceebee")
15
+ assert u2.is_member
16
+ assert u2.has_ears
17
+ assert u2.has_badge()
18
+
19
+ u3 = sa.get_user("scratchattachv2")
20
+ assert not u3.is_member
21
+ assert not u3.has_ears
22
+ assert not u3.has_badge()
23
+
24
+ u4 = sa.get_user("peekir")
25
+ assert u4.is_member
26
+ assert u4.has_ears
27
+ assert u4.has_badge()
28
+
29
+ u5 = sa.get_user("StardreamT2")
30
+ assert u5.is_member
31
+ assert u5.has_ears
32
+ assert not u5.has_badge()
33
+
34
+
35
+ if __name__ == "__main__":
36
+ test_memberships()
@@ -1,17 +1,14 @@
1
1
  import pprint
2
- import sys
3
2
  from datetime import datetime, timedelta, timezone
4
3
  import warnings
4
+ import scratchattach as sa
5
+ from scratchattach.utils import exceptions
6
+ import util
5
7
 
6
8
 
7
9
  def test_other_apis():
8
- sys.path.insert(0, ".")
9
- import scratchattach as sa
10
- from scratchattach.utils import exceptions
11
- import util
12
-
13
- #assert sa.check_email("example@example.com")
14
- #assert not sa.check_email("bad_email")
10
+ assert sa.check_email("example@example.com")
11
+ assert not sa.check_email("bad_email")
15
12
 
16
13
  news = sa.get_news()
17
14
  found_wiki_wednesday = False
@@ -1,20 +1,19 @@
1
- import pprint
2
- import sys
3
1
  import warnings
2
+ from util import session, credentials_available
3
+ import scratchattach as sa
4
+
4
5
 
5
6
  def test_project():
6
- sys.path.insert(0, ".")
7
- import scratchattach as sa
8
- from util import session, credentials_available
9
7
  if not credentials_available():
10
- warnings.warn("Skipped test_project because there were no credentials available.")
8
+ warnings.warn(
9
+ "Skipped test_project because there were no credentials available."
10
+ )
11
11
  return
12
12
  sess = session()
13
13
 
14
-
15
14
  project = sess.connect_project(104)
16
- #tree = project.remix_tree_pretty()
17
- #assert len(tree) > 1000 # there is a lot of chars. Just assert that sth is generated
15
+ # tree = project.remix_tree_pretty()
16
+ # assert len(tree) > 1000 # there is a lot of chars. Just assert that sth is generated
18
17
 
19
18
  project = sess.connect_project(1209355136)
20
19
 
@@ -59,14 +58,15 @@ def test_project():
59
58
  assert remix.author_name == "ScratchAttachV2"
60
59
  assert remix.embed_url == "https://scratch.mit.edu/projects/1209582809/embed"
61
60
 
62
- #assert sess.connect_project(414601586).moderation_status() == "notsafe"
63
- #assert sess.connect_project(1207314193).moderation_status() == "safe"
64
- #assert sess.connect_project(
65
- # 1233).moderation_status() == "notreviewed" # if this becomes reviewed, please update this
61
+ # assert sess.connect_project(414601586).moderation_status() == "notsafe"
62
+ # assert sess.connect_project(1207314193).moderation_status() == "safe"
63
+ # assert sess.connect_project(
64
+ # 1233).moderation_status() == "notreviewed" # if this becomes reviewed, please update this
66
65
  # ^^ also this project is an infinite remix loop!
67
66
 
68
67
  assert sa.explore_projects()
69
68
  assert sa.search_projects(query="scratchattach")
70
69
 
71
- if __name__ == '__main__':
72
- test_project()
70
+
71
+ if __name__ == "__main__":
72
+ test_project()