Skip to content

Contributing to pytuik

Thanks for helping out. pytuik is a small package with a clear shape, so most contributions are quick — this file exists mainly to record the conventions that keep it that way.

Setting up

git clone https://github.com/gohnJalt/pytuik
cd pytuik
python -m venv .venv && source .venv/bin/activate   # or: uv venv
pip install -e ".[dev,geo,docs]"

Python ≥ 3.10. The geo extra pulls in geopandas (and GEOS/pyproj underneath); skip it if you are not touching geo_map(geometry=True) — the test that needs it is skipped automatically when it is missing.

The checks CI runs

ruff check .          # lint (E, F, I, UP, B, W; line length 100)
mypy                  # --strict over src/
pytest --cov=pytuik   # unit + mocked-transport tests; coverage gate is 90%

All three must pass. ruff format is available if you prefer it to fixing whitespace by hand.

Architecture rules

Two layering rules carry most of the design (see DESIGN.md §5):

  1. client.py owns all HTTP. The portal, SDMX, and geo modules receive a TuikClient, build URLs, and parse responses. They never import httpx. This keeps the cookie session dance, retry policy, and header discipline in one place.
  2. Parsers are pure functions. (json_or_xml) -> DataFrame wherever possible, at the same granularity as tuikr's helpers. Every parser must be testable from a fixture with no network.

Everything SDMX-specific stays behind _sdmx_parser.py. The structure message is parsed with sdmx1; the generic data message is parsed with lxml, because sdmx1 cannot read a generic data message without a DSD ("No DSD when creating DataSet") and that would break the labels=False fast path.

Public API changes need a matching update to DESIGN.md §4 — the design doc is the contract, not an archive.

Tests

  • tests/unit/ — pure parsers and validators, fed from tests/fixtures/.
  • tests/test_*.py — client behavior with respx-mocked transports: the session dance, retries, envelope errors, cache TTL.
  • tests/contract/ — live-network tests, see below.

New fixtures should be trimmed real responses, not invented ones. Keep any upstream defect the parser has to survive — the array-wrapped ["FeatureCollection"] in the geometry fixtures is there on purpose.

Contract tests

pytest -m contract

These hit the real TUIK services and are excluded from the default run. They assert shapes and magnitudes that TUIK is unlikely to change (26 regions, 81 provinces, 973 districts; ≥ 15 themes; ≥ 50 indicators), never individual values. A weekly GitHub Actions job runs them and opens an issue on failure — that issue means upstream drifted, and it is the highest-priority kind of bug this package gets.

Be polite when running them: requests go out one at a time with a courtesy delay. Do not parallelize them.

Commits and pull requests

  • Conventional-ish commit subjects (fix:, feat:, docs:, test:) are appreciated but not enforced.
  • One logical change per PR; include a test for anything behavioral.
  • Note user-visible changes in CHANGELOG.md under ## [Unreleased].

Releasing

Maintainers only:

  1. Move the Unreleased entries in CHANGELOG.md under a new version heading with today's date.
  2. Bump version in pyproject.toml and __version__ in src/pytuik/__init__.py — they must match.
  3. Tag and push: git tag v0.1.0 && git push origin v0.1.0.
  4. release.yml builds the sdist and wheel and publishes to PyPI via trusted publishing (OIDC — there is no API token to leak), then creates the GitHub release.

Attribution

pytuik is a rewrite of emraher/tuikr (MIT). Ported behavior should stay recognizable to tuikr users, and deviations belong in DESIGN.md §4.2 with a reason. If your change ports something new from tuikr, say so in the PR.