dstack 0.19.16__py3-none-any.whl → 0.19.18__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.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/secrets.py +92 -0
- dstack/_internal/cli/main.py +2 -0
- dstack/_internal/cli/services/completion.py +5 -0
- dstack/_internal/cli/services/configurators/fleet.py +13 -1
- dstack/_internal/cli/services/configurators/run.py +59 -17
- dstack/_internal/cli/utils/secrets.py +25 -0
- dstack/_internal/core/backends/__init__.py +10 -4
- dstack/_internal/core/backends/aws/compute.py +237 -18
- dstack/_internal/core/backends/base/compute.py +20 -2
- dstack/_internal/core/backends/cudo/compute.py +23 -9
- dstack/_internal/core/backends/gcp/compute.py +13 -7
- dstack/_internal/core/backends/lambdalabs/compute.py +2 -1
- dstack/_internal/core/compatibility/fleets.py +12 -11
- dstack/_internal/core/compatibility/gateways.py +9 -8
- dstack/_internal/core/compatibility/logs.py +4 -3
- dstack/_internal/core/compatibility/runs.py +41 -17
- dstack/_internal/core/compatibility/volumes.py +9 -8
- dstack/_internal/core/errors.py +4 -0
- dstack/_internal/core/models/common.py +7 -0
- dstack/_internal/core/models/configurations.py +11 -0
- dstack/_internal/core/models/files.py +67 -0
- dstack/_internal/core/models/runs.py +14 -0
- dstack/_internal/core/models/secrets.py +9 -2
- dstack/_internal/core/services/diff.py +36 -3
- dstack/_internal/server/app.py +22 -0
- dstack/_internal/server/background/__init__.py +61 -37
- dstack/_internal/server/background/tasks/process_fleets.py +19 -3
- dstack/_internal/server/background/tasks/process_gateways.py +1 -1
- dstack/_internal/server/background/tasks/process_instances.py +13 -2
- dstack/_internal/server/background/tasks/process_placement_groups.py +4 -2
- dstack/_internal/server/background/tasks/process_running_jobs.py +123 -15
- dstack/_internal/server/background/tasks/process_runs.py +23 -7
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +36 -7
- dstack/_internal/server/background/tasks/process_terminating_jobs.py +5 -3
- dstack/_internal/server/background/tasks/process_volumes.py +2 -2
- dstack/_internal/server/migrations/versions/5f1707c525d2_add_filearchivemodel.py +39 -0
- dstack/_internal/server/migrations/versions/644b8a114187_add_secretmodel.py +49 -0
- dstack/_internal/server/models.py +33 -0
- dstack/_internal/server/routers/files.py +67 -0
- dstack/_internal/server/routers/secrets.py +57 -15
- dstack/_internal/server/schemas/files.py +5 -0
- dstack/_internal/server/schemas/runner.py +2 -0
- dstack/_internal/server/schemas/secrets.py +7 -11
- dstack/_internal/server/services/backends/__init__.py +1 -1
- dstack/_internal/server/services/files.py +91 -0
- dstack/_internal/server/services/fleets.py +5 -4
- dstack/_internal/server/services/gateways/__init__.py +4 -2
- dstack/_internal/server/services/jobs/__init__.py +19 -8
- dstack/_internal/server/services/jobs/configurators/base.py +25 -3
- dstack/_internal/server/services/jobs/configurators/dev.py +3 -3
- dstack/_internal/server/services/locking.py +101 -12
- dstack/_internal/server/services/proxy/repo.py +3 -0
- dstack/_internal/server/services/runner/client.py +8 -0
- dstack/_internal/server/services/runs.py +76 -47
- dstack/_internal/server/services/secrets.py +204 -0
- dstack/_internal/server/services/storage/base.py +21 -0
- dstack/_internal/server/services/storage/gcs.py +28 -6
- dstack/_internal/server/services/storage/s3.py +27 -9
- dstack/_internal/server/services/volumes.py +2 -2
- dstack/_internal/server/settings.py +19 -5
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-a4eafa74304e587d037c.js → main-d1ac2e8c38ed5f08a114.js} +86 -34
- dstack/_internal/server/statics/{main-a4eafa74304e587d037c.js.map → main-d1ac2e8c38ed5f08a114.js.map} +1 -1
- dstack/_internal/server/statics/{main-f53d6d0d42f8d61df1de.css → main-d58fc0460cb0eae7cb5c.css} +1 -1
- dstack/_internal/server/statics/static/media/google.b194b06fafd0a52aeb566922160ea514.svg +1 -0
- dstack/_internal/server/testing/common.py +50 -8
- dstack/_internal/settings.py +4 -0
- dstack/_internal/utils/files.py +69 -0
- dstack/_internal/utils/nested_list.py +47 -0
- dstack/_internal/utils/path.py +12 -4
- dstack/api/_public/runs.py +67 -7
- dstack/api/server/__init__.py +6 -0
- dstack/api/server/_files.py +18 -0
- dstack/api/server/_secrets.py +15 -15
- dstack/version.py +1 -1
- {dstack-0.19.16.dist-info → dstack-0.19.18.dist-info}/METADATA +13 -13
- {dstack-0.19.16.dist-info → dstack-0.19.18.dist-info}/RECORD +80 -67
- {dstack-0.19.16.dist-info → dstack-0.19.18.dist-info}/WHEEL +0 -0
- {dstack-0.19.16.dist-info → dstack-0.19.18.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.16.dist-info → dstack-0.19.18.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from dstack._internal.cli.commands import APIBaseCommand
|
|
4
|
+
from dstack._internal.cli.services.completion import SecretNameCompleter
|
|
5
|
+
from dstack._internal.cli.utils.common import (
|
|
6
|
+
confirm_ask,
|
|
7
|
+
console,
|
|
8
|
+
)
|
|
9
|
+
from dstack._internal.cli.utils.secrets import print_secrets_table
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SecretCommand(APIBaseCommand):
|
|
13
|
+
NAME = "secret"
|
|
14
|
+
DESCRIPTION = "Manage secrets"
|
|
15
|
+
|
|
16
|
+
def _register(self):
|
|
17
|
+
super()._register()
|
|
18
|
+
self._parser.set_defaults(subfunc=self._list)
|
|
19
|
+
subparsers = self._parser.add_subparsers(dest="action")
|
|
20
|
+
|
|
21
|
+
list_parser = subparsers.add_parser(
|
|
22
|
+
"list", help="List secrets", formatter_class=self._parser.formatter_class
|
|
23
|
+
)
|
|
24
|
+
list_parser.set_defaults(subfunc=self._list)
|
|
25
|
+
|
|
26
|
+
get_parser = subparsers.add_parser(
|
|
27
|
+
"get", help="Get secret value", formatter_class=self._parser.formatter_class
|
|
28
|
+
)
|
|
29
|
+
get_parser.add_argument(
|
|
30
|
+
"name",
|
|
31
|
+
help="The name of the secret",
|
|
32
|
+
).completer = SecretNameCompleter()
|
|
33
|
+
get_parser.set_defaults(subfunc=self._get)
|
|
34
|
+
|
|
35
|
+
set_parser = subparsers.add_parser(
|
|
36
|
+
"set", help="Set secret", formatter_class=self._parser.formatter_class
|
|
37
|
+
)
|
|
38
|
+
set_parser.add_argument(
|
|
39
|
+
"name",
|
|
40
|
+
help="The name of the secret",
|
|
41
|
+
)
|
|
42
|
+
set_parser.add_argument(
|
|
43
|
+
"value",
|
|
44
|
+
help="The value of the secret",
|
|
45
|
+
)
|
|
46
|
+
set_parser.set_defaults(subfunc=self._set)
|
|
47
|
+
|
|
48
|
+
delete_parser = subparsers.add_parser(
|
|
49
|
+
"delete",
|
|
50
|
+
help="Delete secrets",
|
|
51
|
+
formatter_class=self._parser.formatter_class,
|
|
52
|
+
)
|
|
53
|
+
delete_parser.add_argument(
|
|
54
|
+
"name",
|
|
55
|
+
help="The name of the secret",
|
|
56
|
+
).completer = SecretNameCompleter()
|
|
57
|
+
delete_parser.add_argument(
|
|
58
|
+
"-y", "--yes", help="Don't ask for confirmation", action="store_true"
|
|
59
|
+
)
|
|
60
|
+
delete_parser.set_defaults(subfunc=self._delete)
|
|
61
|
+
|
|
62
|
+
def _command(self, args: argparse.Namespace):
|
|
63
|
+
super()._command(args)
|
|
64
|
+
args.subfunc(args)
|
|
65
|
+
|
|
66
|
+
def _list(self, args: argparse.Namespace):
|
|
67
|
+
secrets = self.api.client.secrets.list(self.api.project)
|
|
68
|
+
print_secrets_table(secrets)
|
|
69
|
+
|
|
70
|
+
def _get(self, args: argparse.Namespace):
|
|
71
|
+
secret = self.api.client.secrets.get(self.api.project, name=args.name)
|
|
72
|
+
print_secrets_table([secret])
|
|
73
|
+
|
|
74
|
+
def _set(self, args: argparse.Namespace):
|
|
75
|
+
self.api.client.secrets.create_or_update(
|
|
76
|
+
self.api.project,
|
|
77
|
+
name=args.name,
|
|
78
|
+
value=args.value,
|
|
79
|
+
)
|
|
80
|
+
console.print("[grey58]OK[/]")
|
|
81
|
+
|
|
82
|
+
def _delete(self, args: argparse.Namespace):
|
|
83
|
+
if not args.yes and not confirm_ask(f"Delete the secret [code]{args.name}[/]?"):
|
|
84
|
+
console.print("\nExiting...")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
with console.status("Deleting secret..."):
|
|
88
|
+
self.api.client.secrets.delete(
|
|
89
|
+
project_name=self.api.project,
|
|
90
|
+
names=[args.name],
|
|
91
|
+
)
|
|
92
|
+
console.print("[grey58]OK[/]")
|
dstack/_internal/cli/main.py
CHANGED
|
@@ -17,6 +17,7 @@ from dstack._internal.cli.commands.metrics import MetricsCommand
|
|
|
17
17
|
from dstack._internal.cli.commands.offer import OfferCommand
|
|
18
18
|
from dstack._internal.cli.commands.project import ProjectCommand
|
|
19
19
|
from dstack._internal.cli.commands.ps import PsCommand
|
|
20
|
+
from dstack._internal.cli.commands.secrets import SecretCommand
|
|
20
21
|
from dstack._internal.cli.commands.server import ServerCommand
|
|
21
22
|
from dstack._internal.cli.commands.stats import StatsCommand
|
|
22
23
|
from dstack._internal.cli.commands.stop import StopCommand
|
|
@@ -72,6 +73,7 @@ def main():
|
|
|
72
73
|
MetricsCommand.register(subparsers)
|
|
73
74
|
ProjectCommand.register(subparsers)
|
|
74
75
|
PsCommand.register(subparsers)
|
|
76
|
+
SecretCommand.register(subparsers)
|
|
75
77
|
ServerCommand.register(subparsers)
|
|
76
78
|
StatsCommand.register(subparsers)
|
|
77
79
|
StopCommand.register(subparsers)
|
|
@@ -75,6 +75,11 @@ class GatewayNameCompleter(BaseAPINameCompleter):
|
|
|
75
75
|
return [r.name for r in api.client.gateways.list(api.project)]
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
class SecretNameCompleter(BaseAPINameCompleter):
|
|
79
|
+
def fetch_resource_names(self, api: Client) -> Iterable[str]:
|
|
80
|
+
return [r.name for r in api.client.secrets.list(api.project)]
|
|
81
|
+
|
|
82
|
+
|
|
78
83
|
class ProjectNameCompleter(BaseCompleter):
|
|
79
84
|
"""
|
|
80
85
|
Completer for local project names.
|
|
@@ -35,6 +35,7 @@ from dstack._internal.core.models.fleets import (
|
|
|
35
35
|
)
|
|
36
36
|
from dstack._internal.core.models.instances import InstanceAvailability, InstanceStatus, SSHKey
|
|
37
37
|
from dstack._internal.core.models.repos.base import Repo
|
|
38
|
+
from dstack._internal.core.services.diff import diff_models
|
|
38
39
|
from dstack._internal.utils.common import local_time
|
|
39
40
|
from dstack._internal.utils.logging import get_logger
|
|
40
41
|
from dstack._internal.utils.ssh import convert_ssh_key_to_pem, generate_public_key, pkey_from_str
|
|
@@ -82,7 +83,18 @@ class FleetConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
|
82
83
|
confirm_message += "Create the fleet?"
|
|
83
84
|
else:
|
|
84
85
|
action_message += f"Found fleet [code]{plan.spec.configuration.name}[/]."
|
|
85
|
-
|
|
86
|
+
diff = diff_models(
|
|
87
|
+
old=plan.current_resource.spec.configuration,
|
|
88
|
+
new=plan.spec.configuration,
|
|
89
|
+
ignore={
|
|
90
|
+
"ssh_config": {
|
|
91
|
+
"ssh_key": True,
|
|
92
|
+
"proxy_jump": {"ssh_key"},
|
|
93
|
+
"hosts": {"__all__": {"ssh_key": True, "proxy_jump": {"ssh_key"}}},
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
if not diff:
|
|
86
98
|
if command_args.yes and not command_args.force:
|
|
87
99
|
# --force is required only with --yes,
|
|
88
100
|
# otherwise we may ask for force apply interactively.
|
|
@@ -41,12 +41,13 @@ from dstack._internal.core.models.configurations import (
|
|
|
41
41
|
)
|
|
42
42
|
from dstack._internal.core.models.repos.base import Repo
|
|
43
43
|
from dstack._internal.core.models.resources import CPUSpec
|
|
44
|
-
from dstack._internal.core.models.runs import JobStatus, JobSubmission, RunStatus
|
|
44
|
+
from dstack._internal.core.models.runs import JobStatus, JobSubmission, RunSpec, RunStatus
|
|
45
45
|
from dstack._internal.core.services.configs import ConfigManager
|
|
46
46
|
from dstack._internal.core.services.diff import diff_models
|
|
47
47
|
from dstack._internal.utils.common import local_time
|
|
48
48
|
from dstack._internal.utils.interpolator import InterpolatorError, VariablesInterpolator
|
|
49
49
|
from dstack._internal.utils.logging import get_logger
|
|
50
|
+
from dstack._internal.utils.nested_list import NestedList, NestedListItem
|
|
50
51
|
from dstack.api._public.repos import get_ssh_keypair
|
|
51
52
|
from dstack.api._public.runs import Run
|
|
52
53
|
from dstack.api.utils import load_profile
|
|
@@ -102,25 +103,20 @@ class BaseRunConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
|
102
103
|
confirm_message = f"Submit the run [code]{conf.name}[/]?"
|
|
103
104
|
stop_run_name = None
|
|
104
105
|
if run_plan.current_resource is not None:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
changed_fields = list(diff.keys())
|
|
112
|
-
if run_plan.action == ApplyAction.UPDATE and len(changed_fields) > 0:
|
|
106
|
+
diff = render_run_spec_diff(
|
|
107
|
+
run_plan.get_effective_run_spec(),
|
|
108
|
+
run_plan.current_resource.run_spec,
|
|
109
|
+
)
|
|
110
|
+
if run_plan.action == ApplyAction.UPDATE and diff is not None:
|
|
113
111
|
console.print(
|
|
114
112
|
f"Active run [code]{conf.name}[/] already exists."
|
|
115
|
-
" Detected
|
|
116
|
-
f" {changed_fields}"
|
|
113
|
+
f" Detected changes that [code]can[/] be updated in-place:\n{diff}"
|
|
117
114
|
)
|
|
118
115
|
confirm_message = "Update the run?"
|
|
119
|
-
elif run_plan.action == ApplyAction.UPDATE and
|
|
116
|
+
elif run_plan.action == ApplyAction.UPDATE and diff is None:
|
|
120
117
|
stop_run_name = run_plan.current_resource.run_spec.run_name
|
|
121
118
|
console.print(
|
|
122
|
-
f"Active run [code]{conf.name}[/] already exists."
|
|
123
|
-
" Detected no configuration changes."
|
|
119
|
+
f"Active run [code]{conf.name}[/] already exists. Detected no changes."
|
|
124
120
|
)
|
|
125
121
|
if command_args.yes and not command_args.force:
|
|
126
122
|
console.print("Use --force to apply anyway.")
|
|
@@ -129,7 +125,8 @@ class BaseRunConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
|
129
125
|
elif not run_plan.current_resource.status.is_finished():
|
|
130
126
|
stop_run_name = run_plan.current_resource.run_spec.run_name
|
|
131
127
|
console.print(
|
|
132
|
-
f"Active run [code]{conf.name}[/] already exists
|
|
128
|
+
f"Active run [code]{conf.name}[/] already exists."
|
|
129
|
+
f" Detected changes that [error]cannot[/] be updated in-place:\n{diff}"
|
|
133
130
|
)
|
|
134
131
|
confirm_message = "Stop and override the run?"
|
|
135
132
|
|
|
@@ -398,9 +395,10 @@ class BaseRunConfigurator(ApplyEnvVarsConfiguratorMixin, BaseApplyConfigurator):
|
|
|
398
395
|
else:
|
|
399
396
|
has_amd_gpu = vendor == gpuhunt.AcceleratorVendor.AMD
|
|
400
397
|
has_tt_gpu = vendor == gpuhunt.AcceleratorVendor.TENSTORRENT
|
|
401
|
-
|
|
398
|
+
# When docker=True, the system uses Docker-in-Docker image, so no custom image is required
|
|
399
|
+
if has_amd_gpu and conf.image is None and conf.docker is not True:
|
|
402
400
|
raise ConfigurationError("`image` is required if `resources.gpu.vendor` is `amd`")
|
|
403
|
-
if has_tt_gpu and conf.image is None:
|
|
401
|
+
if has_tt_gpu and conf.image is None and conf.docker is not True:
|
|
404
402
|
raise ConfigurationError(
|
|
405
403
|
"`image` is required if `resources.gpu.vendor` is `tenstorrent`"
|
|
406
404
|
)
|
|
@@ -610,3 +608,47 @@ def _run_resubmitted(run: Run, current_job_submission: Optional[JobSubmission])
|
|
|
610
608
|
not run.status.is_finished()
|
|
611
609
|
and run._run.latest_job_submission.submitted_at > current_job_submission.submitted_at
|
|
612
610
|
)
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
def render_run_spec_diff(old_spec: RunSpec, new_spec: RunSpec) -> Optional[str]:
|
|
614
|
+
changed_spec_fields = list(diff_models(old_spec, new_spec))
|
|
615
|
+
if not changed_spec_fields:
|
|
616
|
+
return None
|
|
617
|
+
friendly_spec_field_names = {
|
|
618
|
+
"repo_id": "Repo ID",
|
|
619
|
+
"repo_code_hash": "Repo files",
|
|
620
|
+
"repo_data": "Repo state (branch, commit, or other)",
|
|
621
|
+
"ssh_key_pub": "Public SSH key",
|
|
622
|
+
}
|
|
623
|
+
nested_list = NestedList()
|
|
624
|
+
for spec_field in changed_spec_fields:
|
|
625
|
+
if spec_field == "merged_profile":
|
|
626
|
+
continue
|
|
627
|
+
elif spec_field == "configuration":
|
|
628
|
+
if type(old_spec.configuration) is not type(new_spec.configuration):
|
|
629
|
+
item = NestedListItem("Configuration type")
|
|
630
|
+
else:
|
|
631
|
+
item = NestedListItem(
|
|
632
|
+
"Configuration properties:",
|
|
633
|
+
children=[
|
|
634
|
+
NestedListItem(field)
|
|
635
|
+
for field in diff_models(old_spec.configuration, new_spec.configuration)
|
|
636
|
+
],
|
|
637
|
+
)
|
|
638
|
+
elif spec_field == "profile":
|
|
639
|
+
if type(old_spec.profile) is not type(new_spec.profile):
|
|
640
|
+
item = NestedListItem("Profile")
|
|
641
|
+
else:
|
|
642
|
+
item = NestedListItem(
|
|
643
|
+
"Profile properties:",
|
|
644
|
+
children=[
|
|
645
|
+
NestedListItem(field)
|
|
646
|
+
for field in diff_models(old_spec.profile, new_spec.profile)
|
|
647
|
+
],
|
|
648
|
+
)
|
|
649
|
+
elif spec_field in friendly_spec_field_names:
|
|
650
|
+
item = NestedListItem(friendly_spec_field_names[spec_field])
|
|
651
|
+
else:
|
|
652
|
+
item = NestedListItem(spec_field.replace("_", " ").capitalize())
|
|
653
|
+
nested_list.children.append(item)
|
|
654
|
+
return nested_list.render()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
|
|
5
|
+
from dstack._internal.cli.utils.common import add_row_from_dict, console
|
|
6
|
+
from dstack._internal.core.models.secrets import Secret
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def print_secrets_table(secrets: List[Secret]) -> None:
|
|
10
|
+
console.print(get_secrets_table(secrets))
|
|
11
|
+
console.print()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_secrets_table(secrets: List[Secret]) -> Table:
|
|
15
|
+
table = Table(box=None)
|
|
16
|
+
table.add_column("NAME", no_wrap=True)
|
|
17
|
+
table.add_column("VALUE")
|
|
18
|
+
|
|
19
|
+
for secret in secrets:
|
|
20
|
+
row = {
|
|
21
|
+
"NAME": secret.name,
|
|
22
|
+
"VALUE": secret.value or "*" * 6,
|
|
23
|
+
}
|
|
24
|
+
add_row_from_dict(table, row)
|
|
25
|
+
return table
|
|
@@ -9,18 +9,25 @@ from dstack._internal.core.backends.base.compute import (
|
|
|
9
9
|
)
|
|
10
10
|
from dstack._internal.core.backends.base.configurator import Configurator
|
|
11
11
|
from dstack._internal.core.backends.configurators import list_available_configurator_classes
|
|
12
|
+
from dstack._internal.core.backends.local.compute import LocalCompute
|
|
12
13
|
from dstack._internal.core.models.backends.base import BackendType
|
|
14
|
+
from dstack._internal.settings import LOCAL_BACKEND_ENABLED
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
def _get_backends_with_compute_feature(
|
|
16
18
|
configurator_classes: list[type[Configurator]],
|
|
17
19
|
compute_feature_class: type,
|
|
18
20
|
) -> list[BackendType]:
|
|
21
|
+
backend_types_and_computes = [
|
|
22
|
+
(configurator_class.TYPE, configurator_class.BACKEND_CLASS.COMPUTE_CLASS)
|
|
23
|
+
for configurator_class in configurator_classes
|
|
24
|
+
]
|
|
25
|
+
if LOCAL_BACKEND_ENABLED:
|
|
26
|
+
backend_types_and_computes.append((BackendType.LOCAL, LocalCompute))
|
|
19
27
|
backend_types = []
|
|
20
|
-
for
|
|
21
|
-
compute_class = configurator_class.BACKEND_CLASS.COMPUTE_CLASS
|
|
28
|
+
for backend_type, compute_class in backend_types_and_computes:
|
|
22
29
|
if issubclass(compute_class, compute_feature_class):
|
|
23
|
-
backend_types.append(
|
|
30
|
+
backend_types.append(backend_type)
|
|
24
31
|
return backend_types
|
|
25
32
|
|
|
26
33
|
|
|
@@ -28,7 +35,6 @@ _configurator_classes = list_available_configurator_classes()
|
|
|
28
35
|
|
|
29
36
|
|
|
30
37
|
# The following backend lists do not include unavailable backends (i.e. backends missing deps).
|
|
31
|
-
# TODO: Add LocalBackend to lists if it's enabled
|
|
32
38
|
BACKENDS_WITH_CREATE_INSTANCE_SUPPORT = _get_backends_with_compute_feature(
|
|
33
39
|
configurator_classes=_configurator_classes,
|
|
34
40
|
compute_feature_class=ComputeWithCreateInstanceSupport,
|