seqevi 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. seqevi/__init__.py +7 -0
  2. seqevi/__main__.py +6 -0
  3. seqevi/adapters/__init__.py +41 -0
  4. seqevi/adapters/base.py +131 -0
  5. seqevi/adapters/dbcan_cazyme.py +588 -0
  6. seqevi/adapters/eggnog.py +674 -0
  7. seqevi/adapters/interpro_pfam.py +757 -0
  8. seqevi/adapters/registry.py +68 -0
  9. seqevi/annotate.py +413 -0
  10. seqevi/api.py +390 -0
  11. seqevi/cli.py +610 -0
  12. seqevi/distribution/__init__.py +13 -0
  13. seqevi/distribution/manifest.py +199 -0
  14. seqevi/distribution/oci.py +490 -0
  15. seqevi/distribution/setup.py +752 -0
  16. seqevi/errors.py +73 -0
  17. seqevi/evidence.py +295 -0
  18. seqevi/execution_profile.py +526 -0
  19. seqevi/hashing.py +13 -0
  20. seqevi/kits/__init__.py +1 -0
  21. seqevi/kits/dbcan-cazyme.toml +35 -0
  22. seqevi/resource_lock.py +438 -0
  23. seqevi/result.py +682 -0
  24. seqevi/runner.py +163 -0
  25. seqevi/runtime_identity.py +104 -0
  26. seqevi/sequence.py +383 -0
  27. seqevi/service/__init__.py +11 -0
  28. seqevi/service/app.py +213 -0
  29. seqevi/service/config.py +38 -0
  30. seqevi/service/persistence.py +360 -0
  31. seqevi/store/__init__.py +14 -0
  32. seqevi/store/artifact.py +225 -0
  33. seqevi/store/client.py +311 -0
  34. seqevi/store/contract.py +33 -0
  35. seqevi/store/factory.py +38 -0
  36. seqevi/store/local.py +479 -0
  37. seqevi/store/migration.py +62 -0
  38. seqevi/store/migrations/__init__.py +1 -0
  39. seqevi/store/migrations/env.py +30 -0
  40. seqevi/store/migrations/versions/0001_initial_store.py +103 -0
  41. seqevi/store/migrations/versions/0002_artifact_byte_size_bigint.py +40 -0
  42. seqevi/store/migrations/versions/__init__.py +1 -0
  43. seqevi/store/schema.py +86 -0
  44. seqevi/store/transport.py +224 -0
  45. seqevi-0.2.0.dist-info/METADATA +333 -0
  46. seqevi-0.2.0.dist-info/RECORD +49 -0
  47. seqevi-0.2.0.dist-info/WHEEL +4 -0
  48. seqevi-0.2.0.dist-info/entry_points.txt +5 -0
  49. seqevi-0.2.0.dist-info/licenses/LICENSE +21 -0
