Skip to content

Commit 8967628

Browse files
committed
Use pinned pyspssio fork for file labels
1 parent 3087e53 commit 8967628

15 files changed

Lines changed: 69 additions & 44 deletions

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ openstatspec export --database-url postgresql+psycopg://... --dataset-id respons
3939

4040
## 0.1.0 support status
4141

42-
The adapter requires pyspssio 0.5.1 or a compatible later release as its sole
43-
SPSS engine. There is no
42+
The adapter requires the pinned TonisOrmisson/pyspssio fork declared in
43+
pyproject.toml as its sole SPSS engine. There is no
4444
fallback reader or writer. It supports unencrypted SAV and ZSAV import and
4545
SAV/ZSAV export for the semantics exposed by that engine. SQLite is the local
4646
reference path.
@@ -60,7 +60,7 @@ intentional lossy export auditable.
6060
The matrix is also available to Python callers as
6161
`openstatspec.capability_matrix()`. It distinguishes supported semantics from
6262
unobservable and fail-closed paths; see the SAV profile for the exact
63-
`pyspssio` 0.5.1 boundary.
63+
the pinned pyspssio fork boundary.
6464

6565
See [the SAV profile](docs/sav-profile.md) for feature boundaries and
6666
[release readiness](docs/release-readiness.md) for the pre-tag checklist.

‎THIRD_PARTY_NOTICES.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ SPSS engine is a separate third-party distribution:
55

66
| Component | Version | Applicable terms |
77
| --- | --- | --- |
8-
| pyspssio | 0.5.1 | MIT for the Python wrapper, except for its IBM I/O Module files |
9-
| IBM I/O Modules for IBM SPSS Statistics Data Files | 28.0.1.0, bundled by pyspssio | IBM International License Agreement for Non-Warranted Programs and the accompanying License Information / REDIST files |
8+
| pyspssio | TonisOrmisson/pyspssio pinned commit 0b3f879 | 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 the pinned pyspssio fork | IBM International License Agreement for Non-Warranted Programs and the accompanying License Information / REDIST files |
1010

1111
pyspssio ships the IBM licence, redistribution list, and third-party notices
1212
inside its installed distribution, normally under pyspssio/spssio/license/.
@@ -15,7 +15,7 @@ Apache-2.0 licence.
1515

1616
## Distribution responsibility
1717

18-
OpenStatSpec requires pyspssio==0.5.1; it has no alternate SPSS engine. This
18+
OpenStatSpec requires the pinned pyspssio fork declared in pyproject.toml; it has no alternate SPSS engine. This
1919
repository does not vendor, alter, or publish the IBM binaries. Anyone who
2020
distributes an application bundle containing the installed engine must review
2121
and comply with the IBM materials supplied with that engine. In particular, the

‎docs/pyspssio-spike.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
**Status:** selected as the required, sole Python SPSS engine.
44

