more-compute 0.4.2__py3-none-any.whl → 0.4.3__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.
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/METADATA +1 -1
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/RECORD +10 -10
- morecompute/__version__.py +1 -1
- morecompute/notebook.py +21 -2
- morecompute/utils/notebook_converter.py +171 -3
- morecompute/utils/py_percent_parser.py +13 -3
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/WHEEL +0 -0
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/entry_points.txt +0 -0
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/licenses/LICENSE +0 -0
- {more_compute-0.4.2.dist-info → more_compute-0.4.3.dist-info}/top_level.txt +0 -0
|
@@ -63,11 +63,11 @@ frontend/public/fonts/Fira.ttf,sha256=dbSM4W7Drd9n_EkfXq8P31KuxbjZ1wtuFiZ8aFebvT
|
|
|
63
63
|
frontend/public/fonts/Tiempos.woff2,sha256=h83bJKvAK301wXCMIvK7ZG5j0H2K3tzAfgo0yk4z8OE,13604
|
|
64
64
|
frontend/public/fonts/VeraMono.ttf,sha256=2kKB3H2xej385kpiztkodcWJU0AFXsi6JKORTrl7NJ0,49224
|
|
65
65
|
frontend/types/notebook.ts,sha256=v23RaZe6H3lU5tq6sqnJDPxC2mu0NZFDCJfiN0mgvSs,1359
|
|
66
|
-
more_compute-0.4.
|
|
66
|
+
more_compute-0.4.3.dist-info/licenses/LICENSE,sha256=UQuJtJ2v98w8mXozOPmE8nkNc_8BFY-SVVBgTA327z0,1067
|
|
67
67
|
morecompute/__init__.py,sha256=pcMVq8Q7qb42AOn7tqgoZJOi3epDDBnEriiv2WVKnXY,87
|
|
68
|
-
morecompute/__version__.py,sha256=
|
|
68
|
+
morecompute/__version__.py,sha256=Nyg0pmk5ea9-SLCAFEIF96ByFx4-TJFtrqYPN-Zn6g4,22
|
|
69
69
|
morecompute/cli.py,sha256=kVvzvPBqF8xO6UuhU_-TBn99nKwJ405R2mAS6zU0KBc,734
|
|
70
|
-
morecompute/notebook.py,sha256=
|
|
70
|
+
morecompute/notebook.py,sha256=KGHORGAAZahT-TyX9HYuSc-b6TmfGgoi92FhRMK--Yg,7730
|
|
71
71
|
morecompute/process_worker.py,sha256=KsE3r-XpkYGuyO4w3t54VKkD51LfNHAZc3TYattMtrg,7185
|
|
72
72
|
morecompute/server.py,sha256=mcJwIvT9QHfpKuAq5jP2SW5hPjFB_rySevgQvOHuud8,41689
|
|
73
73
|
morecompute/execution/__init__.py,sha256=jPmBmq8BZWbUEY9XFSpqt5FkgX04uNS10WnUlr7Rnms,134
|
|
@@ -88,16 +88,16 @@ morecompute/utils/cell_magics.py,sha256=YLxltTBprCA8jfgsjqf7Shnk4NCmGKIp3aV5_CYk
|
|
|
88
88
|
morecompute/utils/config_util.py,sha256=I90om4Wf4BODc5Gy9u8Tu34AcqpkfkGAKQO6JE_NMzU,1940
|
|
89
89
|
morecompute/utils/error_utils.py,sha256=e50WLFdD6ngIC30xAgrzdTYtD8tPOIFkKAAh_sPbK0I,11667
|
|
90
90
|
morecompute/utils/line_magics.py,sha256=kTutYBPAWoURY_pk8HXQ38IP712M2rBBfUg3oN8VrP0,33740
|
|
91
|
-
morecompute/utils/notebook_converter.py,sha256=
|
|
91
|
+
morecompute/utils/notebook_converter.py,sha256=_V-iG060Rr04oqk__cg1LbfV3C7uR1Rsh504WNZ218Q,9756
|
|
92
92
|
morecompute/utils/notebook_util.py,sha256=3hH94dtXvhizRVTU9a2b38m_51Y4igoXpkjAXUqpVBQ,1353
|
|
93
|
-
morecompute/utils/py_percent_parser.py,sha256=
|
|
93
|
+
morecompute/utils/py_percent_parser.py,sha256=c2AUOcXjGxmBWaYy0kk-pbNVzs-6YFagMVuVGQlYjY0,5388
|
|
94
94
|
morecompute/utils/python_environment_util.py,sha256=l8WWWPwKbypknw8GwL22NXCji5i1FOy1vWG47J6og4g,7441
|
|
95
95
|
morecompute/utils/shell_utils.py,sha256=fGFLhQLZU-lmMGALbbS-fKPkhQtmMhZ1FkgQ3TeoFhA,1917
|
|
96
96
|
morecompute/utils/special_commands.py,sha256=nf2nVea5SyFqpulNYrnRx7anU4-G-e5_kLJ0PLtEi3Q,22030
|
|
97
97
|
morecompute/utils/system_environment_util.py,sha256=32mQRubo0i4X61o-825T7m-eUSidcEp07qkInP1sWZA,4774
|
|
98
98
|
morecompute/utils/zmq_util.py,sha256=tx7-iS04UN69OFtBzkxcEnRhT7xtI9EzRnrZ_nsH_O0,1889
|
|
99
|
-
more_compute-0.4.
|
|
100
|
-
more_compute-0.4.
|
|
101
|
-
more_compute-0.4.
|
|
102
|
-
more_compute-0.4.
|
|
103
|
-
more_compute-0.4.
|
|
99
|
+
more_compute-0.4.3.dist-info/METADATA,sha256=U9KhKWUCeCulBSndkv55vtyDzwdIzKIt_VuHSt0Bh50,3869
|
|
100
|
+
more_compute-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
more_compute-0.4.3.dist-info/entry_points.txt,sha256=xp7z9eRPNRM4oxkZZVlyXkhkSjN1AjoYI_B7qpDJ1bI,49
|
|
102
|
+
more_compute-0.4.3.dist-info/top_level.txt,sha256=Tamm6ADzjwaQa1z27O7Izcyhyt9f0gVjMv1_tC810aI,32
|
|
103
|
+
more_compute-0.4.3.dist-info/RECORD,,
|
morecompute/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.4.
|
|
1
|
+
__version__ = "0.4.3"
|
morecompute/notebook.py
CHANGED
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import List, Dict, Any
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
from .utils.py_percent_parser import parse_py_percent, generate_py_percent
|
|
6
|
+
from .utils.notebook_converter import detect_colab_format, parse_colab_py
|
|
6
7
|
|
|
7
8
|
class Notebook:
|
|
8
9
|
"""Manages the state of a notebook's cells."""
|
|
@@ -92,11 +93,29 @@ class Notebook:
|
|
|
92
93
|
|
|
93
94
|
# Check file extension
|
|
94
95
|
if path.suffix == '.py':
|
|
95
|
-
# Load .py file
|
|
96
|
+
# Load .py file - auto-detect format
|
|
96
97
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
97
98
|
content = f.read()
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
# Detect if it's Colab format (docstrings) or py:percent format (# %%)
|
|
101
|
+
if detect_colab_format(content):
|
|
102
|
+
print(f" Detected Colab format - auto-converting to py:percent")
|
|
103
|
+
# Parse Colab format (docstrings as markdown)
|
|
104
|
+
loaded_cells = parse_colab_py(content)
|
|
105
|
+
data = {
|
|
106
|
+
'cells': loaded_cells,
|
|
107
|
+
'metadata': {
|
|
108
|
+
'kernelspec': {
|
|
109
|
+
'display_name': 'Python 3',
|
|
110
|
+
'language': 'python',
|
|
111
|
+
'name': 'python3'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else:
|
|
116
|
+
# Parse py:percent format (# %% markers)
|
|
117
|
+
data = parse_py_percent(content)
|
|
118
|
+
|
|
100
119
|
loaded_cells = data.get('cells', [])
|
|
101
120
|
|
|
102
121
|
# Ensure stable IDs for all cells
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import List, Set
|
|
6
|
+
from typing import List, Set, Dict
|
|
7
7
|
from .py_percent_parser import generate_py_percent, parse_py_percent
|
|
8
8
|
|
|
9
9
|
|
|
@@ -97,10 +97,157 @@ def convert_ipynb_to_py(ipynb_path: Path, output_path: Path, include_uv_deps: bo
|
|
|
97
97
|
print(f" Run with: more-compute {output_path.name}")
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
def detect_colab_format(content: str) -> bool:
|
|
101
|
+
"""
|
|
102
|
+
Detect if a .py file is in Colab export format (docstrings as markdown).
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
content: Raw .py file content
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if appears to be Colab format, False otherwise
|
|
109
|
+
"""
|
|
110
|
+
# Check for actual cell markers (# %% at start of line)
|
|
111
|
+
# Must be ONLY # %% optionally followed by [markdown] or whitespace
|
|
112
|
+
# NOT # %%capture, # %%time, etc. (IPython magics)
|
|
113
|
+
has_cell_markers = bool(re.search(r'^\s*# %%\s*(?:\[markdown\])?\s*$', content, re.MULTILINE))
|
|
114
|
+
|
|
115
|
+
# If it has # %% markers, it's NOT Colab format (it's py:percent)
|
|
116
|
+
# Even if it has a Colab header comment!
|
|
117
|
+
if has_cell_markers:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
# Check for multi-line docstrings (Colab's markdown format)
|
|
121
|
+
has_docstrings = '"""' in content
|
|
122
|
+
|
|
123
|
+
# Colab format: has docstrings and no cell markers
|
|
124
|
+
return has_docstrings
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def parse_colab_py(content: str) -> List[Dict]:
|
|
128
|
+
"""
|
|
129
|
+
Parse Colab-exported .py file into cell structure.
|
|
130
|
+
|
|
131
|
+
Colab format uses:
|
|
132
|
+
- Multi-line docstrings ('''..''' or \"\"\"...\"\"\") for markdown cells
|
|
133
|
+
- Regular Python code for code cells
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
content: Raw .py file content from Colab export
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of cell dicts with 'cell_type' and 'source'
|
|
140
|
+
"""
|
|
141
|
+
cells = []
|
|
142
|
+
|
|
143
|
+
# Split on docstring boundaries
|
|
144
|
+
# Pattern matches both ''' and """ with optional content
|
|
145
|
+
pattern = r'("""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\')'
|
|
146
|
+
parts = re.split(pattern, content)
|
|
147
|
+
|
|
148
|
+
for part in parts:
|
|
149
|
+
part = part.strip()
|
|
150
|
+
if not part:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
# Check if this is a docstring (markdown)
|
|
154
|
+
if (part.startswith('"""') and part.endswith('"""')) or \
|
|
155
|
+
(part.startswith("'''") and part.endswith("'''")):
|
|
156
|
+
# It's a markdown cell
|
|
157
|
+
# Remove the triple quotes
|
|
158
|
+
markdown_content = part[3:-3].strip()
|
|
159
|
+
|
|
160
|
+
# Skip empty markdown cells
|
|
161
|
+
if markdown_content:
|
|
162
|
+
cells.append({
|
|
163
|
+
'cell_type': 'markdown',
|
|
164
|
+
'source': markdown_content,
|
|
165
|
+
'metadata': {}
|
|
166
|
+
})
|
|
167
|
+
else:
|
|
168
|
+
# It's a code cell
|
|
169
|
+
# Skip commented out code and special markers
|
|
170
|
+
if part and not part.startswith('# Commented out IPython magic'):
|
|
171
|
+
cells.append({
|
|
172
|
+
'cell_type': 'code',
|
|
173
|
+
'source': part,
|
|
174
|
+
'metadata': {},
|
|
175
|
+
'execution_count': None,
|
|
176
|
+
'outputs': []
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return cells
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def convert_colab_py_to_py_percent(input_path: Path, output_path: Path, include_uv_deps: bool = True) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Convert Colab-exported .py file to py:percent format (# %% markers).
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
input_path: Path to Colab .py file
|
|
188
|
+
output_path: Path to output .py file with # %% markers
|
|
189
|
+
include_uv_deps: Whether to extract and add UV inline script dependencies
|
|
190
|
+
"""
|
|
191
|
+
# Read Colab .py file
|
|
192
|
+
with open(input_path, 'r', encoding='utf-8') as f:
|
|
193
|
+
content = f.read()
|
|
194
|
+
|
|
195
|
+
# Detect format
|
|
196
|
+
if not detect_colab_format(content):
|
|
197
|
+
print(f"Warning: {input_path.name} doesn't appear to be in Colab format")
|
|
198
|
+
print(f" (Already has # %% markers or missing docstrings)")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# Parse Colab format into cells
|
|
202
|
+
cells = parse_colab_py(content)
|
|
203
|
+
|
|
204
|
+
if not cells:
|
|
205
|
+
print(f"Error: No cells found in {input_path.name}")
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
# Extract dependencies if requested
|
|
209
|
+
header_lines = []
|
|
210
|
+
if include_uv_deps:
|
|
211
|
+
# Create a temporary notebook structure to extract dependencies
|
|
212
|
+
temp_notebook = {'cells': cells}
|
|
213
|
+
dependencies = extract_pip_dependencies(temp_notebook)
|
|
214
|
+
|
|
215
|
+
if dependencies:
|
|
216
|
+
header_lines.append('# /// script')
|
|
217
|
+
header_lines.append('# dependencies = [')
|
|
218
|
+
for dep in sorted(dependencies):
|
|
219
|
+
header_lines.append(f'# "{dep}",')
|
|
220
|
+
header_lines.append('# ]')
|
|
221
|
+
header_lines.append('# ///')
|
|
222
|
+
header_lines.append('')
|
|
223
|
+
|
|
224
|
+
# Generate py:percent format
|
|
225
|
+
py_content = generate_py_percent(cells)
|
|
226
|
+
|
|
227
|
+
# Combine header and content
|
|
228
|
+
if header_lines:
|
|
229
|
+
final_content = '\n'.join(header_lines) + '\n' + py_content
|
|
230
|
+
else:
|
|
231
|
+
final_content = py_content
|
|
232
|
+
|
|
233
|
+
# Write output
|
|
234
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
235
|
+
f.write(final_content)
|
|
236
|
+
|
|
237
|
+
print(f"✓ Converted Colab format {input_path.name} → {output_path.name}")
|
|
238
|
+
print(f" Format: Colab docstrings → py:percent (# %%) markers")
|
|
239
|
+
|
|
240
|
+
if include_uv_deps and dependencies:
|
|
241
|
+
print(f" Found dependencies: {', '.join(sorted(dependencies))}")
|
|
242
|
+
print(f" Run with: more-compute {output_path.name}")
|
|
243
|
+
|
|
244
|
+
|
|
100
245
|
def convert_py_to_ipynb(py_path: Path, output_path: Path) -> None:
|
|
101
246
|
"""
|
|
102
247
|
Convert .py notebook to .ipynb format.
|
|
103
248
|
|
|
249
|
+
Automatically detects format (Colab, VSCode, JupyterLab).
|
|
250
|
+
|
|
104
251
|
Args:
|
|
105
252
|
py_path: Path to input .py file
|
|
106
253
|
output_path: Path to output .ipynb file
|
|
@@ -109,8 +256,29 @@ def convert_py_to_ipynb(py_path: Path, output_path: Path) -> None:
|
|
|
109
256
|
with open(py_path, 'r', encoding='utf-8') as f:
|
|
110
257
|
py_content = f.read()
|
|
111
258
|
|
|
112
|
-
#
|
|
113
|
-
|
|
259
|
+
# Detect format and parse accordingly
|
|
260
|
+
if detect_colab_format(py_content):
|
|
261
|
+
# Parse Colab format (docstrings as markdown)
|
|
262
|
+
cells = parse_colab_py(py_content)
|
|
263
|
+
notebook_data = {
|
|
264
|
+
'cells': cells,
|
|
265
|
+
'metadata': {
|
|
266
|
+
'kernelspec': {
|
|
267
|
+
'display_name': 'Python 3',
|
|
268
|
+
'language': 'python',
|
|
269
|
+
'name': 'python3'
|
|
270
|
+
},
|
|
271
|
+
'language_info': {
|
|
272
|
+
'name': 'python',
|
|
273
|
+
'version': '3.8.0'
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
'nbformat': 4,
|
|
277
|
+
'nbformat_minor': 4
|
|
278
|
+
}
|
|
279
|
+
else:
|
|
280
|
+
# Parse py:percent format (# %% or # In[N]:)
|
|
281
|
+
notebook_data = parse_py_percent(py_content)
|
|
114
282
|
|
|
115
283
|
# Ensure source is in list format (Jupyter notebook standard)
|
|
116
284
|
for cell in notebook_data.get('cells', []):
|
|
@@ -8,6 +8,10 @@ def parse_py_percent(content: str) -> Dict:
|
|
|
8
8
|
"""
|
|
9
9
|
Parse py:percent format Python file into notebook structure.
|
|
10
10
|
|
|
11
|
+
Supports multiple formats:
|
|
12
|
+
# %% - VSCode/PyCharm format
|
|
13
|
+
# In[N]: - JupyterLab export format
|
|
14
|
+
|
|
11
15
|
Format:
|
|
12
16
|
# %%
|
|
13
17
|
code cell content
|
|
@@ -23,9 +27,15 @@ def parse_py_percent(content: str) -> Dict:
|
|
|
23
27
|
"""
|
|
24
28
|
cells = []
|
|
25
29
|
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
# Check if using JupyterLab In[] format
|
|
31
|
+
has_in_markers = bool(re.search(r'# In\[\d+\]:', content))
|
|
32
|
+
|
|
33
|
+
if has_in_markers:
|
|
34
|
+
# Parse JupyterLab # In[N]: format
|
|
35
|
+
parts = re.split(r'(# In\[\d+\]:.*?\n)', content)
|
|
36
|
+
else:
|
|
37
|
+
# Parse VSCode # %% format
|
|
38
|
+
parts = re.split(r'(# %%.*?\n)', content)
|
|
29
39
|
|
|
30
40
|
# First part before any cell marker (usually imports/metadata)
|
|
31
41
|
if parts[0].strip():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|