pysof 0.1.30__cp313-cp313-win_amd64.whl
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.
- pysof/__init__.py +332 -0
- pysof/_pysof.cp313-win_amd64.pyd +0 -0
- pysof/_pysof.pyi +49 -0
- pysof/py.typed +0 -0
- pysof-0.1.30.dist-info/METADATA +543 -0
- pysof-0.1.30.dist-info/RECORD +7 -0
- pysof-0.1.30.dist-info/WHEEL +4 -0
pysof/__init__.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""pysof: Python wrapper for the Helios SOF (SQL on FHIR) toolkit.
|
|
2
|
+
|
|
3
|
+
This package provides Python bindings for the Rust `sof` crate, enabling
|
|
4
|
+
transformation of FHIR resources into tabular data using ViewDefinitions.
|
|
5
|
+
|
|
6
|
+
Public API:
|
|
7
|
+
run_view_definition: Transform FHIR data using ViewDefinition
|
|
8
|
+
run_view_definition_with_options: Transform with filtering/pagination
|
|
9
|
+
validate_view_definition: Pre-validate ViewDefinition structure
|
|
10
|
+
validate_bundle: Pre-validate Bundle structure
|
|
11
|
+
get_supported_fhir_versions: List available FHIR versions
|
|
12
|
+
parse_content_type: Parse MIME types to format strings
|
|
13
|
+
|
|
14
|
+
Exception hierarchy:
|
|
15
|
+
SofError: Base exception for all pysof errors
|
|
16
|
+
InvalidViewDefinitionError: ViewDefinition validation errors
|
|
17
|
+
FhirPathError: FHIRPath expression evaluation errors
|
|
18
|
+
SerializationError: JSON/data serialization errors
|
|
19
|
+
UnsupportedContentTypeError: Unsupported output format errors
|
|
20
|
+
CsvError: CSV generation errors
|
|
21
|
+
IoError: File/IO related errors
|
|
22
|
+
InvalidSourceError: Invalid source parameter value
|
|
23
|
+
SourceNotFoundError: Source not found
|
|
24
|
+
SourceFetchError: Failed to fetch from source
|
|
25
|
+
SourceReadError: Failed to read from source
|
|
26
|
+
InvalidSourceContentError: Invalid content in source
|
|
27
|
+
UnsupportedSourceProtocolError: Unsupported source protocol
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from typing import Any, cast
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
# Import the Rust extension module
|
|
34
|
+
from pysof._pysof import (
|
|
35
|
+
CsvError,
|
|
36
|
+
FhirPathError,
|
|
37
|
+
InvalidSourceContentError,
|
|
38
|
+
InvalidSourceError,
|
|
39
|
+
InvalidViewDefinitionError,
|
|
40
|
+
IoError,
|
|
41
|
+
SerializationError,
|
|
42
|
+
# Exception classes
|
|
43
|
+
SofError,
|
|
44
|
+
SourceFetchError,
|
|
45
|
+
SourceNotFoundError,
|
|
46
|
+
SourceReadError,
|
|
47
|
+
UnsupportedContentTypeError,
|
|
48
|
+
UnsupportedSourceProtocolError,
|
|
49
|
+
__version__,
|
|
50
|
+
py_get_supported_fhir_versions,
|
|
51
|
+
py_parse_content_type,
|
|
52
|
+
py_run_view_definition,
|
|
53
|
+
py_run_view_definition_with_options,
|
|
54
|
+
py_validate_bundle,
|
|
55
|
+
py_validate_view_definition,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Create Python-friendly wrapper functions
|
|
59
|
+
def run_view_definition(
|
|
60
|
+
view: dict[str, Any],
|
|
61
|
+
bundle: dict[str, Any],
|
|
62
|
+
format: str,
|
|
63
|
+
*,
|
|
64
|
+
fhir_version: str = "R4",
|
|
65
|
+
) -> bytes:
|
|
66
|
+
"""Transform FHIR Bundle data using a ViewDefinition.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
view: ViewDefinition resource as a Python dictionary
|
|
70
|
+
bundle: FHIR Bundle resource as a Python dictionary
|
|
71
|
+
format: Output format ("csv", "csv_with_header", "json", "ndjson", "parquet")
|
|
72
|
+
fhir_version: FHIR version to use ("R4", "R4B", "R5", "R6"). Defaults to "R4"
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Transformed data in the requested format as bytes
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
InvalidViewDefinitionError: ViewDefinition structure is invalid
|
|
79
|
+
FhirPathError: FHIRPath expression evaluation failed
|
|
80
|
+
SerializationError: JSON parsing/serialization failed
|
|
81
|
+
UnsupportedContentTypeError: Unsupported output format
|
|
82
|
+
CsvError: CSV generation failed
|
|
83
|
+
IoError: I/O operation failed
|
|
84
|
+
"""
|
|
85
|
+
return py_run_view_definition(view, bundle, format, fhir_version)
|
|
86
|
+
|
|
87
|
+
def run_view_definition_with_options(
|
|
88
|
+
view: dict[str, Any],
|
|
89
|
+
bundle: dict[str, Any],
|
|
90
|
+
format: str,
|
|
91
|
+
*,
|
|
92
|
+
since: str | None = None,
|
|
93
|
+
limit: int | None = None,
|
|
94
|
+
page: int | None = None,
|
|
95
|
+
fhir_version: str = "R4",
|
|
96
|
+
) -> bytes:
|
|
97
|
+
"""Transform FHIR Bundle data using a ViewDefinition with additional options.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
view: ViewDefinition resource as a Python dictionary
|
|
101
|
+
bundle: FHIR Bundle resource as a Python dictionary
|
|
102
|
+
format: Output format ("csv", "csv_with_header", "json", "ndjson", "parquet")
|
|
103
|
+
since: Filter resources modified after this ISO8601 datetime
|
|
104
|
+
limit: Limit the number of results returned
|
|
105
|
+
page: Page number for pagination (1-based)
|
|
106
|
+
fhir_version: FHIR version to use ("R4", "R4B", "R5", "R6"). Defaults to "R4"
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Transformed data in the requested format as bytes
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
InvalidViewDefinitionError: ViewDefinition structure is invalid
|
|
113
|
+
FhirPathError: FHIRPath expression evaluation failed
|
|
114
|
+
SerializationError: JSON parsing/serialization failed
|
|
115
|
+
UnsupportedContentTypeError: Unsupported output format
|
|
116
|
+
CsvError: CSV generation failed
|
|
117
|
+
IoError: I/O operation failed
|
|
118
|
+
"""
|
|
119
|
+
return py_run_view_definition_with_options(
|
|
120
|
+
view,
|
|
121
|
+
bundle,
|
|
122
|
+
format,
|
|
123
|
+
since=since,
|
|
124
|
+
limit=limit,
|
|
125
|
+
page=page,
|
|
126
|
+
fhir_version=fhir_version,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def validate_view_definition(
|
|
130
|
+
view: dict[str, Any], *, fhir_version: str = "R4"
|
|
131
|
+
) -> bool:
|
|
132
|
+
"""Validate a ViewDefinition structure without executing it.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
view: ViewDefinition resource as a Python dictionary
|
|
136
|
+
fhir_version: FHIR version to use ("R4", "R4B", "R5", "R6"). Defaults to "R4"
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
True if valid
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
InvalidViewDefinitionError: ViewDefinition structure is invalid
|
|
143
|
+
SerializationError: JSON parsing failed
|
|
144
|
+
"""
|
|
145
|
+
return py_validate_view_definition(view, fhir_version)
|
|
146
|
+
|
|
147
|
+
def validate_bundle(bundle: dict[str, Any], *, fhir_version: str = "R4") -> bool:
|
|
148
|
+
"""Validate a Bundle structure without executing transformations.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
bundle: FHIR Bundle resource as a Python dictionary
|
|
152
|
+
fhir_version: FHIR version to use ("R4", "R4B", "R5", "R6"). Defaults to "R4"
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if valid
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
SerializationError: JSON parsing failed
|
|
159
|
+
"""
|
|
160
|
+
return py_validate_bundle(bundle, fhir_version)
|
|
161
|
+
|
|
162
|
+
def parse_content_type(mime_type: str) -> str:
|
|
163
|
+
"""Parse MIME type string to format identifier.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
mime_type: MIME type string (e.g., "text/csv", "application/json")
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Format identifier suitable for use with run_view_definition
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
UnsupportedContentTypeError: Unknown or unsupported MIME type
|
|
173
|
+
"""
|
|
174
|
+
return py_parse_content_type(mime_type)
|
|
175
|
+
|
|
176
|
+
def get_supported_fhir_versions() -> list[str]:
|
|
177
|
+
"""Get list of supported FHIR versions compiled into this build.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
List of supported FHIR version strings
|
|
181
|
+
"""
|
|
182
|
+
return py_get_supported_fhir_versions()
|
|
183
|
+
|
|
184
|
+
except ImportError as e:
|
|
185
|
+
# Fallback for when the Rust extension is not available
|
|
186
|
+
import warnings
|
|
187
|
+
|
|
188
|
+
warnings.warn(
|
|
189
|
+
f"Rust extension module not available: {e}. Using placeholder functions.",
|
|
190
|
+
ImportWarning,
|
|
191
|
+
stacklevel=2,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Define placeholder exception classes
|
|
195
|
+
class SofError(Exception): # type: ignore[no-redef]
|
|
196
|
+
"""Base exception for all pysof errors"""
|
|
197
|
+
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
class InvalidViewDefinitionError(SofError): # type: ignore[no-redef]
|
|
201
|
+
"""ViewDefinition validation errors"""
|
|
202
|
+
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
class FhirPathError(SofError): # type: ignore[no-redef]
|
|
206
|
+
"""FHIRPath expression evaluation errors"""
|
|
207
|
+
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
class SerializationError(SofError): # type: ignore[no-redef]
|
|
211
|
+
"""JSON/data serialization errors"""
|
|
212
|
+
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
class UnsupportedContentTypeError(SofError): # type: ignore[no-redef]
|
|
216
|
+
"""Unsupported output format errors"""
|
|
217
|
+
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
class CsvError(SofError): # type: ignore[no-redef]
|
|
221
|
+
"""CSV generation errors"""
|
|
222
|
+
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
class IoError(SofError): # type: ignore[no-redef]
|
|
226
|
+
"""File/IO related errors"""
|
|
227
|
+
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
class InvalidSourceError(SofError): # type: ignore[no-redef]
|
|
231
|
+
"""Invalid source parameter value"""
|
|
232
|
+
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
class SourceNotFoundError(SofError): # type: ignore[no-redef]
|
|
236
|
+
"""Source not found"""
|
|
237
|
+
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
class SourceFetchError(SofError): # type: ignore[no-redef]
|
|
241
|
+
"""Failed to fetch from source"""
|
|
242
|
+
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
class SourceReadError(SofError): # type: ignore[no-redef]
|
|
246
|
+
"""Failed to read from source"""
|
|
247
|
+
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
class InvalidSourceContentError(SofError): # type: ignore[no-redef]
|
|
251
|
+
"""Invalid content in source"""
|
|
252
|
+
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
class UnsupportedSourceProtocolError(SofError): # type: ignore[no-redef]
|
|
256
|
+
"""Unsupported source protocol"""
|
|
257
|
+
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
# Define placeholder functions
|
|
261
|
+
def run_view_definition(
|
|
262
|
+
view: dict[str, Any],
|
|
263
|
+
bundle: dict[str, Any],
|
|
264
|
+
format: str,
|
|
265
|
+
*,
|
|
266
|
+
fhir_version: str = "R4",
|
|
267
|
+
) -> bytes:
|
|
268
|
+
raise NotImplementedError("Rust extension module not available")
|
|
269
|
+
|
|
270
|
+
def run_view_definition_with_options(
|
|
271
|
+
view: dict[str, Any],
|
|
272
|
+
bundle: dict[str, Any],
|
|
273
|
+
format: str,
|
|
274
|
+
*,
|
|
275
|
+
since: str | None = None,
|
|
276
|
+
limit: int | None = None,
|
|
277
|
+
page: int | None = None,
|
|
278
|
+
fhir_version: str = "R4",
|
|
279
|
+
) -> bytes:
|
|
280
|
+
raise NotImplementedError("Rust extension module not available")
|
|
281
|
+
|
|
282
|
+
def validate_view_definition(
|
|
283
|
+
view: dict[str, Any], *, fhir_version: str = "R4"
|
|
284
|
+
) -> bool:
|
|
285
|
+
raise NotImplementedError("Rust extension module not available")
|
|
286
|
+
|
|
287
|
+
def validate_bundle(bundle: dict[str, Any], *, fhir_version: str = "R4") -> bool:
|
|
288
|
+
raise NotImplementedError("Rust extension module not available")
|
|
289
|
+
|
|
290
|
+
def parse_content_type(mime_type: str) -> str:
|
|
291
|
+
raise NotImplementedError("Rust extension module not available")
|
|
292
|
+
|
|
293
|
+
def get_supported_fhir_versions() -> list[str]:
|
|
294
|
+
raise NotImplementedError("Rust extension module not available")
|
|
295
|
+
|
|
296
|
+
# Set fallback version when Rust extension is not available
|
|
297
|
+
__version__ = "0.0.0-dev"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
__all__: list[str] = [
|
|
301
|
+
# Core functions
|
|
302
|
+
"run_view_definition",
|
|
303
|
+
"run_view_definition_with_options",
|
|
304
|
+
"validate_view_definition",
|
|
305
|
+
"validate_bundle",
|
|
306
|
+
"get_supported_fhir_versions",
|
|
307
|
+
"parse_content_type",
|
|
308
|
+
# Exception classes
|
|
309
|
+
"SofError",
|
|
310
|
+
"InvalidViewDefinitionError",
|
|
311
|
+
"FhirPathError",
|
|
312
|
+
"SerializationError",
|
|
313
|
+
"UnsupportedContentTypeError",
|
|
314
|
+
"CsvError",
|
|
315
|
+
"IoError",
|
|
316
|
+
"InvalidSourceError",
|
|
317
|
+
"SourceNotFoundError",
|
|
318
|
+
"SourceFetchError",
|
|
319
|
+
"SourceReadError",
|
|
320
|
+
"InvalidSourceContentError",
|
|
321
|
+
"UnsupportedSourceProtocolError",
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def get_version() -> str:
|
|
326
|
+
"""Return the package version."""
|
|
327
|
+
return str(__version__)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def get_status() -> str:
|
|
331
|
+
"""Return the current implementation status."""
|
|
332
|
+
return "v1: Rust bindings available with full SOF transformation capabilities."
|
|
Binary file
|
pysof/_pysof.pyi
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Type stubs for the pysof Rust extension module."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
# Module attributes
|
|
6
|
+
__version__: str
|
|
7
|
+
|
|
8
|
+
# Exception classes
|
|
9
|
+
class SofError(Exception): ...
|
|
10
|
+
class InvalidViewDefinitionError(SofError): ...
|
|
11
|
+
class FhirPathError(SofError): ...
|
|
12
|
+
class SerializationError(SofError): ...
|
|
13
|
+
class UnsupportedContentTypeError(SofError): ...
|
|
14
|
+
class CsvError(SofError): ...
|
|
15
|
+
class IoError(SofError): ...
|
|
16
|
+
class InvalidSourceError(SofError): ...
|
|
17
|
+
class SourceNotFoundError(SofError): ...
|
|
18
|
+
class SourceFetchError(SofError): ...
|
|
19
|
+
class SourceReadError(SofError): ...
|
|
20
|
+
class InvalidSourceContentError(SofError): ...
|
|
21
|
+
class UnsupportedSourceProtocolError(SofError): ...
|
|
22
|
+
|
|
23
|
+
# Core functions
|
|
24
|
+
def py_run_view_definition(
|
|
25
|
+
view: dict[str, Any],
|
|
26
|
+
bundle: dict[str, Any],
|
|
27
|
+
format: str,
|
|
28
|
+
fhir_version: str,
|
|
29
|
+
) -> bytes: ...
|
|
30
|
+
def py_run_view_definition_with_options(
|
|
31
|
+
view: dict[str, Any],
|
|
32
|
+
bundle: dict[str, Any],
|
|
33
|
+
format: str,
|
|
34
|
+
*,
|
|
35
|
+
since: str | None = None,
|
|
36
|
+
limit: int | None = None,
|
|
37
|
+
page: int | None = None,
|
|
38
|
+
fhir_version: str = "R4",
|
|
39
|
+
) -> bytes: ...
|
|
40
|
+
def py_validate_view_definition(
|
|
41
|
+
view: dict[str, Any],
|
|
42
|
+
fhir_version: str,
|
|
43
|
+
) -> bool: ...
|
|
44
|
+
def py_validate_bundle(
|
|
45
|
+
bundle: dict[str, Any],
|
|
46
|
+
fhir_version: str,
|
|
47
|
+
) -> bool: ...
|
|
48
|
+
def py_parse_content_type(mime_type: str) -> str: ...
|
|
49
|
+
def py_get_supported_fhir_versions() -> list[str]: ...
|
pysof/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysof
|
|
3
|
+
Version: 0.1.30
|
|
4
|
+
Classifier: Programming Language :: Python :: 3
|
|
5
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Summary: Python wrapper for the Helios Software SOF (SQL on FHIR) toolkit.
|
|
17
|
+
Keywords: SQL on FHIR,FHIR,healthcare,sof,Helios Software
|
|
18
|
+
Author-email: Helios Software <team@heliossoftware.com>
|
|
19
|
+
License: MIT
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
Project-URL: Homepage, https://github.com/HeliosSoftware/hfs/tree/main/crates/pysof
|
|
23
|
+
Project-URL: Repository, https://github.com/HeliosSoftware/hfs
|
|
24
|
+
Project-URL: Documentation, https://github.com/HeliosSoftware/hfs/tree/main/crates/pysof
|
|
25
|
+
Project-URL: Bug Tracker, https://github.com/HeliosSoftware/hfs/issues
|
|
26
|
+
Project-URL: Source, https://github.com/HeliosSoftware/hfs/tree/main/crates/pysof
|
|
27
|
+
|
|
28
|
+
# pysof - SQL on FHIR for Python
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/pysof/)
|
|
31
|
+
[](https://pypi.org/project/pysof/)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
[](https://pepy.tech/project/pysof)
|
|
34
|
+
|
|
35
|
+
**High-performance FHIR data transformation for Python.** Transform FHIR resources into tabular formats (CSV, JSON, Parquet) using declarative ViewDefinitions from the [SQL on FHIR specification](https://build.fhir.org/ig/FHIR/sql-on-fhir-v2/).
|
|
36
|
+
|
|
37
|
+
Built in Rust for speed, exposed to Python with a simple, Pythonic API. Part of the [Helios FHIR Server](https://github.com/HeliosSoftware/hfs) project.
|
|
38
|
+
|
|
39
|
+
## ✨ Key Features
|
|
40
|
+
|
|
41
|
+
- 🚀 **High Performance**: Native Rust implementation with minimal Python overhead
|
|
42
|
+
- 📊 **Multiple Output Formats**: CSV, JSON, NDJSON, and Parquet
|
|
43
|
+
- 🔄 **Parallel Processing**: Automatic multithreading with 5-7x speedup on multi-core systems
|
|
44
|
+
- 🌐 **Multi-Version FHIR**: Supports R4, R4B, R5, and R6 (based on build features)
|
|
45
|
+
- 🎯 **Type-Safe**: Leverages Rust's type safety with a Pythonic interface
|
|
46
|
+
- ⚡ **GIL-Free**: Python GIL released during processing for true parallelism
|
|
47
|
+
|
|
48
|
+
## 🎯 Why pysof?
|
|
49
|
+
|
|
50
|
+
Working with FHIR data in Python just got faster. **pysof** lets you:
|
|
51
|
+
|
|
52
|
+
- **Transform complex FHIR resources** into clean, analyzable tables without writing custom parsers
|
|
53
|
+
- **Process large datasets efficiently** with automatic parallel processing and Rust-level performance
|
|
54
|
+
- **Use standard SQL on FHIR ViewDefinitions** for portable, maintainable data transformations
|
|
55
|
+
- **Export to multiple formats** (CSV, JSON, NDJSON, Parquet) for analytics, ML, or reporting workflows
|
|
56
|
+
|
|
57
|
+
Perfect for healthcare data engineers, researchers, and developers building FHIR-based analytics pipelines.
|
|
58
|
+
|
|
59
|
+
## 🔗 Quick Links
|
|
60
|
+
|
|
61
|
+
- 📦 **[PyPI Package](https://pypi.org/project/pysof/)**
|
|
62
|
+
- 📚 **[Documentation](https://github.com/HeliosSoftware/hfs/tree/main/crates/pysof)**
|
|
63
|
+
- 🐛 **[Issue Tracker](https://github.com/HeliosSoftware/hfs/issues)**
|
|
64
|
+
- 💻 **[Source Code](https://github.com/HeliosSoftware/hfs)**
|
|
65
|
+
- 📋 **[GitHub Releases](https://github.com/HeliosSoftware/hfs/releases)**
|
|
66
|
+
|
|
67
|
+
## 📥 Installation
|
|
68
|
+
|
|
69
|
+
### From PyPI (Recommended)
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install pysof
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Supported Platforms:**
|
|
76
|
+
- **Linux**: x86_64 (glibc and musl)
|
|
77
|
+
- **Windows**: x86_64 (MSVC)
|
|
78
|
+
- **macOS**: AArch64 (Apple Silicon)
|
|
79
|
+
- **Python**: 3.10, 3.11, 3.12, 3.13
|
|
80
|
+
|
|
81
|
+
### From GitHub Releases
|
|
82
|
+
|
|
83
|
+
Download pre-built wheels from the [releases page](https://github.com/HeliosSoftware/hfs/releases):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install pysof-*.whl
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 🚀 Quick Start
|
|
90
|
+
|
|
91
|
+
Transform FHIR patient data to CSV in just a few lines:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import pysof
|
|
95
|
+
|
|
96
|
+
# Define what data to extract
|
|
97
|
+
view_definition = {
|
|
98
|
+
"resourceType": "ViewDefinition",
|
|
99
|
+
"id": "patient-demographics",
|
|
100
|
+
"name": "PatientDemographics",
|
|
101
|
+
"status": "active",
|
|
102
|
+
"resource": "Patient",
|
|
103
|
+
"select": [{
|
|
104
|
+
"column": [
|
|
105
|
+
{"name": "id", "path": "id"},
|
|
106
|
+
{"name": "family_name", "path": "name.family"},
|
|
107
|
+
{"name": "given_name", "path": "name.given.first()"},
|
|
108
|
+
{"name": "gender", "path": "gender"},
|
|
109
|
+
{"name": "birth_date", "path": "birthDate"}
|
|
110
|
+
]
|
|
111
|
+
}]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Sample FHIR Bundle
|
|
115
|
+
bundle = {
|
|
116
|
+
"resourceType": "Bundle",
|
|
117
|
+
"type": "collection",
|
|
118
|
+
"entry": [{
|
|
119
|
+
"resource": {
|
|
120
|
+
"resourceType": "Patient",
|
|
121
|
+
"id": "patient-1",
|
|
122
|
+
"name": [{"family": "Doe", "given": ["John"]}],
|
|
123
|
+
"gender": "male",
|
|
124
|
+
"birthDate": "1990-01-01"
|
|
125
|
+
}
|
|
126
|
+
}]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Transform to CSV
|
|
130
|
+
csv_output = pysof.run_view_definition(view_definition, bundle, "csv")
|
|
131
|
+
print(csv_output.decode('utf-8'))
|
|
132
|
+
# Output:
|
|
133
|
+
# id,family_name,given_name,gender,birth_date
|
|
134
|
+
# patient-1,Doe,John,male,1990-01-01
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 📖 Usage
|
|
138
|
+
|
|
139
|
+
### Multiple Output Formats
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
import pysof
|
|
143
|
+
import json
|
|
144
|
+
|
|
145
|
+
# Transform to different formats
|
|
146
|
+
csv_result = pysof.run_view_definition(view_definition, bundle, "csv")
|
|
147
|
+
json_result = pysof.run_view_definition(view_definition, bundle, "json")
|
|
148
|
+
ndjson_result = pysof.run_view_definition(view_definition, bundle, "ndjson")
|
|
149
|
+
parquet_result = pysof.run_view_definition(view_definition, bundle, "parquet")
|
|
150
|
+
|
|
151
|
+
print("CSV Output:")
|
|
152
|
+
print(csv_result.decode('utf-8'))
|
|
153
|
+
|
|
154
|
+
print("\nJSON Output:")
|
|
155
|
+
data = json.loads(json_result.decode('utf-8'))
|
|
156
|
+
print(json.dumps(data, indent=2))
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Advanced Options
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
import pysof
|
|
163
|
+
|
|
164
|
+
# Transform with pagination and filtering
|
|
165
|
+
result = pysof.run_view_definition_with_options(
|
|
166
|
+
view_definition,
|
|
167
|
+
bundle,
|
|
168
|
+
"json",
|
|
169
|
+
limit=10, # Limit results
|
|
170
|
+
page=1, # Page number
|
|
171
|
+
since="2023-01-01T00:00:00Z", # Filter by modification date
|
|
172
|
+
fhir_version="R4" # Specify FHIR version
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Utility Functions
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
import pysof
|
|
180
|
+
|
|
181
|
+
# Validate structures
|
|
182
|
+
is_valid_view = pysof.validate_view_definition(view_definition)
|
|
183
|
+
is_valid_bundle = pysof.validate_bundle(bundle)
|
|
184
|
+
|
|
185
|
+
# Parse content types
|
|
186
|
+
format_str = pysof.parse_content_type("text/csv") # Returns "csv_with_header"
|
|
187
|
+
|
|
188
|
+
# Check supported FHIR versions
|
|
189
|
+
versions = pysof.get_supported_fhir_versions() # Returns ["R4"] or more
|
|
190
|
+
print(f"Supported FHIR versions: {versions}")
|
|
191
|
+
|
|
192
|
+
# Package info
|
|
193
|
+
print(f"Version: {pysof.get_version()}")
|
|
194
|
+
print(pysof.get_status())
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Error Handling
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import pysof
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
result = pysof.run_view_definition(view_definition, bundle, "json")
|
|
204
|
+
except pysof.InvalidViewDefinitionError as e:
|
|
205
|
+
print(f"ViewDefinition validation error: {e}")
|
|
206
|
+
except pysof.SerializationError as e:
|
|
207
|
+
print(f"JSON parsing error: {e}")
|
|
208
|
+
except pysof.UnsupportedContentTypeError as e:
|
|
209
|
+
print(f"Unsupported format: {e}")
|
|
210
|
+
except pysof.SofError as e:
|
|
211
|
+
print(f"General SOF error: {e}")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## ⚡ Performance
|
|
215
|
+
|
|
216
|
+
### Automatic Parallel Processing
|
|
217
|
+
|
|
218
|
+
pysof automatically processes FHIR resources in parallel using rayon:
|
|
219
|
+
|
|
220
|
+
- **5-7x speedup** on typical workloads with multi-core CPUs
|
|
221
|
+
- **Zero configuration** - parallelization is always enabled
|
|
222
|
+
- **Python GIL released** during processing for true parallel execution
|
|
223
|
+
|
|
224
|
+
### Controlling Thread Count
|
|
225
|
+
|
|
226
|
+
Set the `RAYON_NUM_THREADS` environment variable before importing pysof:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
import os
|
|
230
|
+
os.environ['RAYON_NUM_THREADS'] = '4' # Must be set before first import
|
|
231
|
+
|
|
232
|
+
import pysof
|
|
233
|
+
result = pysof.run_view_definition(view_definition, bundle, "json")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Or from the command line:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Linux/Mac
|
|
240
|
+
RAYON_NUM_THREADS=4 python my_script.py
|
|
241
|
+
|
|
242
|
+
# Windows PowerShell
|
|
243
|
+
$env:RAYON_NUM_THREADS=4
|
|
244
|
+
python my_script.py
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Performance Tips:**
|
|
248
|
+
- Use all available cores for large datasets (default behavior)
|
|
249
|
+
- Limit threads on shared systems to avoid resource contention
|
|
250
|
+
- Reduce thread count if memory-constrained
|
|
251
|
+
|
|
252
|
+
## 📋 Supported Features
|
|
253
|
+
|
|
254
|
+
### Output Formats
|
|
255
|
+
|
|
256
|
+
| Format | Description | Output |
|
|
257
|
+
|--------|-------------|--------|
|
|
258
|
+
| `csv` | CSV with headers | Comma-separated values with header row |
|
|
259
|
+
| `json` | JSON array | Array of objects, one per result row |
|
|
260
|
+
| `ndjson` | Newline-delimited JSON | One JSON object per line |
|
|
261
|
+
| `parquet` | Parquet format | Columnar binary format for analytics |
|
|
262
|
+
|
|
263
|
+
### FHIR Versions
|
|
264
|
+
|
|
265
|
+
- **R4** (default, always available)
|
|
266
|
+
- **R4B** (if compiled with R4B feature)
|
|
267
|
+
- **R5** (if compiled with R5 feature)
|
|
268
|
+
- **R6** (if compiled with R6 feature)
|
|
269
|
+
|
|
270
|
+
Use `pysof.get_supported_fhir_versions()` to check available versions in your build.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 🔧 Development
|
|
275
|
+
|
|
276
|
+
### Requirements
|
|
277
|
+
|
|
278
|
+
- Python 3.10 or later (3.10, 3.11, 3.12, 3.13 supported)
|
|
279
|
+
- uv (package and environment manager)
|
|
280
|
+
- Rust toolchain (for building from source)
|
|
281
|
+
|
|
282
|
+
> **Note**: This crate is excluded from the default workspace build. When running `cargo build` from the repository root, `pysof` will not be built automatically.
|
|
283
|
+
|
|
284
|
+
### Building from Source
|
|
285
|
+
|
|
286
|
+
### Building with Cargo
|
|
287
|
+
|
|
288
|
+
This crate is excluded from the default workspace build to allow building the core Rust components without Python. To build it explicitly:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
# Your current directory MUST be the pysof crate:
|
|
292
|
+
cd crates/pysof
|
|
293
|
+
|
|
294
|
+
# From the pysof folder
|
|
295
|
+
cargo build
|
|
296
|
+
|
|
297
|
+
# Or build with specific FHIR version features
|
|
298
|
+
cargo build -p pysof --features R4,R5
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Building with Maturin (Recommended)
|
|
302
|
+
|
|
303
|
+
For Python development, it's recommended to use `maturin` via `uv`:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# From repo root
|
|
307
|
+
cd crates/pysof
|
|
308
|
+
|
|
309
|
+
# Create a venv with your preferred Python version (3.10+)
|
|
310
|
+
uv venv --python 3.11 # or 3.10, 3.12, 3.13
|
|
311
|
+
|
|
312
|
+
# Install the project dev dependencies
|
|
313
|
+
uv sync --group dev
|
|
314
|
+
|
|
315
|
+
# Build and install the Rust extension into the venv
|
|
316
|
+
uv run maturin develop --release
|
|
317
|
+
|
|
318
|
+
# Build distributable artifacts
|
|
319
|
+
uv run maturin build --release -o dist # wheels
|
|
320
|
+
uv run maturin sdist -o dist # source distribution
|
|
321
|
+
|
|
322
|
+
# Sanity checks
|
|
323
|
+
uv run python -c "import pysof; print(pysof.__version__); print(pysof.get_status()); print(pysof.get_supported_fhir_versions())"
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Installing from Source
|
|
327
|
+
|
|
328
|
+
Requires Rust toolchain:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
# Install directly
|
|
332
|
+
pip install -e .
|
|
333
|
+
|
|
334
|
+
# Or build wheel locally
|
|
335
|
+
maturin build --release --out dist
|
|
336
|
+
pip install dist/*.whl
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Testing
|
|
340
|
+
|
|
341
|
+
The project has separate test suites for Python and Rust components:
|
|
342
|
+
|
|
343
|
+
#### Python Tests
|
|
344
|
+
|
|
345
|
+
Run the comprehensive Python test suite:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# Run all Python tests
|
|
349
|
+
uv run pytest python-tests/
|
|
350
|
+
|
|
351
|
+
# Run specific test files
|
|
352
|
+
uv run pytest python-tests/test_core_functions.py -v
|
|
353
|
+
uv run pytest python-tests/test_content_types.py -v
|
|
354
|
+
uv run pytest python-tests/test_import.py -v
|
|
355
|
+
|
|
356
|
+
# Run with coverage
|
|
357
|
+
uv run pytest python-tests/ --cov=pysof --cov-report=html
|
|
358
|
+
|
|
359
|
+
# Run tests with detailed output
|
|
360
|
+
uv run pytest python-tests/ -v --tb=short
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Rust Tests
|
|
364
|
+
|
|
365
|
+
Run the Rust unit and integration tests:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
# Run all Rust tests
|
|
369
|
+
cargo test
|
|
370
|
+
|
|
371
|
+
# Run unit tests only
|
|
372
|
+
cargo test --test lib_tests
|
|
373
|
+
|
|
374
|
+
# Run integration tests only
|
|
375
|
+
cargo test --test integration
|
|
376
|
+
|
|
377
|
+
# Run with verbose output
|
|
378
|
+
cargo test -- --nocapture
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Configuring FHIR Version Support
|
|
382
|
+
|
|
383
|
+
By default, pysof is compiled with **R4 support only**. You can configure which FHIR versions are available by modifying the feature compilation settings.
|
|
384
|
+
|
|
385
|
+
### Change Default FHIR Version
|
|
386
|
+
|
|
387
|
+
To change from R4 to another version (e.g., R5):
|
|
388
|
+
|
|
389
|
+
1. **Edit `crates/pysof/Cargo.toml`**:
|
|
390
|
+
```toml
|
|
391
|
+
[features]
|
|
392
|
+
default = ["R5"] # Changed from ["R4"]
|
|
393
|
+
R4 = ["helios-sof/R4", "helios-fhir/R4"]
|
|
394
|
+
R4B = ["helios-sof/R4B", "helios-fhir/R4B"]
|
|
395
|
+
R5 = ["helios-sof/R5", "helios-fhir/R5"]
|
|
396
|
+
R6 = ["helios-sof/R6", "helios-fhir/R6"]
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
2. **Rebuild the extension**:
|
|
400
|
+
```bash
|
|
401
|
+
cd crates/pysof
|
|
402
|
+
uv run maturin develop --release
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
3. **Verify the change**:
|
|
406
|
+
```bash
|
|
407
|
+
uv run python -c "
|
|
408
|
+
import pysof
|
|
409
|
+
versions = pysof.get_supported_fhir_versions()
|
|
410
|
+
print('Supported FHIR versions:', versions)
|
|
411
|
+
"
|
|
412
|
+
```
|
|
413
|
+
This should now show `['R5']` instead of `['R4']`.
|
|
414
|
+
|
|
415
|
+
### Enable Multiple FHIR Versions
|
|
416
|
+
|
|
417
|
+
To support multiple FHIR versions simultaneously:
|
|
418
|
+
|
|
419
|
+
1. **Edit `crates/pysof/Cargo.toml`**:
|
|
420
|
+
```toml
|
|
421
|
+
[features]
|
|
422
|
+
default = ["R4", "R5"] # Enable both R4 and R5
|
|
423
|
+
# Or enable all versions:
|
|
424
|
+
# default = ["R4", "R4B", "R5", "R6"]
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
2. **Rebuild and verify**:
|
|
428
|
+
```bash
|
|
429
|
+
uv run maturin develop --release
|
|
430
|
+
uv run python -c "import pysof; print(pysof.get_supported_fhir_versions())"
|
|
431
|
+
```
|
|
432
|
+
This should show `['R4', 'R5']` (or all enabled versions).
|
|
433
|
+
|
|
434
|
+
3. **Use specific versions in code**:
|
|
435
|
+
```python
|
|
436
|
+
import pysof
|
|
437
|
+
|
|
438
|
+
# Use R4 explicitly
|
|
439
|
+
result_r4 = pysof.run_view_definition(view, bundle, "json", fhir_version="R4")
|
|
440
|
+
|
|
441
|
+
# Use R5 explicitly
|
|
442
|
+
result_r5 = pysof.run_view_definition(view, bundle, "json", fhir_version="R5")
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Build with Specific Features (Without Changing Default)
|
|
446
|
+
|
|
447
|
+
To temporarily build with different features without modifying `Cargo.toml`:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
# Build with only R5
|
|
451
|
+
cargo build --features R5 --no-default-features
|
|
452
|
+
|
|
453
|
+
# Build with R4 and R6
|
|
454
|
+
cargo build --features R4,R6 --no-default-features
|
|
455
|
+
|
|
456
|
+
# With maturin
|
|
457
|
+
uv run --with maturin -- maturin develop --release --cargo-extra-args="--features R5 --no-default-features"
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Testing After Version Changes
|
|
461
|
+
|
|
462
|
+
After changing FHIR version support, run the test suite to ensure compatibility:
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
# Run all tests
|
|
466
|
+
uv run pytest
|
|
467
|
+
|
|
468
|
+
# Run FHIR version-specific tests
|
|
469
|
+
uv run pytest tests/test_fhir_versions.py -v
|
|
470
|
+
|
|
471
|
+
# Test with your new default version
|
|
472
|
+
uv run python -c "
|
|
473
|
+
import pysof
|
|
474
|
+
|
|
475
|
+
# Test with default version (should be your new default)
|
|
476
|
+
view = {'resourceType': 'ViewDefinition', 'id': 'test', 'name': 'Test', 'status': 'active', 'resource': 'Patient', 'select': [{'column': [{'name': 'id', 'path': 'id'}]}]}
|
|
477
|
+
bundle = {'resourceType': 'Bundle', 'type': 'collection', 'entry': [{'resource': {'resourceType': 'Patient', 'id': 'test'}}]}
|
|
478
|
+
|
|
479
|
+
result = pysof.run_view_definition(view, bundle, 'json')
|
|
480
|
+
print('Default version test successful:', len(result), 'bytes')
|
|
481
|
+
"
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## Project layout
|
|
485
|
+
|
|
486
|
+
```
|
|
487
|
+
crates/pysof/
|
|
488
|
+
├─ pyproject.toml # PEP 621 metadata, Python >=3.8, uv-compatible
|
|
489
|
+
├─ README.md
|
|
490
|
+
├─ src/
|
|
491
|
+
│ ├─ pysof/
|
|
492
|
+
│ │ └─ __init__.py # Python package root
|
|
493
|
+
│ └─ lib.rs # Rust PyO3 bindings
|
|
494
|
+
├─ tests/ # Rust tests (17 tests)
|
|
495
|
+
│ ├─ lib_tests.rs # Unit tests for core library functions
|
|
496
|
+
│ ├─ integration.rs # Integration tests for component interactions
|
|
497
|
+
│ └─ integration/ # Organized integration test modules
|
|
498
|
+
│ ├─ mod.rs
|
|
499
|
+
│ ├─ content_types.rs
|
|
500
|
+
│ ├─ error_handling.rs
|
|
501
|
+
│ └─ fhir_versions.rs
|
|
502
|
+
├─ python-tests/ # Python test suite (58 tests)
|
|
503
|
+
│ ├─ __init__.py
|
|
504
|
+
│ ├─ test_core_functions.py
|
|
505
|
+
│ ├─ test_content_types.py
|
|
506
|
+
│ ├─ test_fhir_versions.py
|
|
507
|
+
│ ├─ test_import.py
|
|
508
|
+
│ └─ test_package_metadata.py
|
|
509
|
+
└─ Cargo.toml # Rust crate metadata
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## 📄 License
|
|
513
|
+
|
|
514
|
+
MIT License - See [LICENSE.md](../../LICENSE.md) for details.
|
|
515
|
+
|
|
516
|
+
Copyright (c) 2025 Helios Software
|
|
517
|
+
|
|
518
|
+
## 🤝 Contributing
|
|
519
|
+
|
|
520
|
+
Contributions are welcome! Please see our [Contributing Guidelines](../../CONTRIBUTING.md) for details.
|
|
521
|
+
|
|
522
|
+
### Reporting Issues
|
|
523
|
+
|
|
524
|
+
- **Bug Reports**: [GitHub Issues](https://github.com/HeliosSoftware/hfs/issues)
|
|
525
|
+
- **Security Issues**: Email team@heliossoftware.com
|
|
526
|
+
|
|
527
|
+
### Development Setup
|
|
528
|
+
|
|
529
|
+
See the [Development](#-development) section above for instructions on setting up your development environment.
|
|
530
|
+
|
|
531
|
+
## 🙏 Acknowledgments
|
|
532
|
+
|
|
533
|
+
Built with:
|
|
534
|
+
- [PyO3](https://pyo3.rs/) - Rust bindings for Python
|
|
535
|
+
- [maturin](https://www.maturin.rs/) - Build system for Rust Python extensions
|
|
536
|
+
- [helios-sof](../sof) - Core SQL-on-FHIR implementation in Rust
|
|
537
|
+
|
|
538
|
+
Part of the [Helios FHIR Server](https://github.com/HeliosSoftware/hfs) project.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
**Made with ❤️ by [Helios Software](https://heliossoftware.com)**
|
|
543
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
pysof-0.1.30.dist-info/METADATA,sha256=V_PAZrpkd6gUajiPKoy-p0YFJnO7KFbRUhv_6ORE1-g,16671
|
|
2
|
+
pysof-0.1.30.dist-info/WHEEL,sha256=N98zsR-csMcExBUgwG16bkVqfvUmGS-bae3JTmaVPLE,97
|
|
3
|
+
pysof/__init__.py,sha256=oP9_yZjTGOivf7h_yzYSg-fjWJiFIO4HRQu3J_wtwOk,11018
|
|
4
|
+
pysof/_pysof.cp313-win_amd64.pyd,sha256=rh-HNWYAYIcbW9YEIwDUrz-TIqZlRc1cnB5ynLF7_Aw,87629312
|
|
5
|
+
pysof/_pysof.pyi,sha256=YO6fyyNum9C7eNKd5DSbU3M1Y-_O38a5R7IF1Wsda-M,1409
|
|
6
|
+
pysof/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
pysof-0.1.30.dist-info/RECORD,,
|