Skip to content
Open
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
50 changes: 34 additions & 16 deletions .github/actions/python-maturin/pre-merge/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ description: Python pre-merge testing with maturin github iggy actions

inputs:
task:
description: "Task to run (lint, test, build)"
description: "Task to run (lint, test)"
required: true

runs:
Expand Down Expand Up @@ -90,6 +90,39 @@ runs:
echo "pyrefly version: $(uv run pyrefly --version)"
shell: bash

- name: Build Python wheel
if: inputs.task == 'test'
run: |
cd foreign/python

# Build the module
echo "Building Python wheel..."
uv run maturin build -o dist

# List built artifacts
echo ""
echo "Build artifacts:"
ls -la dist/
shell: bash

- name: Check apache_iggy.pyi is up to date
if: inputs.task == 'test'
run: |
# stub_gen must run from foreign/python; a subdirectory corrupts the stub.
cd foreign/python
cargo run --bin stub_gen

# The tracked stub is post-ruff. Format first: the raw output leaves
# whitespace on blank lines, which `check` reports but only `format` fixes.
uv run --no-sync ruff format apache_iggy.pyi
uv run --no-sync ruff check --fix apache_iggy.pyi

if ! git diff --exit-code -- apache_iggy.pyi; then
echo "::error::apache_iggy.pyi is out of date. Run 'cargo run --bin stub_gen' from foreign/python, let the ruff hooks format it, and commit the result."
exit 1
fi
shell: bash

- name: Build Python wheel with coverage instrumentation
if: inputs.task == 'test'
run: |
Expand All @@ -105,21 +138,6 @@ runs:
uv run --no-sync maturin develop
shell: bash

- name: Build Python wheel
if: inputs.task == 'build'
run: |
cd foreign/python

# Build the module
echo "Building Python wheel..."
uv run maturin build -o dist

# List built artifacts
echo ""
echo "Build artifacts:"
ls -la dist/
shell: bash

- name: Build server Docker image for TLS tests
if: inputs.task == 'test'
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/config/components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ components:
- "ci-infrastructure" # CI changes trigger full regression
paths:
- "foreign/python/**"
tasks: ["lint", "test", "build"]
tasks: ["lint", "test"]

sdk-php:
depends_on:
Expand Down
3 changes: 1 addition & 2 deletions foreign/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,11 @@ Every installation below compiles the Rust extension, so you'll need:
pytest tests/ -v # make sure iggy-server is running and the venv is activated
```

4. To update the stubs, only after changing the pyo3 API surface (nothing in CI checks stub freshness, so unconditional regen just invites `.pyi` churn), use
4. To update the stubs, after changing the pyo3 API surface, use

```bash
# run from foreign/python
cargo run --bin stub_gen
# TODO: Known bug: running this from a subdirectory of `foreign/python` corrupts the tracked stub, see https://github.com/apache/iggy/pull/3825/changes/BASE..773a27971b4ddb7b44773ded395ed23afb1de4c9#r3727691619
```

5. Before committing, test the pre-commit and pre-push hooks. `prek` only inspects staged content, so stage your work first:
Expand Down
50 changes: 25 additions & 25 deletions foreign/python/apache_iggy.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,27 @@ class IggyClient:
Sends a ping request to the server to check connectivity.
Raises `RuntimeError` if the connection fails.
"""
def describe_options(
self, scope: builtins.str
) -> collections.abc.Awaitable[list[OptionSpec]]:
r"""
Describe the option catalog for a resource scope.

This is the discovery surface for the `options` argument on
`create_topic`/`update_topic`: a key outside the catalog is refused at
create, and the binary transports carry only the error code back.

Args:
scope: One of `"topic"`, `"stream"`, `"user"`.

Returns:
An awaitable that resolves to `list[OptionSpec]`, empty for a scope
with no keys yet.

Raises:
ValueError: If the scope name is not one of the three above.
RuntimeError: If the request fails.
"""
def login_user(
self, username: builtins.str, password: builtins.str
) -> collections.abc.Awaitable[None]:
Expand Down Expand Up @@ -1034,27 +1055,6 @@ class IggyClient:
Returns the stream details, or `None` if the stream does not exist.
Raises `RuntimeError` on failure.
"""
def describe_options(
self, scope: builtins.str
) -> collections.abc.Awaitable[builtins.list[OptionSpec]]:
r"""
Describe the option catalog for a resource scope.

This is the discovery surface for the `options` argument on
`create_topic`/`update_topic`: a key outside the catalog is refused at
create, and the binary transports carry only the error code back.

Args:
scope: One of `"topic"`, `"stream"`, `"user"`.

Returns:
An awaitable that resolves to `list[OptionSpec]`, empty for a scope
with no keys yet.

Raises:
ValueError: If the scope name is not one of the three above.
RuntimeError: If the request fails.
"""
def create_topic(
self,
stream: builtins.str | builtins.int,
Expand Down Expand Up @@ -2103,8 +2103,8 @@ class Topic:
r"""
Options admission resolved for the keys the client did not send.

Same shape as `options`. These would have resolved differently under
another server configuration.
Same shape as `options`. These would have resolved differently
under another server configuration.
"""

@typing.final
Expand Down Expand Up @@ -2168,8 +2168,8 @@ class TopicDetails:
r"""
Options admission resolved for the keys the client did not send.

Same shape as `options`. These would have resolved differently under
another server configuration.
Same shape as `options`. These would have resolved differently
under another server configuration.
"""
@property
def partitions(self) -> builtins.list[Partition]:
Expand Down
11 changes: 3 additions & 8 deletions foreign/python/src/bin/stub_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,9 @@ fn main() -> Result<()> {
// `stub_info` is a function defined by `define_stub_info_gatherer!` macro.
let stub = apache_iggy::client::stub_info()?;
stub.generate()?;
let path = Path::new(file!())
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("apache_iggy.pyi");
// Anchor on the manifest dir: `stub.generate()` writes to the crate root, so
// a cwd-relative path leaves the tracked stub without its license header.
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("apache_iggy.pyi");
let mut f = File::open(&path)?;
let mut content = LICENSE.as_bytes().to_owned();
f.read_to_end(&mut content)?;
Expand Down
4 changes: 2 additions & 2 deletions foreign/python/src/topic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ impl Topic {

/// Options admission resolved for the keys the client did not send.
///
/// Same shape as [`Self::options`]. These would have resolved differently
/// Same shape as `options`. These would have resolved differently
/// under another server configuration.
#[getter]
pub fn derived_options<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, UserHeaders>> {
Expand Down Expand Up @@ -345,7 +345,7 @@ impl TopicDetails {

/// Options admission resolved for the keys the client did not send.
///
/// Same shape as [`Self::options`]. These would have resolved differently
/// Same shape as `options`. These would have resolved differently
/// under another server configuration.
#[getter]
pub fn derived_options<'a>(&self, py: Python<'a>) -> PyResult<Bound<'a, UserHeaders>> {
Expand Down
Loading