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.
|
|
Binary file
|
|
@@ -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
|
+
]
|