dnakit 0.1.0.dev0__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.
- dnakit/__init__.py +108 -0
- dnakit/_version.py +5 -0
- dnakit/alignment/__init__.py +6 -0
- dnakit/alignment/pairwise.py +512 -0
- dnakit/alignment/results.py +188 -0
- dnakit/backends/__init__.py +80 -0
- dnakit/backends/external.py +517 -0
- dnakit/backends/registry.py +207 -0
- dnakit/batch.py +452 -0
- dnakit/cache/__init__.py +5 -0
- dnakit/cache/store.py +424 -0
- dnakit/chunking.py +920 -0
- dnakit/cli/__init__.py +5 -0
- dnakit/cli/__main__.py +6 -0
- dnakit/cli/app.py +814 -0
- dnakit/cli/workflow.py +94 -0
- dnakit/config/__init__.py +5 -0
- dnakit/config/loader.py +381 -0
- dnakit/core/__init__.py +80 -0
- dnakit/core/_json.py +206 -0
- dnakit/core/backend_info.py +70 -0
- dnakit/core/collection.py +163 -0
- dnakit/core/coordinates.py +295 -0
- dnakit/core/enums.py +95 -0
- dnakit/core/facade.py +719 -0
- dnakit/core/feature.py +90 -0
- dnakit/core/gap.py +82 -0
- dnakit/core/issues.py +62 -0
- dnakit/core/provenance.py +305 -0
- dnakit/core/record.py +161 -0
- dnakit/core/results.py +134 -0
- dnakit/core/sequence.py +327 -0
- dnakit/datasets/__init__.py +100 -0
- dnakit/datasets/_advanced_shared.py +198 -0
- dnakit/datasets/_metadata.py +33 -0
- dnakit/datasets/advanced_deduplicate.py +183 -0
- dnakit/datasets/advanced_split.py +302 -0
- dnakit/datasets/clustering.py +286 -0
- dnakit/datasets/config.py +531 -0
- dnakit/datasets/deduplicate.py +303 -0
- dnakit/datasets/evaluation.py +244 -0
- dnakit/datasets/filter.py +135 -0
- dnakit/datasets/neural_clustering.py +221 -0
- dnakit/datasets/results.py +520 -0
- dnakit/datasets/split.py +450 -0
- dnakit/descriptors/__init__.py +102 -0
- dnakit/descriptors/_dinucleotide.py +345 -0
- dnakit/descriptors/_shared.py +180 -0
- dnakit/descriptors/all.py +917 -0
- dnakit/descriptors/basic.py +193 -0
- dnakit/descriptors/codon.py +195 -0
- dnakit/descriptors/complexity.py +201 -0
- dnakit/descriptors/entropy.py +101 -0
- dnakit/descriptors/homopolymer.py +105 -0
- dnakit/descriptors/kmer.py +88 -0
- dnakit/descriptors/results.py +286 -0
- dnakit/descriptors/schema.py +414 -0
- dnakit/descriptors/window.py +203 -0
- dnakit/download/__init__.py +54 -0
- dnakit/download/catalogs.py +166 -0
- dnakit/download/ena.py +131 -0
- dnakit/download/encode.py +103 -0
- dnakit/download/files.py +469 -0
- dnakit/download/indexes.py +233 -0
- dnakit/download/metadata.py +164 -0
- dnakit/download/models.py +227 -0
- dnakit/download/ncbi.py +333 -0
- dnakit/download/sequence.py +133 -0
- dnakit/download/ucsc.py +99 -0
- dnakit/evaluation/__init__.py +81 -0
- dnakit/evaluation/_shared.py +288 -0
- dnakit/evaluation/collection.py +270 -0
- dnakit/evaluation/config.py +591 -0
- dnakit/evaluation/distribution.py +287 -0
- dnakit/evaluation/frechet.py +223 -0
- dnakit/evaluation/generative.py +381 -0
- dnakit/evaluation/reference.py +409 -0
- dnakit/evaluation/results.py +137 -0
- dnakit/evaluation/scorecard.py +211 -0
- dnakit/evaluation/sequence.py +403 -0
- dnakit/evaluation/synthesis.py +249 -0
- dnakit/exceptions.py +189 -0
- dnakit/fingerprints/__init__.py +109 -0
- dnakit/fingerprints/_shared.py +116 -0
- dnakit/fingerprints/advanced.py +829 -0
- dnakit/fingerprints/bit.py +229 -0
- dnakit/fingerprints/encoding.py +351 -0
- dnakit/fingerprints/kmer.py +170 -0
- dnakit/fingerprints/results.py +531 -0
- dnakit/fingerprints/sketch.py +246 -0
- dnakit/io/__init__.py +112 -0
- dnakit/io/_advanced_common.py +105 -0
- dnakit/io/_formats.py +994 -0
- dnakit/io/annotations.py +1179 -0
- dnakit/io/api.py +759 -0
- dnakit/io/config.py +315 -0
- dnakit/io/genbank.py +523 -0
- dnakit/io/indexed.py +1146 -0
- dnakit/io/metadata.py +452 -0
- dnakit/io/parquet.py +56 -0
- dnakit/io/results.py +131 -0
- dnakit/io/source.py +138 -0
- dnakit/io/tables.py +1237 -0
- dnakit/molbio/__init__.py +101 -0
- dnakit/molbio/_shared.py +266 -0
- dnakit/molbio/assembly.py +279 -0
- dnakit/molbio/crispr.py +181 -0
- dnakit/molbio/optimization.py +897 -0
- dnakit/molbio/primer3_design.py +667 -0
- dnakit/molbio/primers.py +899 -0
- dnakit/molbio/restriction.py +573 -0
- dnakit/molbio/results.py +583 -0
- dnakit/ops/__init__.py +588 -0
- dnakit/ops/_common.py +201 -0
- dnakit/ops/circular.py +193 -0
- dnakit/ops/concat.py +204 -0
- dnakit/ops/direction.py +27 -0
- dnakit/ops/edit.py +443 -0
- dnakit/ops/evolution.py +991 -0
- dnakit/ops/mutation.py +151 -0
- dnakit/ops/records.py +1012 -0
- dnakit/ops/translation.py +257 -0
- dnakit/patterns/__init__.py +81 -0
- dnakit/patterns/_shared.py +282 -0
- dnakit/patterns/coding.py +360 -0
- dnakit/patterns/crispr.py +337 -0
- dnakit/patterns/motif.py +721 -0
- dnakit/patterns/regions.py +341 -0
- dnakit/patterns/repeats.py +480 -0
- dnakit/patterns/restriction.py +281 -0
- dnakit/patterns/results.py +411 -0
- dnakit/py.typed +1 -0
- dnakit/references/__init__.py +27 -0
- dnakit/references/models.py +155 -0
- dnakit/references/ncbi.py +785 -0
- dnakit/representations/__init__.py +29 -0
- dnakit/representations/backends.py +928 -0
- dnakit/representations/checkpoints.py +356 -0
- dnakit/representations/extraction.py +220 -0
- dnakit/representations/models.py +395 -0
- dnakit/representations/results.py +79 -0
- dnakit/search/__init__.py +132 -0
- dnakit/search/_http.py +271 -0
- dnakit/search/_shared.py +108 -0
- dnakit/search/blast.py +516 -0
- dnakit/search/ena.py +245 -0
- dnakit/search/encode.py +102 -0
- dnakit/search/ensembl.py +874 -0
- dnakit/search/models.py +159 -0
- dnakit/search/ncbi.py +756 -0
- dnakit/search/ucsc.py +388 -0
- dnakit/secondary_structure/__init__.py +38 -0
- dnakit/secondary_structure/dotbracket.py +410 -0
- dnakit/secondary_structure/nupack.py +589 -0
- dnakit/secondary_structure/results.py +142 -0
- dnakit/similarity/__init__.py +125 -0
- dnakit/similarity/_shared.py +152 -0
- dnakit/similarity/approximate.py +258 -0
- dnakit/similarity/compare.py +162 -0
- dnakit/similarity/dashing.py +936 -0
- dnakit/similarity/distance.py +279 -0
- dnakit/similarity/index.py +365 -0
- dnakit/similarity/matrix.py +254 -0
- dnakit/similarity/results.py +676 -0
- dnakit/similarity/search.py +286 -0
- dnakit/similarity/sketch.py +96 -0
- dnakit/similarity/vector.py +662 -0
- dnakit/standardize/__init__.py +77 -0
- dnakit/standardize/_shared.py +100 -0
- dnakit/standardize/config.py +411 -0
- dnakit/standardize/gaps.py +1060 -0
- dnakit/standardize/normalize.py +565 -0
- dnakit/standardize/results.py +247 -0
- dnakit/standardize/validate.py +487 -0
- dnakit/structure3d/__init__.py +47 -0
- dnakit/structure3d/_shared.py +47 -0
- dnakit/structure3d/geometry.py +773 -0
- dnakit/structure3d/pdb.py +253 -0
- dnakit/structure3d/results.py +233 -0
- dnakit/structure3d/threedna.py +174 -0
- dnakit/thermodynamics/__init__.py +112 -0
- dnakit/thermodynamics/_shared.py +92 -0
- dnakit/thermodynamics/backends.py +653 -0
- dnakit/thermodynamics/calculations.py +837 -0
- dnakit/thermodynamics/config.py +175 -0
- dnakit/thermodynamics/equilibrium.py +340 -0
- dnakit/thermodynamics/optics.py +385 -0
- dnakit/thermodynamics/parameters.py +79 -0
- dnakit/thermodynamics/results.py +445 -0
- dnakit/visualization/__init__.py +43 -0
- dnakit/visualization/_svg.py +185 -0
- dnakit/visualization/advanced.py +314 -0
- dnakit/visualization/config.py +255 -0
- dnakit/visualization/export.py +349 -0
- dnakit/visualization/matrix.py +279 -0
- dnakit/visualization/report.py +431 -0
- dnakit/visualization/results.py +236 -0
- dnakit/visualization/sequence.py +459 -0
- dnakit/workflows/__init__.py +45 -0
- dnakit/workflows/manifest.py +365 -0
- dnakit/workflows/runner.py +1548 -0
- dnakit/workflows/schema.py +898 -0
- dnakit-0.1.0.dev0.dist-info/METADATA +126 -0
- dnakit-0.1.0.dev0.dist-info/RECORD +210 -0
- dnakit-0.1.0.dev0.dist-info/WHEEL +5 -0
- dnakit-0.1.0.dev0.dist-info/entry_points.txt +2 -0
- dnakit-0.1.0.dev0.dist-info/licenses/DISCLAIMER.md +26 -0
- dnakit-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
- dnakit-0.1.0.dev0.dist-info/licenses/THIRD_PARTY_NOTICES.md +73 -0
- dnakit-0.1.0.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Immutable pairwise-alignment result objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any, Literal, cast
|
|
8
|
+
|
|
9
|
+
from dnakit.core import (
|
|
10
|
+
ExecutionMode,
|
|
11
|
+
ImplementationInfo,
|
|
12
|
+
ImplementationLabel,
|
|
13
|
+
Issue,
|
|
14
|
+
OriginClass,
|
|
15
|
+
Provenance,
|
|
16
|
+
)
|
|
17
|
+
from dnakit.core._json import FrozenDict, to_json_compatible
|
|
18
|
+
from dnakit.exceptions import ConfigurationError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _alignment_provenance() -> Provenance:
|
|
22
|
+
return Provenance(
|
|
23
|
+
implementation=ImplementationInfo(
|
|
24
|
+
label=ImplementationLabel.REIMPLEMENTATION,
|
|
25
|
+
execution_mode=ExecutionMode.INTERNAL,
|
|
26
|
+
origin_class=OriginClass.PUBLISHED_ALGORITHM,
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True, slots=True)
|
|
32
|
+
class AlignmentColumn:
|
|
33
|
+
"""One alignment column and its optional source coordinates."""
|
|
34
|
+
|
|
35
|
+
query_symbol: str
|
|
36
|
+
target_symbol: str
|
|
37
|
+
query_position: int | None
|
|
38
|
+
target_position: int | None
|
|
39
|
+
operation: Literal["match", "mismatch", "insertion", "deletion"]
|
|
40
|
+
|
|
41
|
+
def __post_init__(self) -> None:
|
|
42
|
+
for name in ("query_symbol", "target_symbol"):
|
|
43
|
+
value = getattr(self, name)
|
|
44
|
+
if not isinstance(value, str) or len(value) != 1:
|
|
45
|
+
raise ConfigurationError(f"Alignment column {name} must be one character.")
|
|
46
|
+
for name in ("query_position", "target_position"):
|
|
47
|
+
value = getattr(self, name)
|
|
48
|
+
if value is not None and (
|
|
49
|
+
isinstance(value, bool) or not isinstance(value, int) or value < 0
|
|
50
|
+
):
|
|
51
|
+
raise ConfigurationError(f"Alignment column {name} must be non-negative or None.")
|
|
52
|
+
if self.operation not in ("match", "mismatch", "insertion", "deletion"):
|
|
53
|
+
raise ConfigurationError("Alignment column operation is invalid.")
|
|
54
|
+
if self.operation == "insertion":
|
|
55
|
+
valid = (
|
|
56
|
+
self.query_symbol == "-"
|
|
57
|
+
and self.query_position is None
|
|
58
|
+
and self.target_symbol != "-"
|
|
59
|
+
and self.target_position is not None
|
|
60
|
+
)
|
|
61
|
+
elif self.operation == "deletion":
|
|
62
|
+
valid = (
|
|
63
|
+
self.target_symbol == "-"
|
|
64
|
+
and self.target_position is None
|
|
65
|
+
and self.query_symbol != "-"
|
|
66
|
+
and self.query_position is not None
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
valid = (
|
|
70
|
+
self.query_symbol != "-"
|
|
71
|
+
and self.target_symbol != "-"
|
|
72
|
+
and self.query_position is not None
|
|
73
|
+
and self.target_position is not None
|
|
74
|
+
and ((self.query_symbol == self.target_symbol) == (self.operation == "match"))
|
|
75
|
+
)
|
|
76
|
+
if not valid:
|
|
77
|
+
raise ConfigurationError("Alignment column fields conflict with its operation.")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True, slots=True)
|
|
81
|
+
class AlignmentResult:
|
|
82
|
+
"""Traceable global or local alignment using alignment-only '-' symbols."""
|
|
83
|
+
|
|
84
|
+
name: str
|
|
85
|
+
method: Literal["global", "local", "semi_global"]
|
|
86
|
+
algorithm_version: str
|
|
87
|
+
score: float
|
|
88
|
+
aligned_query: str
|
|
89
|
+
aligned_target: str
|
|
90
|
+
query_id: str | None
|
|
91
|
+
target_id: str | None
|
|
92
|
+
query_start: int
|
|
93
|
+
query_end: int
|
|
94
|
+
target_start: int
|
|
95
|
+
target_end: int
|
|
96
|
+
matches: int
|
|
97
|
+
mismatches: int
|
|
98
|
+
insertions: int
|
|
99
|
+
deletions: int
|
|
100
|
+
identity: float | None
|
|
101
|
+
query_coverage: float
|
|
102
|
+
target_coverage: float
|
|
103
|
+
columns: tuple[AlignmentColumn, ...]
|
|
104
|
+
parameters: FrozenDict
|
|
105
|
+
provenance: Provenance = field(default_factory=_alignment_provenance)
|
|
106
|
+
issues: tuple[Issue, ...] = ()
|
|
107
|
+
|
|
108
|
+
def __post_init__(self) -> None:
|
|
109
|
+
for name in ("name", "algorithm_version"):
|
|
110
|
+
value = getattr(self, name)
|
|
111
|
+
if not isinstance(value, str) or not value.strip():
|
|
112
|
+
raise ConfigurationError(f"Alignment {name} must be non-empty.")
|
|
113
|
+
if self.method not in ("global", "local", "semi_global"):
|
|
114
|
+
raise ConfigurationError("Alignment method must be global, local, or semi_global.")
|
|
115
|
+
for name in ("query_id", "target_id"):
|
|
116
|
+
value = getattr(self, name)
|
|
117
|
+
if value is not None and (not isinstance(value, str) or not value.strip()):
|
|
118
|
+
raise ConfigurationError(f"Alignment {name} must be non-empty or None.")
|
|
119
|
+
if (
|
|
120
|
+
isinstance(self.score, bool)
|
|
121
|
+
or not isinstance(self.score, (int, float))
|
|
122
|
+
or not math.isfinite(self.score)
|
|
123
|
+
):
|
|
124
|
+
raise ConfigurationError("Alignment score must be finite.")
|
|
125
|
+
if not isinstance(self.aligned_query, str) or not isinstance(self.aligned_target, str):
|
|
126
|
+
raise ConfigurationError("Aligned values must be strings.")
|
|
127
|
+
if len(self.aligned_query) != len(self.aligned_target):
|
|
128
|
+
raise ConfigurationError("Aligned strings must have equal lengths.")
|
|
129
|
+
if not isinstance(self.columns, tuple) or any(
|
|
130
|
+
not isinstance(column, AlignmentColumn) for column in self.columns
|
|
131
|
+
):
|
|
132
|
+
raise ConfigurationError("Alignment columns must be AlignmentColumn objects.")
|
|
133
|
+
if len(self.columns) != len(self.aligned_query) or any(
|
|
134
|
+
column.query_symbol != query_symbol or column.target_symbol != target_symbol
|
|
135
|
+
for column, query_symbol, target_symbol in zip(
|
|
136
|
+
self.columns, self.aligned_query, self.aligned_target, strict=True
|
|
137
|
+
)
|
|
138
|
+
):
|
|
139
|
+
raise ConfigurationError("Alignment columns must match aligned-string length.")
|
|
140
|
+
for name in ("query_start", "query_end", "target_start", "target_end"):
|
|
141
|
+
value = getattr(self, name)
|
|
142
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
143
|
+
raise ConfigurationError(f"Alignment {name} must be non-negative.")
|
|
144
|
+
if self.query_end < self.query_start or self.target_end < self.target_start:
|
|
145
|
+
raise ConfigurationError("Alignment source coordinates must be ordered.")
|
|
146
|
+
for name in ("matches", "mismatches", "insertions", "deletions"):
|
|
147
|
+
value = getattr(self, name)
|
|
148
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
149
|
+
raise ConfigurationError(f"Alignment {name} must be non-negative.")
|
|
150
|
+
counts = {
|
|
151
|
+
"match": self.matches,
|
|
152
|
+
"mismatch": self.mismatches,
|
|
153
|
+
"insertion": self.insertions,
|
|
154
|
+
"deletion": self.deletions,
|
|
155
|
+
}
|
|
156
|
+
if any(
|
|
157
|
+
sum(column.operation == operation for column in self.columns) != count
|
|
158
|
+
for operation, count in counts.items()
|
|
159
|
+
):
|
|
160
|
+
raise ConfigurationError("Alignment operation counts do not match its columns.")
|
|
161
|
+
if not 0.0 <= self.query_coverage <= 1.0 or not 0.0 <= self.target_coverage <= 1.0:
|
|
162
|
+
raise ConfigurationError("Alignment coverage must be between zero and one.")
|
|
163
|
+
if self.identity is not None and not 0.0 <= self.identity <= 1.0:
|
|
164
|
+
raise ConfigurationError("Alignment identity must be between zero and one.")
|
|
165
|
+
expected_identity = self.matches / len(self.columns) if self.columns else None
|
|
166
|
+
if self.identity is None:
|
|
167
|
+
if expected_identity is not None:
|
|
168
|
+
raise ConfigurationError("Non-empty alignment must report identity.")
|
|
169
|
+
elif expected_identity is None or not math.isclose(self.identity, expected_identity):
|
|
170
|
+
raise ConfigurationError("Alignment identity does not match its columns.")
|
|
171
|
+
if not isinstance(self.parameters, FrozenDict):
|
|
172
|
+
raise ConfigurationError("Alignment parameters must be FrozenDict.")
|
|
173
|
+
if not isinstance(self.provenance, Provenance):
|
|
174
|
+
raise ConfigurationError("Alignment provenance must be Provenance.")
|
|
175
|
+
if not isinstance(self.issues, tuple) or any(
|
|
176
|
+
not isinstance(issue, Issue) for issue in self.issues
|
|
177
|
+
):
|
|
178
|
+
raise ConfigurationError("Alignment issues must contain Issue objects.")
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def alignment_length(self) -> int:
|
|
182
|
+
return len(self.columns)
|
|
183
|
+
|
|
184
|
+
def to_dict(self) -> dict[str, Any]:
|
|
185
|
+
return cast(dict[str, Any], to_json_compatible(self))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
__all__ = ["AlignmentColumn", "AlignmentResult"]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Lazy optional-backend registration without import-time probing."""
|
|
2
|
+
|
|
3
|
+
from dnakit.backends.external import (
|
|
4
|
+
EXTERNAL_CLI_ADAPTERS,
|
|
5
|
+
BoundedCommandResult,
|
|
6
|
+
ExternalCLIAdapter,
|
|
7
|
+
execute_bounded_command,
|
|
8
|
+
)
|
|
9
|
+
from dnakit.backends.registry import BackendRegistration, BackendRegistry, backend_registry
|
|
10
|
+
from dnakit.core import BackendInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _probe_primer3_cli() -> BackendInfo:
|
|
14
|
+
from dnakit.thermodynamics import probe_primer3
|
|
15
|
+
|
|
16
|
+
return probe_primer3()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_primer3_cli() -> object:
|
|
20
|
+
from dnakit.thermodynamics import Primer3CLIAdapter
|
|
21
|
+
|
|
22
|
+
return Primer3CLIAdapter()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _probe_nupack() -> BackendInfo:
|
|
26
|
+
from dnakit.secondary_structure import probe_nupack
|
|
27
|
+
|
|
28
|
+
return probe_nupack()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _load_nupack() -> object:
|
|
32
|
+
from dnakit.secondary_structure import NupackAdapter
|
|
33
|
+
|
|
34
|
+
return NupackAdapter()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _external_registration(adapter: ExternalCLIAdapter) -> BackendRegistration:
|
|
38
|
+
def load() -> object:
|
|
39
|
+
return adapter
|
|
40
|
+
|
|
41
|
+
return BackendRegistration(
|
|
42
|
+
adapter.backend_id,
|
|
43
|
+
adapter.probe,
|
|
44
|
+
load,
|
|
45
|
+
source="builtin:external-cli",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _register_builtin_backends() -> None:
|
|
50
|
+
existing = set(backend_registry)
|
|
51
|
+
for registration in (
|
|
52
|
+
BackendRegistration(
|
|
53
|
+
"primer3-cli",
|
|
54
|
+
_probe_primer3_cli,
|
|
55
|
+
_load_primer3_cli,
|
|
56
|
+
source="builtin:thermodynamics",
|
|
57
|
+
),
|
|
58
|
+
BackendRegistration(
|
|
59
|
+
"nupack",
|
|
60
|
+
_probe_nupack,
|
|
61
|
+
_load_nupack,
|
|
62
|
+
source="builtin:secondary-structure",
|
|
63
|
+
),
|
|
64
|
+
*(_external_registration(adapter) for adapter in EXTERNAL_CLI_ADAPTERS),
|
|
65
|
+
):
|
|
66
|
+
if registration.backend_id not in existing:
|
|
67
|
+
backend_registry.register(registration)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_register_builtin_backends()
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
"EXTERNAL_CLI_ADAPTERS",
|
|
74
|
+
"BackendRegistration",
|
|
75
|
+
"BackendRegistry",
|
|
76
|
+
"BoundedCommandResult",
|
|
77
|
+
"ExternalCLIAdapter",
|
|
78
|
+
"backend_registry",
|
|
79
|
+
"execute_bounded_command",
|
|
80
|
+
]
|