Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 1eaa846

Browse files
authored
Merge branch 'main' into output-mode
2 parents 6b66ae6 + 299c5c1 commit 1eaa846

6 files changed

Lines changed: 59 additions & 15 deletions

File tree

docs/source/installation/python-package.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Python Packages and CLI
22

33
## Release install
4+
5+
```{warning}
6+
Until we release 0.6.0 please continue to use the [developer install method](#development-install). 0.5.0 is
7+
heavily outdated and not fully released to pip, it is not recommended now.
8+
```
9+
410
The [Jumpstarter Python packages](https://docs.jumpstarter.dev/packages/)
511
contain all the necessary tools to run an exporter or interact with your
612
hardware as a client.
Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
11
from contextlib import asynccontextmanager
22
from dataclasses import dataclass, field
33

4+
from anyio import (
5+
create_memory_object_stream,
6+
)
7+
from anyio._backends._asyncio import StreamReaderWrapper, StreamWriterWrapper
48
from anyio.abc import ObjectStream
5-
from anyio.to_thread import run_sync
6-
from serial import Serial, serial_for_url
9+
from anyio.streams.stapled import StapledObjectStream
10+
from serial import serial_for_url
11+
from serial_asyncio import open_serial_connection
712

813
from jumpstarter.driver import Driver, exportstream
914

15+
LOOP = "loop://"
16+
1017

1118
@dataclass(kw_only=True)
1219
class AsyncSerial(ObjectStream):
13-
device: Serial
20+
reader: StreamReaderWrapper
21+
writer: StreamWriterWrapper
1422

1523
async def send(self, item):
16-
await run_sync(self.device.write, item)
24+
await self.writer.send(item)
1725

1826
async def receive(self):
19-
size = max(self.device.in_waiting, 1)
20-
return await run_sync(self.device.read, size)
27+
return await self.reader.receive()
2128

2229
async def send_eof(self):
23-
await run_sync(self.device.close)
30+
pass
2431

2532
async def aclose(self):
26-
await run_sync(self.device.close)
33+
await self.writer.aclose()
34+
await self.reader.aclose()
2735

2836

2937
@dataclass(kw_only=True)
@@ -35,7 +43,7 @@ class PySerial(Driver):
3543
def __post_init__(self):
3644
if hasattr(super(), "__post_init__"):
3745
super().__post_init__()
38-
if self.check_present:
46+
if self.check_present and self.url != LOOP:
3947
serial_for_url(self.url, baudrate=self.baudrate)
4048

4149
@classmethod
@@ -46,7 +54,16 @@ def client(cls) -> str:
4654
@asynccontextmanager
4755
async def connect(self):
4856
self.logger.info("Connecting to %s, baudrate: %d", self.url, self.baudrate)
49-
device = await run_sync(serial_for_url, self.url, self.baudrate)
50-
async with AsyncSerial(device=device) as stream:
51-
yield stream
52-
self.logger.info("Disconnected from %s", self.url)
57+
if self.url != LOOP:
58+
reader, writer = await open_serial_connection(url=self.url, baudrate=self.baudrate, limit=1)
59+
writer.transport.set_write_buffer_limits(high=4096, low=0)
60+
async with AsyncSerial(
61+
reader=StreamReaderWrapper(reader),
62+
writer=StreamWriterWrapper(writer),
63+
) as stream:
64+
yield stream
65+
self.logger.info("Disconnected from %s", self.url)
66+
else:
67+
tx, rx = create_memory_object_stream[bytes](32)
68+
async with StapledObjectStream(tx, rx) as stream:
69+
yield stream

packages/jumpstarter-driver-pyserial/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ dependencies = [
1313
"jumpstarter",
1414
"jumpstarter-driver-network",
1515
"pyserial>=3.5",
16-
"asyncclick>=8.1.7.2"
16+
"asyncclick>=8.1.7.2",
17+
"pyserial-asyncio>=0.6",
1718
]
1819

1920
[dependency-groups]

packages/jumpstarter/jumpstarter/config/common.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
from os import getenv
2+
from pathlib import Path
3+
14
from pydantic import BaseModel
25
from xdg_base_dirs import xdg_config_home
36

7+
from .env import JMP_CLIENT_CONFIG_HOME
8+
49
CONFIG_API_VERSION = "jumpstarter.dev/v1alpha1"
5-
CONFIG_PATH = xdg_config_home() / "jumpstarter"
10+
CONFIG_PATH = Path(getenv(JMP_CLIENT_CONFIG_HOME, xdg_config_home() / "jumpstarter"))
611

712

813
class ObjectMeta(BaseModel):

packages/jumpstarter/jumpstarter/config/env.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Common environment variables for client/exporter config
2+
JMP_CLIENT_CONFIG_HOME = "JMP_CLIENT_CONFIG_HOME"
23
JMP_CLIENT_CONFIG = "JMP_CLIENT_CONFIG"
34
JMP_NAMESPACE = "JMP_NAMESPACE"
45
JMP_NAME = "JMP_NAME"

uv.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)