openstatspec 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. openstatspec-0.1.0/.github/workflows/ci.yml +140 -0
  2. openstatspec-0.1.0/.github/workflows/release.yml +66 -0
  3. openstatspec-0.1.0/.gitignore +7 -0
  4. openstatspec-0.1.0/CHANGELOG.md +28 -0
  5. openstatspec-0.1.0/PKG-INFO +90 -0
  6. openstatspec-0.1.0/README.md +73 -0
  7. openstatspec-0.1.0/THIRD_PARTY_NOTICES.md +34 -0
  8. openstatspec-0.1.0/docs/architecture.md +14 -0
  9. openstatspec-0.1.0/docs/pyspssio-spike.md +45 -0
  10. openstatspec-0.1.0/docs/release-readiness.md +64 -0
  11. openstatspec-0.1.0/docs/sav-profile.md +57 -0
  12. openstatspec-0.1.0/pyproject.toml +28 -0
  13. openstatspec-0.1.0/src/openstatspec/__init__.py +6 -0
  14. openstatspec-0.1.0/src/openstatspec/api.py +95 -0
  15. openstatspec-0.1.0/src/openstatspec/cli.py +42 -0
  16. openstatspec-0.1.0/src/openstatspec/core/__init__.py +33 -0
  17. openstatspec-0.1.0/src/openstatspec/core/results.py +47 -0
  18. openstatspec-0.1.0/src/openstatspec/spss/__init__.py +22 -0
  19. openstatspec-0.1.0/src/openstatspec/spss/dictionary.py +180 -0
  20. openstatspec-0.1.0/src/openstatspec/spss/raw_dictionary.py +393 -0
  21. openstatspec-0.1.0/src/openstatspec/spss/sav.py +569 -0
  22. openstatspec-0.1.0/src/openstatspec/sql/__init__.py +14 -0
  23. openstatspec-0.1.0/src/openstatspec/sql/capabilities.py +260 -0
  24. openstatspec-0.1.0/src/openstatspec/sql/normative.py +513 -0
  25. openstatspec-0.1.0/src/openstatspec/sql/profiles.py +217 -0
  26. openstatspec-0.1.0/src/openstatspec/sql/wide.py +1087 -0
  27. openstatspec-0.1.0/tests/conformance.py +141 -0
  28. openstatspec-0.1.0/tests/test_atomic_import.py +115 -0
  29. openstatspec-0.1.0/tests/test_attribute_catalog.py +139 -0
  30. openstatspec-0.1.0/tests/test_cli.py +95 -0
  31. openstatspec-0.1.0/tests/test_document_round_trip.py +129 -0
  32. openstatspec-0.1.0/tests/test_loss_reports.py +156 -0
  33. openstatspec-0.1.0/tests/test_normative_catalog.py +170 -0
  34. openstatspec-0.1.0/tests/test_official_conformance.py +371 -0
  35. openstatspec-0.1.0/tests/test_pyspssio_catalog_authority.py +99 -0
  36. openstatspec-0.1.0/tests/test_sav_sqlite.py +217 -0
  37. openstatspec-0.1.0/tests/test_spss_catalog_preflight.py +168 -0
  38. openstatspec-0.1.0/tests/test_sql_profiles.py +112 -0
  39. openstatspec-0.1.0/tests/test_sql_services.py +77 -0
