plash-cli 0.1.2__tar.gz → 0.2.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.
- {plash_cli-0.1.2/plash_cli.egg-info → plash_cli-0.2.0}/PKG-INFO +6 -1
- {plash_cli-0.1.2 → plash_cli-0.2.0}/README.md +5 -0
- plash_cli-0.2.0/plash_cli/__init__.py +1 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli/_bash_magic.py +11 -7
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli/_modidx.py +1 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli/core.py +34 -23
- {plash_cli-0.1.2 → plash_cli-0.2.0/plash_cli.egg-info}/PKG-INFO +6 -1
- {plash_cli-0.1.2 → plash_cli-0.2.0}/settings.ini +2 -2
- plash_cli-0.1.2/plash_cli/__init__.py +0 -1
- {plash_cli-0.1.2 → plash_cli-0.2.0}/LICENSE +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/MANIFEST.in +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/SOURCES.txt +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/dependency_links.txt +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/entry_points.txt +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/not-zip-safe +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/requires.txt +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/plash_cli.egg-info/top_level.txt +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/pyproject.toml +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/setup.cfg +0 -0
- {plash_cli-0.1.2 → plash_cli-0.2.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plash_cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: CLI for the Plash hosting service
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/plash_cli
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -133,6 +133,11 @@ serve()
|
|
|
133
133
|
This is a basic [FastHTML](https://fastht.ml) app. Plash also supports
|
|
134
134
|
other popular python frameworks.
|
|
135
135
|
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
>
|
|
138
|
+
> Your app must run on port `5001` which is the default port for
|
|
139
|
+
> FastHTML apps.
|
|
140
|
+
|
|
136
141
|
And create the `requirements.txt` file:
|
|
137
142
|
|
|
138
143
|
``` python
|
|
@@ -92,6 +92,11 @@ serve()
|
|
|
92
92
|
This is a basic [FastHTML](https://fastht.ml) app. Plash also supports
|
|
93
93
|
other popular python frameworks.
|
|
94
94
|
|
|
95
|
+
> [!WARNING]
|
|
96
|
+
>
|
|
97
|
+
> Your app must run on port `5001` which is the default port for
|
|
98
|
+
> FastHTML apps.
|
|
99
|
+
|
|
95
100
|
And create the `requirements.txt` file:
|
|
96
101
|
|
|
97
102
|
``` python
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -6,20 +6,24 @@ from pathlib import Path
|
|
|
6
6
|
bk = BashKernel()
|
|
7
7
|
ip = get_ipython()
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# Find all $`varname` patterns and interpolate from IPython namespace
|
|
14
|
-
for match in re.finditer(r'\$`([^`]+)`', code):
|
|
9
|
+
def interpolate_variables(text):
|
|
10
|
+
"""Find all $`varname` patterns and interpolate from IPython namespace"""
|
|
11
|
+
for match in re.finditer(r'\$`([^`]+)`', text):
|
|
15
12
|
varname = match.group(1)
|
|
16
13
|
if varname not in ip.user_ns:
|
|
17
14
|
raise NameError(f"Variable '{varname}' not found in IPython namespace")
|
|
18
|
-
|
|
15
|
+
text = text.replace(match.group(0), str(ip.user_ns[varname]))
|
|
16
|
+
return text
|
|
17
|
+
|
|
18
|
+
@register_line_cell_magic
|
|
19
|
+
def bash(line, cell=None):
|
|
20
|
+
code = f'{line}\n{cell}' if cell else line
|
|
21
|
+
code = interpolate_variables(code)
|
|
19
22
|
print(bk.bashwrapper.run_command(code))
|
|
20
23
|
|
|
21
24
|
@register_line_cell_magic
|
|
22
25
|
def writefile(line, cell):
|
|
26
|
+
cell = interpolate_variables(cell)
|
|
23
27
|
pwd = bk.bashwrapper.run_command('pwd').strip()
|
|
24
28
|
fpath = Path(pwd) / line
|
|
25
29
|
fpath.write_text(cell)
|
|
@@ -7,6 +7,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
7
7
|
'lib_path': 'plash_cli'},
|
|
8
8
|
'syms': { 'plash_cli.core': { 'plash_cli.core.PlashError': ('core.html#plasherror', 'plash_cli/core.py'),
|
|
9
9
|
'plash_cli.core._deps': ('core.html#_deps', 'plash_cli/core.py'),
|
|
10
|
+
'plash_cli.core._gen_app_name': ('core.html#_gen_app_name', 'plash_cli/core.py'),
|
|
10
11
|
'plash_cli.core.apps': ('core.html#apps', 'plash_cli/core.py'),
|
|
11
12
|
'plash_cli.core.create_tar_archive': ('core.html#create_tar_archive', 'plash_cli/core.py'),
|
|
12
13
|
'plash_cli.core.delete': ('core.html#delete', 'plash_cli/core.py'),
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
6
|
__all__ = ['PLASH_CONFIG_HOME', 'PLASH_DOMAIN', 'pat', 'stop', 'start', 'log_modes', 'get_client', 'mk_auth_req', 'get_app_name',
|
|
7
|
-
'endpoint', 'is_included', '
|
|
7
|
+
'endpoint', 'is_included', 'PlashError', 'poll_cookies', 'login', 'validate_app', 'create_tar_archive',
|
|
8
8
|
'deploy', 'view', 'delete', 'endpoint_func', 'logs', 'download', 'apps']
|
|
9
9
|
|
|
10
10
|
# %% ../nbs/00_core.ipynb 2
|
|
11
11
|
from fastcore.all import *
|
|
12
12
|
from fastcore.xdg import *
|
|
13
|
-
import secrets, webbrowser, json, httpx, io, tarfile
|
|
13
|
+
import secrets, webbrowser, json, httpx, io, tarfile, random, string
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from uuid import uuid4
|
|
16
16
|
from time import time, sleep
|
|
@@ -62,7 +62,10 @@ def is_included(path):
|
|
|
62
62
|
'.vscode', '.idea', '.sesskey'}
|
|
63
63
|
return not any(p in excludes for p in path.parts)
|
|
64
64
|
|
|
65
|
-
# %% ../nbs/00_core.ipynb
|
|
65
|
+
# %% ../nbs/00_core.ipynb 11
|
|
66
|
+
class PlashError(Exception): pass
|
|
67
|
+
|
|
68
|
+
# %% ../nbs/00_core.ipynb 13
|
|
66
69
|
def poll_cookies(paircode, interval=1, timeout=180):
|
|
67
70
|
"Poll server for token until received or timeout"
|
|
68
71
|
start = time()
|
|
@@ -87,7 +90,7 @@ def login():
|
|
|
87
90
|
print(f"Authentication successful! Config saved to {PLASH_CONFIG_HOME}")
|
|
88
91
|
else: print("Authentication timed out.")
|
|
89
92
|
|
|
90
|
-
# %% ../nbs/00_core.ipynb
|
|
93
|
+
# %% ../nbs/00_core.ipynb 16
|
|
91
94
|
pat = r'(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$'
|
|
92
95
|
|
|
93
96
|
def _deps(script: bytes | str) -> dict | None:
|
|
@@ -102,9 +105,7 @@ def _deps(script: bytes | str) -> dict | None:
|
|
|
102
105
|
return '\n'.join(tomllib.loads(content)['dependencies'])
|
|
103
106
|
else: return None
|
|
104
107
|
|
|
105
|
-
# %% ../nbs/00_core.ipynb
|
|
106
|
-
class PlashError(Exception): pass
|
|
107
|
-
|
|
108
|
+
# %% ../nbs/00_core.ipynb 19
|
|
108
109
|
def validate_app(path):
|
|
109
110
|
"Validates directory `path` is a deployable Plash app"
|
|
110
111
|
if not (path / 'main.py').exists():
|
|
@@ -114,10 +115,11 @@ def validate_app(path):
|
|
|
114
115
|
raise PlashError('A Plash app should not contain both a requirements.txt file and inline dependencies (see PEP723).')
|
|
115
116
|
|
|
116
117
|
# %% ../nbs/00_core.ipynb 23
|
|
117
|
-
def create_tar_archive(path:Path) -> tuple[io.BytesIO, int]:
|
|
118
|
+
def create_tar_archive(path:Path, force_data:bool=False) -> tuple[io.BytesIO, int]:
|
|
118
119
|
"Creates a tar archive of a directory, excluding files based on is_included"
|
|
119
120
|
tarz = io.BytesIO()
|
|
120
121
|
files = L(path if path.is_file() else Path(path).iterdir()).filter(is_included)
|
|
122
|
+
if not force_data: files = files.filter(lambda f: f.name != 'data')
|
|
121
123
|
with tarfile.open(fileobj=tarz, mode='w:gz') as tar:
|
|
122
124
|
for f in files: tar.add(f, arcname=f.name)
|
|
123
125
|
if deps:=_deps((path / 'main.py').read_bytes()):
|
|
@@ -127,12 +129,21 @@ def create_tar_archive(path:Path) -> tuple[io.BytesIO, int]:
|
|
|
127
129
|
tarz.seek(0)
|
|
128
130
|
return tarz, len(files)
|
|
129
131
|
|
|
130
|
-
# %% ../nbs/00_core.ipynb
|
|
132
|
+
# %% ../nbs/00_core.ipynb 25
|
|
133
|
+
def _gen_app_name():
|
|
134
|
+
adjectives = ['admiring', 'adoring', 'amazing', 'awesome', 'beautiful', 'blissful', 'bold', 'brave', 'busy', 'charming', 'clever', 'compassionate', 'confident', 'cool', 'dazzling', 'determined', 'dreamy', 'eager', 'ecstatic', 'elastic', 'elated', 'elegant', 'epic', 'exciting', 'fervent', 'festive', 'flamboyant', 'focused', 'friendly', 'frosty', 'funny', 'gallant', 'gifted', 'goofy', 'gracious', 'great', 'happy', 'hopeful', 'hungry', 'inspiring', 'intelligent', 'interesting', 'jolly', 'jovial', 'keen', 'kind', 'laughing', 'loving', 'lucid', 'magical', 'modest', 'nice', 'nifty', 'nostalgic', 'objective', 'optimistic', 'peaceful', 'pensive', 'practical', 'priceless', 'quirky', 'quizzical', 'relaxed', 'reverent', 'romantic', 'serene', 'sharp', 'silly', 'sleepy', 'stoic', 'sweet', 'tender', 'trusting', 'upbeat', 'vibrant', 'vigilant', 'vigorous', 'wizardly', 'wonderful', 'youthful', 'zealous', 'zen', 'golden', 'silver', 'crimson', 'azure', 'emerald', 'violet', 'amber', 'coral', 'turquoise', 'lavender', 'minty', 'citrus', 'vanilla', 'woody', 'floral', 'fresh', 'gentle', 'sparkling', 'precise', 'curious']
|
|
135
|
+
nouns = ['tiger', 'eagle', 'river', 'mountain', 'forest', 'ocean', 'star', 'moon', 'wind', 'dragon', 'phoenix', 'wolf', 'bear', 'lion', 'shark', 'falcon', 'raven', 'crystal', 'diamond', 'ruby', 'sapphire', 'pearl', 'wave', 'tide', 'cloud', 'rainbow', 'sunset', 'sunrise', 'galaxy', 'comet', 'meteor', 'planet', 'nebula', 'cosmos', 'universe', 'atom', 'photon', 'quantum', 'matrix', 'cipher', 'code', 'signal', 'pulse', 'beam', 'ray', 'spark', 'frost', 'ice', 'snow', 'mist', 'fog', 'dew', 'rain', 'hail', 'helix', 'prism', 'lens', 'mirror', 'echo', 'heart', 'mind', 'dream', 'vision', 'hope', 'wish', 'magic', 'spell', 'charm', 'rune', 'symbol', 'token', 'key', 'door', 'gate', 'bridge', 'tower', 'castle', 'fortress', 'shield', 'dolphin', 'whale', 'penguin', 'butterfly', 'hummingbird', 'deer', 'rabbit', 'fox', 'otter', 'panda', 'koala', 'zebra', 'giraffe', 'elephant', 'valley', 'canyon', 'meadow', 'prairie', 'island', 'lake', 'pond', 'stream', 'waterfall', 'cliff', 'peak', 'hill', 'grove', 'garden', 'sunlight', 'breeze', 'melody', 'sparkle', 'whirlpool', 'windmill', 'carousel', 'spiral', 'glow']
|
|
136
|
+
verbs = ['runs', 'flies', 'jumps', 'builds', 'creates', 'flows', 'shines', 'grows', 'moves', 'works', 'dances', 'sings', 'plays', 'dreams', 'thinks', 'learns', 'teaches', 'helps', 'heals', 'saves', 'protects', 'guards', 'watches', 'sees', 'hears', 'feels', 'knows', 'understands', 'discovers', 'explores', 'searches', 'finds', 'seeks', 'holds', 'carries', 'lifts', 'pushes', 'pulls', 'makes', 'crafts', 'forges', 'shapes', 'forms', 'molds', 'carves', 'joins', 'connects', 'links', 'binds', 'ties', 'opens', 'closes', 'starts', 'stops', 'begins', 'ends', 'finishes', 'completes', 'wins', 'triumphs', 'succeeds', 'achieves', 'accomplishes', 'reaches', 'arrives', 'departs', 'leaves', 'returns', 'comes', 'goes', 'travels', 'journeys', 'walks', 'sprints', 'races', 'speeds', 'rushes', 'hurries', 'waits', 'pauses', 'rests', 'sleeps', 'wakes', 'rises', 'climbs', 'ascends', 'descends', 'swims', 'dives', 'surfs', 'sails', 'paddles', 'hikes', 'treks', 'wanders', 'roams', 'ventures', 'navigates', 'glides', 'soars', 'floats', 'drifts', 'tosses', 'divides', 'shares', 'secures', 'settles', 'places', 'wonders', 'questions']
|
|
137
|
+
suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=3))
|
|
138
|
+
return f"{random.choice(adjectives)}-{random.choice(nouns)}-{random.choice(verbs)}-{suffix}"
|
|
139
|
+
|
|
140
|
+
# %% ../nbs/00_core.ipynb 26
|
|
131
141
|
@call_parse
|
|
132
142
|
def deploy(
|
|
133
|
-
path:Path=Path('.'),
|
|
134
|
-
name:str=None
|
|
135
|
-
|
|
143
|
+
path:Path=Path('.'), # Path to project
|
|
144
|
+
name:str=None, # Overrides the .plash file in project root if provided
|
|
145
|
+
force_data:bool=False): # Overwrite data/ directory during deployment
|
|
146
|
+
"Deploy app to production, ignores paths starting with '.', excludes data/ directory by default unless --force_data is used."
|
|
136
147
|
print('Initializing deployment...')
|
|
137
148
|
if name == '': print('Error: App name cannot be an empty string'); return
|
|
138
149
|
if not path.is_dir(): print("Error: Path should point to the project directory"); return
|
|
@@ -143,18 +154,18 @@ def deploy(
|
|
|
143
154
|
if not name: name = get_app_name(path)
|
|
144
155
|
except FileNotFoundError:
|
|
145
156
|
plash_app = path / '.plash'
|
|
146
|
-
name =
|
|
157
|
+
name = _gen_app_name()
|
|
147
158
|
plash_app.write_text(f'export PLASH_APP_NAME={name}')
|
|
148
|
-
|
|
149
159
|
|
|
150
|
-
tarz, _ = create_tar_archive(path)
|
|
151
|
-
resp = mk_auth_req(endpoint(rt="/upload"), "post", files={'file': tarz}, timeout=300.0,
|
|
160
|
+
tarz, _ = create_tar_archive(path, force_data)
|
|
161
|
+
resp = mk_auth_req(endpoint(rt="/upload"), "post", files={'file': tarz}, timeout=300.0,
|
|
162
|
+
data={'name': name, 'force_data': force_data})
|
|
152
163
|
if resp.status_code == 200:
|
|
153
164
|
print('✅ Upload complete! Your app is currently being built.')
|
|
154
165
|
print(f'It will be live at {name if '.' in name else endpoint(sub=name)}')
|
|
155
166
|
else: print(f'Failure: {resp.status_code}\n{resp.text}')
|
|
156
167
|
|
|
157
|
-
# %% ../nbs/00_core.ipynb
|
|
168
|
+
# %% ../nbs/00_core.ipynb 28
|
|
158
169
|
@call_parse
|
|
159
170
|
def view(
|
|
160
171
|
path:Path=Path('.'), # Path to project directory
|
|
@@ -166,7 +177,7 @@ def view(
|
|
|
166
177
|
print(f"Opening browser to view app :\n{url}\n")
|
|
167
178
|
webbrowser.open(url)
|
|
168
179
|
|
|
169
|
-
# %% ../nbs/00_core.ipynb
|
|
180
|
+
# %% ../nbs/00_core.ipynb 30
|
|
170
181
|
@call_parse
|
|
171
182
|
def delete(
|
|
172
183
|
path:Path=Path('.'), # Path to project
|
|
@@ -184,7 +195,7 @@ def delete(
|
|
|
184
195
|
r = mk_auth_req(endpoint(rt=f"/delete?name={name}"), "delete")
|
|
185
196
|
return r.text
|
|
186
197
|
|
|
187
|
-
# %% ../nbs/00_core.ipynb
|
|
198
|
+
# %% ../nbs/00_core.ipynb 32
|
|
188
199
|
def endpoint_func(endpoint_name):
|
|
189
200
|
'Creates a function for a specific API endpoint'
|
|
190
201
|
def func(
|
|
@@ -205,10 +216,10 @@ def endpoint_func(endpoint_name):
|
|
|
205
216
|
stop = endpoint_func('/stop')
|
|
206
217
|
start = endpoint_func('/start')
|
|
207
218
|
|
|
208
|
-
# %% ../nbs/00_core.ipynb
|
|
219
|
+
# %% ../nbs/00_core.ipynb 34
|
|
209
220
|
log_modes = str_enum('log_modes', 'build', 'app')
|
|
210
221
|
|
|
211
|
-
# %% ../nbs/00_core.ipynb
|
|
222
|
+
# %% ../nbs/00_core.ipynb 35
|
|
212
223
|
@call_parse
|
|
213
224
|
def logs(
|
|
214
225
|
path:Path=Path('.'), # Path to project
|
|
@@ -234,7 +245,7 @@ def logs(
|
|
|
234
245
|
r = mk_auth_req(endpoint(rt=f"/logs?name={name}&mode={mode}"))
|
|
235
246
|
return r.text
|
|
236
247
|
|
|
237
|
-
# %% ../nbs/00_core.ipynb
|
|
248
|
+
# %% ../nbs/00_core.ipynb 37
|
|
238
249
|
@call_parse
|
|
239
250
|
def download(
|
|
240
251
|
path:Path=Path('.'), # Path to project
|
|
@@ -250,7 +261,7 @@ def download(
|
|
|
250
261
|
with tarfile.open(fileobj=file_bytes, mode="r:gz") as tar: tar.extractall(path=save_path)
|
|
251
262
|
print(f"Downloaded your app to: {save_path}")
|
|
252
263
|
|
|
253
|
-
# %% ../nbs/00_core.ipynb
|
|
264
|
+
# %% ../nbs/00_core.ipynb 39
|
|
254
265
|
@call_parse
|
|
255
266
|
def apps(verbose:bool=False):
|
|
256
267
|
"List your deployed apps (verbose shows status table: 1=running, 0=stopped)"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plash_cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: CLI for the Plash hosting service
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/plash_cli
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -133,6 +133,11 @@ serve()
|
|
|
133
133
|
This is a basic [FastHTML](https://fastht.ml) app. Plash also supports
|
|
134
134
|
other popular python frameworks.
|
|
135
135
|
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
>
|
|
138
|
+
> Your app must run on port `5001` which is the default port for
|
|
139
|
+
> FastHTML apps.
|
|
140
|
+
|
|
136
141
|
And create the `requirements.txt` file:
|
|
137
142
|
|
|
138
143
|
``` python
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
|
+
jupyter_hooks = False
|
|
2
3
|
repo = plash_cli
|
|
3
4
|
lib_name = plash_cli
|
|
4
|
-
version = 0.
|
|
5
|
+
version = 0.2.0
|
|
5
6
|
min_python = 3.7
|
|
6
7
|
license = apache2
|
|
7
8
|
black_formatting = False
|
|
@@ -40,7 +41,6 @@ console_scripts = plash_deploy=plash_cli.core:deploy
|
|
|
40
41
|
readme_nb = index.ipynb
|
|
41
42
|
allowed_metadata_keys =
|
|
42
43
|
allowed_cell_metadata_keys =
|
|
43
|
-
jupyter_hooks = False
|
|
44
44
|
clean_ids = True
|
|
45
45
|
clear_all = False
|
|
46
46
|
cell_number = True
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|