httk-core 2.0.0__tar.gz → 2.1.1__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.
- {httk_core-2.0.0/src/httk_core.egg-info → httk_core-2.1.1}/PKG-INFO +21 -13
- httk_core-2.1.1/README.md +36 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/pyproject.toml +48 -14
- httk_core-2.1.1/src/httk/core/__init__.py +245 -0
- httk_core-2.1.1/src/httk/core/_discover.py +48 -0
- httk_core-2.1.1/src/httk/core/_iris.py +5 -0
- httk_core-2.1.1/src/httk/core/_json.py +70 -0
- httk_core-2.1.1/src/httk/core/_manifest.py +203 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/_plugins.py +6 -2
- httk_core-2.1.1/src/httk/core/_registry_tool.py +312 -0
- httk_core-2.1.1/src/httk/core/_sentinel.py +39 -0
- httk_core-2.1.1/src/httk/core/atomic_write.py +42 -0
- httk_core-2.1.1/src/httk/core/building.py +452 -0
- httk_core-2.1.1/src/httk/core/citations.py +181 -0
- httk_core-2.1.1/src/httk/core/cli.py +147 -0
- httk_core-2.1.1/src/httk/core/converting.py +62 -0
- httk_core-2.1.1/src/httk/core/crypto.py +285 -0
- httk_core-2.1.1/src/httk/core/data_records.py +177 -0
- httk_core-2.1.1/src/httk/core/dataset_loader.py +607 -0
- httk_core-2.1.1/src/httk/core/datasets.py +222 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/__init__.py +23 -18
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_api.py +8 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_backend.py +4 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_bytes.py +19 -6
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_bytes_view.py +16 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_common.py +20 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_file.py +18 -5
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_file_view.py +74 -2
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_filename.py +20 -7
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_filename_view.py +17 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_request.py +23 -10
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_request_view.py +9 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_url.py +31 -13
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_url_view.py +11 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_view.py +6 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/compression.py +38 -4
- httk_core-2.1.1/src/httk/core/datastream/datastream_url.py +48 -0
- httk_core-2.1.1/src/httk/core/datastream/network_policy.py +32 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_api.py +8 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_backend.py +4 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_common.py +20 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_file.py +17 -5
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_file_view.py +78 -3
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_filename.py +20 -7
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_filename_view.py +18 -3
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_request.py +23 -10
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_request_view.py +9 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_string.py +18 -6
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_string_view.py +16 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_url.py +31 -13
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_url_view.py +11 -1
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_view.py +6 -0
- httk_core-2.1.1/src/httk/core/digests.py +71 -0
- httk_core-2.1.1/src/httk/core/docs/__init__.py +114 -0
- httk_core-2.0.0/src/httk/core/loading.py → httk_core-2.1.1/src/httk/core/docs/__main__.py +7 -8
- httk_core-2.1.1/src/httk/core/docs/assets/selector.css +25 -0
- httk_core-2.1.1/src/httk/core/docs/assets/selector.js +67 -0
- httk_core-2.1.1/src/httk/core/docs/cli.py +383 -0
- httk_core-2.1.1/src/httk/core/docs/config.py +155 -0
- httk_core-2.1.1/src/httk/core/docs/ecosystem.py +255 -0
- httk_core-2.1.1/src/httk/core/docs/gitsite.py +198 -0
- httk_core-2.1.1/src/httk/core/docs/inventories.py +138 -0
- httk_core-2.1.1/src/httk/core/docs/lockfile.py +300 -0
- httk_core-2.1.1/src/httk/core/docs/manifests.py +137 -0
- httk_core-2.1.1/src/httk/core/docs/redirect.py +53 -0
- httk_core-2.1.1/src/httk/core/docs/release.py +174 -0
- httk_core-2.1.1/src/httk/core/docs/semver.py +122 -0
- httk_core-2.1.1/src/httk/core/docs/sitetree.py +386 -0
- httk_core-2.1.1/src/httk/core/docs/sphinx_ext.py +314 -0
- httk_core-2.1.1/src/httk/core/entry_ids.py +177 -0
- httk_core-2.1.1/src/httk/core/entry_provider.py +211 -0
- httk_core-2.1.1/src/httk/core/entry_types.py +326 -0
- httk_core-2.1.1/src/httk/core/exactmath.py +2139 -0
- httk_core-2.1.1/src/httk/core/fetching.py +62 -0
- httk_core-2.1.1/src/httk/core/files.py +117 -0
- httk_core-2.1.1/src/httk/core/identity.py +719 -0
- httk_core-2.1.1/src/httk/core/identity_cli.py +235 -0
- httk_core-2.1.1/src/httk/core/loading.py +256 -0
- httk_core-2.1.1/src/httk/core/memguard.py +197 -0
- httk_core-2.1.1/src/httk/core/optimade/__init__.py +67 -0
- httk_core-2.1.1/src/httk/core/optimade/entries.py +528 -0
- httk_core-2.1.1/src/httk/core/optimade/filter/README.md +29 -0
- httk_core-2.1.1/src/httk/core/optimade/filter/__init__.py +64 -0
- httk_core-2.1.1/src/httk/core/optimade/filter/_miniparser.py +1417 -0
- httk_core-2.1.1/src/httk/core/optimade/filter/optimade_filter_grammar.ebnf +171 -0
- httk_core-2.1.1/src/httk/core/optimade/filter/parser.py +387 -0
- httk_core-2.1.1/src/httk/core/optimade/resources.py +504 -0
- httk_core-2.1.1/src/httk/core/optimade/standard_names.py +260 -0
- httk_core-2.1.1/src/httk/core/plugins/__init__.py +31 -0
- httk_core-2.1.1/src/httk/core/plugins/cli.py +300 -0
- httk_core-2.1.1/src/httk/core/plugins/install.py +490 -0
- httk_core-2.1.1/src/httk/core/plugins/installed.py +131 -0
- httk_core-2.1.1/src/httk/core/plugins/manifest.py +150 -0
- httk_core-2.1.1/src/httk/core/precision.py +142 -0
- httk_core-2.1.1/src/httk/core/project/__init__.py +140 -0
- httk_core-2.1.1/src/httk/core/project/anchor.py +483 -0
- httk_core-2.1.1/src/httk/core/project/cli.py +888 -0
- httk_core-2.1.1/src/httk/core/project/export.py +389 -0
- httk_core-2.1.1/src/httk/core/project/manifests.py +439 -0
- httk_core-2.1.1/src/httk/core/project/members.py +388 -0
- httk_core-2.1.1/src/httk/core/project/sealing.py +763 -0
- httk_core-2.1.1/src/httk/core/project/templates.py +475 -0
- httk_core-2.1.1/src/httk/core/property_definitions.py +806 -0
- httk_core-2.1.1/src/httk/core/provenance.py +252 -0
- httk_core-2.1.1/src/httk/core/records.py +88 -0
- httk_core-2.1.1/src/httk/core/register/__init__.py +145 -0
- httk_core-2.0.0/src/httk/core/_discover.py → httk_core-2.1.1/src/httk/core/register/_base.py +11 -17
- httk_core-2.1.1/src/httk/core/register/cli.py +109 -0
- httk_core-2.1.1/src/httk/core/register/entries.py +358 -0
- httk_core-2.1.1/src/httk/core/register/io.py +263 -0
- httk_core-2.1.1/src/httk/core/register/members.py +76 -0
- httk_core-2.1.1/src/httk/core/register/schemas.py +127 -0
- httk_core-2.1.1/src/httk/core/report.py +490 -0
- httk_core-2.1.1/src/httk/core/saving.py +118 -0
- httk_core-2.1.1/src/httk/core/schema_check.py +236 -0
- httk_core-2.1.1/src/httk/core/services.py +151 -0
- httk_core-2.1.1/src/httk/core/storage/__init__.py +73 -0
- httk_core-2.1.1/src/httk/core/storage/identity.py +970 -0
- httk_core-2.1.1/src/httk/core/storage/markers.py +316 -0
- httk_core-2.1.1/src/httk/core/storage/rational_text.py +12 -0
- httk_core-2.1.1/src/httk/core/storage/stored_properties.py +374 -0
- httk_core-2.1.1/src/httk/core/system.py +76 -0
- httk_core-2.1.1/src/httk/core/userdirs.py +38 -0
- httk_core-2.1.1/src/httk/core/validation/__init__.py +10 -0
- httk_core-2.1.1/src/httk/core/validation/iris.py +114 -0
- httk_core-2.1.1/src/httk/core/vectors/__init__.py +159 -0
- httk_core-2.1.1/src/httk/core/vectors/_nested.py +377 -0
- httk_core-2.1.1/src/httk/core/vectors/_squarefree.py +72 -0
- httk_core-2.1.1/src/httk/core/vectors/fracvector.py +1767 -0
- httk_core-2.1.1/src/httk/core/vectors/leaf_codecs.py +219 -0
- httk_core-2.1.1/src/httk/core/vectors/mutablefracvector.py +294 -0
- httk_core-2.1.1/src/httk/core/vectors/numeric.py +145 -0
- httk_core-2.1.1/src/httk/core/vectors/scalar_like.py +10 -0
- httk_core-2.1.1/src/httk/core/vectors/surdvector.py +978 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_api.py +90 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_backend.py +24 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_frac_view.py +105 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_like.py +22 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_native_backend.py +108 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_native_view.py +84 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_numpy_backend.py +103 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_numpy_view.py +186 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_surd_view.py +98 -0
- httk_core-2.1.1/src/httk/core/vectors/vector_view.py +24 -0
- httk_core-2.1.1/src/httk/core/vectors/vectormath.py +758 -0
- httk_core-2.1.1/src/httk/core/views/__init__.py +17 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/views/backend.py +31 -7
- httk_core-2.1.1/src/httk/core/views/coercion.py +150 -0
- httk_core-2.1.1/src/httk/core/views/unviewing.py +23 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/views/unwrapping.py +3 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/views/view.py +42 -6
- httk_core-2.1.1/src/httk/registry/cli/core/__init__.py +58 -0
- httk_core-2.1.1/src/httk/registry/entries/core/__init__.py +136 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/LICENSE +21 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/LICENSE.httk +21 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/README.md +54 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/__init__.py +24 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/calculations.json +128 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/files.json +537 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/records.json +128 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/references.json +972 -0
- httk_core-2.1.1/src/httk/registry/schemas/core/runs.json +184 -0
- {httk_core-2.0.0 → httk_core-2.1.1/src/httk_core.egg-info}/PKG-INFO +21 -13
- httk_core-2.1.1/src/httk_core.egg-info/SOURCES.txt +254 -0
- httk_core-2.1.1/src/httk_core.egg-info/entry_points.txt +2 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk_core.egg-info/requires.txt +11 -3
- httk_core-2.1.1/tests/test_building.py +147 -0
- httk_core-2.1.1/tests/test_building_engine.py +258 -0
- httk_core-2.1.1/tests/test_citations.py +98 -0
- httk_core-2.1.1/tests/test_cli.py +138 -0
- httk_core-2.1.1/tests/test_coercion.py +212 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/tests/test_compression.py +8 -4
- httk_core-2.1.1/tests/test_converting.py +64 -0
- httk_core-2.1.1/tests/test_crypto.py +94 -0
- httk_core-2.1.1/tests/test_data_records.py +86 -0
- httk_core-2.1.1/tests/test_dataset_loader.py +518 -0
- httk_core-2.1.1/tests/test_datasets.py +210 -0
- httk_core-2.1.1/tests/test_datastream_url.py +48 -0
- httk_core-2.1.1/tests/test_digests.py +88 -0
- httk_core-2.1.1/tests/test_docs_cli.py +335 -0
- httk_core-2.1.1/tests/test_docs_config.py +65 -0
- httk_core-2.1.1/tests/test_docs_ecosystem.py +204 -0
- httk_core-2.1.1/tests/test_docs_gitsite.py +36 -0
- httk_core-2.1.1/tests/test_docs_integration.py +118 -0
- httk_core-2.1.1/tests/test_docs_inventories_release.py +100 -0
- httk_core-2.1.1/tests/test_docs_lockfile.py +146 -0
- httk_core-2.1.1/tests/test_docs_manifests_redirect.py +49 -0
- httk_core-2.1.1/tests/test_docs_repair.py +100 -0
- httk_core-2.1.1/tests/test_docs_semver.py +23 -0
- httk_core-2.1.1/tests/test_docs_sitetree.py +205 -0
- httk_core-2.1.1/tests/test_docs_sphinx_ext.py +330 -0
- httk_core-2.1.1/tests/test_entry_ids.py +220 -0
- httk_core-2.1.1/tests/test_entry_provider.py +125 -0
- httk_core-2.1.1/tests/test_entry_types.py +92 -0
- httk_core-2.1.1/tests/test_exactmath.py +413 -0
- httk_core-2.1.1/tests/test_exactmath_api.py +245 -0
- httk_core-2.1.1/tests/test_examples.py +110 -0
- httk_core-2.1.1/tests/test_fetching.py +132 -0
- httk_core-2.1.1/tests/test_files.py +127 -0
- httk_core-2.1.1/tests/test_fracvector.py +633 -0
- httk_core-2.1.1/tests/test_identity.py +1053 -0
- httk_core-2.1.1/tests/test_identity_cli.py +208 -0
- httk_core-2.1.1/tests/test_leaf_codecs.py +183 -0
- httk_core-2.1.1/tests/test_load_many.py +127 -0
- httk_core-2.1.1/tests/test_loading.py +198 -0
- httk_core-2.1.1/tests/test_memguard.py +115 -0
- httk_core-2.1.1/tests/test_mutablefracvector.py +144 -0
- httk_core-2.1.1/tests/test_network_policy.py +169 -0
- httk_core-2.1.1/tests/test_numeric.py +120 -0
- httk_core-2.1.1/tests/test_operator_identity.py +368 -0
- httk_core-2.1.1/tests/test_optimade_filter.py +257 -0
- httk_core-2.1.1/tests/test_optimade_resources.py +698 -0
- httk_core-2.1.1/tests/test_optimade_standard_names.py +362 -0
- httk_core-2.1.1/tests/test_optimade_timestamps.py +158 -0
- httk_core-2.1.1/tests/test_plugins_cli.py +140 -0
- httk_core-2.1.1/tests/test_plugins_install.py +416 -0
- httk_core-2.1.1/tests/test_plugins_manifest.py +179 -0
- httk_core-2.1.1/tests/test_precision.py +163 -0
- httk_core-2.1.1/tests/test_project.py +257 -0
- httk_core-2.1.1/tests/test_project_cli_seal.py +221 -0
- httk_core-2.1.1/tests/test_project_export.py +180 -0
- httk_core-2.1.1/tests/test_project_init_template.py +231 -0
- httk_core-2.1.1/tests/test_project_manifests.py +57 -0
- httk_core-2.1.1/tests/test_project_members.py +165 -0
- httk_core-2.1.1/tests/test_project_sealing.py +170 -0
- httk_core-2.1.1/tests/test_project_templates.py +372 -0
- httk_core-2.1.1/tests/test_property_definitions.py +359 -0
- httk_core-2.1.1/tests/test_provenance.py +172 -0
- httk_core-2.1.1/tests/test_records.py +76 -0
- httk_core-2.1.1/tests/test_registry_discovery.py +185 -0
- httk_core-2.1.1/tests/test_registry_tool.py +63 -0
- httk_core-2.1.1/tests/test_release_checker.py +176 -0
- httk_core-2.1.1/tests/test_release_prepare.py +191 -0
- httk_core-2.1.1/tests/test_report.py +345 -0
- httk_core-2.1.1/tests/test_saving.py +115 -0
- httk_core-2.1.1/tests/test_schema_check.py +261 -0
- httk_core-2.1.1/tests/test_services.py +131 -0
- httk_core-2.1.1/tests/test_storage_markers.py +172 -0
- httk_core-2.1.1/tests/test_stored_properties.py +145 -0
- httk_core-2.1.1/tests/test_surdvector.py +481 -0
- httk_core-2.1.1/tests/test_system_cli.py +91 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/tests/test_textstream.py +57 -45
- httk_core-2.1.1/tests/test_unview.py +107 -0
- httk_core-2.1.1/tests/test_userdirs.py +39 -0
- httk_core-2.1.1/tests/test_validation_iris.py +148 -0
- httk_core-2.1.1/tests/test_vector_family.py +730 -0
- httk_core-2.1.1/tests/test_vector_hash_equality.py +185 -0
- httk_core-2.1.1/tests/test_vectormath.py +137 -0
- httk_core-2.1.1/tests/test_view_laziness.py +137 -0
- httk_core-2.0.0/README.md +0 -34
- httk_core-2.0.0/src/httk/core/__init__.py +0 -112
- httk_core-2.0.0/src/httk/core/dataloader.py +0 -263
- httk_core-2.0.0/src/httk/core/register.py +0 -30
- httk_core-2.0.0/src/httk/core/views/__init__.py +0 -5
- httk_core-2.0.0/src/httk/handlers/core/__init__.py +0 -0
- httk_core-2.0.0/src/httk_core.egg-info/SOURCES.txt +0 -55
- httk_core-2.0.0/tests/test_dataloader.py +0 -247
- {httk_core-2.0.0 → httk_core-2.1.1}/LICENSE +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/setup.cfg +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/bytestream_like.py +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/datastream/textstream_like.py +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk/core/py.typed +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk_core.egg-info/dependency_links.txt +0 -0
- {httk_core-2.0.0 → httk_core-2.1.1}/src/httk_core.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: httk-core
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: httk₂ core functionality
|
|
5
5
|
Author-email: Rickard Armiento <rickard-gpg@armiento.net>
|
|
6
6
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -20,13 +20,19 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
+
Provides-Extra: default
|
|
24
|
+
Requires-Dist: numpy>=1.26; extra == "default"
|
|
25
|
+
Requires-Dist: cryptography>=42; extra == "default"
|
|
26
|
+
Provides-Extra: numpy
|
|
27
|
+
Requires-Dist: numpy>=1.26; extra == "numpy"
|
|
23
28
|
Provides-Extra: dev
|
|
24
29
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist: ruff; extra == "dev"
|
|
27
|
-
Requires-Dist: isort; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-xdist>=3.6; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff<0.17,>=0.16; extra == "dev"
|
|
28
32
|
Requires-Dist: pyright; extra == "dev"
|
|
29
33
|
Requires-Dist: mypy; extra == "dev"
|
|
34
|
+
Requires-Dist: numpy>=1.26; extra == "dev"
|
|
35
|
+
Requires-Dist: pydoclint<1,>=0.9; extra == "dev"
|
|
30
36
|
Provides-Extra: docs
|
|
31
37
|
Requires-Dist: sphinx; extra == "docs"
|
|
32
38
|
Requires-Dist: furo; extra == "docs"
|
|
@@ -42,22 +48,24 @@ Dynamic: license-file
|
|
|
42
48
|
|
|
43
49
|
# httk-core
|
|
44
50
|
|
|
45
|
-

