pythonhere 0.4.0__py3-none-any.whl → 0.4.1__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.
- pythonhere/.agents/skills/pythonhere/SKILL.md +3 -1
- pythonhere/.agents/skills/pythonhere/references/tasks.md +58 -0
- pythonhere/main.py +3 -0
- pythonhere/server_here.py +1 -0
- pythonhere/tasks_here.py +1292 -0
- pythonhere/ui_here/connection_address_here.kv +40 -0
- pythonhere/version_here.py +1 -1
- {pythonhere-0.4.0.dist-info → pythonhere-0.4.1.dist-info}/METADATA +3 -2
- {pythonhere-0.4.0.dist-info → pythonhere-0.4.1.dist-info}/RECORD +12 -10
- {pythonhere-0.4.0.dist-info → pythonhere-0.4.1.dist-info}/WHEEL +0 -0
- {pythonhere-0.4.0.dist-info → pythonhere-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {pythonhere-0.4.0.dist-info → pythonhere-0.4.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pythonhere
|
|
3
|
-
description: "Build, inspect, and debug PythonHere applications by executing Python through the `there` CLI in an already-running Kivy/Python-for-Android process. Use when a task targets a PythonHere device or `there.env` connection and involves live Kivy UI, KV language, Android APIs, Pyjnius, permissions, installed Android packages, media/files, Plyer device features, BLE with able, MIDI with midistream, or on-device AI with LiteRT-LM and `.litertlm` models."
|
|
3
|
+
description: "Build, inspect, and debug PythonHere applications by executing Python through the `there` CLI in an already-running Kivy/Python-for-Android process. Use when a task targets a PythonHere device or `there.env` connection and involves live Kivy UI, KV language, Android APIs, Pyjnius, permissions, installed Android packages, media/files, Plyer device features, BLE with able, MIDI with midistream, durable on-device task runs, or on-device AI with LiteRT-LM and `.litertlm` models."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Operate PythonHere with `there`
|
|
@@ -42,6 +42,8 @@ when the task depends on it broadly or requires its exact current contents.
|
|
|
42
42
|
- Consult [midi.md](references/midi.md) for MIDI or synthesizer work.
|
|
43
43
|
- Consult [litert.md](references/litert.md) for PythonHere's ML model helpers
|
|
44
44
|
and LiteRT-LM usage.
|
|
45
|
+
- Consult [tasks.md](references/tasks.md) for durable sequential work that must
|
|
46
|
+
continue after a client disconnect or recover after PythonHere restarts.
|
|
45
47
|
|
|
46
48
|
Combine references when a feature genuinely needs details from several
|
|
47
49
|
concerns; for example, an Android gallery may need permission, MediaStore, and
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Durable task runs
|
|
2
|
+
|
|
3
|
+
Use `TaskRunner` for sequential work that must continue after the client
|
|
4
|
+
disconnects, retain progress across a PythonHere restart, or retry individual
|
|
5
|
+
items.
|
|
6
|
+
|
|
7
|
+
## Start a run
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from tasks_here import TaskRunner
|
|
11
|
+
|
|
12
|
+
runner = TaskRunner.create("data-import", max_attempts=3)
|
|
13
|
+
runner.add([
|
|
14
|
+
{"id": "part-1", "path": "one.json"},
|
|
15
|
+
{"id": "part-2", "path": "two.json"},
|
|
16
|
+
])
|
|
17
|
+
runner.start(execute_task)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Tasks and results must contain plain JSON-compatible values. Every task needs a
|
|
21
|
+
unique nonempty string `id`. `start()` returns promptly and processes one task
|
|
22
|
+
at a time. Preserve the runner in a named global when later commands need it.
|
|
23
|
+
|
|
24
|
+
Inspect `runner.status()`, `runner.results()`, `runner.get(id)`, or
|
|
25
|
+
`runner.failed()` through normal `there get` calls. `pause()` finishes the
|
|
26
|
+
current item and stops before claiming another.
|
|
27
|
+
|
|
28
|
+
## Wait and inspect the outcome
|
|
29
|
+
|
|
30
|
+
After starting the runner, wait in bounded intervals through the CLI:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
there --json --timeout 60 get --worker \
|
|
34
|
+
"{'stopped': runner.wait(timeout=50), 'status': runner.status()}"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Here, `--worker` keeps the wait off the app's event loop.
|
|
38
|
+
Keep the CLI timeout longer than the wait timeout to allow time for connection
|
|
39
|
+
and response. If `stopped` is `false`, tasks continue running; repeat the command
|
|
40
|
+
to wait again. A `true` value only means the worker is no longer running (or was
|
|
41
|
+
never started), not that every task succeeded. Inspect `status.state`,
|
|
42
|
+
`status.failed`, and `status.worker_error`. A `completed` state includes tasks
|
|
43
|
+
that exhausted their retries; inspect `runner.failed()` for their errors.
|
|
44
|
+
|
|
45
|
+
## Resume after restart
|
|
46
|
+
|
|
47
|
+
After PythonHere restarts, reopen the named run, recreate resources and the
|
|
48
|
+
executor callable, then restart execution:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
runner = TaskRunner.open("data-import")
|
|
52
|
+
runner.start(execute_task)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Execution is at least once: an item interrupted by process death can run again.
|
|
56
|
+
Make irreversible executor side effects idempotent or check their durable state
|
|
57
|
+
before applying them. The executor runs in a background thread; marshal Kivy UI
|
|
58
|
+
access to the main thread according to `kivy-runtime.md`.
|
pythonhere/main.py
CHANGED
|
@@ -26,6 +26,7 @@ from kivy.app import App
|
|
|
26
26
|
from kivy.clock import Clock
|
|
27
27
|
from kivy.config import Config, ConfigParser
|
|
28
28
|
from kivy.logger import Logger
|
|
29
|
+
from kivy.properties import StringProperty # pylint: disable=no-name-in-module
|
|
29
30
|
from patches_here import monkeypatch_kivy
|
|
30
31
|
from server_here import run_ssh_server
|
|
31
32
|
from window_here import reset_window_environment
|
|
@@ -42,6 +43,8 @@ def configure_logging():
|
|
|
42
43
|
class PythonHereApp(App):
|
|
43
44
|
"""PythonHere main app."""
|
|
44
45
|
|
|
46
|
+
ssh_host_key_sha256 = StringProperty("")
|
|
47
|
+
|
|
45
48
|
def __init__(self):
|
|
46
49
|
super().__init__()
|
|
47
50
|
self.server_task: asyncio.Task | None = None
|
pythonhere/server_here.py
CHANGED
|
@@ -39,6 +39,7 @@ async def run_ssh_server(app):
|
|
|
39
39
|
namespace=app.ssh_server_namespace,
|
|
40
40
|
server_factory=PythonHereServer,
|
|
41
41
|
)
|
|
42
|
+
app.ssh_host_key_sha256 = server.host_key_sha256
|
|
42
43
|
except Exception as exc:
|
|
43
44
|
Logger.error("PythonHere: SSH server start error")
|
|
44
45
|
Logger.exception(exc)
|