5-
OpenStatSpec Python uses [pyspssio](https://pypi.org/project/pyspssio/) 0.5.1
6-
for both SAV and ZSAV import and export. There is no alternate SPSS engine.
5+
OpenStatSpec Python uses the pinned TonisOrmisson/pyspssio fork, based on
6+
[pyspssio](https://pypi.org/project/pyspssio/), for both SAV and ZSAV import and export. There is no alternate SPSS engine.
77

88
## Probe result
99

@@ -28,7 +28,7 @@ that require explicit tests or upstream work include:
2828

2929
1. variable-set round trips;
3030
2. independent print and write formats;
31-
3. file label and document text;
31+
3. ordered document text;
3232
4. legacy code-page files and long-string edge cases.
3333

3434
Until a feature is proved, the adapter must report it as unsupported rather

‎docs/release-readiness.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ relevant diagnostic codes through `allow_loss` or repeats `--allow-loss CODE`
2929
in the CLI.
3030

3131
The concrete pyspssio boundaries are documented in the [SAV profile](sav-profile.md):
32-
file labels/documents are unobservable, legacy compatible names are fail-closed
32+
document text is unobservable; file labels are preserved by the pinned fork, legacy compatible names are fail-closed
3333
on export, and only UTF-8 has a source-encoding fidelity contract. Separate
3434
print/write formats, variable sets, multiple-response sets, alignment, scalar
3535
attributes, and ordered custom-attribute arrays have verified paths. A caller

‎docs/sav-profile.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ chunks, or creates a cell/EAV representation.
77

88
## SPSS engine
99

10-
OpenStatSpec Python requires pyspssio 0.5.1 as its sole SPSS engine. There is
11-
no fallback reader or writer. The engine reads unencrypted .sav and .zsav
10+
OpenStatSpec Python requires the pinned TonisOrmisson/pyspssio fork declared in
11+
pyproject.toml as its sole SPSS engine. There is no fallback reader or writer. The engine reads unencrypted .sav and .zsav
1212
files, and writes both formats through the same implementation.
1313

1414
## Verified SQL profiles
@@ -35,9 +35,10 @@ The `openstatspec capabilities` command and the
3535
of this boundary. They deliberately distinguish a supported feature from a
3636
feature that the underlying engine cannot observe or write faithfully.
3737

38-
| SPSS semantic | pyspssio 0.5.1 status | Behaviour |
38+
| SPSS semantic | Pinned pyspssio status | Behaviour |
3939
| --- | --- | --- |
40-
| File label and document text | Unobservable | The public engine API exposes neither. Import records `file-label-and-documents-unobservable`; export requires that audited loss to be accepted. |
40+
| File label | Supported through the pinned fork | The adapter persists it in the dataset catalog and writes it through the IBM I/O identifier-string API. |
41+
| Ordered document text | Unobservable | The engine exposes file-to-file document copying, but not reading or creating document text. Import records documents-unobservable; export requires that audited loss to be accepted. |
4142
| 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. |
4243
| 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. |
4344
| Legacy compatible variable names | Fail-closed on export | A compatible name that differs from the long source name is retained in the catalog, but cannot be set through the public writer. Export requires `compatible-variable-name-not-exportable`. |

‎pyproject.toml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ readme = "README.md"
1010
requires-python = ">=3.11"
1111
license = {text = "Apache-2.0"}
1212
authors = [{name = "OpenStatSpec"}]
13-
dependencies = ["SQLAlchemy>=2.0", "pandas>=2.2", "pyspssio>=0.5.1"]
13+
dependencies = ["SQLAlchemy>=2.0", "pandas>=2.2", "pyspssio @ git+https://github.com/TonisOrmisson/pyspssio.git@0b3f879"]
1414

1515
[project.optional-dependencies]
1616
dev = ["pytest>=8"]
@@ -24,5 +24,8 @@ testpaths = ["tests"]
2424
addopts = "-q"
2525
markers = ["services: requires a configured PostgreSQL or MySQL/MariaDB service"]
2626

27+
[tool.hatch.metadata]
28+
allow-direct-references = true
29+
2730
[tool.hatch.build.targets.wheel]
2831
packages = ["src/openstatspec"]

‎src/openstatspec/api.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def capability_matrix() -> Mapping[str, Any]:
2525
"values": "supported", "variable_labels": "supported",
2626
"value_labels": "supported",
2727
"print_format": "supported",
28-
"write_format": "unobservable",
29-
"file_label": "unobservable",
28+
"write_format": "supported",
29+
"file_label": "supported",
3030
"documents": "unobservable",
3131
"source_encoding": {
3232
"utf8": "supported",
@@ -35,11 +35,11 @@ def capability_matrix() -> Mapping[str, Any]:
3535
"measurement_level": "supported", "user_missing_rules": "supported",
3636
"multiple_response_sets": "supported",
3737
"variable_alignment": "supported",
38-
"variable_sets": "fail-closed-on-export",
38+
"variable_sets": "supported",
3939
"compatible_variable_names": "fail-closed-on-export",
4040
"custom_attributes": {
4141
"scalar_values": "supported",
42-
"ordered_value_arrays": "fail-closed",
42+
"ordered_value_arrays": "supported",
4343
},
4444
"variable_role": "supported",
4545
},

‎src/openstatspec/spss/sav.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def inspect_sav(source: str | Path) -> dict[str, Any]:
178178
"source_name": source_path.name,
179179
"source_sha256": _sha256(source_path),
180180
"source_encoding": metadata.get("encoding"),
181-
"file_label": "",
181+
"file_label": str(metadata.get("file_label") or ""),
182182
"documents": [],
183183
"file_attributes": dict(metadata.get("file_attributes") or {}),
184184
"case_weight_variable": metadata.get("case_weight_var") or None,
@@ -209,7 +209,7 @@ def import_sav_dataset(*, source: str | Path, database_url: str, dataset_id: str
209209
source_format=source_path.suffix[1:].upper(),
210210
rows=_rows(frame, variables),
211211
variables=variables,
212-
file_label="", # pyspssio 0.5.x has no public file-label API.
212+
file_label=str(metadata.get("file_label") or ""),
213213
source_encoding=metadata.get("encoding"),
214214
source_sha256=_sha256(source_path),
215215
imported_at=datetime.now(UTC).isoformat(),
@@ -289,6 +289,7 @@ def _write_with_dictionary_bridge(
289289
metadata = _writer_metadata(dataset, variables)
290290
with pyspssio.Writer(str(destination), mode="w") as writer:
291291
writer.compression = 2 if destination.suffix.lower() == ".zsav" else 1
292+
writer.file_label = str(dataset.get("file_label") or "")
292293
for variable in variables:
293294
writer._add_var( # pylint: disable=protected-access
294295
variable["source_name"],
@@ -384,9 +385,9 @@ def _typed_value_labels(variable: dict[str, Any]) -> dict[Any, str]:
384385

385386
def _engine_loss_report(metadata: dict[str, Any]) -> dict[str, dict[str, Any]]:
386387
events: dict[str, dict[str, Any]] = {
387-
"file-label-and-documents-unobservable": {
388-
"code": "file-label-and-documents-unobservable",
389-
"detail": "pyspssio 0.5.x exposes neither SAV file label nor document text through its public API.",
388+
"documents-unobservable": {
389+
"code": "documents-unobservable",
390+
"detail": "pyspssio does not expose SAV document text through its public API; the file label is preserved separately.",
390391
},
391392
}
392393
variable_sets = metadata.get("_var_sets")

‎tests/conformance.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ def write_supported_semantics_fixture(destination: str | Path) -> dict[str, Any]
8686
"var_attributes": {"code": {"Origin": "fixture"}},
8787
"mrsets": {"$responses": {"label": "Responses", "counted_value": 1, "variable_list": ["resp_a", "resp_b"]}},
8888
"file_attributes": {"Fixture": "OpenStatSpec"},
89+
"file_label": "OpenStatSpec supported fixture",
8990
"case_weight_var": "code",
9091
}
9192
pyspssio.write_sav(str(destination), frame, metadata=metadata)
92-
return {"long_text": long_text}
93+
return {"long_text": long_text, "file_label": metadata["file_label"]}
9394

9495

9596
def sys_float_max() -> float:
@@ -126,7 +127,7 @@ def compare_sav_semantics(source: str | Path, exported: str | Path) -> dict[str,
126127
except AssertionError:
127128
failures.append("values-or-case-order")
128129
for attribute in (
129-
"encoding", "case_weight_var", "file_attributes", "mrsets", "var_types", "var_formats",
130+
"encoding", "file_label", "case_weight_var", "file_attributes", "mrsets", "var_types", "var_formats",
130131
"var_labels", "var_alignments", "var_column_widths", "var_measure_levels", "var_roles",
131132
"var_value_labels", "var_attributes",
132133
):

‎tests/test_attribute_catalog.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from openstatspec.sql.wide import create_wide_dataset, read_wide_dataset
1111

1212

13-
_REQUIRED_ENGINE_LOSS = ["file-label-and-documents-unobservable"]
13+
_REQUIRED_ENGINE_LOSS = ["documents-unobservable"]
1414

1515

1616
@pytest.mark.parametrize("suffix", [".sav", ".zsav"])

0 commit comments

Comments
 (0)