|
|
46
52
|
|
|
47
|
-
> **⚠️
|
|
53
|
+
> **⚠️ EARLY BETA**
|
|
48
54
|
>
|
|
49
|
-
>
|
|
50
|
-
> and
|
|
51
|
-
>
|
|
55
|
+
> This is an early beta release of *httk₂*. The organization of the packages
|
|
56
|
+
> and their APIs should not yet be regarded as stable, and may change between
|
|
57
|
+
> releases.
|
|
52
58
|
|
|
53
|
-
`httk-core` is the central lightweight dependency shared by httk
|
|
59
|
+
`httk-core` is the central lightweight dependency shared by *httk₂* modules.
|
|
54
60
|
|
|
55
61
|
It provides:
|
|
56
62
|
|
|
57
63
|
- the `httk.core` package within the PEP 420 native `httk` namespace;
|
|
58
64
|
- shared view/backend and data-stream primitives; and
|
|
59
65
|
- plugin discovery and aggregate operations such as `httk.core.load`, to which
|
|
60
|
-
other httk modules can register
|
|
66
|
+
other httk modules can register capabilities.
|
|
67
|
+
- the extensible `httk` executable, a lazy top-level command registry, and
|
|
68
|
+
stdlib-backed Ed25519 signing.
|
|
61
69
|
|
|
62
70
|
Most users should install the [`httk2`](https://github.com/httk/httk2)
|
|
63
71
|
metapackage, which selects a useful set of httk modules:
|
|
@@ -72,5 +80,5 @@ Install only the core package with:
|
|
|
72
80
|
pip install httk-core
|
|
73
81
|
```
|
|
74
82
|
|
|
75
|
-
Other distributions, such as `httk-
|
|
76
|
-
same `httk` namespace and can then be imported as `httk.
|
|
83
|
+
Other distributions, such as `httk-atomistic`, install their own packages under the
|
|
84
|
+
same `httk` namespace and can then be imported as `httk.atomistic`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# httk-core
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
> **⚠️ EARLY BETA**
|
|
6
|
+
>
|
|
7
|
+
> This is an early beta release of *httk₂*. The organization of the packages
|
|
8
|
+
> and their APIs should not yet be regarded as stable, and may change between
|
|
9
|
+
> releases.
|
|
10
|
+
|
|
11
|
+
`httk-core` is the central lightweight dependency shared by *httk₂* modules.
|
|
12
|
+
|
|
13
|
+
It provides:
|
|
14
|
+
|
|
15
|
+
- the `httk.core` package within the PEP 420 native `httk` namespace;
|
|
16
|
+
- shared view/backend and data-stream primitives; and
|
|
17
|
+
- plugin discovery and aggregate operations such as `httk.core.load`, to which
|
|
18
|
+
other httk modules can register capabilities.
|
|
19
|
+
- the extensible `httk` executable, a lazy top-level command registry, and
|
|
20
|
+
stdlib-backed Ed25519 signing.
|
|
21
|
+
|
|
22
|
+
Most users should install the [`httk2`](https://github.com/httk/httk2)
|
|
23
|
+
metapackage, which selects a useful set of httk modules:
|
|
24
|
+
|
|
25
|
+
```console
|
|
26
|
+
pip install httk2
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Install only the core package with:
|
|
30
|
+
|
|
31
|
+
```console
|
|
32
|
+
pip install httk-core
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Other distributions, such as `httk-atomistic`, install their own packages under the
|
|
36
|
+
same `httk` namespace and can then be imported as `httk.atomistic`.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "httk-core"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.1.1"
|
|
8
8
|
description = "httk₂ core functionality"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-or-later"
|
|
@@ -35,23 +35,40 @@ Documentation = "https://docs.httk.org/httk-core/"
|
|
|
35
35
|
Repository = "https://github.com/httk/httk-core"
|
|
36
36
|
Issues = "https://github.com/httk/httk-core/issues"
|
|
37
37
|
|
|
38
|
+
[project.scripts]
|
|
39
|
+
httk = "httk.core.cli:main"
|
|
40
|
+
|
|
38
41
|
[tool.setuptools]
|
|
39
42
|
package-dir = {"" = "src"}
|
|
40
43
|
|
|
41
44
|
[tool.setuptools.packages.find]
|
|
42
45
|
where = ["src"]
|
|
46
|
+
# httk is a PEP 420 namespace shared across the httk-* repositories, so the
|
|
47
|
+
# top-level httk/ has no __init__.py; find must be told to include namespaces or
|
|
48
|
+
# it discovers nothing under it. httk.core.project is the anchor package this
|
|
49
|
+
# build adds inside httk.core.
|
|
50
|
+
namespaces = true
|
|
43
51
|
|
|
44
52
|
[tool.setuptools.package-data]
|
|
45
53
|
"httk.core" = ["py.typed"]
|
|
54
|
+
"httk.core.optimade.filter" = ["*.ebnf", "README.md"]
|
|
55
|
+
"httk.registry.schemas.core" = ["*.json", "LICENSE", "LICENSE.httk", "README.md"]
|
|
56
|
+
"httk.core.docs" = ["assets/*"]
|
|
46
57
|
|
|
47
58
|
[project.optional-dependencies]
|
|
59
|
+
default = [
|
|
60
|
+
"numpy>=1.26",
|
|
61
|
+
"cryptography>=42",
|
|
62
|
+
]
|
|
63
|
+
numpy = ["numpy>=1.26"]
|
|
48
64
|
dev = [
|
|
49
65
|
"pytest>=8.0",
|
|
50
|
-
"
|
|
51
|
-
"ruff",
|
|
52
|
-
"isort",
|
|
66
|
+
"pytest-xdist>=3.6",
|
|
67
|
+
"ruff>=0.16,<0.17",
|
|
53
68
|
"pyright",
|
|
54
|
-
"mypy"
|
|
69
|
+
"mypy",
|
|
70
|
+
"numpy>=1.26",
|
|
71
|
+
"pydoclint>=0.9,<1"
|
|
55
72
|
]
|
|
56
73
|
docs = [
|
|
57
74
|
"sphinx",
|
|
@@ -68,24 +85,41 @@ release = [
|
|
|
68
85
|
]
|
|
69
86
|
[tool.pytest.ini_options]
|
|
70
87
|
testpaths = ["tests"]
|
|
71
|
-
|
|
72
|
-
[
|
|
73
|
-
|
|
88
|
+
addopts = "-n 4 --dist loadgroup -m 'not extended'"
|
|
89
|
+
markers = [
|
|
90
|
+
"extended: full-depth test case; selected only by the extended test profile",
|
|
91
|
+
]
|
|
74
92
|
|
|
75
93
|
[tool.ruff]
|
|
76
94
|
target-version = "py312"
|
|
77
95
|
src = ["src", "examples"]
|
|
78
|
-
|
|
79
|
-
[tool.black]
|
|
80
|
-
target-version = ["py312"]
|
|
81
96
|
line-length = 120
|
|
82
|
-
|
|
97
|
+
|
|
98
|
+
[tool.ruff.lint]
|
|
99
|
+
# TRY002: raising plain Exception is accepted httk style.
|
|
100
|
+
# TRY004: the ValueError/TypeError choice is public API; do not churn it.
|
|
101
|
+
# BLE001: broad excepts deliberately isolate plugin/loader/handler failures.
|
|
102
|
+
ignore = ["TRY002", "TRY004", "BLE001"]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.format]
|
|
105
|
+
# preserve replaces black's skip-string-normalization: existing quote styles are kept.
|
|
106
|
+
quote-style = "preserve"
|
|
107
|
+
|
|
108
|
+
[tool.pydoclint]
|
|
109
|
+
style = "sphinx"
|
|
110
|
+
arg-type-hints-in-docstring = false
|
|
111
|
+
check-return-types = false
|
|
112
|
+
check-yield-types = false
|
|
113
|
+
check-class-attributes = false
|
|
114
|
+
skip-checking-raises = true
|
|
83
115
|
|
|
84
116
|
[tool.pyright]
|
|
85
117
|
pythonVersion = "3.12"
|
|
86
118
|
include = ["src", "examples"]
|
|
87
119
|
reportMissingImports = true
|
|
88
|
-
|
|
120
|
+
# The examples import httk as an installed package would; point pyright at the
|
|
121
|
+
# in-tree sources so they resolve without relying on an editable install.
|
|
122
|
+
extraPaths = ["src"]
|
|
89
123
|
|
|
90
124
|
[tool.mypy]
|
|
91
125
|
python_version = "3.12"
|
|
@@ -96,5 +130,5 @@ explicit_package_bases = true
|
|
|
96
130
|
warn_unused_ignores = true
|
|
97
131
|
|
|
98
132
|
[[tool.mypy.overrides]]
|
|
99
|
-
module = ["yaml", "markdown", "docutils", "docutils.*"]
|
|
133
|
+
module = ["yaml", "markdown", "docutils", "docutils.*", "sphinx", "sphinx.*"]
|
|
100
134
|
ignore_missing_imports = true
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Public core APIs for loading, reporting, project anchors, and registration.
|
|
2
|
+
|
|
3
|
+
Importing this package discovers installed capability modules and exposes
|
|
4
|
+
their registered readers, writers, adapters, and related public APIs.
|
|
5
|
+
|
|
6
|
+
The deliberate public surface is defined by ``__all__``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
#
|
|
10
|
+
# The high-throughput toolkit (httk)
|
|
11
|
+
# Copyright (C) 2012-2024 the httk AUTHORS
|
|
12
|
+
#
|
|
13
|
+
# This program is free software: you can redistribute it and/or modify
|
|
14
|
+
# it under the terms of the GNU Affero General Public License as
|
|
15
|
+
# published by the Free Software Foundation, either version 3 of the
|
|
16
|
+
# License, or (at your option) any later version.
|
|
17
|
+
#
|
|
18
|
+
# This program is distributed in the hope that it will be useful,
|
|
19
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
# GNU Affero General Public License for more details.
|
|
22
|
+
#
|
|
23
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
24
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
25
|
+
|
|
26
|
+
from . import exactmath # noqa: F401 - intentional module-level import for ``from httk.core import exactmath``
|
|
27
|
+
from ._discover import discover_and_register as _discover_and_register
|
|
28
|
+
from ._sentinel import MISSING, MissingType
|
|
29
|
+
from .citations import credits, register_citation
|
|
30
|
+
from .cli import CLIContext
|
|
31
|
+
from .data_records import DataRecord, DataRecordEntry
|
|
32
|
+
from .dataset_loader import DatasetLoader, DatasetLoaderRecord, DatasetMeta
|
|
33
|
+
from .datasets import Dataset, DatasetDistribution, DatasetRecord
|
|
34
|
+
from .datastream import (
|
|
35
|
+
BytestreamFileView,
|
|
36
|
+
BytestreamLike,
|
|
37
|
+
BytestreamURLView,
|
|
38
|
+
CompressionCodec,
|
|
39
|
+
DatastreamLike,
|
|
40
|
+
DatastreamURL,
|
|
41
|
+
TextstreamFileView,
|
|
42
|
+
TextstreamLike,
|
|
43
|
+
TextstreamURLView,
|
|
44
|
+
known_compressions,
|
|
45
|
+
register_compression,
|
|
46
|
+
)
|
|
47
|
+
from .entry_ids import (
|
|
48
|
+
ALTERNATIVE_ID_PATTERN,
|
|
49
|
+
ALTERNATIVE_KIND_PATTERN,
|
|
50
|
+
ENTRY_ID_PATTERN,
|
|
51
|
+
IMMUTABLE_ID_PATTERN,
|
|
52
|
+
check_entry_id,
|
|
53
|
+
check_immutable_id,
|
|
54
|
+
format_alternative_id,
|
|
55
|
+
format_entry_id,
|
|
56
|
+
format_immutable_id,
|
|
57
|
+
is_url_safe_id,
|
|
58
|
+
parse_alternative_id,
|
|
59
|
+
parse_entry_id,
|
|
60
|
+
parse_immutable_id,
|
|
61
|
+
)
|
|
62
|
+
from .entry_provider import EntryProvider, RelatedEntry
|
|
63
|
+
from .entry_types import Calculation, File, Reference
|
|
64
|
+
from .fetching import fetch
|
|
65
|
+
from .files import FileEntry, FileRecord
|
|
66
|
+
from .loading import has_reader_for, load, load_many, load_source
|
|
67
|
+
from .precision import combined_precision, decimal_precision
|
|
68
|
+
from .property_definitions import (
|
|
69
|
+
EntryTypeDefinition,
|
|
70
|
+
PropertyDefinition,
|
|
71
|
+
apply_definition_prefix,
|
|
72
|
+
known_definition_prefixes,
|
|
73
|
+
register_definition_prefix,
|
|
74
|
+
standard_entry_type,
|
|
75
|
+
)
|
|
76
|
+
from .provenance import ProductLink, Run, RunEdge, RunEntry
|
|
77
|
+
from .register.cli import register_cli_command
|
|
78
|
+
from .register.entries import (
|
|
79
|
+
known_entry_providers,
|
|
80
|
+
register_entry_family,
|
|
81
|
+
register_entry_provider,
|
|
82
|
+
register_entry_record,
|
|
83
|
+
register_optimade_entry_binding,
|
|
84
|
+
)
|
|
85
|
+
from .register.io import (
|
|
86
|
+
register_format_adapter,
|
|
87
|
+
register_format_serializer,
|
|
88
|
+
register_reader,
|
|
89
|
+
register_writer,
|
|
90
|
+
)
|
|
91
|
+
from .register.members import (
|
|
92
|
+
known_project_member_kinds,
|
|
93
|
+
project_member_handler,
|
|
94
|
+
register_project_member_kind,
|
|
95
|
+
)
|
|
96
|
+
from .register.schemas import (
|
|
97
|
+
load_entry_type_definition,
|
|
98
|
+
load_property_definition,
|
|
99
|
+
register_entry_type_definition,
|
|
100
|
+
register_property_definition,
|
|
101
|
+
)
|
|
102
|
+
from .saving import has_writer_for, save
|
|
103
|
+
from .services import Service, ServiceRecord
|
|
104
|
+
from .storage.identity import content_id
|
|
105
|
+
from .storage.markers import (
|
|
106
|
+
IdentitySkip,
|
|
107
|
+
Indexed,
|
|
108
|
+
Related,
|
|
109
|
+
Shape,
|
|
110
|
+
Skip,
|
|
111
|
+
StorageInfo,
|
|
112
|
+
StrongLink,
|
|
113
|
+
Unique,
|
|
114
|
+
WeakLink,
|
|
115
|
+
stored_property,
|
|
116
|
+
)
|
|
117
|
+
from .vectors import (
|
|
118
|
+
FracScalar,
|
|
119
|
+
FracVector,
|
|
120
|
+
MutableFracVector,
|
|
121
|
+
NumericVector,
|
|
122
|
+
ScalarLike,
|
|
123
|
+
SurdScalar,
|
|
124
|
+
SurdVector,
|
|
125
|
+
VectorLike,
|
|
126
|
+
numpy_available,
|
|
127
|
+
to_numeric,
|
|
128
|
+
to_numeric_scalar,
|
|
129
|
+
)
|
|
130
|
+
from .views import Backend, View, coerce, coerce_view, unview, unwrap
|
|
131
|
+
|
|
132
|
+
_discover_and_register()
|
|
133
|
+
|
|
134
|
+
__all__ = [
|
|
135
|
+
"ALTERNATIVE_ID_PATTERN",
|
|
136
|
+
"ALTERNATIVE_KIND_PATTERN",
|
|
137
|
+
"ENTRY_ID_PATTERN",
|
|
138
|
+
"IMMUTABLE_ID_PATTERN",
|
|
139
|
+
"MISSING",
|
|
140
|
+
"Backend",
|
|
141
|
+
"BytestreamFileView",
|
|
142
|
+
"BytestreamLike",
|
|
143
|
+
"BytestreamURLView",
|
|
144
|
+
"CLIContext",
|
|
145
|
+
"Calculation",
|
|
146
|
+
"CompressionCodec",
|
|
147
|
+
"DataRecord",
|
|
148
|
+
"DataRecordEntry",
|
|
149
|
+
"Dataset",
|
|
150
|
+
"DatasetDistribution",
|
|
151
|
+
"DatasetLoader",
|
|
152
|
+
"DatasetLoaderRecord",
|
|
153
|
+
"DatasetMeta",
|
|
154
|
+
"DatasetRecord",
|
|
155
|
+
"DatastreamLike",
|
|
156
|
+
"DatastreamURL",
|
|
157
|
+
"EntryProvider",
|
|
158
|
+
"EntryTypeDefinition",
|
|
159
|
+
"File",
|
|
160
|
+
"FileEntry",
|
|
161
|
+
"FileRecord",
|
|
162
|
+
"FracScalar",
|
|
163
|
+
"FracVector",
|
|
164
|
+
"IdentitySkip",
|
|
165
|
+
"Indexed",
|
|
166
|
+
"MissingType",
|
|
167
|
+
"MutableFracVector",
|
|
168
|
+
"NumericVector",
|
|
169
|
+
"ProductLink",
|
|
170
|
+
"PropertyDefinition",
|
|
171
|
+
"Reference",
|
|
172
|
+
"Related",
|
|
173
|
+
"RelatedEntry",
|
|
174
|
+
"Run",
|
|
175
|
+
"RunEdge",
|
|
176
|
+
"RunEntry",
|
|
177
|
+
"ScalarLike",
|
|
178
|
+
"Service",
|
|
179
|
+
"ServiceRecord",
|
|
180
|
+
"Shape",
|
|
181
|
+
"Skip",
|
|
182
|
+
"StorageInfo",
|
|
183
|
+
"StrongLink",
|
|
184
|
+
"SurdScalar",
|
|
185
|
+
"SurdVector",
|
|
186
|
+
"TextstreamFileView",
|
|
187
|
+
"TextstreamLike",
|
|
188
|
+
"TextstreamURLView",
|
|
189
|
+
"Unique",
|
|
190
|
+
"VectorLike",
|
|
191
|
+
"View",
|
|
192
|
+
"WeakLink",
|
|
193
|
+
"apply_definition_prefix",
|
|
194
|
+
"check_entry_id",
|
|
195
|
+
"check_immutable_id",
|
|
196
|
+
"coerce",
|
|
197
|
+
"coerce_view",
|
|
198
|
+
"combined_precision",
|
|
199
|
+
"content_id",
|
|
200
|
+
"credits",
|
|
201
|
+
"decimal_precision",
|
|
202
|
+
"fetch",
|
|
203
|
+
"format_alternative_id",
|
|
204
|
+
"format_entry_id",
|
|
205
|
+
"format_immutable_id",
|
|
206
|
+
"has_reader_for",
|
|
207
|
+
"has_writer_for",
|
|
208
|
+
"is_url_safe_id",
|
|
209
|
+
"known_compressions",
|
|
210
|
+
"known_definition_prefixes",
|
|
211
|
+
"known_entry_providers",
|
|
212
|
+
"known_project_member_kinds",
|
|
213
|
+
"load",
|
|
214
|
+
"load_entry_type_definition",
|
|
215
|
+
"load_many",
|
|
216
|
+
"load_property_definition",
|
|
217
|
+
"load_source",
|
|
218
|
+
"numpy_available",
|
|
219
|
+
"parse_alternative_id",
|
|
220
|
+
"parse_entry_id",
|
|
221
|
+
"parse_immutable_id",
|
|
222
|
+
"project_member_handler",
|
|
223
|
+
"register_citation",
|
|
224
|
+
"register_cli_command",
|
|
225
|
+
"register_compression",
|
|
226
|
+
"register_definition_prefix",
|
|
227
|
+
"register_entry_family",
|
|
228
|
+
"register_entry_provider",
|
|
229
|
+
"register_entry_record",
|
|
230
|
+
"register_entry_type_definition",
|
|
231
|
+
"register_format_adapter",
|
|
232
|
+
"register_format_serializer",
|
|
233
|
+
"register_optimade_entry_binding",
|
|
234
|
+
"register_project_member_kind",
|
|
235
|
+
"register_property_definition",
|
|
236
|
+
"register_reader",
|
|
237
|
+
"register_writer",
|
|
238
|
+
"save",
|
|
239
|
+
"standard_entry_type",
|
|
240
|
+
"stored_property",
|
|
241
|
+
"to_numeric",
|
|
242
|
+
"to_numeric_scalar",
|
|
243
|
+
"unview",
|
|
244
|
+
"unwrap",
|
|
245
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The high-throughput toolkit (httk)
|
|
3
|
+
# Copyright (C) 2012-2024 the httk AUTHORS
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
# License, or (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU Affero General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import importlib
|
|
19
|
+
import importlib.util
|
|
20
|
+
import pkgutil
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _import_registry_packages(path, prefix: str) -> None:
|
|
24
|
+
for module in sorted(pkgutil.iter_modules(path, prefix), key=lambda item: item.name):
|
|
25
|
+
if not module.ispkg:
|
|
26
|
+
continue
|
|
27
|
+
if importlib.util.find_spec(module.name) is not None:
|
|
28
|
+
importlib.import_module(module.name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def discover_and_register() -> None:
|
|
32
|
+
"""Eagerly import registration packages from the available registry tiers.
|
|
33
|
+
|
|
34
|
+
The reserved ``cli``, ``entries``, ``io``, and ``schemas`` sub-namespaces
|
|
35
|
+
are each walked independently. Registration packages are imported eagerly
|
|
36
|
+
so installation errors fail fast, but they must only register lazy
|
|
37
|
+
references: they must not resolve registries or load resource data while
|
|
38
|
+
being imported.
|
|
39
|
+
"""
|
|
40
|
+
importlib.import_module("httk.registry")
|
|
41
|
+
|
|
42
|
+
prefix = "httk.registry."
|
|
43
|
+
for namespace in ("cli", "entries", "io", "schemas"):
|
|
44
|
+
namespace_name = f"{prefix}{namespace}"
|
|
45
|
+
if importlib.util.find_spec(namespace_name) is None:
|
|
46
|
+
continue
|
|
47
|
+
namespace_module = importlib.import_module(namespace_name)
|
|
48
|
+
_import_registry_packages(namespace_module.__path__, f"{namespace_name}.")
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Deterministic JSON helpers shared across httk.core.
|
|
2
|
+
|
|
3
|
+
The on-disk representation of ``project.json`` is shared with *httk-workflow*:
|
|
4
|
+
compact, key-sorted, non-ASCII-preserving encoding with a trailing newline,
|
|
5
|
+
written atomically by rename.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import tempfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def json_bytes(value: object) -> bytes:
|
|
15
|
+
"""Encode compact, deterministic UTF-8 JSON."""
|
|
16
|
+
|
|
17
|
+
return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def read_json(path: Path) -> dict[str, object]:
|
|
21
|
+
"""Read a JSON object from *path*.
|
|
22
|
+
|
|
23
|
+
:param path: File to read.
|
|
24
|
+
:return: The decoded JSON object.
|
|
25
|
+
:raises ValueError: If the file cannot be read or does not hold a JSON object.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
value = json.loads(path.read_text(encoding="utf-8"))
|
|
30
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
|
31
|
+
raise ValueError(f"cannot read JSON object {path}: {exc}") from exc
|
|
32
|
+
if not isinstance(value, dict):
|
|
33
|
+
raise ValueError(f"expected JSON object in {path}")
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _fsync_directory(path: Path) -> None:
|
|
38
|
+
descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_DIRECTORY", 0))
|
|
39
|
+
try:
|
|
40
|
+
os.fsync(descriptor)
|
|
41
|
+
finally:
|
|
42
|
+
os.close(descriptor)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def write_json_atomic(path: Path, value: object, *, durable: bool = False) -> None:
|
|
46
|
+
"""Atomically replace *path* with the JSON encoding of *value*.
|
|
47
|
+
|
|
48
|
+
:param path: File to replace.
|
|
49
|
+
:param value: JSON-encodable value to write.
|
|
50
|
+
:param durable: Whether to ``fsync`` the file and its directory so the write survives a crash.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.tmp.", dir=path.parent)
|
|
55
|
+
temporary = Path(temporary_name)
|
|
56
|
+
try:
|
|
57
|
+
with os.fdopen(descriptor, "wb") as handle:
|
|
58
|
+
handle.write(json_bytes(value))
|
|
59
|
+
handle.write(b"\n")
|
|
60
|
+
if durable:
|
|
61
|
+
handle.flush()
|
|
62
|
+
os.fsync(handle.fileno())
|
|
63
|
+
os.replace(temporary, path)
|
|
64
|
+
if durable:
|
|
65
|
+
_fsync_directory(path.parent)
|
|
66
|
+
finally:
|
|
67
|
+
try:
|
|
68
|
+
temporary.unlink()
|
|
69
|
+
except FileNotFoundError:
|
|
70
|
+
pass
|