Feat/python replication#247
Open
NKeleher wants to merge 14 commits into
Open
Conversation
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.2.0 to 8.3.2. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](astral-sh/setup-uv@fac544c...11f9893) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 8.3.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
…astral-sh/setup-uv-8.3.2 chore(actions): bump astral-sh/setup-uv from 8.2.0 to 8.3.2
…t.yaml references; add corresponding tests
…eneration; update tests accordingly
…pt generators and data_dict.py
There was a problem hiding this comment.
Pull request overview
Adds Python-based replication outputs to DataSure’s replication package generation, alongside Parquet dataset exports and a data-dict.yaml data dictionary, to improve reproducibility without requiring Stata.
Changes:
- Generate Python/Polars replication scripts (
0_main.py, import, prep, corrections) with PEP 723 inline metadata foruv run. - Add Parquet exports (raw/prepped/corrected) and a
data-dict.yamlfile into the replication zip. - Extend/adjust replication README content and add/expand tests covering the new artifacts.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Locks new dependency (pyyaml) and updates resolution markers from re-lock. |
| pyproject.toml | Adds pyyaml runtime dependency for YAML generation/parsing. |
| CLAUDE.md | Updates repository module map to include new replication modules. |
| src/datasure/replication/package_builder.py | Adds Parquet + data-dict.yaml outputs and writes Python replication scripts into the zip. |
| src/datasure/replication/readme.py | Updates replication README to describe Python workflow, Parquet artifacts, and data dictionary. |
| src/datasure/replication/data_dict.py | New generator for data-dict.yaml from a Polars DataFrame. |
| src/datasure/replication/py_script_generators.py | New generators for Python master/import/corrections replication scripts (PEP 723). |
| src/datasure/replication/py_prep_script_generator.py | New generator translating prep logs into a Python/Polars prepare script. |
| tests/replication/test_readme.py | Adds assertions for new README references (Python scripts, Parquet, data-dict.yaml). |
| tests/replication/test_package_builder.py | Adds zip-content tests for data-dict.yaml, Parquet, and Python scripts. |
| tests/replication/test_data_dict.py | New tests for data-dict.yaml structure and column typing/constraints. |
| tests/replication/test_py_script_generators.py | New tests validating generated Python scripts parse and contain expected content. |
| tests/replication/test_py_prep_script_generator.py | New tests for prep-log-to-Python translation logic and script validity. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- _to_parquet_bytes: only skip writing Parquet for truly columnless DataFrames (no schema). A DataFrame with columns but zero rows now writes a valid empty Parquet file instead of a 0-byte, invalid one. package_builder.py now guards the raw Parquet zip entry the same way the prepped/corrected entries already were. - generate_master_script_py: invoke each step via `uv run` instead of sys.executable, so a step's own PEP 723 inline dependencies are honored; drop the now-unused `sys` import. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…and Python scripts _package_tree() (rendered under "What's inside the zip?") predated the Parquet exports, data-dict.yaml, and .py replication scripts added to build_replication_package(), so the preview no longer matched what the zip actually contains. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reformats generate_readme() as proper Markdown (ATX headings, fenced code blocks for the folder tree and shell commands, GFM tables) instead of a plain-text banner report, and writes it into the zip as 0_README.md instead of 0_README.txt. Updates the zip-contents preview and tests accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Python import script re-derived column types from the raw CSV via pl.read_csv(..., infer_schema_length=10000), even though package_builder.py already bundles an authoritative _raw.parquet written straight from the real DuckDB-stored data. That heuristic re-inference could disagree with the real schema (e.g. a column typed Int64 in DuckDB getting inferred as String/Float64 from the CSV alone), silently diverging the Python-reproduced dataset from the true one. The CSV round-trip is only needed on the Stata side, since Stata can't read Parquet. 3_prepare_data.py already read directly from the bundled _raw.parquet, so removing generate_import_script_py and the 2_import_data.py step is a pure simplification with no behavior loss: the master script now runs prepare -> corrections only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
NKeleher
marked this pull request as ready for review
July 17, 2026 22:22
Contributor
Author
|
For review after #244. |
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.



