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):
client.pyowns all HTTP. The portal, SDMX, and geo modules receive aTuikClient, build URLs, and parse responses. They never importhttpx. This keeps the cookie session dance, retry policy, and header discipline in one place.- Parsers are pure functions.
(json_or_xml) -> DataFramewherever possible, at the same granularity astuikr'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 fromtests/fixtures/.tests/test_*.py— client behavior withrespx-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¶
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.mdunder## [Unreleased].
Releasing¶
Maintainers only:
- Move the
Unreleasedentries inCHANGELOG.mdunder a new version heading with today's date. - Bump
versioninpyproject.tomland__version__insrc/pytuik/__init__.py— they must match. - Tag and push:
git tag v0.1.0 && git push origin v0.1.0. release.ymlbuilds 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.