-
Notifications
You must be signed in to change notification settings - Fork 23
feat(model): Expose chunks_task_count_override on the v1 iterator #344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mainline
Are you sure you want to change the base?
Changes from all commits
6bb9e07
6b3d9be
e152030
6485825
99fdecf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -123,14 +123,52 @@ pub(crate) struct PyStepParameterSpaceIterator { | |
| /// every `NodeIterator` impl is `Send + Sync` (enforced at the | ||
| /// trait bound in `openjd-model`). | ||
| iter: Mutex<StepParameterSpaceIterator>, | ||
| /// The `chunks_task_count_override` this was constructed with, kept | ||
| /// so `__getitem__` can rebuild an equivalent iterator. Without it, | ||
| /// random access would silently fall back to the template's own | ||
| /// `defaultTaskCount` and disagree with iteration. | ||
| chunk_override: Option<usize>, | ||
| } | ||
|
|
||
| #[cfg_attr(feature = "stub-gen", gen_stub_pymethods)] | ||
| #[pymethods] | ||
| impl PyStepParameterSpaceIterator { | ||
| /// Construct an iterator over a step's parameter space. | ||
| /// | ||
| /// `chunks_task_count_override` overrides the `defaultTaskCount` of a | ||
| /// `CHUNK[INT]` parameter and turns adaptive chunking off, so a chunked space | ||
| /// can be walked at a caller-chosen granularity. Pass `1` to iterate individual | ||
| /// tasks. Ignored when the space has no chunked parameter, matching the | ||
| /// pure-Python reference — though a non-positive value is still rejected in | ||
| /// that case, since validating an argument is cheaper to reason about than | ||
| /// silently discarding a bad one. | ||
| /// | ||
| /// Without this, a statically chunked space could only be walked at the | ||
| /// template's own chunk size: `chunks_default_task_count` is settable for | ||
| /// adaptive spaces only. | ||
| #[new] | ||
| #[pyo3(signature = (*, step=None, space=None))] | ||
| fn new(step: Option<&PyStep>, space: Option<&PyStepParameterSpace>) -> PyResult<Self> { | ||
| #[pyo3(signature = (*, step=None, space=None, chunks_task_count_override=None))] | ||
| fn new( | ||
| step: Option<&PyStep>, | ||
| space: Option<&PyStepParameterSpace>, | ||
| chunks_task_count_override: Option<i64>, | ||
| ) -> PyResult<Self> { | ||
| // Taken as i64, not usize, so a negative value reaches this check instead of | ||
| // failing pyo3's unsigned extraction with OverflowError. Every non-positive | ||
| // value should report the same ValueError the message and the spec promise. | ||
| // | ||
| // Rejected rather than clamped: openjd-model applies `.max(1)` to the override, | ||
| // so 0 would silently mean 1. The `chunks_default_task_count` setter already | ||
| // refuses 0, and the two should not disagree. | ||
| let chunks_task_count_override: Option<usize> = match chunks_task_count_override { | ||
| Some(n) if n <= 0 => { | ||
|
leongdl marked this conversation as resolved.
|
||
| return Err(pyo3::exceptions::PyValueError::new_err( | ||
| "chunks_task_count_override must be a positive integer.", | ||
| )); | ||
| } | ||
| Some(n) => Some(n as usize), | ||
| None => None, | ||
| }; | ||
| let ps = if let Some(s) = space { | ||
| s.inner.clone() | ||
| } else if let Some(st) = step { | ||
|
|
@@ -148,14 +186,17 @@ impl PyStepParameterSpaceIterator { | |
| combination: None, | ||
| } | ||
| }; | ||
| let iter = StepParameterSpaceIterator::new(&ps).map_err(model_err_to_py)?; | ||
| let iter = | ||
| StepParameterSpaceIterator::new_with_chunk_override(&ps, chunks_task_count_override) | ||
| .map_err(model_err_to_py)?; | ||
| let len = iter.len(); | ||
| let names = iter.names().clone(); | ||
| Ok(Self { | ||
| space: ps, | ||
| len, | ||
| names, | ||
| iter: Mutex::new(iter), | ||
| chunk_override: chunks_task_count_override, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -185,8 +226,12 @@ impl PyStepParameterSpaceIterator { | |
| index as usize | ||
| }; | ||
| // Random access uses a fresh iterator — don't disturb the | ||
| // persistent iter's cursor or its adaptive Arc. | ||
| let iter = StepParameterSpaceIterator::new(&self.space).map_err(model_err_to_py)?; | ||
| // persistent iter's cursor or its adaptive Arc. It must carry the | ||
| // same chunk override, or indexing would report chunks that | ||
| // iteration never yields. | ||
| let iter = | ||
| StepParameterSpaceIterator::new_with_chunk_override(&self.space, self.chunk_override) | ||
|
leongdl marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The invariant this change codifies — "indexing must not report chunks that iteration never yields" — is still violated on the setter path, which this PR leaves untouched.
it = StepParameterSpaceIterator(step=step) # template defaultTaskCount = 10
it.chunks_default_task_count = 5
next(it) # a 5-task chunk, e.g. "1-5"
it[0] # rebuilt fresh at defaultTaskCount=10 -> "1-10"
Two options that would close it without much code: have Not introduced here, but the comment added on these lines now asserts the property, and it does not hold for the mutation route. |
||
| .map_err(model_err_to_py)?; | ||
| match iter.get(idx) { | ||
| Some(params) => task_param_set_to_py(py, ¶ms), | ||
| None => Err(pyo3::exceptions::PyIndexError::new_err( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2381,7 +2381,23 @@ class StepParameterSpaceIterator: | |
| *, | ||
| step: typing.Optional[Step] = None, | ||
| space: typing.Optional[StepParameterSpace] = None, | ||
| ) -> StepParameterSpaceIterator: ... | ||
| chunks_task_count_override: typing.Optional[builtins.int] = None, | ||
| ) -> StepParameterSpaceIterator: | ||
| r""" | ||
| Construct an iterator over a step's parameter space. | ||
|
|
||
| `chunks_task_count_override` overrides the `defaultTaskCount` of a | ||
| `CHUNK[INT]` parameter and turns adaptive chunking off, so a chunked space | ||
| can be walked at a caller-chosen granularity. Pass `1` to iterate individual | ||
| tasks. Ignored when the space has no chunked parameter, matching the | ||
| pure-Python reference -- though a non-positive value is still rejected in | ||
|
leongdl marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This docstring looks hand-edited rather than regenerated, so the checked-in stub will not round-trip through The Rust source (
but here it is So the next person who runs the documented regeneration flow ( |
||
| that case. | ||
|
|
||
| Without this, a statically chunked space could only be walked at the | ||
| template's own chunk size: `chunks_default_task_count` is settable for | ||
| adaptive spaces only. | ||
| """ | ||
|
|
||
| def __len__(self) -> builtins.int: ... | ||
| def __getitem__(self, index: builtins.int) -> dict: ... | ||
| def __iter__(self) -> StepParameterSpaceIterator: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.