Pull Request Summary 🚀
What does this PR do? 📝
Adds Parquet exports, a
data-dict.yamldata dictionary, and a full Python/Polarsreplication pipeline to the replication package export, alongside a Markdown
README.mdand Parquet support for local file import.The exported zip now includes:
(
3_data/{1_raw,2_intermediate,3_final}/*.parquet), so the package is usablewithout Stata.
1_docs/2_codebooks/data-dict.yaml, a data dictionary compliant with thedata-dict.yaml spec (column types, ranges,
enums, and a
primary_keyconstraint on the key column).2_scripts/{0_main,3_prepare_data,4_corrections}.py)that reproduce the prep/correction pipeline using Polars — no Stata required.
Each script carries a PEP 723 inline-metadata header, so it's independently
runnable via
uv run <script>.py, which provisions its dependenciesautomatically (no separate install-packages step needed).
README.md(was0_README.txt) — the package README is now real Markdown(headings, fenced code blocks, tables) instead of a plain-text banner report.
(
src/datasure/connectors/local.py), alongside the existing csv/xlsx/xls/json/dtatypes.
Why is this change needed? 🤔
The replication package previously only supported Stata: raw data as CSV, and
do-files that require Stata to run. Researchers without a Stata license had no
way to reproduce a project's corrected dataset, and there was no machine-readable
data dictionary for downstream tooling. This PR makes the package
language-agnostic (Stata and Python) and adds a standard data dictionary, all
without changing the existing Stata workflow.
How was this implemented? 🛠️
replication/data_dict.py(new) generates the data dictionary from thecorrected dataset, mirroring
codebook.py's dtype classification.replication/py_script_generators.pyandreplication/py_prep_script_generator.py(new) translate the prep log and correction log into Polars code, mirroring
the real semantics in
processing/prep.py/processing/corrections.pyrather than approximating them the way the Stata do-file translation does —
in some cases this makes the Python output more complete than Stata's (e.g.
get dummiesanduuidtranslate directly instead of falling back to a"could not translate" note).
2_import_data.pythatre-derived column types from the raw CSV via
pl.read_csv(...). Sincepackage_builder.pyalready bundles an authoritative_raw.parquetwrittenstraight from the real DuckDB-stored data, that re-inference was both
redundant and a correctness risk (it could disagree with the true schema).
It was removed;
3_prepare_data.pyreads the bundled Parquet directly. TheCSV round-trip is kept only on the Stata side, since Stata can't read Parquet.
readme.pyandreplication_view.py's "What's inside the zip?" preview wereupdated to describe the new file layout accurately.
_to_parquet_bytesno longer writes aninvalid 0-byte Parquet file for a columnless DataFrame, and the Python master
script invokes each step via
uv run(notsys.executable) so a step's ownPEP 723 dependencies are honored.
ux-reviewinto this branch (no conflicts) to pick up the shared UIhelpers it introduced.
unrelated, pre-existing Stata do-file bug found while porting the row-removal
logic (equal_to/not_equal_to conditions may invert direction vs. the real
app) — flagged for a separate fix, not touched here.
How to test or reproduce? 🧪
uv sync uv run python -m pytest tests/replication/ tests/connectors/test_local.py tests/views/test_replication_view.py -q just lint-py && just fmt-pythonManually:
uv run datasure→ Replication Package Export → select a page → expand"What's inside the zip?" to see the updated contents → build the package →
unzip it and confirm
data-dict.yaml, the.parquetfiles, andREADME.mdarepresent, then run
uv run 0_main.pyfrom2_scripts/to reproduce the preppedand corrected datasets without Stata.
Screenshots (if applicable) 📷
Exports now look like:

Checklist ✅
generators function-for-function, plus a data dictionary generator, each
with matching test coverage — the line count is mostly parallel structure
and tests, not one dense change.
pyyamlfordata-dict.yamlgeneration.Reviewer Emoji Legend
:code::smiley::+1::100:...and I want the author to know it! This is a way to highlight positive parts of a code review.
:star: :star: :star:And I am providing reasons why it needs to be addressed as well as suggested improvements.
:star: :star:And I am providing suggestions where it could be improved either in this PR or later.
:star:...and consider this a suggestion, not a requirement.
:question:This should be a fully formed question with sufficient information and context that requires a response.
:memo::pick:This does not require any changes and is often better left unsaid. This may include stylistic, formatting, or organization suggestions and should likely be prevented/enforced by linting if they really matter
:recycle:Should include enough context to be actionable and not be considered a nitpick.