@@ -0,0 +1,140 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ env:
8
+ OPENSTATSPEC_SPECIFICATION_DIR: ${{ github.workspace }}/openstatspec-specification
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - name: Checkout OpenStatSpec specification fixtures
20
+ uses: actions/checkout@v7
21
+ with: &specification-checkout
22
+ repository: OpenStatSpec/specification
23
+ ref: 6b9d1fc38f2f083c0ac5cf1c64874a6d07b95045
24
+ path: openstatspec-specification
25
+ - name: Checkout required SPSS engine
26
+ uses: actions/checkout@v7
27
+ with: &engine-checkout
28
+ repository: TonisOrmisson/pyspssio
29
+ ref: e069adf33c70bcd9e8e6ee495106479463a84fa2
30
+ path: openstatspec-pyspssio
31
+ - uses: actions/setup-python@v7
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ - run: python -m pip install --upgrade pip
35
+ - run: python -m pip install ./openstatspec-pyspssio
36
+ - run: python -m pip install -e ".[dev]"
37
+ - run: python -m pytest -m "not services"
38
+
39
+ package-smoke:
40
+ name: Build and install package artifact
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v7
44
+ - name: Checkout OpenStatSpec specification fixtures
45
+ uses: actions/checkout@v7
46
+ with: *specification-checkout
47
+ - name: Checkout required SPSS engine
48
+ uses: actions/checkout@v7
49
+ with: *engine-checkout
50
+ - uses: actions/setup-python@v7
51
+ with:
52
+ python-version: "3.12"
53
+ - run: python -m pip install --upgrade pip build
54
+ - run: python -m build
55
+ - run: python -m venv /tmp/openstatspec-wheel-smoke
56
+ - run: /tmp/openstatspec-wheel-smoke/bin/python -m pip install ./openstatspec-pyspssio
57
+ - run: /tmp/openstatspec-wheel-smoke/bin/python -m pip install dist/*.whl
58
+ - run: /tmp/openstatspec-wheel-smoke/bin/openstatspec capabilities
59
+
60
+ postgres-integration:
61
+ name: PostgreSQL ${{ matrix.postgres }} integration
62
+ runs-on: ubuntu-latest
63
+ strategy:
64
+ fail-fast: false
65
+ matrix:
66
+ postgres: ["17", "18"]
67
+ services:
68
+ postgres:
69
+ image: postgres:${{ matrix.postgres }}
70
+ ports: ["5432:5432"]
71
+ env:
72
+ POSTGRES_DB: openstatspec
73
+ POSTGRES_USER: openstatspec
74
+ POSTGRES_PASSWORD: openstatspec
75
+ options: >-
76
+ --health-cmd "pg_isready -U openstatspec -d openstatspec"
77
+ --health-interval 10s --health-timeout 5s --health-retries 5
78
+ env:
79
+ OPENSTATSPEC_POSTGRES_URL: postgresql+psycopg://openstatspec:openstatspec@localhost:5432/openstatspec
80
+ steps: &sql-test-steps
81
+ - uses: actions/checkout@v7
82
+ - name: Checkout OpenStatSpec specification fixtures
83
+ uses: actions/checkout@v7
84
+ with: *specification-checkout
85
+ - name: Checkout required SPSS engine
86
+ uses: actions/checkout@v7
87
+ with: *engine-checkout
88
+ - uses: actions/setup-python@v7
89
+ with:
90
+ python-version: "3.12"
91
+ - run: python -m pip install --upgrade pip
92
+ - run: python -m pip install ./openstatspec-pyspssio
93
+ - run: python -m pip install -e ".[dev,sql]"
94
+ - run: python -m pytest -m services
95
+
96
+ mysql-integration:
97
+ name: MySQL ${{ matrix.mysql }} integration
98
+ runs-on: ubuntu-latest
99
+ strategy:
100
+ fail-fast: false
101
+ matrix:
102
+ mysql: ["8.4", "9.7"]
103
+ services:
104
+ mysql:
105
+ image: mysql:${{ matrix.mysql }}
106
+ ports: ["3306:3306"]
107
+ env:
108
+ MYSQL_DATABASE: openstatspec
109
+ MYSQL_USER: openstatspec
110
+ MYSQL_PASSWORD: openstatspec
111
+ MYSQL_ROOT_PASSWORD: root
112
+ options: >-
113
+ --health-cmd "mysqladmin ping -h 127.0.0.1 -uopenstatspec -popenstatspec"
114
+ --health-interval 10s --health-timeout 5s --health-retries 10
115
+ env:
116
+ OPENSTATSPEC_MYSQL_URL: mysql+pymysql://openstatspec:openstatspec@localhost:3306/openstatspec
117
+ steps: *sql-test-steps
118
+
119
+ mariadb-integration:
120
+ name: MariaDB ${{ matrix.mariadb }} integration
121
+ runs-on: ubuntu-latest
122
+ strategy:
123
+ fail-fast: false
124
+ matrix:
125
+ mariadb: ["11.4", "11.8", "12.3"]
126
+ services:
127
+ mariadb:
128
+ image: mariadb:${{ matrix.mariadb }}
129
+ ports: ["3306:3306"]
130
+ env:
131
+ MARIADB_DATABASE: openstatspec
132
+ MARIADB_USER: openstatspec
133
+ MARIADB_PASSWORD: openstatspec
134
+ MARIADB_ROOT_PASSWORD: root
135
+ options: >-
136
+ --health-cmd "healthcheck.sh --connect --innodb_initialized"
137
+ --health-interval 10s --health-timeout 5s --health-retries 10
138
+ env:
139
+ OPENSTATSPEC_MARIADB_URL: mysql+pymysql://openstatspec:openstatspec@localhost:3306/openstatspec
140
+ steps: *sql-test-steps
@@ -0,0 +1,66 @@
1
+ name: Release
2
+
3
+ env:
4
+ OPENSTATSPEC_SPECIFICATION_DIR: ${{ github.workspace }}/openstatspec-specification
5
+
6
+ on:
7
+ push:
8
+ tags:
9
+ - "v*"
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v7
19
+ - name: Checkout normative specification fixtures
20
+ uses: actions/checkout@v7
21
+ with:
22
+ repository: OpenStatSpec/specification
23
+ ref: 6b9d1fc38f2f083c0ac5cf1c64874a6d07b95045
24
+ path: openstatspec-specification
25
+ - name: Checkout required SPSS engine
26
+ uses: actions/checkout@v7
27
+ with:
28
+ repository: TonisOrmisson/pyspssio
29
+ ref: e069adf33c70bcd9e8e6ee495106479463a84fa2
30
+ path: openstatspec-pyspssio
31
+ - uses: actions/setup-python@v7
32
+ with:
33
+ python-version: "3.13"
34
+ - run: python -m pip install --upgrade pip build
35
+ - name: Verify required SPSS engine is publicly installable
36
+ run: >-
37
+ python -m pip download --only-binary=:all: --no-deps
38
+ --dest /tmp/openstatspec-engine-release
39
+ openstatspec-pyspssio==0.5.1.post2
40
+ - run: python -m pip install ./openstatspec-pyspssio
41
+ - run: python -m pip install -e ".[dev]"
42
+ - run: python -m pytest -m "not services"
43
+ - run: python -m build
44
+ - run: python -m venv /tmp/openstatspec-release-smoke
45
+ - run: /tmp/openstatspec-release-smoke/bin/python -m pip install ./openstatspec-pyspssio
46
+ - run: /tmp/openstatspec-release-smoke/bin/python -m pip install dist/*.whl
47
+ - run: /tmp/openstatspec-release-smoke/bin/openstatspec capabilities
48
+ - uses: actions/upload-artifact@v7
49
+ with:
50
+ name: python-distributions
51
+ path: dist/
52
+
53
+ publish:
54
+ needs: build
55
+ runs-on: ubuntu-latest
56
+ environment:
57
+ name: pypi
58
+ url: https://pypi.org/p/openstatspec
59
+ permissions:
60
+ id-token: write
61
+ steps:
62
+ - uses: actions/download-artifact@v8
63
+ with:
64
+ name: python-distributions
65
+ path: dist/
66
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ .pytest_cache/
3
+ *.py[cod]
4
+ .venv/
5
+ build/
6
+ dist/
7
+ *.egg-info/
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this reference implementation are documented here.
4
+
5
+ ## 0.1.0 — unreleased
6
+
7
+ First public reference implementation of the OpenStatSpec strict wide-table
8
+ SPSS profile.
9
+
10
+ ### Included
11
+
12
+ - Import of unencrypted SAV and ZSAV sources into one dedicated wide SQL table
13
+ and its metadata catalog.
14
+ - Export of supported dataset semantics to SAV and ZSAV.
15
+ - SQLite, PostgreSQL, MySQL, and MariaDB profiles, including service-backed
16
+ PostgreSQL 17/18, MySQL 8.4/9.7, and MariaDB 11.4/11.8/12.3 CI coverage.
17
+ - Preflight checks for target profile limits, atomic imports, validation, a
18
+ command-line interface, and machine-readable capability and loss reports.
19
+
20
+ ### Important boundaries
21
+
22
+ - This release promises semantic round-trip equivalence only for supported
23
+ features; it never promises byte-identical SAV/ZSAV output.
24
+ - The adapter requires explicit `allow_loss` consent before exporting known
25
+ unsupported or lossy dictionary semantics.
26
+ - Multiple-response sets, variable alignment, variable sets, and custom
27
+ attributes have explicit capability diagnostics; see the SAV profile.
28
+ - Encrypted SPSS files are not supported.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: openstatspec
3
+ Version: 0.1.0
4
+ Summary: Reference adapter for the OpenStatSpec relational contract
5
+ Author: OpenStatSpec
6
+ License: Apache-2.0
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: openstatspec-pyspssio==0.5.1.post2
9
+ Requires-Dist: pandas>=2.2
10
+ Requires-Dist: sqlalchemy>=2.0
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=8; extra == 'dev'
13
+ Provides-Extra: sql
14
+ Requires-Dist: psycopg[binary]>=3.2; extra == 'sql'
15
+ Requires-Dist: pymysql>=1.1; extra == 'sql'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # OpenStatSpec Python
19
+
20
+ The reference Python implementation of the OpenStatSpec specification.
21
+
22
+ This package implements the specification; it does not define or extend it.
23
+ The normative model lives in the `OpenStatSpec/specification` repository.
24
+
25
+ ## Boundaries
26
+
27
+ For each supported import, one source dataset becomes one dedicated wide SQL
28
+ table. Cases are rows and source variables are physical SQL columns. The
29
+ singular UUID-keyed tables from the specification (`dataset`, `variable`,
30
+ `operation`, `fidelity_event`, and related metadata tables) are the public
31
+ catalog contract. Historical `*_catalog` tables are an internal compatibility
32
+ layer for the current exporter and are not the standard database interface.
33
+ The adapter does not reshape data, create EAV
34
+ or long-form tables, or harmonize studies or waves.
35
+
36
+ Unsupported source features, SQL targets, or export paths fail explicitly.
37
+ There is no silent truncation, type conversion, metadata loss, or partial
38
+ import.
39
+
40
+ ## Package layout
41
+
42
+ - `openstatspec.core`: pure standard concepts, validation, versions,
43
+ capabilities, and loss reports.
44
+ - `openstatspec.sql`: database connection and wide-table/catalog operations.
45
+ - `openstatspec.spss`: SAV/ZSAV adapter boundary.
46
+
47
+ ## Intended workflow
48
+
49
+ ```python
50
+ from openstatspec import export_sav, import_sav
51
+
52
+ import_sav("responses.sav", database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026")
53
+ export_sav(database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026", destination="responses-roundtrip.sav")
54
+ ```
55
+
56
+ ```text
57
+ openstatspec import responses.sav --database-url postgresql+psycopg://... --dataset-id responses-2026
58
+ openstatspec export --database-url postgresql+psycopg://... --dataset-id responses-2026 --output responses-roundtrip.sav
59
+ ```
60
+
61
+ ## 0.1.0 support status
62
+
63
+ The adapter requires `openstatspec-pyspssio==0.5.1.post2` as its sole SPSS
64
+ engine. Its import module remains `pyspssio`; the exact source commit is recorded
65
+ in operation metadata. There is no fallback reader or writer. It supports unencrypted SAV and ZSAV import and
66
+ SAV/ZSAV export for the semantics exposed by that engine. SQLite is the local
67
+ reference path.
68
+ PostgreSQL, MySQL, and MariaDB are each covered by separate service-backed CI
69
+ conformance checks. Use these explicit SQLAlchemy URLs:
70
+
71
+ - SQLite: `sqlite:///dataset.sqlite`
72
+ - PostgreSQL: `postgresql+psycopg://user:password@host/database`
73
+ - MySQL/MariaDB: `mysql+pymysql://user:password@host/database`
74
+
75
+ Run `openstatspec capabilities` before an integration to inspect the
76
+ machine-readable feature matrix. Export is deliberately strict: if known
77
+ dictionary semantics cannot be reproduced, it stops until you pass the exact
78
+ diagnostic code with `--allow-loss`. This avoids silent loss while making an
79
+ intentional lossy export auditable.
80
+
81
+ The matrix is also available to Python callers as
82
+ `openstatspec.capability_matrix()`. It distinguishes supported semantics from
83
+ unobservable and fail-closed paths; see the SAV profile for the exact
84
+ the openstatspec-pyspssio boundary.
85
+
86
+ See [the SAV profile](docs/sav-profile.md) for feature boundaries and
87
+ [release readiness](docs/release-readiness.md) for the pre-tag checklist.
88
+ Read [third-party notices](THIRD_PARTY_NOTICES.md) before distributing a
89
+ bundled application: the required engine includes IBM redistributables under
90
+ separate terms.
@@ -0,0 +1,73 @@
1
+ # OpenStatSpec Python
2
+
3
+ The reference Python implementation of the OpenStatSpec specification.
4
+
5
+ This package implements the specification; it does not define or extend it.
6
+ The normative model lives in the `OpenStatSpec/specification` repository.
7
+
8
+ ## Boundaries
9
+
10
+ For each supported import, one source dataset becomes one dedicated wide SQL
11
+ table. Cases are rows and source variables are physical SQL columns. The
12
+ singular UUID-keyed tables from the specification (`dataset`, `variable`,
13
+ `operation`, `fidelity_event`, and related metadata tables) are the public
14
+ catalog contract. Historical `*_catalog` tables are an internal compatibility
15
+ layer for the current exporter and are not the standard database interface.
16
+ The adapter does not reshape data, create EAV
17
+ or long-form tables, or harmonize studies or waves.
18
+
19
+ Unsupported source features, SQL targets, or export paths fail explicitly.
20
+ There is no silent truncation, type conversion, metadata loss, or partial
21
+ import.
22
+
23
+ ## Package layout
24
+
25
+ - `openstatspec.core`: pure standard concepts, validation, versions,
26
+ capabilities, and loss reports.
27
+ - `openstatspec.sql`: database connection and wide-table/catalog operations.
28
+ - `openstatspec.spss`: SAV/ZSAV adapter boundary.
29
+
30
+ ## Intended workflow
31
+
32
+ ```python
33
+ from openstatspec import export_sav, import_sav
34
+
35
+ import_sav("responses.sav", database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026")
36
+ export_sav(database_url="postgresql+psycopg://user:password@server/database", dataset_id="responses-2026", destination="responses-roundtrip.sav")
37
+ ```
38
+
39
+ ```text
40
+ openstatspec import responses.sav --database-url postgresql+psycopg://... --dataset-id responses-2026
41
+ openstatspec export --database-url postgresql+psycopg://... --dataset-id responses-2026 --output responses-roundtrip.sav
42
+ ```
43
+
44
+ ## 0.1.0 support status
45
+
46
+ The adapter requires `openstatspec-pyspssio==0.5.1.post2` as its sole SPSS
47
+ engine. Its import module remains `pyspssio`; the exact source commit is recorded
48
+ in operation metadata. There is no fallback reader or writer. It supports unencrypted SAV and ZSAV import and
49
+ SAV/ZSAV export for the semantics exposed by that engine. SQLite is the local
50
+ reference path.
51
+ PostgreSQL, MySQL, and MariaDB are each covered by separate service-backed CI
52
+ conformance checks. Use these explicit SQLAlchemy URLs:
53
+
54
+ - SQLite: `sqlite:///dataset.sqlite`
55
+ - PostgreSQL: `postgresql+psycopg://user:password@host/database`
56
+ - MySQL/MariaDB: `mysql+pymysql://user:password@host/database`
57
+
58
+ Run `openstatspec capabilities` before an integration to inspect the
59
+ machine-readable feature matrix. Export is deliberately strict: if known
60
+ dictionary semantics cannot be reproduced, it stops until you pass the exact
61
+ diagnostic code with `--allow-loss`. This avoids silent loss while making an
62
+ intentional lossy export auditable.
63
+
64
+ The matrix is also available to Python callers as
65
+ `openstatspec.capability_matrix()`. It distinguishes supported semantics from
66
+ unobservable and fail-closed paths; see the SAV profile for the exact
67
+ the openstatspec-pyspssio boundary.
68
+
69
+ See [the SAV profile](docs/sav-profile.md) for feature boundaries and
70
+ [release readiness](docs/release-readiness.md) for the pre-tag checklist.
71
+ Read [third-party notices](THIRD_PARTY_NOTICES.md) before distributing a
72
+ bundled application: the required engine includes IBM redistributables under
73
+ separate terms.
@@ -0,0 +1,34 @@
1
+ # Third-party notices
2
+
3
+ OpenStatSpec Python source code is licensed under Apache-2.0. Its required
4
+ SPSS engine is a separate third-party distribution:
5
+
6
+ | Component | Version | Applicable terms |
7
+ | --- | --- | --- |
8
+ | openstatspec-pyspssio (import module `pyspssio`) | 0.5.1.post2, source commit e069adf33c70bcd9e8e6ee495106479463a84fa2 | MIT for the Python wrapper, except for its IBM I/O Module files |
9
+ | IBM I/O Modules for IBM SPSS Statistics Data Files | bundled by openstatspec-pyspssio | IBM International License Agreement for Non-Warranted Programs and the accompanying License Information / REDIST files |
10
+
11
+ pyspssio ships the IBM licence, redistribution list, and third-party notices
12
+ inside its installed distribution, normally under pyspssio/spssio/license/.
13
+ Those materials are authoritative. They are not relicensed by this repository's
14
+ Apache-2.0 licence.
15
+
16
+ ## Distribution responsibility
17
+
18
+ OpenStatSpec requires the exact openstatspec-pyspssio release declared in pyproject.toml; it has no alternate SPSS engine. This
19
+ repository does not vendor or alter the IBM binaries. Anyone who
20
+ distributes an application bundle containing the installed engine must review
21
+ and comply with the IBM materials supplied with that engine. In particular, the
22
+ IBM redistribution conditions require object-code-only distribution as part of
23
+ the distributor's application, preservation of notices, restrictions on use
24
+ and onward distribution, and end-user terms that protect IBM at least as much
25
+ as the IBM agreement. They also include other conditions, including support,
26
+ indemnity, and trademark restrictions.
27
+
28
+ Do not remove the pyspssio licence directory or notices from a bundled
29
+ installation. Do not describe IBM or its trademarks as endorsing OpenStatSpec.
30
+
31
+ This is a factual third-party notice, not legal advice. A party preparing a
32
+ binary or application distribution should have its own legal review of the
33
+ exact LA_en, REDIST, and notices files installed with the selected pyspssio
34
+ release.
@@ -0,0 +1,14 @@
1
+ # Architecture
2
+
3
+ `openstatspec.core` contains only pure concepts defined by the standard:
4
+ versioning, validation concepts, capability declarations, and loss reports.
5
+ `openstatspec.sql` owns database-target and strict wide-table/catalog work.
6
+ `openstatspec.spss` owns SAV/ZSAV source and export behavior.
7
+
8
+ The public workflow remains database-connected. A supported import receives an
9
+ SPSS source, a database URL or connection, and a dataset identity. It creates
10
+ one dedicated wide data table plus separate catalog metadata. Export identifies
11
+ the database dataset and writes a supported SPSS file.
12
+
13
+ No layer may silently substitute EAV, long-form views, JSON, pivots, automatic
14
+ harmonization, truncation, or partial import for the standard's strict mapping.
@@ -0,0 +1,45 @@
1
+ # pyspssio engine assessment
2
+
3
+ **Status:** selected as the required, sole Python SPSS engine.
4
+
5
+ OpenStatSpec Python requires `openstatspec-pyspssio==0.5.1.post2`, built from
6
+ TonisOrmisson/pyspssio commit `e069adf33c70bcd9e8e6ee495106479463a84fa2`, for both SAV and ZSAV import and
7
+ export. There is no alternate SPSS engine.
8
+
9
+ ## Probe result
10
+
11
+ A local round trip created both .sav and .zsav, then read each back. The
12
+ following metadata survived in the probe:
13
+
14
+ - UTF-8 encoding, variable names and storage widths;
15
+ - variable and value labels;
16
+ - numeric user-missing values;
17
+ - measurement levels, roles, alignments and display widths;
18
+ - file attributes and variable attributes;
19
+ - case-weight variable;
20
+ - a dichotomous multiple-response set.
21
+
22
+ The public API used was read_metadata(), read_sav(), and write_sav().
23
+
24
+ ## Required conformance work
25
+
26
+ This engine selection does not relax the OpenStatSpec profile. Every claimed
27
+ feature must have a hermetic round-trip fixture. Current engine/API questions
28
+ that require explicit tests or upstream work include:
29
+
30
+ 1. variable-set round trips;
31
+ 2. independent print and write formats;
32
+ 3. ordered document text;
33
+ 4. legacy code-page files and unusual very-long-string boundaries beyond the covered 340-byte UTF-8 fixture.
34
+
35
+ Until a feature is proved, the adapter must report it as unsupported rather
36
+ than silently losing it.
37
+
38
+ ## Licence boundary
39
+
40
+ The pyspssio wrapper is MIT, except for IBM I/O Module files bundled in its
41
+ wheel. Those redistributables have their own IBM terms. pyspssio is a required
42
+ runtime dependency, not an optional plugin; OpenStatSpec does not copy or
43
+ modify its IBM binaries. See [third-party notices](../THIRD_PARTY_NOTICES.md)
44
+ for the distribution boundary and the location of the authoritative installed
45
+ terms.
@@ -0,0 +1,64 @@
1
+ # 0.1.0 release readiness
2
+
3
+ This page records the expected release contract, not a publication event.
4
+ Creating a version tag remains a separate maintainer action.
5
+
6
+ ## Supported workflow
7
+
8
+ For a supported unencrypted SAV or ZSAV source, the adapter imports one source
9
+ dataset into exactly one dedicated wide data table plus the complete singular,
10
+ UUID-keyed normative catalog from the SPSS profile. It can validate that
11
+ representation and export it back to SAV or ZSAV. Historical `*_catalog`
12
+ tables remain private compatibility storage and do not replace the normative
13
+ database contract.
14
+
15
+ ## SQL profiles
16
+
17
+ | Profile | Connection URL | Verification |
18
+ | --- | --- | --- |
19
+ | SQLite | `sqlite:///dataset.sqlite` | Python 3.11–3.14 local reference jobs |
20
+ | PostgreSQL | `postgresql+psycopg://…` | PostgreSQL 17 and 18 service jobs |
21
+ | MySQL | `mysql+pymysql://…` | MySQL 8.4 and 9.7 service jobs |
22
+ | MariaDB | `mysql+pymysql://…` | MariaDB 11.4, 11.8, and 12.3 service jobs |
23
+
24
+ Separate service matrices test the shared MySQL/MariaDB profile contract while
25
+ retaining distinct active-server identities and version claims. This does not
26
+ claim coverage for every server configuration. The machine-readable capability
27
+ declaration reports both the theoretical profile boundaries and effective
28
+ limits observed from the active connection.
29
+
30
+ ## Export-loss policy
31
+
32
+ `capability_matrix()` and `openstatspec capabilities` expose the current
33
+ feature state. Before export, the adapter combines source and writer
34
+ diagnostics. An export with known loss fails unless the caller passes the
35
+ relevant diagnostic codes through `allow_loss` or repeats `--allow-loss CODE`
36
+ in the CLI.
37
+
38
+ The concrete pyspssio boundaries are documented in the [SAV profile](sav-profile.md).
39
+ The pinned OpenStatSpec fork preserves document text, file labels, legacy
40
+ compatible names, separate print/write formats, variable sets,
41
+ multiple-response sets, alignment, scalar attributes, and ordered
42
+ custom-attribute arrays. Non-UTF-8 output requires an explicit matching locale;
43
+ otherwise the strict export policy reports and blocks the loss. A caller
44
+ that supplies consent for an available loss code receives the
45
+ machine-readable loss report with the export result.
46
+
47
+ ## Maintainer checks before tagging
48
+
49
+ 1. Publish the pinned `openstatspec-pyspssio==0.5.1.post2` engine distribution
50
+ first and confirm that a clean environment can download it from PyPI. The
51
+ main package has no fallback SPSS engine.
52
+ 2. Run `python -m pytest`.
53
+ 3. Confirm the GitHub Actions matrix is green for PostgreSQL 17/18, MySQL
54
+ 8.4/9.7, and MariaDB 11.4/11.8/12.3.
55
+ 4. Build with `python -m build` and install the generated wheel in a clean
56
+ environment.
57
+ 5. Confirm `openstatspec capabilities` reflects the intended support boundary.
58
+ 6. Review this document, the README, and CHANGELOG for accurate scope.
59
+
60
+ The tag-triggered release workflow repeats the non-service test suite, builds
61
+ the distributions, and installs the wheel with the exact required SPSS engine
62
+ in a clean environment before it can reach the protected `pypi` environment.
63
+ It fails closed if that exact engine version is not already downloadable from
64
+ PyPI, preventing publication of an uninstallable OpenStatSpec release.
@@ -0,0 +1,57 @@
1
+ # SAV/ZSAV profile status
2
+
3
+ The Python adapter implements a strict, source-faithful wide-table mapping.
4
+ One SAV or ZSAV dataset becomes one table; every source variable is a physical
5
+ column and __case_ordinal preserves case order. The adapter never pivots,
6
+ chunks, or creates a cell/EAV representation.
7
+
8
+ ## SPSS engine
9
+
10
+ OpenStatSpec Python requires `openstatspec-pyspssio==0.5.1.post2` as its sole
11
+ SPSS engine. There is no fallback reader or writer. The engine reads
12
+ unencrypted .sav and .zsav files, and writes both formats through the same implementation.
13
+
14
+ ## Verified SQL profiles
15
+
16
+ SQLite has a local reference fixture. PostgreSQL, MySQL, and MariaDB have
17
+ separate service-backed conformance checks in GitHub Actions that import,
18
+ validate, and export the supported fixture. MySQL 8.4 and MariaDB 11.4 exercise
19
+ the shared profile contract; this does not claim coverage for every server
20
+ configuration. An import that exceeds a target's strict single-table column
21
+ limit fails before it creates a dataset.
22
+
23
+ ## Fidelity contract
24
+
25
+ The adapter must preserve and validate only the source semantics that the
26
+ selected engine or the adapter's narrow, fail-closed type-6 document bridge exposes and that a conformance fixture proves. Unsupported
27
+ semantics fail explicitly; they never silently disappear. Encryption and
28
+ byte-identical reproduction are outside the contract. The contract is semantic
29
+ equivalence of supported values, order, and dictionary metadata.
30
+
31
+ ## Engine capability boundary
32
+
33
+ The `openstatspec capabilities` command and the
34
+ `openstatspec.capability_matrix()` function are the machine-readable declaration
35
+ of this boundary. It records the pinned source commit and installed engine version, and the adapter stores the same identity in every import and export operation record. The matrix deliberately distinguishes a supported feature from a feature that the underlying engine cannot observe or write faithfully.
36
+
37
+ | SPSS semantic | Pinned pyspssio status | Behaviour |
38
+ | --- | --- | --- |
39
+ | File label | Supported through openstatspec-pyspssio | The adapter persists it in the dataset catalog and writes it through the IBM I/O identifier-string API. |
40
+ | Very-long UTF-8 strings | Supported | The SAV and ZSAV fixture preserves a 340-byte, multi-byte UTF-8 string through the SQL catalog and back. |
41
+ | Ordered document text | Supported through a strict type-6 dictionary bridge plus the pinned fork | Import stores normalized document rows; export creates a temporary UTF-8 SAV source and has IBM I/O copy the records into SAV or ZSAV without invalidating ZSAV dictionary offsets. |
42
+ | Print and write formats independently | Supported as raw IBM I/O tuples | The adapter stores both tuples separately and writes them without collapsing either value. |
43
+ | Variable sets | Supported through raw IBM I/O | The adapter stores source sets in the extension catalog and writes them through the raw dictionary setter. Invalid target definitions fail before data are written. |
44
+ | Legacy compatible variable names | Supported through the strict dictionary bridge | The adapter stores the short-name mapping and rewrites both the fixed variable record and long-name extension before export. |
45
+ | Source encoding | UTF-8 by default; legacy code page with an explicit OS locale | The caller supplies legacy_locale and the writer verifies that the emitted encoding matches the stored source encoding. Without it, export fails before output creation. |
46
+ | Custom attributes with one value | Supported | File and variable scalar attributes round trip through the normalized attribute catalog. |
47
+ | Custom-attribute value arrays | Supported through raw IBM I/O | The adapter represents an array as IBM SPSS `Name[1]`, `Name[2]`, … members and reconstructs the ordered array in the normalized catalog. |
48
+
49
+ The remaining loss codes can be supplied through `allow_loss` only when a
50
+ caller intentionally accepts the documented, machine-readable loss report.
51
+
52
+ ## Distribution
53
+
54
+ The required engine includes IBM I/O Module redistributables under terms that
55
+ are separate from OpenStatSpec's Apache-2.0 source licence. See
56
+ [third-party notices](../THIRD_PARTY_NOTICES.md) before distributing a bundled
57
+ application.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "openstatspec"
7
+ version = "0.1.0"
8
+ description = "Reference adapter for the OpenStatSpec relational contract"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = {text = "Apache-2.0"}
12
+ authors = [{name = "OpenStatSpec"}]
13
+ dependencies = ["SQLAlchemy>=2.0", "pandas>=2.2", "openstatspec-pyspssio==0.5.1.post2"]
14
+
15
+ [project.optional-dependencies]
16
+ dev = ["pytest>=8"]
17
+ sql = ["psycopg[binary]>=3.2", "PyMySQL>=1.1"]
18
+
19
+ [project.scripts]
20
+ openstatspec = "openstatspec.cli:main"
21
+
22
+ [tool.pytest.ini_options]
23
+ testpaths = ["tests"]
24
+ addopts = "-q"
25
+ markers = ["services: requires a configured PostgreSQL or MySQL/MariaDB service"]
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["src/openstatspec"]
@@ -0,0 +1,6 @@
1
+ """Public API for the OpenStatSpec Python reference implementation."""
2
+
3
+ from .api import capabilities, capability_matrix, export_sav, import_sav, inspect, validate
4
+ from .core import CapabilityDeclaration, LossReport, UnsupportedOperationError
5
+
6
+ __all__ = ["CapabilityDeclaration", "LossReport", "UnsupportedOperationError", "capabilities", "capability_matrix", "export_sav", "import_sav", "inspect", "validate"]