pysof 0.2.0__tar.gz → 0.2.3__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.
- {pysof-0.2.0 → pysof-0.2.3}/Cargo.toml +6 -2
- {pysof-0.2.0 → pysof-0.2.3}/PKG-INFO +78 -11
- {pysof-0.2.0/crates/pysof → pysof-0.2.3}/README.md +77 -10
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/Cargo.toml +3 -3
- pysof-0.2.3/crates/fhir/src/compartment/loader.rs +178 -0
- pysof-0.2.3/crates/fhir/src/compartment/mod.rs +15 -0
- pysof-0.2.3/crates/fhir/src/compartment_expressions/r6.rs +711 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/lib.rs +120 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/r4.rs +267 -677
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/r4b.rs +271 -681
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/r5.rs +269 -679
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/r6.rs +38881 -45996
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/search/errors.rs +26 -0
- pysof-0.2.3/crates/fhir/src/search/loader.rs +1309 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/search/mod.rs +1 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/search/registry.rs +376 -11
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir-macro/Cargo.toml +1 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir-macro/src/lib.rs +188 -290
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/Cargo.toml +7 -2
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/README.md +26 -6
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/boundary_functions.rs +172 -11
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/cli.rs +2 -2
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/evaluator.rs +259 -71
- pysof-0.2.3/crates/fhirpath/src/functions.rs +711 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/handlers.rs +2 -0
- pysof-0.2.3/crates/fhirpath/src/lib.rs +766 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/parser.rs +1 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/polymorphic_access.rs +612 -86
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/server.rs +15 -11
- pysof-0.2.3/crates/fhirpath/src/terminology_client.rs +1014 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/terminology_functions.rs +28 -5
- pysof-0.2.3/crates/fhirpath/tests/builtin_functions_evaluator_tests.rs +66 -0
- pysof-0.2.3/crates/fhirpath/tests/cli_version_test.rs +17 -0
- pysof-0.2.3/crates/fhirpath/tests/common/known_failures.rs +333 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/common/mod.rs +2 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/common/runner.rs +4 -1
- pysof-0.2.3/crates/fhirpath/tests/data/r4/rust-known-failures.json +5 -0
- pysof-0.2.3/crates/fhirpath/tests/data/r5/known-test-failures.json +71 -0
- pysof-0.2.3/crates/fhirpath/tests/data/r5/rust-known-failures.json +56 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/tests-fhir-r5.xml +47 -0
- pysof-0.2.3/crates/fhirpath/tests/environment_variables_tests.rs +72 -0
- pysof-0.2.3/crates/fhirpath/tests/external_constants_tests.rs +150 -0
- pysof-0.2.3/crates/fhirpath/tests/parse_expression_diagnostics_tests.rs +72 -0
- pysof-0.2.3/crates/fhirpath/tests/parse_expression_spanned_tests.rs +58 -0
- pysof-0.2.3/crates/fhirpath/tests/polymorphic_version_tests.rs +162 -0
- pysof-0.2.3/crates/fhirpath/tests/primitive_extension_tests.rs +567 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/r4_tests.rs +161 -164
- pysof-0.2.3/crates/fhirpath/tests/r5_tests.rs +601 -0
- pysof-0.2.3/crates/fhirpath/tests/terminology_config_tests.rs +174 -0
- pysof-0.2.3/crates/fhirpath/tests/valueless_primitive_tests.rs +118 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath-support/src/lib.rs +90 -1
- pysof-0.2.3/crates/observability/Cargo.toml +63 -0
- pysof-0.2.3/crates/observability/src/dashboard.rs +1105 -0
- pysof-0.2.3/crates/observability/src/dashboard_counters.rs +2311 -0
- pysof-0.2.3/crates/observability/src/dashboard_metrics.rs +231 -0
- pysof-0.2.3/crates/observability/src/lib.rs +56 -0
- pysof-0.2.3/crates/observability/src/metrics.rs +107 -0
- pysof-0.2.3/crates/observability/src/middleware.rs +315 -0
- pysof-0.2.3/crates/observability/src/mode.rs +188 -0
- pysof-0.2.3/crates/observability/src/reqlog.rs +404 -0
- pysof-0.2.3/crates/observability/src/subscriptions.rs +126 -0
- pysof-0.2.3/crates/observability/src/telemetry.rs +166 -0
- pysof-0.2.3/crates/observability/src/uptime.rs +56 -0
- pysof-0.2.3/crates/observability/tests/middleware.rs +65 -0
- pysof-0.2.3/crates/observability/tests/middleware_full.rs +36 -0
- pysof-0.2.3/crates/observability/tests/middleware_off.rs +36 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/.gitignore +5 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/Cargo.toml +6 -2
- {pysof-0.2.0 → pysof-0.2.3/crates/pysof}/README.md +77 -10
- pysof-0.2.3/crates/pysof/build.rs +13 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_core_functions.py +13 -5
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_error_handling.py +6 -5
- pysof-0.2.3/crates/pysof/python-tests/test_json_string_input.py +116 -0
- pysof-0.2.3/crates/pysof/python-tests/test_ndjson_exports.py +85 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_package_metadata.py +2 -2
- pysof-0.2.3/crates/pysof/python-tests/test_remote_resolve.py +141 -0
- pysof-0.2.3/crates/pysof/python-tests/test_threading.py +108 -0
- pysof-0.2.3/crates/pysof/scripts/bench_threading.py +192 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/src/lib.rs +407 -247
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/src/pysof/__init__.py +218 -18
- pysof-0.2.3/crates/pysof/src/pysof/_pysof.pyi +120 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/integration/content_types.rs +66 -65
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/integration.rs +2 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/lib_coverage_tests.rs +3 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/serde-support/Cargo.toml +4 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/serde-support/src/lib.rs +179 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/Cargo.toml +23 -4
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/README.md +84 -43
- pysof-0.2.3/crates/sof/scripts/bench_arrow_client.py +126 -0
- pysof-0.2.0/crates/sof/src/server.rs → pysof-0.2.3/crates/sof/src/app.rs +60 -313
- pysof-0.2.3/crates/sof/src/canonical.rs +80 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/cli.rs +114 -25
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/error.rs +42 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/handlers.rs +380 -198
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/lib.rs +1098 -262
- pysof-0.2.3/crates/sof/src/lint/tests.rs +1917 -0
- pysof-0.2.3/crates/sof/src/lint.rs +2017 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/models.rs +75 -40
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/params.rs +120 -40
- pysof-0.2.3/crates/sof/src/reference_collector.rs +185 -0
- pysof-0.2.3/crates/sof/src/remote_fetch.rs +369 -0
- pysof-0.2.3/crates/sof/src/remote_resolver.rs +856 -0
- pysof-0.2.3/crates/sof/src/server.rs +252 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/bind.rs +71 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/engine.rs +311 -19
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/library.rs +68 -10
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/mod.rs +22 -1
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/params.rs +33 -45
- pysof-0.2.3/crates/sof/src/sqlquery/scan.rs +524 -0
- pysof-0.2.3/crates/sof/tests/common/mod.rs +11 -0
- pysof-0.2.3/crates/sof/tests/resolve_remote_tests.rs +417 -0
- pysof-0.2.3/crates/sof/tests/resolve_tests.rs +304 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/run_foreach_tests.rs +10 -4
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/server_tests.rs +415 -191
- pysof-0.2.3/crates/sof/tests/sql-on-fhir/README.md +66 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/constant_types.json +12 -9
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fhirpath.json +5 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_boundary.json +3 -2
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_extension.json +1 -2
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_reference_keys.json +2 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/repeat.json +4 -1
- pysof-0.2.3/crates/sof/tests/sql-on-fhir/tests/row_index.json +700 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/where.json +4 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/sql_on_fhir_tests.rs +38 -40
- pysof-0.2.3/crates/sof/tests/test_arrow_ipc_export.rs +249 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_base64_binary_constant.rs +5 -3
- pysof-0.2.3/crates/sof/tests/test_cli_version.rs +17 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_format_parameter_body.rs +14 -23
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_header_parameter_body.rs +19 -28
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_limit_parameter_body.rs +12 -12
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_parameter_validation.rs +3 -3
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_patient_reference_formats.rs +37 -8
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_query_parameter_combinations.rs +74 -32
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_run_operation_parameters.rs +2 -2
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_runner_integration.rs +100 -20
- {pysof-0.2.0 → pysof-0.2.3}/pyproject.toml +1 -1
- {pysof-0.2.0 → pysof-0.2.3}/src/lib.rs +407 -247
- {pysof-0.2.0 → pysof-0.2.3}/src/pysof/__init__.py +218 -18
- pysof-0.2.3/src/pysof/_pysof.pyi +120 -0
- pysof-0.2.0/crates/fhir/src/compartment_expressions/r6.rs +0 -743
- pysof-0.2.0/crates/fhir/src/search/loader.rs +0 -729
- pysof-0.2.0/crates/fhirpath/src/lib.rs +0 -385
- pysof-0.2.0/crates/fhirpath/src/terminology_client.rs +0 -545
- pysof-0.2.0/crates/fhirpath/tests/data/r5/known-test-failures.json +0 -66
- pysof-0.2.0/crates/fhirpath/tests/primitive_extension_tests.rs +0 -199
- pysof-0.2.0/crates/fhirpath/tests/r5_tests.rs +0 -396
- pysof-0.2.0/crates/pysof/multithreading_example.py +0 -100
- pysof-0.2.0/crates/pysof/src/pysof/_pysof.pyi +0 -49
- pysof-0.2.0/crates/pysof/test_multithreading.py +0 -125
- pysof-0.2.0/crates/sof/tests/common/mod.rs +0 -749
- pysof-0.2.0/src/pysof/_pysof.cpython-311-darwin.so +0 -4
- pysof-0.2.0/src/pysof/_pysof.pyi +0 -49
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/README.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/build.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/compartment_expressions/mod.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/compartment_expressions/r4.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/compartment_expressions/r4b.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/compartment_expressions/r5.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/parameters.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/r6.rs.template +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/src/search/types.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/tests/integer_string_integration.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/tests/simple_null_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir/tests/test_contained_stack.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhir-macro/README.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/PRECISION_LIMITATION.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/benches/cli_benches.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/benches/evaluator_benches.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/benches/parser_benches.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/benches/server_benches.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/examples/test_ucum.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/grammar/fhirpath.g4 +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/server-api.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/aggregate_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/aggregate_math_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/bin/fhirpath-cli.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/bin/fhirpath-server.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/boolean_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/collection_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/collection_navigation.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/contains_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/conversion_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/date_operation.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/datetime_impl.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/debug_trace.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/distinct_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/error.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/extension_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/fhir_type_hierarchy.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/format_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/interval_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/json_utils.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/long_conversion.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/models.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/not_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/parse_debug.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/reference_key_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/repeat_all_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/repeat_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/resolve_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/resource_type.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/set_operations.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/subset_functions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/trace_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/type_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/type_inference.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/src/ucum.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/all_function_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/boundary_debug_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/boundary_function_integration_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/comment_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/common/context.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/common/parser.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/context_trace_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/observation-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/observation-example.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/patient-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/patient-example.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/questionnaire-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/questionnaire-example.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r4/tests-fhir-r4.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/appointment-examplereq.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/ccda.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/codesystem-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/conceptmap-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/diagnosticreport-eric.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/explanationofbenefit-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/observation-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/observation-example.xml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/parameters-example-types.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/patient-container-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/patient-example-name.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/patient-example-period.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/patient-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/patient-name-extensions.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/questionnaire-example.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/r5/input/valueset-example-expansion.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/data/testSchema.xsd +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/date_operation_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/datetime_boundary_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/debug_datetime_boundary.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/debug_string_boundary_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/define_variable_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/enhanced_variable_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/evaluator_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/extension_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/fhir_boundary_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/http_compression.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/is_as_method_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/join_function_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/oftype_datetime_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/parse_debug_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/parser_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/polymorphic_r4_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/polymorphic_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/precision_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/reference_key_debug.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/test_boundary_zero.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/trace_api_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/trace_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/tree_navigation_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/truncate_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/type_operation_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/type_preservation_integration_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/type_reflection_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/unicode_scalar_value_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/uri_type_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath/tests/uuid_type_preservation_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath-support/Cargo.toml +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath-support/README.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/fhirpath-support/src/type_info.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/Cargo.lock +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/Makefile +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/PYPI_CHECKLIST.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/WHEEL_BUILDING.md +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/__init__.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_content_types.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_fhir_versions.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_import.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/python-tests/test_source_errors.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/scripts/build-wheels.py +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/src/pysof/py.typed +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/integration/error_handling.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/integration/fhir_versions.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/integration/mod.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/lib_tests.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/multithreading_integration.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/tests/threading_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/pysof/uv.lock +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/benches/parallel_processing_bench.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/compartment.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/constants.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/data_source.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/fhir_format.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/mod.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/parquet_schema.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/parquet_zip.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/sqlquery/output.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/src/traits.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_boolean_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_column_ordering.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_datetime_instant_constants.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_datetime_type_info.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_enum_conversion.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_evaluation_result_conversion.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_extension_function.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_extension_macro_fix.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_extension_simple.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_fhir_resource_structure.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_foreach_combinations.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_inequality.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_instant_constant.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_instant_type_info.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_raw_resource.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/debug_reference_key_types.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/extension_debug_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/foreach_test.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/multiselect_test.rs +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/basic.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/collection.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/combinations.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/constant.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fhirpath_numbers.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_empty.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_first.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_join.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/fn_oftype.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/foreach.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/logic.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/union.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/validate.json +0 -0
- {pysof-0.2.0/crates/sof/tests/sql-on-fhir-v2 → pysof-0.2.3/crates/sof/tests/sql-on-fhir}/tests/view_resource.json +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_boolean_constant_debug.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_boolean_constant_debug2.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_boolean_constant_debug3.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_boolean_validation.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_chunked_processing.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_cli_file_source.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_csv_quote_handling.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_extension_fix_verification.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_extension_value_access.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_ndjson_input.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_parallel_working.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_parquet_export.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_parquet_large_dataset.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_parquet_server_options.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_shortened_format_names.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_where_clause_validation.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/crates/sof/tests/test_x_ndjson_support.rs +0 -0
- {pysof-0.2.0 → pysof-0.2.3}/src/pysof/py.typed +0 -0
|
@@ -17,13 +17,17 @@ crate-type = ["cdylib"]
|
|
|
17
17
|
|
|
18
18
|
[dependencies]
|
|
19
19
|
pyo3 = { version = ">=0.29", features = ["extension-module", "generate-import-lib"] }
|
|
20
|
-
helios-sof = { path = "../sof", version = "0.2.
|
|
21
|
-
helios-fhir = { path = "../fhir", version = "0.2.
|
|
20
|
+
helios-sof = { path = "../sof", version = "0.2.3", default-features = false }
|
|
21
|
+
helios-fhir = { path = "../fhir", version = "0.2.3", default-features = false }
|
|
22
22
|
serde = { workspace = true }
|
|
23
23
|
serde_json = { workspace = true }
|
|
24
24
|
chrono = { version = "0.4", features = ["serde"] }
|
|
25
25
|
pythonize = "0.29"
|
|
26
26
|
csv = "1.3"
|
|
27
|
+
# Drives helios-sof's async remote-resolve functions from the synchronous
|
|
28
|
+
# PyO3 entry points via a current-thread runtime.
|
|
29
|
+
tokio = { version = "1", default-features = false, features = ["rt", "net", "time"] }
|
|
30
|
+
mimalloc = "0.1.52"
|
|
27
31
|
|
|
28
32
|
[features]
|
|
29
33
|
default = ["R4"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pysof
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.10
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -34,7 +34,7 @@ Project-URL: Source, https://github.com/HeliosSoftware/hfs/tree/main/crates/pyso
|
|
|
34
34
|
[](https://opensource.org/licenses/MIT)
|
|
35
35
|
[](https://pepy.tech/project/pysof)
|
|
36
36
|
|
|
37
|
-
**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](
|
|
37
|
+
**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](http://hl7.org/fhir/uv/sql-on-fhir/).
|
|
38
38
|
|
|
39
39
|
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.
|
|
40
40
|
|
|
@@ -46,7 +46,8 @@ Built in Rust for speed, exposed to Python with a simple, Pythonic API. Part of
|
|
|
46
46
|
- 📦 **Streaming Support**: Memory-efficient chunked processing for large NDJSON files
|
|
47
47
|
- 🌐 **Multi-Version FHIR**: Supports R4, R4B, R5, and R6 (based on build features)
|
|
48
48
|
- 🎯 **Type-Safe**: Leverages Rust's type safety with a Pythonic interface
|
|
49
|
-
- ⚡ **GIL-
|
|
49
|
+
- ⚡ **GIL-Friendly**: the GIL is released during parsing and processing, so other Python threads stay responsive
|
|
50
|
+
- 📨 **Flexible Input**: ViewDefinitions and Bundles as Python dicts or pre-serialized JSON (`str`/`bytes`)
|
|
50
51
|
|
|
51
52
|
## 🎯 Why pysof?
|
|
52
53
|
|
|
@@ -176,6 +177,44 @@ result = pysof.run_view_definition_with_options(
|
|
|
176
177
|
)
|
|
177
178
|
```
|
|
178
179
|
|
|
180
|
+
### Remote `resolve()` (Trusted Servers)
|
|
181
|
+
|
|
182
|
+
`resolve()` dereferences references against the input bundle by default. You can
|
|
183
|
+
optionally let it fetch references that point at **explicitly trusted** FHIR
|
|
184
|
+
servers and fold them into the resolution pool before rows are generated. This is
|
|
185
|
+
**off by default** and gated by a strict allowlist (matching scheme + host + port
|
|
186
|
+
+ path-prefix; private/loopback addresses are blocked unless opted in).
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
import pysof
|
|
190
|
+
|
|
191
|
+
config = pysof.RemoteResolveConfig(
|
|
192
|
+
["https://fhir.example.org/r4"], # trusted base URLs (allowlist)
|
|
193
|
+
max_fetches=256, # per-run/-stream fetch cap
|
|
194
|
+
max_depth=1, # chained-reference rounds
|
|
195
|
+
allow_private_addresses=False, # allow internal LBs (e.g. Traefik) by hostname
|
|
196
|
+
bearer_tokens={"fhir.example.org": "TOKEN"}, # optional per-host auth
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Or build it from SOF_RESOLVE_* environment variables:
|
|
200
|
+
# config = pysof.RemoteResolveConfig.from_env()
|
|
201
|
+
|
|
202
|
+
result = pysof.run_view_definition_remote(
|
|
203
|
+
view_definition, bundle, "json", config, fhir_version="R4"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Streaming NDJSON with remote resolve() (one shared cache across chunks):
|
|
207
|
+
stats = pysof.process_ndjson_to_file_remote(
|
|
208
|
+
view_definition, "input.ndjson", "output.json", "ndjson", config, chunk_size=1000
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
When `config.is_active()` is `False` (disabled or empty allowlist) these behave
|
|
213
|
+
exactly like their non-remote counterparts. Enabling remote resolution makes
|
|
214
|
+
output depend on remote server state at run time. See the
|
|
215
|
+
[SQL-on-FHIR guide](../../book/src/ch06-sql-on-fhir.md) for the full security
|
|
216
|
+
model and configuration reference.
|
|
217
|
+
|
|
179
218
|
### Utility Functions
|
|
180
219
|
|
|
181
220
|
```python
|
|
@@ -276,18 +315,46 @@ pysof automatically processes FHIR resources in parallel using rayon:
|
|
|
276
315
|
- **5-7x speedup** on typical batch workloads with multi-core CPUs
|
|
277
316
|
- **Streaming benefits**: `ChunkedProcessor` and `process_ndjson_to_file` also use parallel processing
|
|
278
317
|
- **Zero configuration** - parallelization is always enabled
|
|
279
|
-
- **Python GIL released** during processing
|
|
318
|
+
- **Python GIL released** during parsing and processing, so other Python threads stay responsive
|
|
319
|
+
|
|
320
|
+
Because each call already fans out across all cores via rayon's shared thread
|
|
321
|
+
pool, running pysof from multiple Python threads does **not** multiply
|
|
322
|
+
throughput — one call can saturate the machine. Use Python threads for
|
|
323
|
+
concurrency (overlapping I/O, serving requests), not to speed up transforms.
|
|
324
|
+
|
|
325
|
+
### Pass Pre-Serialized JSON for Maximum Speed
|
|
326
|
+
|
|
327
|
+
If your data is already JSON text (from a file, an HTTP body, a database), pass
|
|
328
|
+
the `str` or `bytes` directly instead of `json.loads`-ing it first:
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
view_json: str = load_view_definition_text()
|
|
332
|
+
bundle_json: bytes = fetch_bundle_bytes()
|
|
333
|
+
|
|
334
|
+
result = pysof.run_view_definition(view_json, bundle_json, "csv")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
This skips the Python-dict conversion entirely and parses the JSON with the
|
|
338
|
+
GIL released — measured 20-27% faster per call on large bundles, with GIL hold
|
|
339
|
+
time dropping from tens of milliseconds to microseconds.
|
|
280
340
|
|
|
281
341
|
### Performance Benchmarks
|
|
282
342
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
|
287
|
-
|
|
288
|
-
| **
|
|
343
|
+
Measured with `scripts/bench_threading.py` (a 7-column Patient ViewDefinition;
|
|
344
|
+
Windows 11, 12 logical CPUs, Python 3.11, release build with mimalloc):
|
|
345
|
+
|
|
346
|
+
| Path | Dataset | Throughput |
|
|
347
|
+
|------|---------|-----------|
|
|
348
|
+
| **Batch** (dict input) | 2,000-patient bundle | ~28k patients/sec |
|
|
349
|
+
| **Batch** (JSON `str` input) | 2,000-patient bundle | ~31k patients/sec |
|
|
350
|
+
| **Streaming** (`process_ndjson_to_file`) | 5,000-patient NDJSON | ~31k patients/sec |
|
|
351
|
+
|
|
352
|
+
Numbers vary with hardware and ViewDefinition complexity — re-run
|
|
353
|
+
`scripts/bench_threading.py` on your target machine.
|
|
289
354
|
|
|
290
|
-
Streaming mode (`ChunkedProcessor`, `process_ndjson_to_file`)
|
|
355
|
+
Streaming mode (`ChunkedProcessor`, `process_ndjson_to_file`) keeps memory
|
|
356
|
+
bounded by `chunk_size` regardless of input size, so it is recommended for
|
|
357
|
+
large NDJSON files; batch mode holds the whole bundle and result in memory.
|
|
291
358
|
|
|
292
359
|
### Controlling Thread Count (RAYON_NUM_THREADS)
|
|
293
360
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://pepy.tech/project/pysof)
|
|
7
7
|
|
|
8
|
-
**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](
|
|
8
|
+
**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](http://hl7.org/fhir/uv/sql-on-fhir/).
|
|
9
9
|
|
|
10
10
|
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.
|
|
11
11
|
|
|
@@ -17,7 +17,8 @@ Built in Rust for speed, exposed to Python with a simple, Pythonic API. Part of
|
|
|
17
17
|
- 📦 **Streaming Support**: Memory-efficient chunked processing for large NDJSON files
|
|
18
18
|
- 🌐 **Multi-Version FHIR**: Supports R4, R4B, R5, and R6 (based on build features)
|
|
19
19
|
- 🎯 **Type-Safe**: Leverages Rust's type safety with a Pythonic interface
|
|
20
|
-
- ⚡ **GIL-
|
|
20
|
+
- ⚡ **GIL-Friendly**: the GIL is released during parsing and processing, so other Python threads stay responsive
|
|
21
|
+
- 📨 **Flexible Input**: ViewDefinitions and Bundles as Python dicts or pre-serialized JSON (`str`/`bytes`)
|
|
21
22
|
|
|
22
23
|
## 🎯 Why pysof?
|
|
23
24
|
|
|
@@ -147,6 +148,44 @@ result = pysof.run_view_definition_with_options(
|
|
|
147
148
|
)
|
|
148
149
|
```
|
|
149
150
|
|
|
151
|
+
### Remote `resolve()` (Trusted Servers)
|
|
152
|
+
|
|
153
|
+
`resolve()` dereferences references against the input bundle by default. You can
|
|
154
|
+
optionally let it fetch references that point at **explicitly trusted** FHIR
|
|
155
|
+
servers and fold them into the resolution pool before rows are generated. This is
|
|
156
|
+
**off by default** and gated by a strict allowlist (matching scheme + host + port
|
|
157
|
+
+ path-prefix; private/loopback addresses are blocked unless opted in).
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
import pysof
|
|
161
|
+
|
|
162
|
+
config = pysof.RemoteResolveConfig(
|
|
163
|
+
["https://fhir.example.org/r4"], # trusted base URLs (allowlist)
|
|
164
|
+
max_fetches=256, # per-run/-stream fetch cap
|
|
165
|
+
max_depth=1, # chained-reference rounds
|
|
166
|
+
allow_private_addresses=False, # allow internal LBs (e.g. Traefik) by hostname
|
|
167
|
+
bearer_tokens={"fhir.example.org": "TOKEN"}, # optional per-host auth
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Or build it from SOF_RESOLVE_* environment variables:
|
|
171
|
+
# config = pysof.RemoteResolveConfig.from_env()
|
|
172
|
+
|
|
173
|
+
result = pysof.run_view_definition_remote(
|
|
174
|
+
view_definition, bundle, "json", config, fhir_version="R4"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Streaming NDJSON with remote resolve() (one shared cache across chunks):
|
|
178
|
+
stats = pysof.process_ndjson_to_file_remote(
|
|
179
|
+
view_definition, "input.ndjson", "output.json", "ndjson", config, chunk_size=1000
|
|
180
|
+
)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
When `config.is_active()` is `False` (disabled or empty allowlist) these behave
|
|
184
|
+
exactly like their non-remote counterparts. Enabling remote resolution makes
|
|
185
|
+
output depend on remote server state at run time. See the
|
|
186
|
+
[SQL-on-FHIR guide](../../book/src/ch06-sql-on-fhir.md) for the full security
|
|
187
|
+
model and configuration reference.
|
|
188
|
+
|
|
150
189
|
### Utility Functions
|
|
151
190
|
|
|
152
191
|
```python
|
|
@@ -247,18 +286,46 @@ pysof automatically processes FHIR resources in parallel using rayon:
|
|
|
247
286
|
- **5-7x speedup** on typical batch workloads with multi-core CPUs
|
|
248
287
|
- **Streaming benefits**: `ChunkedProcessor` and `process_ndjson_to_file` also use parallel processing
|
|
249
288
|
- **Zero configuration** - parallelization is always enabled
|
|
250
|
-
- **Python GIL released** during processing
|
|
289
|
+
- **Python GIL released** during parsing and processing, so other Python threads stay responsive
|
|
290
|
+
|
|
291
|
+
Because each call already fans out across all cores via rayon's shared thread
|
|
292
|
+
pool, running pysof from multiple Python threads does **not** multiply
|
|
293
|
+
throughput — one call can saturate the machine. Use Python threads for
|
|
294
|
+
concurrency (overlapping I/O, serving requests), not to speed up transforms.
|
|
295
|
+
|
|
296
|
+
### Pass Pre-Serialized JSON for Maximum Speed
|
|
297
|
+
|
|
298
|
+
If your data is already JSON text (from a file, an HTTP body, a database), pass
|
|
299
|
+
the `str` or `bytes` directly instead of `json.loads`-ing it first:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
view_json: str = load_view_definition_text()
|
|
303
|
+
bundle_json: bytes = fetch_bundle_bytes()
|
|
304
|
+
|
|
305
|
+
result = pysof.run_view_definition(view_json, bundle_json, "csv")
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
This skips the Python-dict conversion entirely and parses the JSON with the
|
|
309
|
+
GIL released — measured 20-27% faster per call on large bundles, with GIL hold
|
|
310
|
+
time dropping from tens of milliseconds to microseconds.
|
|
251
311
|
|
|
252
312
|
### Performance Benchmarks
|
|
253
313
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
|
258
|
-
|
|
259
|
-
| **
|
|
314
|
+
Measured with `scripts/bench_threading.py` (a 7-column Patient ViewDefinition;
|
|
315
|
+
Windows 11, 12 logical CPUs, Python 3.11, release build with mimalloc):
|
|
316
|
+
|
|
317
|
+
| Path | Dataset | Throughput |
|
|
318
|
+
|------|---------|-----------|
|
|
319
|
+
| **Batch** (dict input) | 2,000-patient bundle | ~28k patients/sec |
|
|
320
|
+
| **Batch** (JSON `str` input) | 2,000-patient bundle | ~31k patients/sec |
|
|
321
|
+
| **Streaming** (`process_ndjson_to_file`) | 5,000-patient NDJSON | ~31k patients/sec |
|
|
322
|
+
|
|
323
|
+
Numbers vary with hardware and ViewDefinition complexity — re-run
|
|
324
|
+
`scripts/bench_threading.py` on your target machine.
|
|
260
325
|
|
|
261
|
-
Streaming mode (`ChunkedProcessor`, `process_ndjson_to_file`)
|
|
326
|
+
Streaming mode (`ChunkedProcessor`, `process_ndjson_to_file`) keeps memory
|
|
327
|
+
bounded by `chunk_size` regardless of input size, so it is recommended for
|
|
328
|
+
large NDJSON files; batch mode holds the whole bundle and result in memory.
|
|
262
329
|
|
|
263
330
|
### Controlling Thread Count (RAYON_NUM_THREADS)
|
|
264
331
|
|
|
@@ -24,9 +24,9 @@ xml = ["helios-serde-support/xml"]
|
|
|
24
24
|
serde = { workspace = true }
|
|
25
25
|
serde_json = { workspace = true, features = ["raw_value"] }
|
|
26
26
|
clap = { version = "4.0", features = ["derive"] }
|
|
27
|
-
helios-fhir-macro = { path = "../fhir-macro", version = "0.2.
|
|
28
|
-
helios-serde-support = { path = "../serde-support", version = "0.2.
|
|
29
|
-
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.2.
|
|
27
|
+
helios-fhir-macro = { path = "../fhir-macro", version = "0.2.3" }
|
|
28
|
+
helios-serde-support = { path = "../serde-support", version = "0.2.3" }
|
|
29
|
+
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.2.3" }
|
|
30
30
|
time = "0.3"
|
|
31
31
|
chrono = { workspace = true }
|
|
32
32
|
# Re-add serde-with-arbitrary-precision, keep macros
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//! CompartmentDefinition loader.
|
|
2
|
+
//!
|
|
3
|
+
//! Reads the FHIR spec `CompartmentDefinition` resources from the data
|
|
4
|
+
//! directory, as stored-shape JSON ready to seed into storage. This is the
|
|
5
|
+
//! compartment analogue of
|
|
6
|
+
//! [`SearchParameterLoader::load_spec_resources`](crate::search::loader::SearchParameterLoader::load_spec_resources):
|
|
7
|
+
//! the server seeds these into primary storage at startup so
|
|
8
|
+
//! `GET /CompartmentDefinition` and the web UI read the same definitions the
|
|
9
|
+
//! codegen'd membership table (`crate::get_compartment_params`) is built from.
|
|
10
|
+
|
|
11
|
+
use std::path::Path;
|
|
12
|
+
|
|
13
|
+
use serde_json::Value;
|
|
14
|
+
|
|
15
|
+
use crate::FhirVersion;
|
|
16
|
+
use crate::search::errors::LoaderError;
|
|
17
|
+
|
|
18
|
+
/// Loader for CompartmentDefinition spec resources.
|
|
19
|
+
pub struct CompartmentDefinitionLoader {
|
|
20
|
+
fhir_version: FhirVersion,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl CompartmentDefinitionLoader {
|
|
24
|
+
/// Creates a new loader for the specified FHIR version.
|
|
25
|
+
pub fn new(fhir_version: FhirVersion) -> Self {
|
|
26
|
+
Self { fhir_version }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Returns the FHIR version.
|
|
30
|
+
pub fn version(&self) -> FhirVersion {
|
|
31
|
+
self.fhir_version
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// Returns the spec bundle filename for the configured FHIR version.
|
|
35
|
+
///
|
|
36
|
+
/// Mirrors the `compartment-definitions-{version}.json` bundles shipped
|
|
37
|
+
/// under the data directory, alongside `search-parameters-{version}.json`.
|
|
38
|
+
#[allow(unreachable_patterns)]
|
|
39
|
+
pub fn spec_filename(&self) -> &'static str {
|
|
40
|
+
match self.fhir_version {
|
|
41
|
+
#[cfg(feature = "R4")]
|
|
42
|
+
FhirVersion::R4 => "compartment-definitions-r4.json",
|
|
43
|
+
#[cfg(feature = "R4B")]
|
|
44
|
+
FhirVersion::R4B => "compartment-definitions-r4b.json",
|
|
45
|
+
#[cfg(feature = "R5")]
|
|
46
|
+
FhirVersion::R5 => "compartment-definitions-r5.json",
|
|
47
|
+
#[cfg(feature = "R6")]
|
|
48
|
+
FhirVersion::R6 => "compartment-definitions-r6.json",
|
|
49
|
+
_ => "compartment-definitions-r4.json",
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// Returns the raw CompartmentDefinition resources from the spec bundle,
|
|
54
|
+
/// as stored-shape JSON.
|
|
55
|
+
///
|
|
56
|
+
/// The seeding companion the server hands to storage: each entry carries
|
|
57
|
+
/// its bundle id (`patient`, `encounter`, …), so re-seeding is idempotent
|
|
58
|
+
/// via `create`'s `AlreadyExists`.
|
|
59
|
+
pub fn load_spec_resources(&self, data_dir: &Path) -> Result<Vec<Value>, LoaderError> {
|
|
60
|
+
let path = data_dir.join(self.spec_filename());
|
|
61
|
+
let content =
|
|
62
|
+
std::fs::read_to_string(&path).map_err(|e| LoaderError::ConfigLoadFailed {
|
|
63
|
+
path: path.display().to_string(),
|
|
64
|
+
message: e.to_string(),
|
|
65
|
+
})?;
|
|
66
|
+
let json: Value =
|
|
67
|
+
serde_json::from_str(&content).map_err(|e| LoaderError::ConfigLoadFailed {
|
|
68
|
+
path: path.display().to_string(),
|
|
69
|
+
message: format!("Invalid JSON: {}", e),
|
|
70
|
+
})?;
|
|
71
|
+
|
|
72
|
+
let mut resources = Vec::new();
|
|
73
|
+
if let Some(entries) = json.get("entry").and_then(|e| e.as_array()) {
|
|
74
|
+
for entry in entries {
|
|
75
|
+
if let Some(resource) = entry.get("resource")
|
|
76
|
+
&& resource.get("resourceType").and_then(|t| t.as_str())
|
|
77
|
+
== Some("CompartmentDefinition")
|
|
78
|
+
{
|
|
79
|
+
resources.push(resource.clone());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
Ok(resources)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[cfg(test)]
|
|
88
|
+
mod tests {
|
|
89
|
+
use super::*;
|
|
90
|
+
use std::path::PathBuf;
|
|
91
|
+
|
|
92
|
+
/// Repo-root `data/` directory, from this crate's manifest dir.
|
|
93
|
+
fn workspace_data_dir() -> PathBuf {
|
|
94
|
+
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../data")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#[cfg(feature = "R4")]
|
|
98
|
+
#[test]
|
|
99
|
+
fn loads_r4_compartment_definitions() {
|
|
100
|
+
let loader = CompartmentDefinitionLoader::new(FhirVersion::R4);
|
|
101
|
+
let resources = loader.load_spec_resources(&workspace_data_dir()).unwrap();
|
|
102
|
+
// Device, Encounter, Patient, Practitioner, RelatedPerson (questionnaire
|
|
103
|
+
// has no code and is excluded from the bundle).
|
|
104
|
+
assert_eq!(resources.len(), 5);
|
|
105
|
+
assert!(
|
|
106
|
+
resources
|
|
107
|
+
.iter()
|
|
108
|
+
.all(|r| r["resourceType"] == "CompartmentDefinition")
|
|
109
|
+
);
|
|
110
|
+
assert!(resources.iter().any(|r| r["code"] == "Patient"));
|
|
111
|
+
assert!(
|
|
112
|
+
resources
|
|
113
|
+
.iter()
|
|
114
|
+
.all(|r| r.get("id").and_then(|i| i.as_str()).is_some())
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#[test]
|
|
119
|
+
fn missing_dir_errors() {
|
|
120
|
+
let loader = CompartmentDefinitionLoader::new(FhirVersion::default_enabled());
|
|
121
|
+
assert!(
|
|
122
|
+
loader
|
|
123
|
+
.load_spec_resources(Path::new("/nonexistent/data/dir"))
|
|
124
|
+
.is_err()
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// The shipped `data/compartment-definitions-*.json` bundles must say
|
|
129
|
+
/// exactly what the codegen'd `get_compartment_params` table says — every
|
|
130
|
+
/// `(compartment, resource)` slot, every version. This is the guard against
|
|
131
|
+
/// the seeded copies drifting from `crates/fhir-gen/resources/`.
|
|
132
|
+
fn assert_parity(version: FhirVersion) {
|
|
133
|
+
let loader = CompartmentDefinitionLoader::new(version);
|
|
134
|
+
let defs = loader.load_spec_resources(&workspace_data_dir()).unwrap();
|
|
135
|
+
assert!(!defs.is_empty(), "{version:?} bundle is empty");
|
|
136
|
+
for def in &defs {
|
|
137
|
+
let code = def["code"].as_str().unwrap();
|
|
138
|
+
for resource in def["resource"].as_array().unwrap() {
|
|
139
|
+
let rtype = resource["code"].as_str().unwrap();
|
|
140
|
+
let params: Vec<&str> = resource
|
|
141
|
+
.get("param")
|
|
142
|
+
.and_then(|p| p.as_array())
|
|
143
|
+
.map(|a| a.iter().filter_map(|v| v.as_str()).collect())
|
|
144
|
+
.unwrap_or_default();
|
|
145
|
+
let runtime = crate::get_compartment_params(version, code, rtype);
|
|
146
|
+
assert_eq!(
|
|
147
|
+
runtime,
|
|
148
|
+
params.as_slice(),
|
|
149
|
+
"{version:?} compartment {code} / {rtype}"
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
#[cfg(feature = "R4")]
|
|
156
|
+
#[test]
|
|
157
|
+
fn r4_bundle_matches_codegen_table() {
|
|
158
|
+
assert_parity(FhirVersion::R4);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#[cfg(feature = "R4B")]
|
|
162
|
+
#[test]
|
|
163
|
+
fn r4b_bundle_matches_codegen_table() {
|
|
164
|
+
assert_parity(FhirVersion::R4B);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#[cfg(feature = "R5")]
|
|
168
|
+
#[test]
|
|
169
|
+
fn r5_bundle_matches_codegen_table() {
|
|
170
|
+
assert_parity(FhirVersion::R5);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[cfg(feature = "R6")]
|
|
174
|
+
#[test]
|
|
175
|
+
fn r6_bundle_matches_codegen_table() {
|
|
176
|
+
assert_parity(FhirVersion::R6);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//! FHIR CompartmentDefinition spec data.
|
|
2
|
+
//!
|
|
3
|
+
//! Companion to [`crate::search`]: where SearchParameters drive search
|
|
4
|
+
//! resolution, CompartmentDefinitions describe the compartment membership the
|
|
5
|
+
//! REST compartment-search handler enforces (via the codegen'd
|
|
6
|
+
//! [`crate::get_compartment_params`] table). This module only *loads* the spec
|
|
7
|
+
//! resources so the server can seed them into storage — the source of truth the
|
|
8
|
+
//! `GET /CompartmentDefinition` route and the web UI read from. Membership
|
|
9
|
+
//! itself stays codegen-driven.
|
|
10
|
+
//!
|
|
11
|
+
//! - [`loader`] – [`CompartmentDefinitionLoader`] for reading the spec bundle.
|
|
12
|
+
|
|
13
|
+
pub mod loader;
|
|
14
|
+
|
|
15
|
+
pub use loader::CompartmentDefinitionLoader;
|