Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions python/packages/jumpstarter-driver-someip/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ pip3 install --extra-index-url https://pkg.jumpstarter.dev/simple/ jumpstarter-d

## Configuration

| Parameter | Type | Default | Description |
|-------------------|--------|---------------|--------------------------------------------|
| `host` | str | required | Local IP address to bind |
| `port` | int | 30490 | Local SOME/IP port |
| `transport_mode` | str | `UDP` | Transport protocol: `UDP` or `TCP` |
| `multicast_group` | str | `239.127.0.1` | SD multicast group address |
| `multicast_port` | int | 30490 | SD multicast port |
| Parameter | Type | Default | Description |
|-------------------|-------------|---------------|-------------------------------------------------------|
| `host` | str | required | Local IP address to bind |
| `port` | int | 30490 | Local SOME/IP port |
| `transport_mode` | str | `UDP` | Transport protocol: `UDP` or `TCP` |
| `multicast_group` | str | `239.127.0.1` | SD multicast group address |
| `multicast_port` | int | 30490 | SD multicast port |
| `remote_host` | str \| None | `None` | Remote ECU IP for static routing (bypasses SD) |
| `remote_port` | int \| None | `None` | Remote ECU port (defaults to `port` when `remote_host` is set) |

### UDP (default)

Expand Down Expand Up @@ -48,6 +50,24 @@ export:
transport_mode: TCP
```

### Static remote endpoint (no Service Discovery)

When the target ECU does not run SOME/IP-SD (e.g. Zephyr firmware with
multicast TX disabled), set `remote_host` and optionally `remote_port`
to send messages directly without Service Discovery:

```yaml
export:
someip:
type: jumpstarter_driver_someip.driver.SomeIp
config:
host: "192.168.100.1"
port: 30490
transport_mode: UDP
remote_host: "192.168.100.10"
remote_port: 30490
```

## API Reference

### RPC
Expand Down
18 changes: 18 additions & 0 deletions python/packages/jumpstarter-driver-someip/examples/exporter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,21 @@ export:
transport_mode: UDP
multicast_group: "239.127.0.1"
multicast_port: 30490
---
# Static endpoint (no Service Discovery) — for ECUs that don't run SOME/IP-SD
apiVersion: jumpstarter.dev/v1alpha1
kind: ExporterConfig
metadata:
namespace: default
name: someip-static-exporter
endpoint: ""
token: ""
export:
someip:
type: jumpstarter_driver_someip.driver.SomeIp
config:
host: "192.168.100.1"
port: 30490
transport_mode: UDP
remote_host: "192.168.100.10"
remote_port: 30490
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class SomeIp(Driver):
transport_mode: str = "UDP"
multicast_group: str = "239.127.0.1"
multicast_port: int = 30490
remote_host: str | None = None
remote_port: int | None = None

_osip_client: OsipClient | None = field(init=False, repr=False, default=None)
_osip_config: ClientConfig = field(init=False, repr=False)
Expand All @@ -91,15 +93,33 @@ def __post_init__(self):
)
mode = TransportMode.TCP if transport_upper == "TCP" else TransportMode.UDP

self._osip_config = ClientConfig(
local_endpoint=Endpoint(self.host, self.port),
sd_config=SdConfig(
multicast_endpoint=Endpoint(self.multicast_group, self.multicast_port),
unicast_endpoint=Endpoint(self.host, self.port),
),
transport_mode=mode,
local_ep = Endpoint(self.host, self.port)
sd_cfg = SdConfig(
multicast_endpoint=Endpoint(self.multicast_group, self.multicast_port),
unicast_endpoint=Endpoint(self.host, self.port),
)

if self.remote_port is not None and self.remote_host is None:
raise ValueError("remote_port requires remote_host to be set")

if self.remote_host is not None:
remote_ep = Endpoint(
self.remote_host,
self.remote_port if self.remote_port is not None else self.port,
)
self._osip_config = ClientConfig(
local_endpoint=local_ep,
sd_config=sd_cfg,
transport_mode=mode,
remote_endpoint=remote_ep,
)
else:
self._osip_config = ClientConfig(
local_endpoint=local_ep,
sd_config=sd_cfg,
transport_mode=mode,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

def _ensure_client(self) -> OsipClient:
"""Create and start the OsipClient on first use (thread-safe)."""
if self._osip_client is None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,93 @@ def test_someip_tcp_transport_mode(mock_osip_cls):
assert config.transport_mode == TransportMode.TCP


# =========================================================================
# Static remote endpoint tests
# =========================================================================


@patch("jumpstarter_driver_someip.driver.ClientConfig")
@patch("jumpstarter_driver_someip.driver.OsipClient")
def test_someip_remote_endpoint_forwarded(mock_osip_cls, mock_config_cls):
"""Verify remote_host/remote_port are forwarded to ClientConfig."""
mock_osip_cls.return_value = _make_mock_osip_client()

driver = SomeIp(
host="192.168.100.1",
port=30490,
remote_host="192.168.100.10",
remote_port=31000,
)

with serve(driver) as client:
client.start()

kwargs = mock_config_cls.call_args[1]
assert kwargs["remote_endpoint"].ip == "192.168.100.10"
assert kwargs["remote_endpoint"].port == 31000


@patch("jumpstarter_driver_someip.driver.ClientConfig")
@patch("jumpstarter_driver_someip.driver.OsipClient")
def test_someip_remote_endpoint_defaults_port(mock_osip_cls, mock_config_cls):
"""When remote_port is omitted, it defaults to the local port."""
mock_osip_cls.return_value = _make_mock_osip_client()

driver = SomeIp(
host="192.168.100.1",
port=30490,
remote_host="192.168.100.10",
)

with serve(driver) as client:
client.start()

kwargs = mock_config_cls.call_args[1]
assert kwargs["remote_endpoint"].ip == "192.168.100.10"
assert kwargs["remote_endpoint"].port == 30490


@patch("jumpstarter_driver_someip.driver.ClientConfig")
@patch("jumpstarter_driver_someip.driver.OsipClient")
def test_someip_no_remote_endpoint_by_default(mock_osip_cls, mock_config_cls):
"""Without remote_host, remote_endpoint is not passed (SD-based discovery)."""
mock_osip_cls.return_value = _make_mock_osip_client()

driver = SomeIp(host="127.0.0.1", port=30490)

with serve(driver) as client:
client.start()

kwargs = mock_config_cls.call_args[1]
assert "remote_endpoint" not in kwargs


def test_someip_remote_port_without_remote_host_rejected():
"""remote_port without remote_host is rejected."""
with pytest.raises(ValueError, match="remote_port requires remote_host"):
SomeIp(host="127.0.0.1", port=30490, remote_port=31000)


@patch("jumpstarter_driver_someip.driver.ClientConfig")
@patch("jumpstarter_driver_someip.driver.OsipClient")
def test_someip_rpc_call_with_remote_endpoint(mock_osip_cls, _mock_config_cls):
"""RPC call works when a static remote endpoint is configured."""
mock_client = _make_mock_osip_client()
mock_osip_cls.return_value = mock_client

driver = SomeIp(
host="192.168.100.1",
port=30490,
remote_host="192.168.100.10",
remote_port=30490,
)
with serve(driver) as client:
resp = client.rpc_call(0x1234, 0x0001, b"\x01\x02\x03")
assert resp.service_id == 0x1234
assert resp.method_id == 0x0001
assert resp.payload == "010203"


# =========================================================================
# Stateful integration tests
#
Expand Down
6 changes: 5 additions & 1 deletion python/packages/jumpstarter-driver-someip/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ authors = [
requires-python = ">=3.11"
dependencies = [
"jumpstarter",
"opensomeip>=0.1.2,<0.2.0",
# TODO: replace with "opensomeip>=0.1.4,<0.2.0" once v0.1.4 is published on PyPI
"opensomeip @ git+https://github.com/vtz/opensomeip-python.git@ac1afdeb1ffa002ce3af4e5a3ca2c6fc9a690346",
Comment on lines +13 to +14
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether opensomeip 0.1.4 is yet available on PyPI
curl -s https://pypi.org/pypi/opensomeip/json | jq '.releases | keys'

Repository: jumpstarter-dev/jumpstarter

Length of output: 95


🏁 Script executed:

cat -n python/packages/jumpstarter-driver-someip/pyproject.toml

Repository: jumpstarter-dev/jumpstarter

Length of output: 1757


🏁 Script executed:

# Check if there are any issues/discussions about opensomeip 0.1.4 release
rg -i "opensomeip.*0\.1\.4|0\.1\.4.*opensomeip" --type py --type toml --type md

Repository: jumpstarter-dev/jumpstarter

Length of output: 212


🏁 Script executed:

# Check the specific commit referenced to understand why it's pinned
git log --oneline vtz/opensomeip-python 2>/dev/null | head -5 || echo "Commit info not directly accessible"

# Check if the driver package is in workspace members (indicating publication intent)
fd pyproject.toml -x grep -l "jumpstarter-driver-someip" {} \; 2>/dev/null | head -5

Repository: jumpstarter-dev/jumpstarter

Length of output: 202


🏁 Script executed:

# Search for mentions of 0.1.4 release in issues, discussions, or PR context
rg -i "0\.1\.4|opensomeip.*release" --type md --type py -B 2 -A 2

Repository: jumpstarter-dev/jumpstarter

Length of output: 53


🏁 Script executed:

# Check git log for context about why this specific commit was pinned
git log --all --oneline --grep="opensomeip\|someip" 2>/dev/null | head -20

Repository: jumpstarter-dev/jumpstarter

Length of output: 296


🏁 Script executed:

# Look for any build/CI configuration that might block publishing with direct references
find . -name "*.yml" -o -name "*.yaml" | xargs grep -l "publish\|pypi\|direct.*reference" 2>/dev/null | head -5

Repository: jumpstarter-dev/jumpstarter

Length of output: 288


Direct Git reference blocks PyPI publication and should be release-gated.

This dependency creates a known publication blocker:

  1. PyPI publication: Direct URL references are rejected by PyPI (PEP 440), so this package cannot be uploaded to PyPI while pinned to the Git commit. The TODO acknowledges this dependency is temporary pending opensomeip 0.1.4 release.

  2. Commit availability: The pinned commit ac1afdeb resides on the maintainer's fork. While the maintainer (vtz) controls both this driver and the opensomeip-python binding, the reliance on a single upstream commit creates a publication blocker until v0.1.4 is released on PyPI.

Ensure this is treated as a release-blocker: the package should not be published to PyPI while this Git reference is active, and the v0.1.4 PyPI release should be tracked as a prerequisite for publishing this driver.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@python/packages/jumpstarter-driver-someip/pyproject.toml` around lines 13 -
14, The pyproject.toml currently pins opensomeip to a direct git commit
("opensomeip @
git+https://github.com/vtz/opensomeip-python.git@ac1afdeb1ffa002ce3af4e5a3ca2c6fc9a690346")
which blocks PyPI publication; replace this git URL with the release version
spec "opensomeip>=0.1.4,<0.2.0" once v0.1.4 is published and add a release-gate
to your packaging/pipeline that fails publishing if any dependency string
contains a VCS/git URL (detect the "git+https://" pattern or the specific
opensomeip git ref) so the package cannot be uploaded while the temporary git
reference/TODO remains.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vtz can you release 0.1.4 for opensomeip and update this afterwards please?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

]

[project.entry-points."jumpstarter.drivers"]
Expand All @@ -22,6 +23,9 @@ dev = [
"pytest>=8.3.3",
]

[tool.hatch.metadata]
allow-direct-references = true

[tool.hatch.metadata.hooks.vcs.urls]
Homepage = "https://jumpstarter.dev"
source_archive = "https://github.com/jumpstarter-dev/repo/archive/{commit_hash}.zip"
Expand Down
42 changes: 10 additions & 32 deletions python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading