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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plash_cli
3
- Version: 0.1.2
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
- @register_line_cell_magic
10
- def bash(line, cell=None):
11
- code = f'{line}\n{cell}' if cell else line
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
- code = code.replace(match.group(0), str(ip.user_ns[varname]))
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', 'poll_cookies', 'login', 'PlashError', 'validate_app', 'create_tar_archive',
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 12
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 15
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 18
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 24
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('.'), # Path to project
134
- name:str=None): # Overrides the .plash file in project root if provided
135
- "Deploy app to production (ignores paths starting with '.')"
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 = f'fasthtml-app-{str(uuid4())[:8]}'
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, data={'name': name})
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 26
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 28
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 30
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 32
219
+ # %% ../nbs/00_core.ipynb 34
209
220
  log_modes = str_enum('log_modes', 'build', 'app')
210
221
 
211
- # %% ../nbs/00_core.ipynb 33
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 35
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 37
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.1.2
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.1.2
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