seqevi/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Content-addressed protein sequence annotation evidence."""
2
+
3
+ __version__ = "0.2.0"
4
+
5
+ from .api import annotate, scan_annotations
6
+
7
+ __all__ = ["__version__", "annotate", "scan_annotations"]
seqevi/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Run SeqEvi with ``python -m seqevi``."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,41 @@
1
+ """Explicit first-party annotation adapter boundaries."""
2
+
3
+ from .base import (
4
+ AdapterBatchResult,
5
+ AdapterContract,
6
+ AdapterSequenceResult,
7
+ AnnotationAdapter,
8
+ )
9
+ from .dbcan_cazyme import DBCAN_EVIDENCE_SCHEMA, DBCanCazymeAdapter, DBCanParameters
10
+ from .eggnog import (
11
+ EGGNOG_EVIDENCE_SCHEMA,
12
+ EggnogAdapter,
13
+ EggnogParameters,
14
+ )
15
+ from .interpro_pfam import (
16
+ ADAPTER_CONTRACT_VERSION,
17
+ INTERPRO_PFAM_EVIDENCE_SCHEMA,
18
+ InterProPfamAdapter,
19
+ InterProPfamParameters,
20
+ )
21
+ from .registry import AdapterConfiguration, AdapterName, create_adapter
22
+
23
+ __all__ = [
24
+ "AdapterBatchResult",
25
+ "AdapterConfiguration",
26
+ "AdapterContract",
27
+ "AdapterName",
28
+ "AdapterSequenceResult",
29
+ "AnnotationAdapter",
30
+ "DBCAN_EVIDENCE_SCHEMA",
31
+ "DBCanCazymeAdapter",
32
+ "DBCanParameters",
33
+ "EGGNOG_EVIDENCE_SCHEMA",
34
+ "EggnogAdapter",
35
+ "EggnogParameters",
36
+ "ADAPTER_CONTRACT_VERSION",
37
+ "INTERPRO_PFAM_EVIDENCE_SCHEMA",
38
+ "InterProPfamAdapter",
39
+ "InterProPfamParameters",
40
+ "create_adapter",
41
+ ]
@@ -0,0 +1,131 @@
1
+ """Typed boundary shared by SeqEvi's official adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Mapping
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Protocol
10
+
11
+ import polars as pl
12
+
13
+ from seqevi.evidence import (
14
+ ArtifactFile,
15
+ EvidenceKey,
16
+ EvidenceStatus,
17
+ canonical_semantic_parameters,
18
+ )
19
+ from seqevi.runner import ToolRunner
20
+ from seqevi.sequence import SequenceIdentity
21
+
22
+
23
+ @dataclass(frozen=True, slots=True)
24
+ class AdapterContract:
25
+ """Resolved immutable identity shared by one adapter invocation."""
26
+
27
+ name: str
28
+ version: str
29
+ tool_runtime_digest: str
30
+ resource_id: str
31
+ semantic_parameters_json: str
32
+
33
+ def __post_init__(self) -> None:
34
+ for field_name in ("name", "version", "tool_runtime_digest", "resource_id"):
35
+ if not getattr(self, field_name):
36
+ raise ValueError(f"adapter contract {field_name} must not be empty")
37
+ parsed = json.loads(self.semantic_parameters_json)
38
+ if not isinstance(parsed, dict):
39
+ raise ValueError("adapter semantic parameters must be a JSON object")
40
+ if canonical_semantic_parameters(parsed) != self.semantic_parameters_json:
41
+ raise ValueError("adapter semantic parameters are not canonical")
42
+
43
+ @classmethod
44
+ def from_parameters(
45
+ cls,
46
+ *,
47
+ name: str,
48
+ version: str,
49
+ tool_runtime_digest: str,
50
+ resource_id: str,
51
+ semantic_parameters: Mapping[str, object],
52
+ ) -> AdapterContract:
53
+ """Construct a resolved contract with canonical semantic parameters."""
54
+
55
+ return cls(
56
+ name=name,
57
+ version=version,
58
+ tool_runtime_digest=tool_runtime_digest,
59
+ resource_id=resource_id,
60
+ semantic_parameters_json=canonical_semantic_parameters(semantic_parameters),
61
+ )
62
+
63
+ @property
64
+ def semantic_parameters(self) -> dict[str, object]:
65
+ """Return a fresh JSON-compatible semantic-parameter mapping."""
66
+
67
+ parameters = json.loads(self.semantic_parameters_json)
68
+ assert isinstance(parameters, dict)
69
+ return parameters
70
+
71
+ def evidence_key(self, identity: SequenceIdentity) -> EvidenceKey:
72
+ """Build the exact evidence key for one canonical sequence."""
73
+
74
+ return EvidenceKey(
75
+ sequence_id=identity.sequence_id,
76
+ adapter_contract_version=self.version,
77
+ tool_runtime_digest=self.tool_runtime_digest,
78
+ resource_id=self.resource_id,
79
+ semantic_parameters_json=self.semantic_parameters_json,
80
+ )
81
+
82
+
83
+ @dataclass(frozen=True, slots=True)
84
+ class AdapterSequenceResult:
85
+ """Terminal adapter result for one requested sequence."""
86
+
87
+ sequence_id: str
88
+ status: EvidenceStatus
89
+ payload_digest: str
90
+
91
+
92
+ @dataclass(frozen=True, slots=True)
93
+ class AdapterBatchResult:
94
+ """Validated batch artifacts and per-sequence terminal states."""
95
+
96
+ sequences: tuple[AdapterSequenceResult, ...]
97
+ raw_artifact: ArtifactFile
98
+ normalized_artifact: ArtifactFile | None = None
99
+
100
+ def __post_init__(self) -> None:
101
+ if not self.sequences:
102
+ raise ValueError("adapter batch result must not be empty")
103
+ sequence_ids = [result.sequence_id for result in self.sequences]
104
+ if len(sequence_ids) != len(set(sequence_ids)):
105
+ raise ValueError("adapter batch result contains duplicate SequenceIDs")
106
+ if (
107
+ any(result.status is EvidenceStatus.HIT for result in self.sequences)
108
+ and self.normalized_artifact is None
109
+ ):
110
+ raise ValueError("adapter hits require a normalized artifact")
111
+
112
+
113
+ class AnnotationAdapter(Protocol):
114
+ """Behavior required by the annotation orchestrator."""
115
+
116
+ @property
117
+ def contract(self) -> AdapterContract: ...
118
+
119
+ @property
120
+ def evidence_schema(self) -> Mapping[str, pl.DataType]: ...
121
+
122
+ def run_batch(
123
+ self,
124
+ *,
125
+ identities: tuple[SequenceIdentity, ...],
126
+ input_fasta: Path,
127
+ work_dir: Path,
128
+ runner: ToolRunner,
129
+ timeout_seconds: float | None,
130
+ threads: int,
131
+ ) -> AdapterBatchResult: ...