Skip to content

feat(typespec): nested LIST + categorical dtype fidelity, Pandas categorical (item 54, gaps 2-3) - #295

Merged
discreteds merged 6 commits into
developfrom
feature/typespec-list-categorical-fidelity
Aug 17, 2026
Merged

feat(typespec): nested LIST + categorical dtype fidelity, Pandas categorical (item 54, gaps 2-3)#295
discreteds merged 6 commits into
developfrom
feature/typespec-list-categorical-fidelity

Conversation

@discreteds

Copy link
Copy Markdown
Member

Summary

Item 54, PR-2 (gaps 2–3), stacked on PR #294 (gap 1 + validation strictness — same develop baseline; will be rebased if #294 merges first).

This PR:

  • Gap 2 — nested LIST inner type via the existing Frictionless item_type carriage: _resolve_field_native now parameterizes a bare list container on all applicable targets (pl.List(Int64) / nw.List(Int64) / pa.list_(pa.int64()) / "array<int64>"; Pandas stays "object" — no native parameterized list dtype). Also fixes a second latent bug: PyArrow's bare fallback silently defaulted every untyped list to a string element.
  • Gap 3 — categorical fidelity: new shared categorical_values() helper (typespec/_categorical.py) consumed by BOTH conform's stage-5b and the converters (previously copy-paste-identical inline extraction). to_polars_schema maps categories/categoriesOrderedpl.Enum([...]) / pl.Categorical, categories-first precedence (mirrors conform stage 5 branch order — a field with both categories and an invalid backend_type never raises). Pandas un-deferred: to_pandas_dtypes return type widened Dict[str,str]Dict[str,Any] (zero production callers — verified), categorical fields now return a real pd.CategoricalDtype instance.
  • Blast-radius + cross-backend materialization (spec §7 tests 7–8): chain tests prove item 42's empty_frame AND item 53's inline-read cast both produce the upgraded dtypes (the shared-resolver design's whole point), plus Ibis/Narwhals backend-native schema + executed-op assertions post-wrap, plus the no-raise partner regression (backend_type=None/"" still falls through).
  • Gap 4 (backend_type="Enum"/"Categorical" bare forms) resolves as a documented consequence — no separate code.
  • OBJECT/STRUCT inner schema remains out of scope → tracked as new item 102 (user-confirmed carve-out).

Spec: 2026-08-16-converters-resolver-dtype-fidelity-design.md (rev 2, GLM-5.2 adversarial review folded). Plan tasks 8–13, RED-first TDD, one commit per task.

Verification

  • Full suite: 22964 passed, 0 failed (baseline 22935 + 29 new)
  • hatch run ruff:check: clean
  • hatch run mypy:check: 98 errors — identical count to baseline (all pre-existing import-untyped/fixture errors; touched files contribute none new)
  • Narwhals version floor re-verified empirically (task 13): Array/Enum/Categorical/Decimal all present + top-level exported + DTypeClass instantiation mechanism present at the pinned floor 2.20.0 — no floor change needed

…yArrow strings; review round (GLM-5.2)

Adversarial whole-branch review (opencode-go/glm-5.2, high reasoning, job 001)
verdict: DONE_WITH_CONCERNS — Approved with one Important + two Minor findings.
All folded:

Important 1: PyArrow regex branches propagated raw Arrow constructor
ValueErrors (timestamp[badunit], decimal128(999, 10), time64[badunit])
instead of returning None, breaking the InvalidBackendTypeError contract
in _resolve_field_native. Regex constructors now wrapped in try/except
(KeyError, ValueError) -> None; resolver-level regression test asserts the
typed error surfaces through to_arrow_schema (plan-mandated gap: spec
§2.2's code omitted the wrap).

Minor 1: target_narwhals redundant disjunct (isinstance(result, type) or
result is not None == result is not None) simplified, intent documented.

Minor 2: inline-read blast-radius test was Polars-only; parametrized
cross-backend with boundary-honest categorical assertion (ibis/Arrow
converts Enum -> String — documented boundary conversion, not a defect).

Traceability: test_namespace_classes_present_at_pinned_floor documents
task 13's floor verification (narwhals 2.20.0 wheel inspected 2026-08-17).

Full suite: 22974 passed / 0 failed. ruff: clean.
@discreteds
discreteds force-pushed the feature/typespec-list-categorical-fidelity branch from a15d51c to d5e5446 Compare August 17, 2026 04:52
@discreteds
discreteds merged commit badb092 into develop Aug 17, 2026
4 checks passed
@discreteds
discreteds deleted the feature/typespec-list-categorical-fidelity branch August 17, 2026 05:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant