protspace 4.16.2__tar.gz → 4.16.4__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.
- {protspace-4.16.2 → protspace-4.16.4}/CHANGELOG.md +54 -0
- {protspace-4.16.2 → protspace-4.16.4}/CLAUDE.md +3 -2
- {protspace-4.16.2 → protspace-4.16.4}/PKG-INFO +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/pyproject.toml +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/pyproject.toml +7 -1
- protspace-4.16.4/src/protspace/__init__.py +1 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/main.py +6 -7
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/utils/add_annotation_style.py +9 -26
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/utils/arrow_reader.py +122 -81
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_manager.py +38 -40
- protspace-4.16.4/tests/test_arrow_reader.py +132 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_backend_switch.py +7 -4
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_base_data_processor.py +4 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_biocentral_embedder.py +14 -14
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_biocentral_retriever.py +2 -2
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_version.py +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_cath_names.py +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_fasta.py +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_interpro_annotation_retriever.py +17 -29
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_legacy_cache_refresh.py +7 -16
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_pfam_clan.py +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_taxonomy_annotation_retriever.py +1 -1
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_ted_retriever.py +3 -7
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_transfer_cli.py +8 -10
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_transformer.py +2 -2
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_uniprot_annotation_retriever.py +8 -10
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_uniprot_parser_encoding.py +1 -1
- protspace-4.16.2/src/protspace/__init__.py +0 -1
- {protspace-4.16.2 → protspace-4.16.4}/.env.example +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/.githooks/pre-commit +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/.gitignore +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/LICENSE +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/3FTx/3FTx.csv +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/3FTx/3FTx_raw_data.xlsx +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/3FTx/parquetbundle/3FTx_accession.csv +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/3FTx/parquetbundle/3ftx_with_db_styled.parquetbundle +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/3FTx/parquetbundle/styles.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/Pla2g2.csv +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/Pla2g2.fasta +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/Pla2g2_pdb.zip +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/protspace_files/Pla2g2.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/protspace_files/Pla2g2_customized.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/Pla2g2/style.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/rebuild_mature_fasta.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/toxins.csv +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/toxins.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/toxins_full.fasta +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/toxins_mature.fasta +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/jmb_2025/toxprot/toxins_style.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/data/toxins/data.parquetbundle +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/docs/annotations.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/docs/cli.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/docs/protspace_example.png +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/docs/styling.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/notebooks/ClickThrough_GenerateEmbeddings.ipynb +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/notebooks/ProtSpace_Preparation.ipynb +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/notebooks/ProtSpace_Transfer.ipynb +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/package.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/benchmarks/bench_knn.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/benchmarks/bench_memory.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/src/protlabel/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/src/protlabel/backends.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/src/protlabel/lookup.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/src/protlabel/reliability.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/src/protlabel/transfer.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_backends.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_boundary.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_lookup.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_reliability.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_transfer.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/packages/protlabel/tests/test_protlabel_version.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/count_h5_rows.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/count_parquetbundle_entries.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/download_clan_fastas.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/build_showcase.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/datasets.toml +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/generate.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/inputs/three-finger-toxins.accessions.txt +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/showcase.toml +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/stage_perf.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/styles/beta-lactamase.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/styles/demo.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/styles/human-fly.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/styles/swissprot.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/styles/three-finger-toxins.json +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_examples/write_manifest.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_toxprot_demo.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/generate_v3_fixture.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/inspect_bundle.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/pfam_clans/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/pfam_clans/create_pfam_clan_query.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/pfam_clans/merge_h5_batches.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/scripts/sync_lock_version.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/analysis/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/analysis/classification.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/app.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/annotated_image.png +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/custom.css +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/help_content/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/help_content/help_faq.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/help_content/help_how_it_works.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/help_content/help_json.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/help_content/help_overview.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/assets/rostlab_logo.png +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/annotate.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/app.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/bundle.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/common_options.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/convert.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/embed.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/prepare.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/project.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/serve.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/stats.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/style.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/cli/transfer.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/core/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/core/config.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/core/constants.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/cache.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/configuration.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/encoding.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/manager.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/merging.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/base_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/biocentral_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/cath_names.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/http_utils.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/interpro_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/taxonomy_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/ted_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/retrievers/uniprot_retriever.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/scores.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/transformers/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/transformers/interpro_transforms.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/transformers/transformer.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/annotations/transformers/uniprot_transforms.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/biocentral_connection.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/embedding/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/embedding/biocentral.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/embedding/local.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/embedding/store.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/atomic.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/bundle.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/bundle_v3.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/fasta.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/formatters.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/predictions.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/settings_converter.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/io/writers.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/embedding_set.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/fasta.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/h5.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/query.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/loaders/similarity.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/parsers/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/parsers/uniprot_parser.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/processors/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/processors/base_processor.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/data/processors/pipeline.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/_sampling.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/annotation_select.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/base.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/carriage.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/cluster/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/cluster/kmeans_elbow.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/driver.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/metrics/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/metrics/annotation_validity.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/metrics/faithfulness.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/stats/metrics/validity.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/ui/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/ui/callbacks.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/ui/layout.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/ui/styles.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/utils/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/utils/constants.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/utils/reducers.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/visualization/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/visualization/molstar.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/src/protspace/visualization/plotting.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/README.md +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/__init__.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/bundle_v3_helpers.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/cli_output.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/conftest.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/prep_source.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotate_cache_dir.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotate_cli.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_checkpoints.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_encoding.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_retrieval_e2e.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_select.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_annotation_validity.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_atomic_publication.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_biocentral_connection.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_biocentral_live.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_build_showcase.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_internal_columns.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_overlay.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_settings.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_v3_container.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_v3_decode.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_v3_encode.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_bundle_v3_fixture.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_classification.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_cli_no_frontend.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_cli_no_similarity.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_config_validation.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_convert.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_display_decode.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_docs_extras_sync.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_embed_completeness.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_failed_source_cache.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_formatters.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_h5_parse_identifier.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_http_retry.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_local_embedder.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_notebooks.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_pipeline_utils.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_predictions_overlay.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_protein_families_parser.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_query.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_reducers.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_run_log.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_scores_ted.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_settings_converter.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_stage_perf.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_stats.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_stats_bundle.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_stats_carriage.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_stats_cli.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_style_warnings.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_toxprot_demo.py +0 -0
- {protspace-4.16.2 → protspace-4.16.4}/tests/test_write_manifest.py +0 -0
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v4.16.4 (2026-10-15)
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- **reader**: Refuse only a bundle file as save_data's default target
|
|
9
|
+
([`d91ff68`](https://github.com/tsenoner/protspace/commit/d91ff68fa4b82dc02e006e7633a140728e60ce7b))
|
|
10
|
+
|
|
11
|
+
- **reader**: Refuse save_data without a directory to write into
|
|
12
|
+
([`b8c8b8c`](https://github.com/tsenoner/protspace/commit/b8c8b8c6a242baef45b577ef7ef5283a79d8b448))
|
|
13
|
+
|
|
14
|
+
- **reader**: Save annotations where ArrowReader reloads them
|
|
15
|
+
([`6b5baa8`](https://github.com/tsenoner/protspace/commit/6b5baa84b4c450d6c0547bfc5a99f294bbb12494))
|
|
16
|
+
|
|
17
|
+
### Refactoring
|
|
18
|
+
|
|
19
|
+
- **reader**: Write save_data's tables in one loop, test via the shared fixture
|
|
20
|
+
([`2e3ed84`](https://github.com/tsenoner/protspace/commit/2e3ed8478b0b7cdec3a461c530403626c1fbe729))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## v4.16.3 (2026-10-13)
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
- **reader**: Read a .parquetbundle directly instead of refusing it
|
|
28
|
+
([`ed1d127`](https://github.com/tsenoner/protspace/commit/ed1d12737089df465b896b4ac3c85c405a1c73cf))
|
|
29
|
+
|
|
30
|
+
### Chores
|
|
31
|
+
|
|
32
|
+
- **protspace**: Ban src.* imports with ruff TID251
|
|
33
|
+
([`cca83a1`](https://github.com/tsenoner/protspace/commit/cca83a173c127462c4d019cae3879290837861be))
|
|
34
|
+
|
|
35
|
+
### Refactoring
|
|
36
|
+
|
|
37
|
+
- **reader**: Fold the path check into _load_data and test it
|
|
38
|
+
([`d662690`](https://github.com/tsenoner/protspace/commit/d662690b5869f0361bd23342b2c61264beb194d9))
|
|
39
|
+
|
|
40
|
+
### Testing
|
|
41
|
+
|
|
42
|
+
- **protspace**: Import protspace, not src.protspace, in the tests
|
|
43
|
+
([`238d432`](https://github.com/tsenoner/protspace/commit/238d432afda2adc43d23c2a9c44722cd751d4a51))
|
|
44
|
+
|
|
45
|
+
- **protspace**: Match the remaining transfer CLI errors through plain()
|
|
46
|
+
([`aa15d57`](https://github.com/tsenoner/protspace/commit/aa15d57a6bf0979d35616a6ce6a0bad08f0e14b8))
|
|
47
|
+
|
|
48
|
+
- **protspace**: Pin the embed usage errors and the MDS max(s) shift
|
|
49
|
+
([`07a8403`](https://github.com/tsenoner/protspace/commit/07a8403fc4b3dde50475a0ffc473a4c80dec9639))
|
|
50
|
+
|
|
51
|
+
- **protspace**: Reuse plain() and one _forbid helper in the audit tests
|
|
52
|
+
([`7aa03f1`](https://github.com/tsenoner/protspace/commit/7aa03f1da2d0749035fabc96673ae7437040b01e))
|
|
53
|
+
|
|
54
|
+
|
|
4
55
|
## v4.16.2 (2026-10-07)
|
|
5
56
|
|
|
6
57
|
### Bug Fixes
|
|
@@ -886,6 +937,9 @@
|
|
|
886
937
|
- **prepare**: Make -f reach every HDF5 input and reject a path that is not there
|
|
887
938
|
([`0591944`](https://github.com/tsenoner/protspace/commit/0591944008d7a79786a36ccab125f8970a004d57))
|
|
888
939
|
|
|
940
|
+
- **reader**: Raise instead of returning an empty dataset for a bad path
|
|
941
|
+
([`a6b00ec`](https://github.com/tsenoner/protspace/commit/a6b00ec52d7d2fbe586ba8a963ceca2a6b30c5da))
|
|
942
|
+
|
|
889
943
|
### Build System
|
|
890
944
|
|
|
891
945
|
- **deps**: Give protobuf a floor instead of leaving it unconstrained
|
|
@@ -317,13 +317,13 @@ For a live count run `uv run pytest tests/ --collect-only -q`.
|
|
|
317
317
|
| `test_config_validation.py` | DimensionReductionConfig parameter validation |
|
|
318
318
|
| `test_style_warnings.py` | `protspace style` warnings: numeric-column detection (tsenoner/protspace-legacy#67) + `selectedPaletteId` validation (categorical vs gradient palette, per column type) + pinned palette-catalog contract |
|
|
319
319
|
| `test_h5_parse_identifier.py` | HDF5 key parsing, identifier extraction |
|
|
320
|
-
| `test_base_data_processor.py` | BaseProcessor: reduction, output creation, save (incl. settings in unbundled output) |
|
|
320
|
+
| `test_base_data_processor.py` | BaseProcessor: reduction (the MDS input actually fed: a similarity matrix converted with `sqrt(max(s) - s)`, a distance matrix passed through), output creation, save (incl. settings in unbundled output) |
|
|
321
321
|
| `test_ted_retriever.py` | TED domain retriever (mocked AlphaFold API, CATH names), final retry pass for failed lookups (10-in-a-row cut-off, 404 never retried), parallel lookups equal to one at a time under jitter, one session, CATH names loaded once, a slow first-pass lookup not holding up the others, a tripped final-pass breaker stopping lookups still retrying |
|
|
322
322
|
| `test_pfam_clan.py` | Pfam CLAN transformer (mapping, dedup, edge cases) |
|
|
323
323
|
| `test_formatters.py` | ProteinAnnotations → DataFrame formatting |
|
|
324
324
|
| `test_bundle_settings.py` | Parquetbundle settings read/write |
|
|
325
325
|
| `test_annotation_encoding.py` | Percent-encoding round-trip, reserved-char-only encoding, schema-metadata stamping through parquet, `upgrade_cell_grammar` taking the version from the caller |
|
|
326
|
-
| `test_transfer_cli.py` | Transfer orchestration core and CLI
|
|
326
|
+
| `test_transfer_cli.py` | Transfer orchestration core (k>1 confidence, cosine metric, self-transfer) and the `transfer` CLI: bad input (no matching embedding, no query match, both id columns) as a usage error with its message, `--help` rendering, `protein_id` and legacy v1 bundles end to end |
|
|
327
327
|
| `test_predictions_overlay.py` | Building the per-cell prediction overlay columns |
|
|
328
328
|
| `test_display_decode.py` | Display-side decoding of encoded values, multi-hit rendering, gated-off passthrough |
|
|
329
329
|
| `test_toxprot_demo.py` | Signal-peptide bound parsing, mature-FASTA stripping, bundle post-processing (column filter/reorder) |
|
|
@@ -334,6 +334,7 @@ For a live count run `uv run pytest tests/ --collect-only -q`.
|
|
|
334
334
|
| `test_atomic_publication.py` | `data/io/atomic.py`: staged rename keeps the previous content on failure, and a published file (bundle, statistics parquet, retained FASTA) carries the process umask rather than `mkstemp`'s owner-only mode |
|
|
335
335
|
| `test_classification.py` | Query/reference rules: id-prefix and case-insensitive `where` substring, query-over-reference precedence, empty-match and missing-column errors |
|
|
336
336
|
| `test_bundle_version.py` | `format_version=2` stamped on the table the `prepare` factory hands to `write_bundle`; a `prepare` bundle reads back its proteins, annotation cells and coordinates; `bundle -a` passes stamped annotate output and the `prepare` annotation cache through (an unstamped cache is recognised by its cache-version attribute), reads an unstamped table as v1 plain text (a pandas `category` column too), stores a list column as hits and reports an unstorable column as a usage error; `ArrowReader.save_data` keeps the stamp it read |
|
|
337
|
+
| `test_arrow_reader.py` | `ArrowReader(Path)` reads a `.parquetbundle` in memory exactly as its extracted directory (data, settings, format version), and refuses a path that holds no ProtSpace data (a non-bundle file, a missing path, a directory with no core table) instead of reading it as an empty dataset; an annotations-only directory still loads; `save_data` writes a directory that reloads with its annotations, projections and restyled colours (the `protspace style` path for an unbundled parquet directory) and recreates its source directory when saved in place, a bundle saves to a directory that reads back alike, while `save_data` without an output directory (bundle source) or on a dict-built reader is refused before anything is written |
|
|
337
338
|
| `test_bundle_v3_encode.py` | v3 encoder: the physical contract behind the browser's zero-copy read (non-nullable PLAIN columns, one row group, little-endian payloads) and v2-reader-parity classification and code order (part 1 rows and projections in the v2 browser's order, numeric inference over the placed proteins with the `placedNumeric` mark, a v1 hit split at its last pipe); NaN for uncovered projections, added unannotated rows, dimension from the data, `true`/`false` booleans, list columns as one hit per element, unstamped and v1 tables refused |
|
|
338
339
|
| `test_bundle_v3_decode.py` | v3 decoder: `decode_v3(encode_v3(T)) == T` on pipeline-shaped tables, plus the deliberate canonicalisations; `sourceType` restoration (64-bit integers past 2^53 exactly), finite-only projection rows, manifest dimensions, payload tiling checks |
|
|
339
340
|
| `test_bundle_v3_container.py` | Six-part container boundary: every write emits v3 with part 6 pinned, every read hands back v2-shaped tables, legacy bundles read as written, the delimiter guard covers part 6; detection by `protspace_container_version`, `style` writing a legacy input as v3, `replace_annotations` keeping label columns labels and the `placedNumeric` mark, a corrupt part 1 as a bundle error |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: protspace
|
|
3
|
-
Version: 4.16.
|
|
3
|
+
Version: 4.16.4
|
|
4
4
|
Summary: Prepare protein language model (pLM) embeddings for interactive exploration at protspace.app — embed, project, annotate, transfer labels (EAT), and bundle.
|
|
5
5
|
Project-URL: Homepage, https://protspace.app
|
|
6
6
|
Project-URL: Repository, https://github.com/tsenoner/protspace
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "protlabel"
|
|
3
|
-
version = "4.16.
|
|
3
|
+
version = "4.16.4"
|
|
4
4
|
description = "Embedding Annotation Transfer (EAT): nearest-neighbour label transfer in protein-language-model embedding space"
|
|
5
5
|
authors = [{ name = "Tobias Senoner", email = "tobias.senoner@tum.de" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "protspace"
|
|
3
|
-
version = "4.16.
|
|
3
|
+
version = "4.16.4"
|
|
4
4
|
description = "Prepare protein language model (pLM) embeddings for interactive exploration at protspace.app — embed, project, annotate, transfer labels (EAT), and bundle."
|
|
5
5
|
authors = [{ name = "Tobias Senoner", email = "tobias.senoner@tum.de" }]
|
|
6
6
|
readme = "README.md"
|
|
@@ -136,6 +136,7 @@ select = [
|
|
|
136
136
|
"ARG001", # unused-function-args
|
|
137
137
|
"F401", # unused-import
|
|
138
138
|
"F841", # unused-variable
|
|
139
|
+
"TID251", # banned-api: see flake8-tidy-imports.banned-api below
|
|
139
140
|
]
|
|
140
141
|
ignore = [
|
|
141
142
|
"E501", # line too long, handled by black
|
|
@@ -143,6 +144,11 @@ ignore = [
|
|
|
143
144
|
"C901", # too complex
|
|
144
145
|
]
|
|
145
146
|
|
|
147
|
+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
|
|
148
|
+
# Importing through `src.` loads a second copy of every protspace module next to the
|
|
149
|
+
# installed one, so a `patch("src.protspace...")` misses the copy the code under test uses.
|
|
150
|
+
"src".msg = "Import from `protspace`, not `src.protspace`."
|
|
151
|
+
|
|
146
152
|
[tool.ruff.lint.isort]
|
|
147
153
|
# protlabel lives in the packages/protlabel workspace member (not src/), so
|
|
148
154
|
# declare both packages first-party for stable import grouping.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.16.4"
|
|
@@ -3,7 +3,6 @@ import warnings
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from protspace.app import ProtSpace
|
|
6
|
-
from protspace.data.io.bundle import extract_bundle_to_dir
|
|
7
6
|
|
|
8
7
|
DEFAULT_PORT = 8050
|
|
9
8
|
|
|
@@ -11,18 +10,18 @@ warnings.filterwarnings("ignore", category=SyntaxWarning)
|
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def detect_data_type(data_path: str) -> str:
|
|
14
|
-
"""Resolve *data_path* to
|
|
13
|
+
"""Resolve *data_path* to something ``ArrowReader`` reads.
|
|
15
14
|
|
|
16
|
-
Accepts a ``.parquetbundle`` file
|
|
17
|
-
|
|
15
|
+
Accepts a ``.parquetbundle`` file or a directory containing ``.parquet``
|
|
16
|
+
files (or a ``data.parquetbundle``).
|
|
18
17
|
|
|
19
18
|
Returns:
|
|
20
|
-
Path to the Parquet directory.
|
|
19
|
+
Path to the bundle file or the Parquet directory.
|
|
21
20
|
"""
|
|
22
21
|
path = Path(data_path)
|
|
23
22
|
|
|
24
23
|
if path.is_file() and path.suffix.lower() == ".parquetbundle":
|
|
25
|
-
return
|
|
24
|
+
return str(path)
|
|
26
25
|
elif path.is_dir():
|
|
27
26
|
parquet_files = list(path.glob("*.parquet"))
|
|
28
27
|
if parquet_files:
|
|
@@ -30,7 +29,7 @@ def detect_data_type(data_path: str) -> str:
|
|
|
30
29
|
|
|
31
30
|
bundle_file = path / "data.parquetbundle"
|
|
32
31
|
if bundle_file.exists():
|
|
33
|
-
return
|
|
32
|
+
return str(bundle_file)
|
|
34
33
|
|
|
35
34
|
raise ValueError(
|
|
36
35
|
f"Directory '{data_path}' does not contain .parquet files or data.parquetbundle"
|
|
@@ -281,15 +281,8 @@ def generate_template(input_file: str) -> dict:
|
|
|
281
281
|
with values in frequency-descending order. ``<NA>`` gets a default
|
|
282
282
|
light-gray color.
|
|
283
283
|
"""
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if data_format == "parquetbundle":
|
|
287
|
-
from protspace.data.io.bundle import extract_bundle_to_dir
|
|
288
|
-
|
|
289
|
-
temp_dir = extract_bundle_to_dir(Path(input_file))
|
|
290
|
-
reader = ArrowReader(Path(temp_dir))
|
|
291
|
-
elif data_format == "parquet":
|
|
292
|
-
reader = ArrowReader(Path(input_file))
|
|
284
|
+
detect_data_format(input_file)
|
|
285
|
+
reader = ArrowReader(Path(input_file))
|
|
293
286
|
|
|
294
287
|
frequencies = compute_value_frequencies(reader)
|
|
295
288
|
template: dict = {}
|
|
@@ -366,7 +359,7 @@ def add_annotation_styles_bundle(
|
|
|
366
359
|
) -> None:
|
|
367
360
|
"""Add annotation styles to a .parquetbundle file.
|
|
368
361
|
|
|
369
|
-
|
|
362
|
+
Reads the bundle with ArrowReader, applies styles, converts
|
|
370
363
|
the visualization_state back to settings_json, then writes the output
|
|
371
364
|
bundle with updated settings.
|
|
372
365
|
|
|
@@ -375,25 +368,15 @@ def add_annotation_styles_bundle(
|
|
|
375
368
|
``selectedPaletteId``) alongside ``colors`` and ``shapes``. These are
|
|
376
369
|
forwarded to the settings converter.
|
|
377
370
|
"""
|
|
378
|
-
from protspace.data.io.bundle import
|
|
379
|
-
SETTINGS_FILENAME,
|
|
380
|
-
extract_bundle_to_dir,
|
|
381
|
-
read_settings_from_file,
|
|
382
|
-
replace_settings_in_bundle,
|
|
383
|
-
)
|
|
371
|
+
from protspace.data.io.bundle import replace_settings_in_bundle
|
|
384
372
|
from protspace.data.io.settings_converter import visualization_state_to_settings
|
|
385
373
|
|
|
386
|
-
|
|
387
|
-
temp_dir = extract_bundle_to_dir(Path(bundle_file))
|
|
388
|
-
reader = ArrowReader(Path(temp_dir))
|
|
374
|
+
reader = ArrowReader(Path(bundle_file))
|
|
389
375
|
|
|
390
|
-
#
|
|
391
|
-
#
|
|
392
|
-
#
|
|
393
|
-
|
|
394
|
-
existing_settings = (
|
|
395
|
-
read_settings_from_file(settings_path) if settings_path.exists() else None
|
|
396
|
-
)
|
|
376
|
+
# Existing settings (if any), to preserve extra fields. From the reader,
|
|
377
|
+
# not the bundle again: this command reads the bundle once (and so logs a
|
|
378
|
+
# legacy-format warning once).
|
|
379
|
+
existing_settings = reader.settings
|
|
397
380
|
|
|
398
381
|
# Collect settings-level overrides from the styles input
|
|
399
382
|
style_overrides: dict[str, dict] = {}
|
|
@@ -28,27 +28,30 @@ def _read_format_version(schema_metadata: dict | None) -> int:
|
|
|
28
28
|
class ArrowReader:
|
|
29
29
|
"""Read and manipulate ProtSpace data from Parquet files or a dict.
|
|
30
30
|
|
|
31
|
-
Accepts
|
|
32
|
-
pre-built ``dict`` (the same structure previously
|
|
33
|
-
legacy ``JsonReader``).
|
|
31
|
+
Accepts a ``Path`` to a ``.parquetbundle`` file or to a directory of
|
|
32
|
+
Parquet files, or a pre-built ``dict`` (the same structure previously
|
|
33
|
+
handled by the legacy ``JsonReader``).
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
def __init__(self, source: Path | dict):
|
|
37
37
|
"""
|
|
38
38
|
Args:
|
|
39
|
-
source: Path to
|
|
40
|
-
pre-built data dict
|
|
39
|
+
source: Path to a .parquetbundle file or to a directory
|
|
40
|
+
containing .parquet files, or a pre-built data dict
|
|
41
|
+
(protein_data, projections, …).
|
|
41
42
|
"""
|
|
42
43
|
if isinstance(source, dict):
|
|
43
44
|
# Direct dict input
|
|
44
45
|
self.data = source
|
|
45
46
|
self.data_path = None
|
|
47
|
+
self.settings = None
|
|
46
48
|
self._protein_annotations_df = None
|
|
47
49
|
self._projections_metadata_df = None
|
|
48
50
|
self._projections_data_df = None
|
|
49
51
|
else:
|
|
50
52
|
# Parquet file loading
|
|
51
53
|
self.data_path = Path(source)
|
|
54
|
+
self.settings = None
|
|
52
55
|
self._protein_annotations_df = None
|
|
53
56
|
self._projections_metadata_df = None
|
|
54
57
|
self._projections_data_df = None
|
|
@@ -61,43 +64,61 @@ class ArrowReader:
|
|
|
61
64
|
self._build_data_structure()
|
|
62
65
|
|
|
63
66
|
def _load_data(self):
|
|
64
|
-
"""Load
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
self._protein_annotations_df = annotations_table.to_pandas()
|
|
73
|
-
self.data["format_version"] = _read_format_version(
|
|
74
|
-
annotations_table.schema.metadata
|
|
75
|
-
)
|
|
76
|
-
else:
|
|
77
|
-
self._protein_annotations_df = pd.DataFrame(columns=["protein_id"])
|
|
78
|
-
|
|
79
|
-
if projections_metadata_path.exists():
|
|
80
|
-
self._projections_metadata_df = pq.read_table(
|
|
81
|
-
str(projections_metadata_path)
|
|
82
|
-
).to_pandas()
|
|
83
|
-
else:
|
|
84
|
-
self._projections_metadata_df = pd.DataFrame(
|
|
85
|
-
columns=["projection_name", "dimensions", "info_json"]
|
|
86
|
-
)
|
|
67
|
+
"""Load the core tables from a .parquetbundle file or a directory."""
|
|
68
|
+
path = self.data_path
|
|
69
|
+
if path.is_file():
|
|
70
|
+
tables = self._read_bundle(path)
|
|
71
|
+
elif path.is_dir():
|
|
72
|
+
tables = self._read_directory(path)
|
|
73
|
+
else:
|
|
74
|
+
raise FileNotFoundError(f"No such file or directory: '{path}'")
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
76
|
+
annotations, metadata, projections = tables
|
|
77
|
+
if annotations is not None:
|
|
78
|
+
self._protein_annotations_df = annotations.to_pandas()
|
|
79
|
+
self.data["format_version"] = _read_format_version(
|
|
80
|
+
annotations.schema.metadata
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
self._protein_annotations_df = pd.DataFrame(columns=["protein_id"])
|
|
84
|
+
self._projections_metadata_df = (
|
|
85
|
+
metadata.to_pandas()
|
|
86
|
+
if metadata is not None
|
|
87
|
+
else pd.DataFrame(columns=["projection_name", "dimensions", "info_json"])
|
|
88
|
+
)
|
|
89
|
+
self._projections_data_df = (
|
|
90
|
+
projections.to_pandas()
|
|
91
|
+
if projections is not None
|
|
92
|
+
else pd.DataFrame(columns=["projection_name", "identifier", "x", "y", "z"])
|
|
93
|
+
)
|
|
96
94
|
|
|
97
|
-
|
|
95
|
+
def _read_bundle(self, path: Path) -> tuple[pa.Table, pa.Table, pa.Table]:
|
|
96
|
+
"""Read a bundle in memory, whatever its container version."""
|
|
97
|
+
from protspace.data.io.bundle import read_bundle_contents
|
|
98
|
+
|
|
99
|
+
contents = read_bundle_contents(path)
|
|
100
|
+
self.settings = contents.settings
|
|
101
|
+
return contents.annotations, contents.metadata, contents.projections
|
|
102
|
+
|
|
103
|
+
def _read_directory(self, path: Path) -> tuple[pa.Table | None, ...]:
|
|
104
|
+
"""Read the core tables a directory holds; a missing one reads as None.
|
|
105
|
+
|
|
106
|
+
An annotations-only directory is valid, but one holding none of the core
|
|
107
|
+
tables is not ProtSpace data and must not read as an empty dataset.
|
|
108
|
+
"""
|
|
109
|
+
from protspace.data.io.bundle import CORE_FILENAMES
|
|
110
|
+
|
|
111
|
+
core_paths = [path / name for name in CORE_FILENAMES]
|
|
112
|
+
if not any(p.exists() for p in core_paths):
|
|
98
113
|
raise ValueError(
|
|
99
|
-
f"
|
|
100
|
-
)
|
|
114
|
+
f"Directory '{path}' contains none of {', '.join(CORE_FILENAMES)}."
|
|
115
|
+
)
|
|
116
|
+
try:
|
|
117
|
+
return tuple(
|
|
118
|
+
pq.read_table(str(p)) if p.exists() else None for p in core_paths
|
|
119
|
+
)
|
|
120
|
+
except Exception as e:
|
|
121
|
+
raise ValueError(f"Error loading Arrow data from {path}: {e}") from e
|
|
101
122
|
|
|
102
123
|
def _build_data_structure(self):
|
|
103
124
|
"""Build the data structure matching the ProtSpace data schema."""
|
|
@@ -158,65 +179,85 @@ class ArrowReader:
|
|
|
158
179
|
def _load_visualization_state(self):
|
|
159
180
|
"""Load visualization state.
|
|
160
181
|
|
|
161
|
-
|
|
182
|
+
A bundle carries it in its settings part. A directory may hold
|
|
183
|
+
visualization_state.json, which wins over settings.parquet.
|
|
162
184
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
if self.data_path.is_dir():
|
|
186
|
+
viz_state_path = self.data_path / "visualization_state.json"
|
|
187
|
+
if viz_state_path.exists():
|
|
188
|
+
try:
|
|
189
|
+
with open(viz_state_path) as f:
|
|
190
|
+
self.data["visualization_state"] = json.load(f)
|
|
191
|
+
return
|
|
192
|
+
except (json.JSONDecodeError, FileNotFoundError):
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
try:
|
|
196
|
+
from protspace.data.io.bundle import (
|
|
197
|
+
SETTINGS_FILENAME,
|
|
198
|
+
read_settings_from_file,
|
|
199
|
+
)
|
|
200
|
+
from protspace.data.io.settings_converter import (
|
|
201
|
+
settings_to_visualization_state,
|
|
202
|
+
)
|
|
181
203
|
|
|
182
|
-
|
|
204
|
+
settings_path = self.data_path / SETTINGS_FILENAME
|
|
205
|
+
if self.data_path.is_dir() and settings_path.exists():
|
|
206
|
+
self.settings = read_settings_from_file(settings_path)
|
|
207
|
+
if self.settings:
|
|
183
208
|
self.data["visualization_state"] = settings_to_visualization_state(
|
|
184
|
-
settings
|
|
185
|
-
)
|
|
186
|
-
except Exception:
|
|
187
|
-
logger.debug(
|
|
188
|
-
"Failed to load visualization state from settings", exc_info=True
|
|
209
|
+
self.settings
|
|
189
210
|
)
|
|
211
|
+
except Exception:
|
|
212
|
+
logger.debug(
|
|
213
|
+
"Failed to load visualization state from settings", exc_info=True
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def save_data(self, output_path: Path | None = None):
|
|
217
|
+
"""Save the current data back to parquet files.
|
|
190
218
|
|
|
191
|
-
|
|
192
|
-
|
|
219
|
+
Defaults to the directory the data was read from. A reader built from
|
|
220
|
+
a .parquetbundle needs an explicit ``output_path``: the bundle is a
|
|
221
|
+
file, not a directory the tables could be written into.
|
|
222
|
+
"""
|
|
223
|
+
if self.data_path is None:
|
|
224
|
+
raise ValueError(
|
|
225
|
+
"save_data writes the parquet tables a reader loaded; this "
|
|
226
|
+
"reader was built from a dict and holds none."
|
|
227
|
+
)
|
|
193
228
|
if output_path is None:
|
|
229
|
+
if self.data_path.is_file():
|
|
230
|
+
raise ValueError(
|
|
231
|
+
f"'{self.data_path}' is a bundle file, not a directory; "
|
|
232
|
+
"pass save_data an output directory."
|
|
233
|
+
)
|
|
194
234
|
output_path = self.data_path
|
|
195
235
|
else:
|
|
196
236
|
output_path = Path(output_path)
|
|
197
237
|
|
|
198
238
|
output_path.mkdir(parents=True, exist_ok=True)
|
|
199
239
|
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
|
|
240
|
+
# The names _read_directory reads back (and a bundle's parts extract to)
|
|
241
|
+
from protspace.data.io.bundle import CORE_FILENAMES
|
|
242
|
+
|
|
243
|
+
annotations, metadata, projections = (
|
|
244
|
+
pa.Table.from_pandas(df)
|
|
245
|
+
for df in (
|
|
246
|
+
self._protein_annotations_df,
|
|
247
|
+
self._projections_metadata_df,
|
|
248
|
+
self._projections_data_df,
|
|
249
|
+
)
|
|
250
|
+
)
|
|
203
251
|
if self.should_decode():
|
|
204
252
|
# The cells are written back as they were read, so they keep the
|
|
205
253
|
# grammar stamp that says so; unstamped they would read as v1.
|
|
206
254
|
from protspace.data.annotations.encoding import stamp_format_version
|
|
207
255
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
projections_metadata_table = pa.Table.from_pandas(self._projections_metadata_df)
|
|
214
|
-
pq.write_table(projections_metadata_table, str(projections_metadata_path))
|
|
215
|
-
|
|
216
|
-
# Save projections data
|
|
217
|
-
projections_data_path = output_path / "projections_data.parquet"
|
|
218
|
-
projections_data_table = pa.Table.from_pandas(self._projections_data_df)
|
|
219
|
-
pq.write_table(projections_data_table, str(projections_data_path))
|
|
256
|
+
annotations = stamp_format_version(annotations)
|
|
257
|
+
for name, table in zip(
|
|
258
|
+
CORE_FILENAMES, (annotations, metadata, projections), strict=True
|
|
259
|
+
):
|
|
260
|
+
pq.write_table(table, str(output_path / name))
|
|
220
261
|
|
|
221
262
|
# Save visualization state as a separate JSON file
|
|
222
263
|
viz_state_path = output_path / "visualization_state.json"
|