data_pipeline_api 0.6.0__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/.travis.yml +3 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/PKG-INFO +1 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/README.md +4 -0
- data_pipeline_api-0.7.0/data_pipeline_api/data_processing_api.py +14 -0
- data_pipeline_api-0.7.0/data_pipeline_api/file_api.py +318 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/file_formats/object_file.py +22 -15
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/file_formats/parameter_file.py +8 -2
- data_pipeline_api-0.7.0/data_pipeline_api/git_info.py +56 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/metadata.py +1 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/metadata_store.py +15 -12
- data_pipeline_api-0.7.0/data_pipeline_api/registry/README.md +166 -0
- data_pipeline_api-0.7.0/data_pipeline_api/registry/__init__.py +1 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/registry/access_upload.py +71 -55
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/registry/common.py +124 -83
- data_pipeline_api-0.7.0/data_pipeline_api/registry/download.py +133 -0
- data_pipeline_api-0.7.0/data_pipeline_api/registry/downloader.py +407 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/registry/upload.py +64 -3
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/registry/upload_data_product.py +17 -11
- data_pipeline_api-0.7.0/data_pipeline_api/registry/utils.py +22 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/standard_api.py +14 -9
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/PKG-INFO +1 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/SOURCES.txt +62 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/entry_points.txt +1 -2
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/requires.txt +1 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/environment.yml +1 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/examples/standard_api_usage.py +6 -14
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/build.gradle +13 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/api/FileApi.java +22 -6
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/MetadataItem.java +58 -13
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/overrides/OverridesApplier.java +4 -2
- data_pipeline_api-0.7.0/java_api/standard_api/build.gradle +68 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/api/StandardApi.java +130 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/distribution/Distribution.java +149 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/distribution/MinMax.java +39 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/distribution/MinMaxDeserializer.java +59 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/distribution/MinMaxSerializer.java +23 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/estimate/Estimate.java +38 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/objects/NumericalArray.java +13 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/objects/NumericalArrayImpl.java +34 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/objects/StandardArrayDataReader.java +7 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/objects/StandardTable.java +3 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/objects/StandardTableDataReader.java +7 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/Component.java +3 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/Components.java +13 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ComponentsDeserializer.java +59 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ComponentsSerializer.java +45 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ParameterDataReader.java +8 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ParameterDataReaderImpl.java +24 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ParameterDataWriter.java +8 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ParameterDataWriterImpl.java +29 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/ReadComponent.java +12 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/parameters/WriteComponent.java +3 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/samples/Sampler.java +20 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/samples/Samples.java +44 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TOMLFactory.java +11 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TOMLInputDecorator.java +45 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TOMLMapper.java +13 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TOMLOutputDecorator.java +55 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TomlReader.java +22 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/main/java/uk/ramp/toml/TomlWriter.java +21 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/api/StandardApiIntegrationTest.java +239 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/api/StandardApiTest.java +143 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/distribution/DistributionTest.java +110 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/estimate/EstimateTest.java +27 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/parameters/ComponentsDeserializerTest.java +75 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/parameters/ComponentsSerializerTest.java +75 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/parameters/ParameterDataReaderTest.java +43 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/parameters/ParameterDataWriterImplTest.java +40 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/samples/SamplerTest.java +27 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/samples/SamplesTest.java +49 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/toml/TomlReaderPairwiseIntegrationTest.java +27 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/java/uk/ramp/toml/TomlWriterPairwiseIntegrationTest.java +68 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/config.yaml +59 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/expectedDistribution.toml +5 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/expectedDistributionCategorical.toml +5 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/expectedEstimate.toml +3 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/expectedSamples.toml +3 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/expectedSamplesMultiple.toml +6 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/metadata.yaml +35 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/object/example5.h5 +4 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/folder/data/parameter/example1.toml +19 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/location.txt +1 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/table.csv +4 -0
- data_pipeline_api-0.7.0/java_api/standard_api/src/test/resources/table.h5 +0 -0
- data_pipeline_api-0.7.0/jitpack.yml +2 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/scripts/convert_access_to_config.py +3 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/setup.py +2 -3
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/object/example.h5 +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/file_formats/test_object_file.py +10 -1
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/file_formats/test_parameter_file.py +14 -2
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/registry/test_access_upload.py +2 -12
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/registry/test_common.py +128 -25
- data_pipeline_api-0.7.0/tests/registry/test_download.py +59 -0
- data_pipeline_api-0.7.0/tests/registry/test_downloader.py +581 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/registry/test_upload.py +85 -17
- data_pipeline_api-0.7.0/tests/registry/test_utils.py +25 -0
- data_pipeline_api-0.7.0/tests/test_data_processing_api.py +21 -0
- data_pipeline_api-0.7.0/tests/test_file_api.py +263 -0
- data_pipeline_api-0.7.0/tests/test_git_info.py +40 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/test_metadata_store.py +4 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/test_standard_api.py +42 -32
- data_pipeline_api-0.6.0/data_pipeline_api/file_api.py +0 -267
- data_pipeline_api-0.6.0/data_pipeline_api/registry/README.md +0 -98
- data_pipeline_api-0.6.0/data_pipeline_api/registry/download.py +0 -353
- data_pipeline_api-0.6.0/java_api/standard_api/build.gradle +0 -14
- data_pipeline_api-0.6.0/java_api/standard_api/src/main/java/uk/ramp/api/StandardApi.java +0 -5
- data_pipeline_api-0.6.0/tests/registry/__init__.py +0 -0
- data_pipeline_api-0.6.0/tests/registry/test_download.py +0 -204
- data_pipeline_api-0.6.0/tests/test_file_api.py +0 -126
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/.gitignore +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/.pylintrc +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/LICENSE +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/conda/meta.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/contributing.md +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/__init__.py +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/overrides.py +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/registry/templates/data_product.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/dependency_links.txt +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api.egg-info/top_level.txt +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/examples/example_configs/simple_network_sim_config.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/examples/example_configs/simple_network_sim_upload.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/build.gradle +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessEntry.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessLog.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessLogWriter.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessLogger.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessLoggerFactory.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/AccessLoggerImpl.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/access/NoImplAccessLogger.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/config/Config.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/config/ConfigFactory.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/config/ConfigReader.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/file/CleanableFileChannel.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/file/FileDirectoryNormaliser.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/file/FileReader.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/hash/HashMetadataAppender.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/hash/Hasher.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/hash/Sha1Hasher.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/IssueItem.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/MatchingMetadataSelector.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/MetaDataReader.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/MetadataFactory.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/MetadataSelector.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/ReadOnlyRunMetadata.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/RunMetadata.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/metadata/WriteOnlyRunMetadata.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/yaml/BaseYamlReader.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/yaml/BaseYamlWriter.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/yaml/YamlFactory.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/yaml/YamlReader.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/main/java/uk/ramp/yaml/YamlWriter.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/access/AccessLoggerTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/api/FileApiIntegrationTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/file/CleanableFileChannelIntegrationTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/file/FileDirectoryNormaliserTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/hash/HashMetadataAppenderTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/hash/Sha1HasherTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/metadata/MatchingMetadataSelectorTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/metadata/MetadataItemTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/metadata/MetadataItemVersionComparisonTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/java/uk/ramp/yaml/BaseYamlWriterTest.java +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/resources/config.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/resources/folder/data/metadata.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/file_api/src/test/resources/folder/data/parameter/example1.toml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/gradle/wrapper/gradle-wrapper.jar +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/gradle/wrapper/gradle-wrapper.properties +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/gradlew +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/gradlew.bat +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/java_api/settings.gradle +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/scripts/release.sh +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/setup.cfg +0 -0
- {data_pipeline_api-0.6.0/data_pipeline_api/registry → data_pipeline_api-0.7.0/tests}/__init__.py +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/conftest.py +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/config.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/demographics.h5 +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/metadata.yaml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/object/example-v2.h5 +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/parameter/example-v2.toml +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/data/parameter/example.toml +0 -0
- {data_pipeline_api-0.6.0/tests → data_pipeline_api-0.7.0/tests/registry}/__init__.py +0 -0
- {data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/tests/test_overrides.py +0 -0
|
@@ -53,6 +53,10 @@ cd data_pipeline_api
|
|
|
53
53
|
git tag --sort=v:refname
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## Data Registry Interactions
|
|
57
|
+
|
|
58
|
+
See [registry README](data_pipeline_api/registry/README.md).
|
|
59
|
+
|
|
56
60
|
## Releasing a new version
|
|
57
61
|
|
|
58
62
|
New versions can be released from the master branch at any time. Whenever a git
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from io import IOBase
|
|
2
|
+
from data_pipeline_api.standard_api import StandardAPI
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DataProcessingAPI(StandardAPI):
|
|
6
|
+
"""The DataProcessingAPI extends the StandardAPI with access to external objects.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def read_external_object(self, doi_or_unique_name: str, component: str) -> IOBase:
|
|
10
|
+
"""Return an open file handle to read the given external object.
|
|
11
|
+
"""
|
|
12
|
+
return self.file_api.open_for_read(
|
|
13
|
+
doi_or_unique_name=doi_or_unique_name, component=component
|
|
14
|
+
)
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
from io import IOBase
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Union, Optional, Any, Iterable, Dict, List
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from hashlib import sha1
|
|
8
|
+
from logging import getLogger, WARNING, DEBUG
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from data_pipeline_api.metadata import Metadata, MetadataKey, log_format_metadata
|
|
12
|
+
from data_pipeline_api.metadata_store import MetadataStore
|
|
13
|
+
from data_pipeline_api.overrides import Overrides
|
|
14
|
+
|
|
15
|
+
logger = getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class FileAccess:
|
|
20
|
+
"""Represents a generic file access.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
timestamp: datetime
|
|
24
|
+
call_metadata: Metadata
|
|
25
|
+
access_metadata: Metadata
|
|
26
|
+
path: Path
|
|
27
|
+
|
|
28
|
+
def to_access_log_record(
|
|
29
|
+
self, hash_cache: Optional[Dict[Path, str]] = None
|
|
30
|
+
) -> Dict[str, Any]:
|
|
31
|
+
if hash_cache is None:
|
|
32
|
+
hash_cache = {}
|
|
33
|
+
access_metadata = self.access_metadata.copy()
|
|
34
|
+
if MetadataKey.calculated_hash not in access_metadata:
|
|
35
|
+
absolute_path = self.path.resolve()
|
|
36
|
+
try:
|
|
37
|
+
access_metadata[MetadataKey.calculated_hash] = hash_cache[absolute_path]
|
|
38
|
+
except KeyError:
|
|
39
|
+
access_metadata[MetadataKey.calculated_hash] = hash_cache[
|
|
40
|
+
absolute_path
|
|
41
|
+
] = FileAPI.calculate_hash(absolute_path)
|
|
42
|
+
return {
|
|
43
|
+
"timestamp": self.timestamp,
|
|
44
|
+
"call_metadata": self.call_metadata,
|
|
45
|
+
"access_metadata": access_metadata,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ReadAccess(FileAccess):
|
|
50
|
+
"""Represents a file read.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def to_access_log_record(
|
|
54
|
+
self, hash_cache: Optional[Dict[Path, str]] = None
|
|
55
|
+
) -> Dict[str, Any]:
|
|
56
|
+
return dict(type="read", **super().to_access_log_record(hash_cache))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class WriteAccess(FileAccess):
|
|
61
|
+
"""Represents a file write.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
file_handle: IOBase
|
|
65
|
+
|
|
66
|
+
def to_access_log_record(
|
|
67
|
+
self, hash_cache: Optional[Dict[Path, str]] = None
|
|
68
|
+
) -> Dict[str, Any]:
|
|
69
|
+
if not self.file_handle.closed:
|
|
70
|
+
logger.warning(
|
|
71
|
+
"the file handle to write to %s is still open, attempting to flush",
|
|
72
|
+
self.path,
|
|
73
|
+
)
|
|
74
|
+
self.file_handle.flush()
|
|
75
|
+
return dict(type="write", **super().to_access_log_record(hash_cache))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class FileAPI:
|
|
79
|
+
RESERVED_RUN_METADATA_KEYS = {
|
|
80
|
+
"run_id",
|
|
81
|
+
"open_timestamp",
|
|
82
|
+
"close_timestamp",
|
|
83
|
+
"data_directory",
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def normalise_path(root: Path, path: Path) -> Path:
|
|
88
|
+
"""Normalise a path by prepending a known root if it is not absolute.
|
|
89
|
+
"""
|
|
90
|
+
return path if path.is_absolute() else root / path
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def calculate_hash(filename: Path, extra_bytes: Optional[bytes] = None) -> str:
|
|
94
|
+
"""Calculate the SHA1 hash of a file, optionally appending extra bytes.
|
|
95
|
+
"""
|
|
96
|
+
with open(filename, "rb") as file:
|
|
97
|
+
message = sha1(file.read())
|
|
98
|
+
if extra_bytes:
|
|
99
|
+
message.update(extra_bytes)
|
|
100
|
+
hexdigest = message.hexdigest()
|
|
101
|
+
logger.debug(
|
|
102
|
+
"calculated hash of %s as %s", filename, hexdigest,
|
|
103
|
+
)
|
|
104
|
+
return hexdigest
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def construct_overrides(overrides: Iterable[Dict[str, Dict[str, Any]]]):
|
|
108
|
+
"""Construct an Overrides given the structure found in the FileAPI config.
|
|
109
|
+
"""
|
|
110
|
+
return Overrides(
|
|
111
|
+
(override.get("where", {}), override.get("use", {}))
|
|
112
|
+
for override in overrides
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def construct_metadata_store(metadata_filename: Path) -> MetadataStore:
|
|
117
|
+
"""Construct a MetadataStore corresponding to a file.
|
|
118
|
+
"""
|
|
119
|
+
metadata_store = None
|
|
120
|
+
if metadata_filename.exists():
|
|
121
|
+
logger.debug("loading metadata from %s", metadata_filename)
|
|
122
|
+
with open(metadata_filename) as metadata_store_file:
|
|
123
|
+
metadata_store = yaml.safe_load(metadata_store_file)
|
|
124
|
+
else:
|
|
125
|
+
logger.warning("could not find metadata.yaml")
|
|
126
|
+
return MetadataStore(metadata_store)
|
|
127
|
+
|
|
128
|
+
def __init__(self, config_filename: Optional[Union[Path, str]] = None):
|
|
129
|
+
"""The FileAPI class provides tracked interaction with the filesystem.
|
|
130
|
+
|
|
131
|
+
Files are located through a metadata lookup system based on two files: a
|
|
132
|
+
metadata database file, which lists all known files and their associated
|
|
133
|
+
metadata, and a configuration file, which provides mechanisms for influencing
|
|
134
|
+
the metadata lookup process.
|
|
135
|
+
"""
|
|
136
|
+
self._accesses: List[FileAccess] = []
|
|
137
|
+
self._run_metadata = {}
|
|
138
|
+
|
|
139
|
+
self._open_timestamp = datetime.now()
|
|
140
|
+
self._run_metadata["open_timestamp"] = self._open_timestamp
|
|
141
|
+
logger.debug("open_timestamp = %s", self._open_timestamp)
|
|
142
|
+
|
|
143
|
+
if config_filename is None:
|
|
144
|
+
self._config_filename = None
|
|
145
|
+
self._config = {}
|
|
146
|
+
self._root = Path()
|
|
147
|
+
else:
|
|
148
|
+
self._config_filename = Path(config_filename)
|
|
149
|
+
with open(self._config_filename) as config_file:
|
|
150
|
+
self._config = yaml.safe_load(config_file)
|
|
151
|
+
self._root = self._config_filename.parent
|
|
152
|
+
logger.debug("config_filename = %s", self._config_filename)
|
|
153
|
+
logger.debug("root = %s", self._root.resolve())
|
|
154
|
+
|
|
155
|
+
self._run_id = self._config.get("run_id", uuid4().hex)
|
|
156
|
+
self._run_metadata["run_id"] = self._run_id
|
|
157
|
+
logger.debug("run_id = %s", self._run_id)
|
|
158
|
+
|
|
159
|
+
self._fail_on_hash_mismatch = self._config.get("fail_on_hash_mismatch", True)
|
|
160
|
+
logger.log(
|
|
161
|
+
DEBUG if self._fail_on_hash_mismatch else WARNING,
|
|
162
|
+
"fail_on_hash_mismatch = %s",
|
|
163
|
+
self._fail_on_hash_mismatch,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
data_directory = self._config.get("data_directory", self._root)
|
|
167
|
+
self._unnormalised_data_directory = Path(data_directory)
|
|
168
|
+
self._run_metadata["data_directory"] = str(self._unnormalised_data_directory)
|
|
169
|
+
self._data_directory = FileAPI.normalise_path(
|
|
170
|
+
self._root, self._unnormalised_data_directory
|
|
171
|
+
)
|
|
172
|
+
logger.debug("data_directory = %s", self._data_directory)
|
|
173
|
+
|
|
174
|
+
access_log = self._config.get("access_log", "access-{run_id}.yaml")
|
|
175
|
+
if access_log is False:
|
|
176
|
+
self._access_log_path = None
|
|
177
|
+
logger.warning("disabled access log")
|
|
178
|
+
else:
|
|
179
|
+
self._access_log_path = FileAPI.normalise_path(
|
|
180
|
+
self._root, Path(access_log.format(run_id=self._run_id)),
|
|
181
|
+
)
|
|
182
|
+
logger.debug("access_log_path = %s", self._access_log_path)
|
|
183
|
+
|
|
184
|
+
self._run_metadata.update(self._config.get("metadata", {}))
|
|
185
|
+
self._read_overrides = FileAPI.construct_overrides(self._config.get("read", ()))
|
|
186
|
+
self._write_overrides = FileAPI.construct_overrides(
|
|
187
|
+
self._config.get("write", ())
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
self.load_metadata_store()
|
|
191
|
+
|
|
192
|
+
def load_metadata_store(self):
|
|
193
|
+
metadata_store_filename = self._data_directory / "metadata.yaml"
|
|
194
|
+
logger.debug("loading metadata store from %s", metadata_store_filename)
|
|
195
|
+
self._metadata_store = FileAPI.construct_metadata_store(metadata_store_filename)
|
|
196
|
+
|
|
197
|
+
def get_read_metadata(self, metadata: Metadata) -> Metadata:
|
|
198
|
+
read_metadata = metadata.copy()
|
|
199
|
+
self._read_overrides.apply(read_metadata)
|
|
200
|
+
return self._metadata_store.find(read_metadata) or read_metadata
|
|
201
|
+
|
|
202
|
+
def open_for_read(self, **call_metadata) -> IOBase:
|
|
203
|
+
"""Return a file open for reading corresponding to the given metadata.
|
|
204
|
+
|
|
205
|
+
The file contents are hashed, and a record is made of the read.
|
|
206
|
+
"""
|
|
207
|
+
logger.debug("starting open_for_read(%s)", log_format_metadata(call_metadata))
|
|
208
|
+
read_metadata = self.get_read_metadata(call_metadata)
|
|
209
|
+
path = self._data_directory / read_metadata[MetadataKey.filename]
|
|
210
|
+
read_metadata[MetadataKey.calculated_hash] = FileAPI.calculate_hash(path)
|
|
211
|
+
|
|
212
|
+
if self._fail_on_hash_mismatch:
|
|
213
|
+
if (
|
|
214
|
+
read_metadata[MetadataKey.calculated_hash]
|
|
215
|
+
== read_metadata[MetadataKey.verified_hash]
|
|
216
|
+
):
|
|
217
|
+
logger.debug("verified hash")
|
|
218
|
+
raise ValueError(
|
|
219
|
+
(
|
|
220
|
+
"calculated hash {calculated_hash} != "
|
|
221
|
+
"verified hash {verified_hash}"
|
|
222
|
+
).format(**read_metadata)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
logger.debug("open('%s', mode='rb')", path)
|
|
226
|
+
file = open(path, mode="rb")
|
|
227
|
+
self._accesses.append(
|
|
228
|
+
ReadAccess(
|
|
229
|
+
timestamp=datetime.now(),
|
|
230
|
+
call_metadata=call_metadata,
|
|
231
|
+
access_metadata=read_metadata,
|
|
232
|
+
path=path,
|
|
233
|
+
)
|
|
234
|
+
)
|
|
235
|
+
logger.info("recorded read(%s)", log_format_metadata(call_metadata))
|
|
236
|
+
return file
|
|
237
|
+
|
|
238
|
+
def get_write_metadata(self, metadata: Metadata) -> Metadata:
|
|
239
|
+
write_metadata = metadata.copy()
|
|
240
|
+
self._write_overrides.apply(write_metadata)
|
|
241
|
+
if MetadataKey.filename not in write_metadata:
|
|
242
|
+
write_metadata[MetadataKey.filename] = str(
|
|
243
|
+
Path(write_metadata[MetadataKey.data_product])
|
|
244
|
+
/ "{}.{}".format(self._run_id, write_metadata[MetadataKey.extension])
|
|
245
|
+
)
|
|
246
|
+
logger.debug("generated filename %s", write_metadata[MetadataKey.filename])
|
|
247
|
+
return write_metadata
|
|
248
|
+
|
|
249
|
+
def open_for_write(self, **call_metadata) -> IOBase:
|
|
250
|
+
"""Return a file open for update corresponding to the given metadata.
|
|
251
|
+
|
|
252
|
+
When the file is closed the file contents are hashed, and a record is made of
|
|
253
|
+
the write.
|
|
254
|
+
"""
|
|
255
|
+
logger.debug("starting open_for_write(%s)", log_format_metadata(call_metadata))
|
|
256
|
+
write_metadata = self.get_write_metadata(call_metadata)
|
|
257
|
+
|
|
258
|
+
path = self._data_directory / write_metadata[MetadataKey.filename]
|
|
259
|
+
if path.exists():
|
|
260
|
+
mode = "r+b"
|
|
261
|
+
else:
|
|
262
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
263
|
+
mode = "w+b"
|
|
264
|
+
logger.debug("open('%s', mode='%s')", path, mode)
|
|
265
|
+
file = open(path, mode=mode)
|
|
266
|
+
self._accesses.append(
|
|
267
|
+
WriteAccess(
|
|
268
|
+
timestamp=datetime.now(),
|
|
269
|
+
call_metadata=call_metadata,
|
|
270
|
+
access_metadata=write_metadata,
|
|
271
|
+
path=path,
|
|
272
|
+
file_handle=file,
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
logger.info("recorded write(%s)", log_format_metadata(call_metadata))
|
|
276
|
+
return file
|
|
277
|
+
|
|
278
|
+
def set_run_metadata(self, key: str, value: Any):
|
|
279
|
+
"""Set run-level metadata.
|
|
280
|
+
"""
|
|
281
|
+
if key in FileAPI.RESERVED_RUN_METADATA_KEYS:
|
|
282
|
+
raise ValueError(f"{key} is reserved")
|
|
283
|
+
self._run_metadata[key] = value
|
|
284
|
+
|
|
285
|
+
def get_run_metadata(self, key: str) -> Any:
|
|
286
|
+
"""Get run-level metadata.
|
|
287
|
+
"""
|
|
288
|
+
return self._run_metadata[key]
|
|
289
|
+
|
|
290
|
+
def _generate_access_log(self) -> Dict[str, Any]:
|
|
291
|
+
calculated_path_hashes = {}
|
|
292
|
+
return {
|
|
293
|
+
"run_metadata": dict(close_timestamp=datetime.now(), **self._run_metadata),
|
|
294
|
+
"config": self._config,
|
|
295
|
+
"io": [
|
|
296
|
+
access.to_access_log_record(calculated_path_hashes)
|
|
297
|
+
for access in self._accesses
|
|
298
|
+
],
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
def close(self):
|
|
302
|
+
"""Close the session and write the access log.
|
|
303
|
+
"""
|
|
304
|
+
if self._access_log_path:
|
|
305
|
+
with open(self._access_log_path, "w") as output_file:
|
|
306
|
+
yaml.dump(
|
|
307
|
+
self._generate_access_log(), output_file, sort_keys=False,
|
|
308
|
+
)
|
|
309
|
+
logger.info("wrote access log")
|
|
310
|
+
else:
|
|
311
|
+
logger.warning("did not write access log")
|
|
312
|
+
|
|
313
|
+
def __enter__(self):
|
|
314
|
+
return self
|
|
315
|
+
|
|
316
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
317
|
+
if exc_type is None:
|
|
318
|
+
self.close()
|
{data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/file_formats/object_file.py
RENAMED
|
@@ -77,16 +77,15 @@ def write_table(file: IOBase, component: str, table: Table):
|
|
|
77
77
|
index=False,
|
|
78
78
|
column_dtypes={
|
|
79
79
|
column: (
|
|
80
|
-
table[column].values.astype(np.string_).dtype
|
|
81
|
-
if dtype == "O"
|
|
82
|
-
else dtype
|
|
80
|
+
table[column].values.astype(np.string_).dtype if dtype == "O" else dtype
|
|
83
81
|
)
|
|
84
82
|
for column, dtype in table.dtypes.items()
|
|
85
83
|
},
|
|
86
84
|
)
|
|
87
|
-
get_write_group(file, component)
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
group = get_write_group(file, component)
|
|
86
|
+
for dataset in group:
|
|
87
|
+
del group[dataset]
|
|
88
|
+
group.create_dataset("table", data=records, track_times=False)
|
|
90
89
|
|
|
91
90
|
|
|
92
91
|
DIMENSION_PREFIX = "Dimension_"
|
|
@@ -172,42 +171,50 @@ def read_array(file: IOBase, component: str) -> Array:
|
|
|
172
171
|
def write_array(file: IOBase, component: str, array: Array):
|
|
173
172
|
# TODO : More validation on the inputs?
|
|
174
173
|
group = get_write_group(file, component)
|
|
175
|
-
group
|
|
176
|
-
|
|
177
|
-
)
|
|
174
|
+
for dataset in group:
|
|
175
|
+
del group[dataset]
|
|
176
|
+
group.create_dataset("array", data=array.data, track_times=False)
|
|
178
177
|
if array.dimensions is not None:
|
|
179
178
|
for i, dimension in enumerate(array.dimensions, start=1):
|
|
180
179
|
if dimension.title is not None:
|
|
181
|
-
group.
|
|
180
|
+
group.create_dataset(
|
|
182
181
|
f"Dimension_{i}_title",
|
|
183
182
|
dtype=h5py.string_dtype(),
|
|
184
183
|
shape=(),
|
|
185
184
|
data=dimension.title,
|
|
185
|
+
track_times=False,
|
|
186
186
|
)
|
|
187
187
|
if dimension.names is not None:
|
|
188
188
|
encoded_names = np.char.encode(dimension.names)
|
|
189
|
-
group.
|
|
189
|
+
group.create_dataset(
|
|
190
190
|
f"Dimension_{i}_names",
|
|
191
191
|
dtype=h5py.string_dtype(),
|
|
192
192
|
shape=encoded_names.shape,
|
|
193
193
|
data=encoded_names,
|
|
194
|
+
track_times=False,
|
|
194
195
|
)
|
|
195
196
|
if dimension.values is not None:
|
|
196
197
|
values = np.array(dimension.values)
|
|
197
|
-
group.
|
|
198
|
+
group.create_dataset(
|
|
198
199
|
f"Dimension_{i}_values",
|
|
199
200
|
dtype=values.dtype,
|
|
200
201
|
shape=values.shape,
|
|
201
202
|
data=values,
|
|
203
|
+
track_times=False,
|
|
202
204
|
)
|
|
203
205
|
if dimension.units is not None:
|
|
204
|
-
group.
|
|
206
|
+
group.create_dataset(
|
|
205
207
|
f"Dimension_{i}_units",
|
|
206
208
|
dtype=h5py.string_dtype(),
|
|
207
209
|
shape=(),
|
|
208
210
|
data=dimension.units,
|
|
211
|
+
track_times=False,
|
|
209
212
|
)
|
|
210
213
|
if array.units is not None:
|
|
211
|
-
group.
|
|
212
|
-
"units",
|
|
214
|
+
group.create_dataset(
|
|
215
|
+
"units",
|
|
216
|
+
dtype=h5py.string_dtype(),
|
|
217
|
+
shape=(),
|
|
218
|
+
data=array.units,
|
|
219
|
+
track_times=False,
|
|
213
220
|
)
|
{data_pipeline_api-0.6.0 → data_pipeline_api-0.7.0}/data_pipeline_api/file_formats/parameter_file.py
RENAMED
|
@@ -79,7 +79,8 @@ def read_distribution(file: TextIOBase, component: str) -> Distribution:
|
|
|
79
79
|
raise ValueError(f"{parameter['type']} != 'distribution'")
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def
|
|
82
|
+
def encode_distribution(distribution: Distribution) -> Dict[str, Any]:
|
|
83
|
+
"""Encode distribution into a serialisable format."""
|
|
83
84
|
shape, loc, scale = distribution.dist._parse_args(
|
|
84
85
|
*distribution.args, **distribution.kwds
|
|
85
86
|
)
|
|
@@ -93,8 +94,13 @@ def write_distribution(file: TextIOBase, component: str, distribution: Distribut
|
|
|
93
94
|
parameter["shape"] = shape[0]
|
|
94
95
|
if scale:
|
|
95
96
|
parameter["scale"] = scale
|
|
97
|
+
return parameter
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def write_distribution(file: TextIOBase, component: str, distribution: Distribution):
|
|
101
|
+
"""Write distribution to file under component."""
|
|
96
102
|
write_parameter(
|
|
97
|
-
file, component,
|
|
103
|
+
file, component, encode_distribution(distribution),
|
|
98
104
|
)
|
|
99
105
|
|
|
100
106
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import NamedTuple, Optional
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
try:
|
|
4
|
+
from git import Repo, InvalidGitRepositoryError # type: ignore
|
|
5
|
+
git_installed = True
|
|
6
|
+
except ImportError:
|
|
7
|
+
Repo = None
|
|
8
|
+
InvalidGitRepositoryError = None
|
|
9
|
+
git_installed = False
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RepoInfo(NamedTuple):
|
|
13
|
+
"""
|
|
14
|
+
The info needed by the data pipeline API
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
git_sha: str
|
|
18
|
+
uri: str
|
|
19
|
+
is_dirty: bool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_repo(path: Optional[Path] = None) -> Repo:
|
|
23
|
+
"""Attempt to get a repo from path, or the working directory.
|
|
24
|
+
"""
|
|
25
|
+
if Repo is None:
|
|
26
|
+
raise ImportError("Could not import git")
|
|
27
|
+
else:
|
|
28
|
+
return Repo(path=path, search_parent_directories=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_repo_info(path: Optional[Path] = None, default_repo: str = "") -> RepoInfo:
|
|
32
|
+
"""Get the git sha and uri for path, or the working directory.
|
|
33
|
+
|
|
34
|
+
:path: A path to use to start looking for the root of the repo.
|
|
35
|
+
:default_repo: A default repo url to use if we cannot obtain one.
|
|
36
|
+
:return: A RepoInfo object. If not inside a git repo, is_dirty will be True, git_sha empty and uri will be a
|
|
37
|
+
default value
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
repo = get_repo(path)
|
|
41
|
+
return RepoInfo(
|
|
42
|
+
git_sha=repo.head.commit.hexsha,
|
|
43
|
+
uri=next(repo.remote("origin").urls),
|
|
44
|
+
is_dirty=repo.is_dirty(),
|
|
45
|
+
)
|
|
46
|
+
except (ImportError, InvalidGitRepositoryError):
|
|
47
|
+
return RepoInfo(git_sha="", uri=default_repo, is_dirty=True)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_path_relative_to_repo(path: Path) -> Path:
|
|
51
|
+
"""Get path relative to the repo which contains it, or raise an exception.
|
|
52
|
+
"""
|
|
53
|
+
result = get_repo(path).git.execute(['git', 'ls-files', '--full-name', str(path)])
|
|
54
|
+
if result:
|
|
55
|
+
return Path(result)
|
|
56
|
+
raise ValueError(f"Could not get {path} relative to repo")
|
|
@@ -20,24 +20,27 @@ class MetadataRecord(NamedTuple):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class MetadataStore:
|
|
23
|
-
def __init__(self, metadata_sequence: Sequence[Metadata]):
|
|
23
|
+
def __init__(self, metadata_sequence: Optional[Sequence[Metadata]] = None):
|
|
24
24
|
"""The MetadataStore class provides a simple metadata lookup mechanism.
|
|
25
25
|
|
|
26
26
|
A MetadataStore is initialised with a Sequence of Metadata objects, which can
|
|
27
27
|
then be searched by matching against Metadata fragments.
|
|
28
28
|
"""
|
|
29
|
-
|
|
30
|
-
self._metadata_records =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
if metadata_sequence is None:
|
|
30
|
+
self._metadata_records = ()
|
|
31
|
+
else:
|
|
32
|
+
try:
|
|
33
|
+
self._metadata_records = tuple(
|
|
34
|
+
MetadataRecord(
|
|
35
|
+
metadata=metadata,
|
|
36
|
+
version=VersionInfo.parse(metadata[MetadataKey.version])
|
|
37
|
+
if MetadataKey.version in metadata
|
|
38
|
+
else None,
|
|
39
|
+
)
|
|
40
|
+
for metadata in metadata_sequence
|
|
36
41
|
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
except Exception as exception:
|
|
40
|
-
raise ValueError("invalid metadata") from exception
|
|
42
|
+
except Exception as exception:
|
|
43
|
+
raise ValueError("invalid metadata") from exception
|
|
41
44
|
|
|
42
45
|
def find(self, metadata: Metadata) -> Optional[Metadata]:
|
|
43
46
|
try:
|