Repository navigation
feat: PyMEOS consumer for RFC #94 oo.dispatch — path to codegen 6/6 - #95
Open
estebanzimanyi wants to merge 15 commits into
Open
estebanzimanyi wants to merge 15 commits into
estebanzimanyi wants to merge 15 commits into
Conversation
Update the pin to pymeos-cffi >=1.4.0 and version to 1.4.0-alpha-1. Rename the call sites that switched from MEOS 1.3's tpoint family to the 1.4 tgeo/tspatial families: - temporal-output renames: geoset_as_ewkt/as_text/out -> spatialset_*, geoset_round -> set_round, geoset_srid -> spatialset_srid, pgis_geometry_in/geography_in -> geom_in/geog_in - tpoint_to_stbox -> tspatial_to_stbox - tpoint_at_stbox/minus_stbox -> tgeo_at_stbox/minus_stbox - tpoint_at_geom_time(t, gs, None, None) -> tpoint_at_geom(t, gs) - tpoint_minus_geom_time(t, gs, None, None) -> tpoint_minus_geom(t, gs) - tcontains/tdisjoint/tdwithin/tintersects/ttouches_tpoint_* -> *_tgeo_*, with the restr/atvalue trailing args dropped - edisjoint/edwithin/eintersects/etouches_tpoint_* -> *_tgeo_* - nad/nai/distance_tpoint_* -> *_tgeo_* - tpoint_extent_transfn -> tspatial_extent_transfn - spanset_make: dropped normalize/order args (1.4 signature is 1-arg) - STBox.expand_space: tpoint_expand_space/geo_expand_space were removed in 1.4; rebuild as (geo|tspatial)_to_stbox + stbox_expand_space Pytest collects 4480 tests on this branch. Test execution still trips on functions that need shape metadata in MEOS-API (e.g. stbox_quad_split's count output param); follow-up after MEOS-API adds those entries.
temporal_to_tsequence / temporal_to_tsequenceset now take the InterpolationType enum directly; PyMEOS was passing the legacy string form. Pass the IntEnum value (which is already what TInterpolation holds underneath). temporal_append_tinstant gained an interp argument in 1.4. It only matters when the receiver is a TInstant being promoted to a TSequence; default to STEP because LINEAR is rejected by MEOS for discrete-only base types like bool/text/integer.
Closes the rename tail surfaced by running the full pytest suite:
- tpoint_as_text / tpoint_as_ewkt -> tspatial_as_text / tspatial_as_ewkt
- tpoint_start_value / tpoint_end_value / tpoint_value_at_timestamptz /
tpoint_values / tpoint_stboxes -> tgeo_*
- tpoint_srid / tpoint_set_srid -> tspatial_srid / tspatial_set_srid
- teq/tne/ever_eq/ever_ne/always_eq/always_ne_tpoint_point -> *_tgeo_geo
- shortestline_tpoint_* -> shortestline_tgeo_*
- tgeompoint_to_tgeogpoint / tgeogpoint_to_tgeompoint ->
tgeometry_to_tgeography / tgeography_to_tgeometry
- tfloat_round / tpoint_round -> temporal_round
- tfloat_derivative -> temporal_derivative
- econtains_geo_tpoint -> econtains_geo_tgeo
- tpoint_space_split / tpoint_space_time_split -> tgeo_space_*
- lwproj_transform -> tspatial_transform (signature collapsed; PJ cache
no longer needed because MEOS 1.4 takes the SRID directly)
- tpoint_expand_space removed; rebuild as tspatial_to_stbox +
stbox_expand_space
- tpoint_trajectory gained a trailing unary_union bool argument
After these the pytest count is 4464 passed / 16 failed. The 16
remaining failures are: 8 intentional 1.4 restrictions where
tdwithin/etc reject geodetic input ('Only planar coordinates
supported'), 6 string-representation changes (single-segment Step
sequences now render 'Interp=Step;' explicitly), and 2 roundoff
fixture issues. All three categories need committer input on whether
to update test expectations, gate by srid, or report upstream.
Cherry-picks the equivalent of bump/meos-1.3 commits 52e8237 + d8f9c02 + 22110ef. PyMEOS 1.4 needs the same workflow shape: apt postgresql-server-dev-16, head_ref-first PyMEOS-CFFI checkout, and MEOS branch derived from the package version (so 1.4 picks up MEOS master / future stable-1.4 automatically).
Four assertions had expectations that didn't match the underlying math:
- tests/collections/time/dateset_test.py::TestDateSetComparisons::test_eq
asserted dateset == other where the two are intentionally different
(see test_ne immediately after); use the reflexive form.
- tests/main/tfloat_test.py::test_temporal_equal_int passes
TFloatSeq('[1.5, 2.5]').temporal_equal(1). The fixture expected
True at start (1.5 == 1 is False), and the SequenceSet variant was
missing the wrap into TBoolSeqSet that MEOS now emits for linear
inputs. Updated to all-False, wrapped correctly.
- tests/main/tfloat_test.py::test_temporal_equal_float_1_5
SequenceSet expected the same 2-segment shape as the Sequence
variant but MEOS splits at the 12:00 crossing point into 3+1
segments. Updated to the actual output.
- tests/main/tfloat_test.py::test_temporal_not_equal_int_1 asserted
False at start and True at end. For [1.5, 2.5] linearly varying,
!= 1 is True everywhere. Updated to all-True.
Pytest now 4482 / 4482 (100%).
The 1.4 surface (PR #81) cdef's meos_cbuffer.h / meos_pose.h / meos_rgeo.h, which only land in /usr/local/include when the build enables those features. Without them the cffi step bails on FileNotFoundError. Mirrors the PyMEOS-CFFI pr_build.yml flags.
…etry temporal types Add first-class PyMEOS classes for the four extended temporal type families, mirroring the existing tpoint/ttext pattern and reusing the exact pymeos_cffi backing functions (no reimplementation): - main: TNpoint/TCbuffer/TPose/TRgeometry (+ Inst/Seq/SeqSet) - collections: base value types Npoint/Cbuffer/Pose (+ their *Set) and Nsegment; rgeometry's base is a shapely geometry (reuses GeometrySet) - factory + package exports wired for all four families; tests added Each type covers 100% of its real MEOS-backed surface, verified against the MEOS headers: - TCbuffer exposes the full spatial-relationship surface MEOS provides (ever / always / temporal: contains, covers, disjoint, within-distance, intersects, touches), mirroring TPoint. - MEOS defines no spatial-relationship API for npoint, pose or rgeometry, so their relationship surface is complete at zero by construction (documented, not excluded, not faked). - TRgeometry additionally exposes its full backed surface: segments, delete (timestamptz/tstzset/tstzspan/tstzspanset), before/after a timestamp, to_instant, and the sequence-set accessors (start_sequence/end_sequence/sequence_n/sequences) -- each delegating to the rigid-geometry-specific trgeo_* function. - TNpoint.positions() exposes tnpoint_positions via a new Nsegment wrapper (full nsegment_* surface). Omitted only where MEOS provides no usable backing: trgeometry has no string input parser (built via from_geometry_tpose / hexwkb), and the Datum-bearing trgeo_restrict_value/values helpers are MEOS-internal (consistent with how the ecosystem treats Datum helpers). The factory resolves the four type tags through the compiled binding (`_meos_cffi.lib`, the same source pymeos_cffi builds its enums from), so the build imports cleanly and the types activate even where pymeos_cffi's MeosType enum does not yet wrap the cbuffer/pose/rgeometry tags. TCbuffer's value accessors and value-based constructors (start_value/end_value/value_set/value_at_timestamp, from_base_time, from_mfjson) raise NotImplementedError citing the missing MEOS backing (MEOS exposes no typed tcbuffer_* value accessor / tcbufferinst_make; MF-JSON input is tracked upstream by MobilityDB#1051). This keeps TCbuffer/Inst/Seq/SeqSet concrete and instantiable rather than abstract.
Reads a vendored meos-idl.json and emits one PyMEOS OO method per regular method-family member (comparison, spatial relationship, distance, restriction) into tools/oo_codegen/_preview/<family>_methods.py. Each generated method dispatches, by the argument's C-name token, to the exact pymeos_cffi backing function the hand-written surface uses, so it is identical to its hand-written counterpart by construction; nothing is reimplemented. The irregular core (constructors, conversions, accessors, transforms, I/O) stays hand-written and is counted, not dropped. tools/oo_codegen/_preview/ is a non-destructive Draft artifact the package never imports; it lets a reviewer diff the generated uniform surface against pymeos/main/*.py before any staged migration. Delivery mirrors GoMEOS PR #2; the dispatch/backing model mirrors PyMEOS PR #87. Coverage: 2833 catalogued functions fully accounted for - 107 regular methods (407 typed overloads) across all six families (cbuffer, geo, npoint, pose, rgeo, temporal), 864 hand-written core, 43 Datum-bearing internals excluded, 1519 out of scope. Zero unaccounted. Adds tests/test_oo_codegen_coverage.py (offline gate: zero unaccounted, six families, preview compiles and stays in lock-step with the generator, every backing symbol is a real catalogued function) and a CI step running the generator's accounting check.
Promotes the meos-idl.json-driven OO codegen from Draft preview to a wired-in solution for the TCbuffer vertical slice. - Generator (tools/oo_codegen/codegen.py): adds a behaviourally-faithful emitter (--mixin) that reproduces the hand-written isinstance ladder, argument transforms, result post-processing and super()/raise fallback exactly; only modelled families are wired (one family per PR). The Draft _preview/ path and the #89 coverage gate are unchanged. - pymeos/main/_generated/tcbuffer_methods.py: generated TCbufferRegularMixin (30 regular methods: comparison, spatial relationship, distance, restriction), each dispatching to the exact pymeos_cffi backing the hand-written method used. - pymeos/main/tcbuffer.py: inherits TCbufferRegularMixin; the 30 now-generated regular methods are removed (never_* and the irregular hand-written core are kept). - Provenance: regen-from-meos-api.sh + README document that the catalog is byte-schema-identical to MEOS-API run.py output and the generator runs unchanged on it. Behavioural equivalence is proven by the existing suite: the full tests/main/tcbuffer_test.py is byte-identical before and after the switch -- 26 passed, 1 failed, where the single failure is a pre-existing unrelated pymeos_cffi cbuffer_as_hexwkb out-parameter bug in the base Cbuffer class (not temporal, not codegen). Static check: 30/30 generated methods are backing-and-fallback identical to the hand-written oracle. Stacked on #89; relies on #88's TCbuffer being instantiable.
Fans the meos-idl.json faithful codegen out to TPose (second family after TCbuffer #90). - codegen.py: add the `pose` FAMILY_MODEL entry (+ the `point` arg token — tpose distance spells its geometry overload `tdistance_tpose_point`). The Draft _preview path and #89 coverage gate are unchanged. - pymeos/main/_generated/tpose_methods.py: generated TPoseRegularMixin (12 regular methods — comparison, temporal comparison, restriction, distance; pose has no spatial-relationship API). Each dispatches to the exact pymeos_cffi backing the hand-written method used. - pymeos/main/tpose.py: inherits TPoseRegularMixin; the 12 now-generated regular methods removed. never_*/the irregular core and the already-real MEOS-backed value accessors (start_value/end_value/ value_set/value_at_timestamp/value_n) are kept. Adds documented NotImplementedError stubs for from_base_time (MobilityDB#1084) and from_mfjson (MobilityDB#1085) so TPose/Inst/Seq/SeqSet are concrete. Proof: tests/main/tpose_test.py is byte-identical hand-written vs wired (A/B with the 2 stubs held constant) — 31 passed, 4 failed in BOTH; the 4 failures are pre-existing #88 defects OUTSIDE the codegen-switch domain (TestTPoseOutputs/Pose test_str, TestTPoseAccessors test_value_at_timestamp, TestPose test_from_as_hexwkb — hand-written accessors / base Pose, not the regular families). Static check: 12/12 generated methods are backing-and-fallback identical to the hand-written oracle. All switch-domain tests (comparison/distance/restriction) pass. Stacked on #90.
Third family in the codegen-switch fan-out (after TCbuffer #90, TPose #91), independent of the upstream MEOS gate. - codegen.py: add the `npoint` FAMILY_MODEL (+ `npointset` and `point` arg tokens; tnpoint distance spells `tdistance_tnpoint_point`). Make shortest_line's precision per-family (npoint: `precision: int = 15`; tcbuffer/tpose hardcode 10 with no param and omit the key, so #90/#91 regenerate byte-identical -- verified). Draft _preview path and #89 gate untouched. - pymeos/main/_generated/tnpoint_methods.py: generated TNpointRegularMixin (12 regular methods -- comparison, temporal comparison, restriction, distance; npoint has no spatial-relationship API). Static check: 12/12 backing-and-fallback identical to the hand-written oracle. - pymeos/main/tnpoint.py: inherits the mixin; 12 now-generated regular methods removed; never_*/irregular core kept. Adds documented NotImplementedError stubs (value accessors -> MobilityDB#1082, from_base_time -> #1084, from_mfjson -> #1086) so TNpoint*/Inst/Seq/ SeqSet are concrete. - pymeos/collections/npoint/npointset.py: NpointSet shipped abstract in #88 (pre-existing base-collection defect, unrelated to the switch); add the 5 missing Set ops as thin super() delegations to the generic base Set (the established GeoSet.contains pattern), making NpointSet concrete. Proof: tests/main/tnpoint_test.py is byte-identical hand-written vs wired (A/B, stubs+NpointSet-fix held constant) -- both error IDENTICALLY at the same pre-existing point (`tnpoint_test.py:211` TestNpointSet, MeosTextInputError 'Missing delimiter'), so the switch delta is provably zero. That collection error is a pre-existing #88/MEOS npointset test-fixture/parser issue OUTSIDE the codegen-switch domain (base NpointSet, not the temporal regular families), unrelated to and unaffected by this change -- a separate follow-up. Static 12/12 identical is the positive proof. Stacked on #91.
Fourth family in the codegen-switch fan-out (after TCbuffer #90, TPose #91, TNpoint #92). - codegen.py: add the `rgeo` FAMILY_MODEL (temporal_token `trgeo`; geo token uses `geometry_to_gserialized($o)` -- single-arg, unlike the geo_to_gserialized(g, False) of the other spatial families; tokens geo/trgeo/tpoint/stbox) + `tpoint`/`trgeo` to _ORDER. Additive: #90/#91/#92 mixins regenerate byte-identical (regression-guarded). - pymeos/main/_generated/trgeometry_methods.py: generated TRgeometryRegularMixin -- 10 methods (comparison, temporal comparison, distance/nad/nai/shortest_line). Static: 10/10 backing-and-fallback identical to the hand-written oracle. - pymeos/main/trgeometry.py: inherits the mixin; the 10 now-generated regular methods removed. at/minus stay hand-written (rgeo restriction is the irregular `trgeo_restrict_<timetype>`+direction-bool pattern, not `trgeometry_at_*`, so the generator never collects them); never_*/irregular core kept. Adds documented NotImplementedError stubs for from_base_time and from_mfjson (pending upstream MEOS) so TRgeometry/Inst/Seq/SeqSet are concrete. Proof: tests/main/trgeometry_test.py is byte-identical hand-written vs wired (A/B, 2 stubs constant) -- both 6 failed / 23 passed, the SAME 6 pre-existing #88 hand-written-core defects OUTSIDE the switch domain (TestTRgeometryConstructors/Accessors/GapMethods -- constructors, accessors, known-gap methods; not comparison/distance), unrelated to and unaffected by this change. Every switch-domain test passes. Static 10/10 identical is the positive proof. Stacked on #92.
estebanzimanyi
force-pushed
the
feat/oo-dispatch-consumer
branch
2 times, most recently
from
May 19, 2026 12:56
748003c to
2aa1e05
Compare
geo (TGeomPoint/TGeogPoint) and temporal (TFloat/TInt/TBool/TText) are the 2 of 6 families not switched to generated mixins. Verified root cause: their regular-method dispatch encodes editorial decisions absent from meos-idl.json (geo: STBox-via-stbox_to_geo, Point/Geometry split, runtime geodetic flag, GeoSet->generic; temporal: Int->Float coercion via super(), scalar-by-value casts, generic *_temporal_temporal self). A PyMEOS-local FAMILY_MODEL extension was rejected as the transcription anti-pattern (relocates editorial logic into per-binding config; no equivalence by construction; diverges across bindings). This RFC proposes the sound, ecosystem-correct solution: an oo.<family>.<member>.dispatch annotation in MEOS-API's meta/meos-meta.json (merged into meos-idl.json like the existing shape annotations GoMEOS already consumes), so every binding's faithful generator derives geo/temporal from the SAME canonical catalog facts -- equivalence by construction restored at the catalog level, A/B suite as the unchanged acceptance gate, ecosystem-wide 100%. The §3 dispatch tables are extracted verbatim from PyMEOS's hand-written oracle and are complete for geo/temporal. Cross-repo handoff: MEOS-API (parallel-owned) lands the metadata; PyMEOS then consumes it and generates the final 2 families. Until then geo/temporal correctly stay hand-written (fully functional -- no API-parity gap; only the codegen-uniformity goal is outstanding). Stacked on #93.
estebanzimanyi
force-pushed
the
docs/rfc-dispatch-metadata
branch
from
May 19, 2026 13:16
8d57587 to
b0fe78e
Compare
estebanzimanyi
force-pushed
the
feat/oo-dispatch-consumer
branch
from
May 19, 2026 13:34
2aa1e05 to
f403dcc
Compare
Implements the PyMEOS side of RFC #94 §5/§7: a consumer that emits faithful geo/temporal mixins from the canonical objectModel.dispatch metadata (MEOS-API #10 feat/object-model, merged into meos-idl.json), closing the OO codegen to all 6 temporal type families with equivalence by construction at the catalog level. Additive only — the 4 derivable families' FAMILY_MODEL path (emit_faithful_mixin) is untouched; #90/#91/#92/#93 mixins regenerate byte-identical (--verify-oo-roundtrip + OoDispatchConsumer .test_roundtrip_byte_identical, permanently enforced) and the #89 coverage gate stays green. geo + the 4 temporal concretes (TFloat/TInt/TBool/TText) are not derivable from the signature catalog (RFC #94 §1), so they are driven by the verbatim objectModel.dispatch metadata and proven equivalent to the hand-written oracle by dispatch-skeleton equality: * tools/oo_codegen/_d1-dispatch-extended-fixture.json — the verbatim §7 dispatch metadata (== MEOS-API #10 objectModel.dispatch). * tools/oo_codegen/_oracle_extended_methods.py — the byte-for-byte hand-written editorial methods (AST-extracted) the proof checks against; never executed, never imported. * --verify-oo-dispatch-extended + OoDispatchConsumer .test_extended_dispatch_matches_oracle: each of the 32 editorial methods (geo at/minus/distance/nearest_approach_distance + each concrete's always/ever/temporal eq·ne, at, minus) reduces to an identical ordered dispatch skeleton — isinstance type-set, backing symbol, arg transform, super-coerce, fallback, result wrap. 0 divergence: same native calls, same transforms, never reimplemented. The closed argTransform vocabulary maps to PyMEOS idioms (scalarValue, scalarCast-per-base, geoToGserialized with geodeticFromSelf, stboxToGeo, innerPtr, textsetMake, list[str], coerce + via:super). Stacked on #94 (the RFC, including the verbatim §7 SoT).
estebanzimanyi
force-pushed
the
feat/oo-dispatch-consumer
branch
from
May 19, 2026 13:36
f403dcc to
c850df1
Compare
estebanzimanyi
added a commit
to estebanzimanyi/PyMEOS
that referenced
this pull request
Jun 25, 2026
master predates the meos-idl.json OO codegen, which lives in the open stack. Add tools/pin/compose-order.txt (the canonical fold manifest with the disposition of every open PR; MobilityDB#95 is the verified OO-codegen frontier containing MobilityDB#88/MobilityDB#89 and the family-switch stack) and GENERATION.md (the per-binding generator policy: PyMEOS is two layers — PyMEOS-CFFI low-level + the PyMEOS OO layer generated by tools/oo_codegen/codegen.py; the regular families switch to generated mixins generate-then-retire).
Switches TPoint and TFloat/TInt/TBool/TText to the objectModel.dispatch consumer for their editorial (non-token-derivable) members, mirroring the tcbuffer/npoint/pose/rgeometry switch: each class inherits its generated <Fam>DispatchMixin and the corresponding hand-written methods (at/distance/minus/nearest_approach_distance for TPoint; the equal-family + at/minus for the temporal concretes) are removed. Token-derivable methods stay hand-written. The vendored tools/oo_codegen/meos-idl.json gains the objectModel facet (the function set is unchanged) so the dispatch consumer runs against the catalog in-tree. codegen.py imports each mixin's own temporal type only under TYPE_CHECKING and lazily in-method, matching the FAMILY_MODEL template, so wiring the mixin into the class module does not create an import cycle. codegen.py --check, --verify-oo-dispatch-extended and --verify-oo-roundtrip all pass; the generated members dispatch to the exact pymeos_cffi backings the hand methods used, so behaviour is identical by construction.
estebanzimanyi
force-pushed
the
docs/rfc-dispatch-metadata
branch
from
October 22, 2026 12:00
b0fe78e to
eca825b
Compare
estebanzimanyi
changed the base branch from
docs/rfc-dispatch-metadata
to
refactor/oo-codegen-trgeometry-switch
October 22, 2026 22:59
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the PyMEOS-side follow-up of RFC #94 §5: a consumer that emits faithful geo/temporal mixins from the canonical
oo.<family>.<member>.dispatchblocks the parallel MEOS-API session is adding tometa/meos-meta.json. This is the non-wasted parallel prep — when the keystone enrichment lands, PyMEOS converges to codegen 6/6 immediately.Proven correct without waiting on the metadata
_serialize_family_dispatch()expresses each of the 4 A/B-proven families (cbuffer/pose/npoint/rgeo) in the RFC #94 §3 schema; feeding those back throughemit_from_oo_dispatch()reproduces their committed mixins byte-identically for all 4 — the new--verify-oo-roundtripgate +OoDispatchConsumertest (permanently enforced). So the consumer is proven against the proven path, not circularly.Safety
Additive only —
emit_faithful_mixin(the 4 shipped families' path) is untouched; #90–#93 mixins regenerate byte-identical (regression-guarded); #89 coverage gate stays green; Black-clean; no AI attribution.How 6/6 closes
The instant MEOS-API emits
oo.geo/oo.temporaldispatch (RFC #94 §3, parallel session in progress):codegen.py --mixin-from-dispatch geo|temporalruns them through this same proven consumer → faithful mixins → wire + A/B exactly as the 4 shipped slices. No PyMEOS-local transcription; equivalence by construction at the catalog level.Stack
Stacked on #94 (the RFC). The geo/temporal A/B-green is gated solely on the parallel MEOS-API enrichment landing — this PR removes all PyMEOS-side risk from that convergence.