monaco-assets 0.3.0__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.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: monaco-assets
3
+ Version: 0.3.0
4
+ Summary: Automatically download Monaco editor assets.
5
+ Keywords: monaco,assets
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Requires-Dist: certifi
18
+ Requires-Dist: platformdirs
19
+
20
+ # Monaco Editor Assets
21
+
22
+ A Python package that provides easy access to Monaco Editor assets. Assets are
23
+ automatically downloaded on first use, eliminating the need to bundle large
24
+ files with the package. The assets can be served by a webserver on a custom port.
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ python3 -m pip install monaco-assets
30
+ # or
31
+ uv pip install monaco-assets
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ import monaco_assets
38
+
39
+ import monaco_assets
40
+ server = monaco_assets.MonacoServer(port=8000)
41
+ ```
42
+
43
+ Now, you can use `http://localhost:8000/` in a webbrowser to see all assets.
44
+
45
+ ## Cache Management
46
+
47
+ ```python
48
+ import monaco_assets
49
+
50
+ # Clear cache to free space before uninstalling the package
51
+ monaco_assets.clear_cache()
52
+ ```
53
+
54
+ ## Cache Locations
55
+
56
+ Assets are cached in platform-appropriate directories using the `platformdirs` library
57
+
58
+ ## How It Works
59
+
60
+ 1. **First Use**: When `get_path()` is called for the first time, the package:
61
+ - Downloads Monaco Editor from npmjs.org
62
+ - Verifies the download integrity with SHA1 hash
63
+ - Extracts assets to the user cache directory
64
+ - Returns the path to the assets
65
+
66
+ 2. **Subsequent Uses**: The package checks the cache and returns the existing assets path
67
+ immediately.
68
+
69
+ ## Download Issues
70
+
71
+ If asset download fails:
72
+
73
+ 1. Check internet connectivity
74
+ 2. Verify firewall settings allow access to registry.npmjs.org
75
+ 3. Check disk space in cache directory
76
+
77
+ ## Cache Issues
78
+
79
+ Clear and re-download if corrupted.
80
+
81
+ ```python
82
+ monaco_assets.clear_cache()
83
+ assets_path = monaco_assets.get_path()
84
+ ```
85
+
86
+ ## Version Correspondence
87
+
88
+ Version correspondence will be ensured after initial bugfixes.
89
+
90
+ | Package Version | Monaco Editor Version |
91
+ | --------------- | --------------------- |
92
+ | 0.3.0 | 0.54.0 |
93
+
94
+ ## Requirements
95
+
96
+ - Python 3.10+
97
+ - Internet connection (only for initial asset download)
98
+ - ~100MB disk space for Monaco Editor assets
99
+
100
+ ## License
101
+
102
+ MIT License - see [LICENSE](license.txt) file for details.
103
+
104
+ Monaco Editor is licensed under the MIT License by Microsoft Corporation.
105
+
@@ -0,0 +1,85 @@
1
+ # Monaco Editor Assets
2
+
3
+ A Python package that provides easy access to Monaco Editor assets. Assets are
4
+ automatically downloaded on first use, eliminating the need to bundle large
5
+ files with the package. The assets can be served by a webserver on a custom port.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ python3 -m pip install monaco-assets
11
+ # or
12
+ uv pip install monaco-assets
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ import monaco_assets
19
+
20
+ import monaco_assets
21
+ server = monaco_assets.MonacoServer(port=8000)
22
+ ```
23
+
24
+ Now, you can use `http://localhost:8000/` in a webbrowser to see all assets.
25
+
26
+ ## Cache Management
27
+
28
+ ```python
29
+ import monaco_assets
30
+
31
+ # Clear cache to free space before uninstalling the package
32
+ monaco_assets.clear_cache()
33
+ ```
34
+
35
+ ## Cache Locations
36
+
37
+ Assets are cached in platform-appropriate directories using the `platformdirs` library
38
+
39
+ ## How It Works
40
+
41
+ 1. **First Use**: When `get_path()` is called for the first time, the package:
42
+ - Downloads Monaco Editor from npmjs.org
43
+ - Verifies the download integrity with SHA1 hash
44
+ - Extracts assets to the user cache directory
45
+ - Returns the path to the assets
46
+
47
+ 2. **Subsequent Uses**: The package checks the cache and returns the existing assets path
48
+ immediately.
49
+
50
+ ## Download Issues
51
+
52
+ If asset download fails:
53
+
54
+ 1. Check internet connectivity
55
+ 2. Verify firewall settings allow access to registry.npmjs.org
56
+ 3. Check disk space in cache directory
57
+
58
+ ## Cache Issues
59
+
60
+ Clear and re-download if corrupted.
61
+
62
+ ```python
63
+ monaco_assets.clear_cache()
64
+ assets_path = monaco_assets.get_path()
65
+ ```
66
+
67
+ ## Version Correspondence
68
+
69
+ Version correspondence will be ensured after initial bugfixes.
70
+
71
+ | Package Version | Monaco Editor Version |
72
+ | --------------- | --------------------- |
73
+ | 0.3.0 | 0.54.0 |
74
+
75
+ ## Requirements
76
+
77
+ - Python 3.10+
78
+ - Internet connection (only for initial asset download)
79
+ - ~100MB disk space for Monaco Editor assets
80
+
81
+ ## License
82
+
83
+ MIT License - see [LICENSE](license.txt) file for details.
84
+
85
+ Monaco Editor is licensed under the MIT License by Microsoft Corporation.
@@ -0,0 +1,208 @@
1
+ """
2
+ Provide Monaco editor assets.
3
+
4
+ Download Monaco editor assets at first use. The assets are downloaded,
5
+ extracted, and made available in a platform specific cache folder. To
6
+ access the assets,a simple webserver can be used.
7
+ """
8
+
9
+ import hashlib
10
+ import http.server
11
+ import inspect
12
+ import logging
13
+ import shutil
14
+ import socketserver
15
+ import ssl
16
+ import tarfile
17
+ import threading
18
+ import urllib.request
19
+ from functools import partial
20
+ from pathlib import Path
21
+
22
+ import certifi
23
+ from platformdirs import user_cache_dir
24
+
25
+ VERSION = "0.54.0"
26
+ EXPECTED_SHA1 = "c0d6ebb46b83f1bef6f67f6aa471e38ba7ef8231"
27
+
28
+ CACHE_DIR = Path(user_cache_dir("monaco-assets", "monaco-assets")) / f"monaco-editor-{VERSION}"
29
+
30
+
31
+ class _MonacoRequestHandler(http.server.SimpleHTTPRequestHandler):
32
+ """Custom HTTP request handler can use logging."""
33
+
34
+ def __init__(self, *args, logger=None, **kwargs):
35
+ """Init with optional logger."""
36
+ self.logger = logger
37
+ super().__init__(*args, **kwargs)
38
+
39
+ def log_message(self, format, *args): # noqa: A002
40
+ """Override log_message to use logger.debug."""
41
+ if self.logger:
42
+ self.logger.debug(format % args)
43
+ else:
44
+ super().log_message(format, *args)
45
+
46
+
47
+ class MonacoServer:
48
+ """HTTP server to serve Monaco editor assets."""
49
+
50
+ def __init__(self, port: int = 8000):
51
+ """
52
+ Initialize and start Monaco Editor assets server.
53
+
54
+ Download assets if needed and start a local HTTP server in a
55
+ background thread. The assets will be available at:
56
+ http://localhost:<port>
57
+
58
+ Parameters
59
+ ----------
60
+ port : int
61
+ Port number for the HTTP server (default: 8000)
62
+ """
63
+ self.logger = logging.getLogger(f"{__name__}.MonacoServer")
64
+ self._port: int = port
65
+ self._httpd: socketserver.TCPServer | None
66
+ self._thread: threading.Thread | None = threading.Thread(
67
+ target=self._run_server, daemon=True
68
+ )
69
+ self.logger.info("starting Monaco webserver.")
70
+ self._thread.start()
71
+
72
+ def _run_server(self):
73
+ """Run the HTTP server in a background thread."""
74
+ handler = partial(_MonacoRequestHandler, directory=get_path(), logger=self.logger)
75
+ self._httpd = socketserver.TCPServer(("", self._port), handler)
76
+ self._httpd.serve_forever()
77
+
78
+ def stop(self) -> bool:
79
+ """
80
+ Stop the Monaco editor assets server.
81
+
82
+ Returns
83
+ -------
84
+ bool
85
+ True if server was stopped, False if no server was running.
86
+ """
87
+ self.logger.info("stopping Monaco webserver.")
88
+ if self._httpd is None:
89
+ self.logger.warning("no Monaco webserver was running!")
90
+ return False
91
+ self._httpd.shutdown()
92
+ self._httpd.server_close()
93
+ if self._thread is not None:
94
+ self._thread.join(timeout=5.0)
95
+ self._thread = None
96
+ self._httpd = None
97
+ self.logger.info("Monaco webserver stopped.")
98
+ return True
99
+
100
+ def is_running(self) -> bool:
101
+ """
102
+ Check if the Monaco Editor assets server is currently running.
103
+
104
+ Returns
105
+ -------
106
+ bool
107
+ True if server is running, False otherwise
108
+ """
109
+ return self._thread is not None and self._thread.is_alive() and self._httpd is not None
110
+
111
+
112
+ def _download_file(url: str, filename: Path) -> None:
113
+ """
114
+ Download a file from a URL to the destination path.
115
+
116
+ Parameters
117
+ ----------
118
+ url : str
119
+ The URL.
120
+ filename : Path
121
+ The filename of the received file.
122
+
123
+ """
124
+ context = ssl.create_default_context(cafile=certifi.where())
125
+ with urllib.request.urlopen(url, context=context) as response:
126
+ with open(filename, "wb") as out_file:
127
+ shutil.copyfileobj(response, out_file) # type: ignore
128
+
129
+
130
+ def _verify_file_hash(filename: Path, expected_sha1: str) -> bool:
131
+ """
132
+ Verify the SHA1 hash of a file.
133
+
134
+ Parameters
135
+ ----------
136
+ filename : Path
137
+ The file to verify.
138
+ expected_sha1 : str
139
+ The expected SHA1 hash.
140
+
141
+ Returns
142
+ -------
143
+ bool
144
+ True if hash matches, False otherwise.
145
+ """
146
+ sha1_hash = hashlib.sha1()
147
+ with open(filename, "rb") as f:
148
+ for chunk in iter(lambda: f.read(4096), b""):
149
+ sha1_hash.update(chunk)
150
+ actual_sha1 = sha1_hash.hexdigest()
151
+ return actual_sha1 == expected_sha1
152
+
153
+
154
+ def _extract_tgz(tgz: Path) -> None:
155
+ """
156
+ Extract a .tgz file to the same directory.
157
+
158
+ Parameters
159
+ ----------
160
+ tgz: Path
161
+ The tar.gz file.
162
+ """
163
+ dest = tgz.parent
164
+ with tarfile.open(tgz, "r:gz") as tar:
165
+ # delete the if clause for Python>=3.12
166
+ supports_filter = "filter" in inspect.signature(tar.extract).parameters
167
+ for member in tar.getmembers():
168
+ if supports_filter:
169
+ tar.extract(member, dest, filter="data")
170
+ else:
171
+ tar.extract(member, dest)
172
+
173
+
174
+ def get_path() -> Path:
175
+ """
176
+ Download Monaco Editor assets if they do not exist.
177
+
178
+ Returns
179
+ -------
180
+ Path
181
+ The path to the assests.
182
+ """
183
+ package_dir = CACHE_DIR / "package"
184
+
185
+ if package_dir.exists() and any(package_dir.iterdir()):
186
+ return package_dir
187
+ try:
188
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
189
+ package = "monaco-editor"
190
+ tgz = f"{package}-{VERSION}.tgz"
191
+ url = f"https://registry.npmjs.org/{package}/-/{tgz}"
192
+ tgz_file = CACHE_DIR / tgz
193
+ _download_file(url, tgz_file)
194
+ if not _verify_file_hash(tgz_file, EXPECTED_SHA1):
195
+ raise ValueError(f"Hash verification failed for {tgz_file}")
196
+ _extract_tgz(tgz_file)
197
+ tgz_file.unlink()
198
+ return package_dir
199
+ except Exception as e:
200
+ if CACHE_DIR.exists():
201
+ shutil.rmtree(CACHE_DIR, ignore_errors=True)
202
+ raise RuntimeError(f"Failed to download Monaco Editor assets: {e}") from e
203
+
204
+
205
+ def clear_cache() -> None:
206
+ """Clear Monaco Editor asset cache."""
207
+ if CACHE_DIR.exists():
208
+ shutil.rmtree(CACHE_DIR)
@@ -0,0 +1,56 @@
1
+ [project]
2
+ name = "monaco-assets"
3
+ version = "0.3.0"
4
+ description = "Automatically download Monaco editor assets."
5
+ requires-python = ">=3.10"
6
+ dependencies = ["certifi", "platformdirs"]
7
+ classifiers = [
8
+ "Development Status :: 4 - Beta",
9
+ "Intended Audience :: Developers",
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.10",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Topic :: Software Development :: Libraries",
16
+ ]
17
+ readme = "README.md"
18
+ license = "MIT"
19
+
20
+ keywords = ["monaco", "assets"]
21
+
22
+ [build-system]
23
+ requires = ["flit_core >=3.6,<4"]
24
+ build-backend = "flit_core.buildapi"
25
+
26
+ [tool.ruff]
27
+ line-length = 99
28
+
29
+ [tool.ruff.lint]
30
+ extend-select = [
31
+ 'D', # pydocstyle (docstring formatting)
32
+ 'A', # flake8 builtins (Python builtins being used as variables or parameters.)
33
+ 'I', # isort (sort imports)
34
+ 'PLW0602', # unassigned global variable
35
+ 'W505', # doc line too long
36
+ 'E501', # line too long
37
+ 'UP', # use most modern code allowed by lowest python version
38
+ ]
39
+
40
+ [tool.ruff.lint.pycodestyle]
41
+ max-line-length = 100
42
+ max-doc-length = 72
43
+
44
+ [tool.ruff.lint.pydocstyle]
45
+ convention = "numpy"
46
+
47
+ [tool.pyrefly]
48
+ project-includes = ["monaco_assets"]
49
+
50
+ [dependency-groups]
51
+ dev = [
52
+ "build>=1.3.0",
53
+ "pyrefly>=0.39.4",
54
+ "ruff>=0.13.0",
55
+ "twine>=6.2.0",
56
+ ]