arkitekt-next 0.7.8__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 arkitekt-next might be problematic. Click here for more details.
- arkitekt_next/__init__.py +43 -0
- arkitekt_next/apps/__init__.py +3 -0
- arkitekt_next/apps/easy.py +99 -0
- arkitekt_next/apps/next.py +40 -0
- arkitekt_next/apps/qt.py +97 -0
- arkitekt_next/apps/service/__init__.py +3 -0
- arkitekt_next/apps/service/fakts.py +88 -0
- arkitekt_next/apps/service/fakts_next.py +79 -0
- arkitekt_next/apps/service/fakts_qt.py +82 -0
- arkitekt_next/apps/service/fluss_next.py +31 -0
- arkitekt_next/apps/service/grant_registry.py +27 -0
- arkitekt_next/apps/service/herre.py +24 -0
- arkitekt_next/apps/service/herre_qt.py +57 -0
- arkitekt_next/apps/service/kabinet.py +31 -0
- arkitekt_next/apps/service/mikro_next.py +81 -0
- arkitekt_next/apps/service/rekuest_next.py +53 -0
- arkitekt_next/apps/service/unlok_next.py +32 -0
- arkitekt_next/apps/types.py +53 -0
- arkitekt_next/builders.py +264 -0
- arkitekt_next/cli/__init__.py +0 -0
- arkitekt_next/cli/commands/call/__init__.py +0 -0
- arkitekt_next/cli/commands/call/local.py +132 -0
- arkitekt_next/cli/commands/call/main.py +22 -0
- arkitekt_next/cli/commands/call/remote.py +90 -0
- arkitekt_next/cli/commands/gen/__init__.py +0 -0
- arkitekt_next/cli/commands/gen/compile.py +45 -0
- arkitekt_next/cli/commands/gen/init.py +122 -0
- arkitekt_next/cli/commands/gen/main.py +29 -0
- arkitekt_next/cli/commands/gen/watch.py +32 -0
- arkitekt_next/cli/commands/init/__init__.py +0 -0
- arkitekt_next/cli/commands/init/main.py +194 -0
- arkitekt_next/cli/commands/inspect/__init__.py +0 -0
- arkitekt_next/cli/commands/inspect/definitions.py +53 -0
- arkitekt_next/cli/commands/inspect/main.py +22 -0
- arkitekt_next/cli/commands/inspect/variables.py +92 -0
- arkitekt_next/cli/commands/manifest/__init__.py +0 -0
- arkitekt_next/cli/commands/manifest/inspect.py +42 -0
- arkitekt_next/cli/commands/manifest/main.py +25 -0
- arkitekt_next/cli/commands/manifest/scopes.py +155 -0
- arkitekt_next/cli/commands/manifest/version.py +147 -0
- arkitekt_next/cli/commands/manifest/wizard.py +94 -0
- arkitekt_next/cli/commands/port/__init__.py +0 -0
- arkitekt_next/cli/commands/port/build.py +231 -0
- arkitekt_next/cli/commands/port/init.py +82 -0
- arkitekt_next/cli/commands/port/main.py +31 -0
- arkitekt_next/cli/commands/port/publish.py +102 -0
- arkitekt_next/cli/commands/port/stage.py +59 -0
- arkitekt_next/cli/commands/port/utils.py +47 -0
- arkitekt_next/cli/commands/port/validate.py +78 -0
- arkitekt_next/cli/commands/port/wizard.py +329 -0
- arkitekt_next/cli/commands/run/__init__.py +0 -0
- arkitekt_next/cli/commands/run/dev.py +349 -0
- arkitekt_next/cli/commands/run/main.py +22 -0
- arkitekt_next/cli/commands/run/prod.py +57 -0
- arkitekt_next/cli/commands/run/utils.py +10 -0
- arkitekt_next/cli/commands/server/__init__.py +0 -0
- arkitekt_next/cli/commands/server/down.py +56 -0
- arkitekt_next/cli/commands/server/init.py +74 -0
- arkitekt_next/cli/commands/server/inspect.py +59 -0
- arkitekt_next/cli/commands/server/main.py +33 -0
- arkitekt_next/cli/commands/server/open.py +66 -0
- arkitekt_next/cli/commands/server/remove.py +60 -0
- arkitekt_next/cli/commands/server/stop.py +56 -0
- arkitekt_next/cli/commands/server/up.py +70 -0
- arkitekt_next/cli/commands/server/utils.py +33 -0
- arkitekt_next/cli/configs/base.yaml +867 -0
- arkitekt_next/cli/constants.py +63 -0
- arkitekt_next/cli/dockerfiles/vanilla.dockerfile +8 -0
- arkitekt_next/cli/errors.py +4 -0
- arkitekt_next/cli/inspect.py +1 -0
- arkitekt_next/cli/io.py +255 -0
- arkitekt_next/cli/main.py +83 -0
- arkitekt_next/cli/options.py +166 -0
- arkitekt_next/cli/schemas/fluss.schema.graphql +2446 -0
- arkitekt_next/cli/schemas/gucker.schema.graphql +8908 -0
- arkitekt_next/cli/schemas/kabinet.schema.graphql +515 -0
- arkitekt_next/cli/schemas/kluster.schema.graphql +109 -0
- arkitekt_next/cli/schemas/konviktion.schema.graphql +70 -0
- arkitekt_next/cli/schemas/kuay.schema.graphql +356 -0
- arkitekt_next/cli/schemas/mikro.schema.graphql +8908 -0
- arkitekt_next/cli/schemas/mikro_next.schema.graphql +1639 -0
- arkitekt_next/cli/schemas/napari.schema.graphql +8908 -0
- arkitekt_next/cli/schemas/omero_ark.schema.graphql +100 -0
- arkitekt_next/cli/schemas/port.schema.graphql +356 -0
- arkitekt_next/cli/schemas/rekuest.schema.graphql +4630 -0
- arkitekt_next/cli/schemas/rekuest_next.schema.graphql +1159 -0
- arkitekt_next/cli/schemas/unlok.schema.graphql +1013 -0
- arkitekt_next/cli/templates/filter.py +26 -0
- arkitekt_next/cli/templates/simple.py +67 -0
- arkitekt_next/cli/texts.py +20 -0
- arkitekt_next/cli/types.py +365 -0
- arkitekt_next/cli/ui.py +111 -0
- arkitekt_next/cli/utils.py +15 -0
- arkitekt_next/cli/validators.py +17 -0
- arkitekt_next/cli/vars.py +39 -0
- arkitekt_next/cli/versions/v1.yaml +1 -0
- arkitekt_next/constants.py +6 -0
- arkitekt_next/model.py +110 -0
- arkitekt_next/qt/__init__.py +9 -0
- arkitekt_next/qt/assets/dark/gear.png +0 -0
- arkitekt_next/qt/assets/dark/green pulse.gif +0 -0
- arkitekt_next/qt/assets/dark/orange pulse.gif +0 -0
- arkitekt_next/qt/assets/dark/pink pulse.gif +0 -0
- arkitekt_next/qt/assets/dark/red pulse.gif +0 -0
- arkitekt_next/qt/assets/light/gear.png +0 -0
- arkitekt_next/qt/assets/light/green pulse.gif +0 -0
- arkitekt_next/qt/assets/light/orange pulse.gif +0 -0
- arkitekt_next/qt/assets/light/pink pulse.gif +0 -0
- arkitekt_next/qt/assets/light/red pulse.gif +0 -0
- arkitekt_next/qt/magic_bar.py +545 -0
- arkitekt_next/qt/utils.py +30 -0
- arkitekt_next/service_registry.py +51 -0
- arkitekt_next/tqdm.py +43 -0
- arkitekt_next/utils.py +38 -0
- arkitekt_next-0.7.8.dist-info/LICENSE +21 -0
- arkitekt_next-0.7.8.dist-info/METADATA +155 -0
- arkitekt_next-0.7.8.dist-info/RECORD +119 -0
- arkitekt_next-0.7.8.dist-info/WHEEL +4 -0
- arkitekt_next-0.7.8.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from arkitekt_next_next import register
|
|
2
|
+
from mikro.api.schema import RepresentationFragment, from_xarray
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@register
|
|
6
|
+
def max_intensity_projection(image: RepresentationFragment) -> RepresentationFragment:
|
|
7
|
+
"""Z-Project the Maximum Intensity
|
|
8
|
+
|
|
9
|
+
This function projects the maximum intensity of the input image
|
|
10
|
+
along the z-axis
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
image : RepresentationFragment
|
|
15
|
+
The input image
|
|
16
|
+
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
RepresentationFragment
|
|
20
|
+
The projected image
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
image = image.data.max(dim="z")
|
|
24
|
+
return from_xarray(
|
|
25
|
+
image, name="Max Intensity Projection" + image.name, origins=[image]
|
|
26
|
+
)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from arkitekt_next_next import register
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@register
|
|
6
|
+
def generate_n_string(n: int = 10, timeout: int = 2) -> str:
|
|
7
|
+
"""Generate N Strings
|
|
8
|
+
|
|
9
|
+
This function generates {{n}} strings with a {{timeout}} ms timeout between each string
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
n : int, optional
|
|
15
|
+
The number of iterations, by default 10
|
|
16
|
+
timeout : int, optional
|
|
17
|
+
The timeout, by default 2
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
str
|
|
22
|
+
A string with Hello {n}
|
|
23
|
+
"""
|
|
24
|
+
for i in range(n):
|
|
25
|
+
print(i)
|
|
26
|
+
time.sleep(timeout)
|
|
27
|
+
yield f"Hello {i}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@register
|
|
31
|
+
def append_world(hello: str) -> str:
|
|
32
|
+
"""Append World
|
|
33
|
+
|
|
34
|
+
This function appends world to the input string
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
hello : str
|
|
39
|
+
The input string
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
str
|
|
44
|
+
{{hello}} World
|
|
45
|
+
""" """"""
|
|
46
|
+
return hello + " World"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@register
|
|
50
|
+
def print_string(input: str) -> str:
|
|
51
|
+
"""Print String
|
|
52
|
+
|
|
53
|
+
This function prints the input string to
|
|
54
|
+
the console
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
input : str
|
|
59
|
+
The input string
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
str
|
|
64
|
+
The printed string
|
|
65
|
+
"""
|
|
66
|
+
print(input)
|
|
67
|
+
return input
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
WELCOME_MESSAGE = (
|
|
2
|
+
"Welcome to ArkitektNext. ArkitektNext is a bioimage analysis framework for building"
|
|
3
|
+
" beautiful and fast interfaces around your python code. It is buid on top of "
|
|
4
|
+
"composable packages like mikro, fluss and rekuest, which allow you to build simple"
|
|
5
|
+
" apps with ease."
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
LOGO = """
|
|
10
|
+
_ _ _ _ _
|
|
11
|
+
__ _ _ __| | _(_) |_ ___| | _| |_
|
|
12
|
+
/ _` | '__| |/ / | __/ _ \ |/ / __|
|
|
13
|
+
| (_| | | | <| | || __/ <| |_
|
|
14
|
+
\__,_|_| |_|\_\_|\__\___|_|\_\\__|
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
ERROR_EPILOGUE = (
|
|
19
|
+
"To find out more, visit [link=https://arkitekt_next.live]https://arkitekt_next.live[/link]"
|
|
20
|
+
)
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field, validator
|
|
2
|
+
import datetime
|
|
3
|
+
from typing import List, Optional, Union, Literal
|
|
4
|
+
from enum import Enum
|
|
5
|
+
import semver
|
|
6
|
+
import uuid
|
|
7
|
+
from rekuest.api.schema import DefinitionInput
|
|
8
|
+
from string import Formatter
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
ALLOWED_BUILDER_KEYS = [
|
|
12
|
+
"tag",
|
|
13
|
+
"dockerfile",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Framework(str, Enum):
|
|
18
|
+
"""Do we support other frameworks?"""
|
|
19
|
+
|
|
20
|
+
VANILLA = "vanilla"
|
|
21
|
+
TENSORFLOW = "tensorflow"
|
|
22
|
+
PYTORCH = "pytorch"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Requirement(str, Enum):
|
|
26
|
+
""" """
|
|
27
|
+
|
|
28
|
+
GPU = "gpu"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Packager(str, Enum):
|
|
32
|
+
CONDA = "conda"
|
|
33
|
+
POETRY = "poetry"
|
|
34
|
+
PIP = "pip"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Manifest(BaseModel):
|
|
38
|
+
identifier: str
|
|
39
|
+
version: str
|
|
40
|
+
author: str
|
|
41
|
+
logo: Optional[str]
|
|
42
|
+
entrypoint: str
|
|
43
|
+
scopes: List[str]
|
|
44
|
+
requirements: List[Requirement] = Field(default_factory=list)
|
|
45
|
+
created_at: datetime.datetime = Field(default_factory=datetime.datetime.now)
|
|
46
|
+
|
|
47
|
+
@validator("version", pre=True)
|
|
48
|
+
def version_must_be_semver(cls, v) -> str:
|
|
49
|
+
"""Checks that the version is a valid semver version"""
|
|
50
|
+
if isinstance(v, str):
|
|
51
|
+
try:
|
|
52
|
+
semver.VersionInfo.parse(v)
|
|
53
|
+
except ValueError:
|
|
54
|
+
raise ValueError("Version must be a valid semver version")
|
|
55
|
+
return str(v)
|
|
56
|
+
|
|
57
|
+
def to_console_string(self):
|
|
58
|
+
return f"📦 {self.identifier} ({self.version}) by {self.author}"
|
|
59
|
+
|
|
60
|
+
def to_builder_dict(self):
|
|
61
|
+
return {
|
|
62
|
+
"identifier": self.identifier,
|
|
63
|
+
"version": self.version,
|
|
64
|
+
"logo": self.logo,
|
|
65
|
+
"scopes": self.scopes,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class Config:
|
|
69
|
+
validate_assignment = True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SelectorType(str, Enum):
|
|
73
|
+
RAM = "ram"
|
|
74
|
+
CPU = "cpu"
|
|
75
|
+
GPU = "gpu"
|
|
76
|
+
LABEL = "label"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class BaseSelector(BaseModel):
|
|
80
|
+
"""A selector is a way to describe a flavours preference for a
|
|
81
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
required: bool = True
|
|
85
|
+
|
|
86
|
+
class Config:
|
|
87
|
+
extra = "forbid"
|
|
88
|
+
|
|
89
|
+
def build_docker_params(self) -> List[str]:
|
|
90
|
+
"""Builds the docker params for this selector
|
|
91
|
+
|
|
92
|
+
Should return a list of strings that can be used as docker params
|
|
93
|
+
If the selector is not required, it should return an empty list
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
List[str]
|
|
98
|
+
The docker params for this selector
|
|
99
|
+
"""
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
def build_arkitekt_next_params(self) -> List[str]:
|
|
103
|
+
"""Builds the arkitekt_next params for this selector
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
List[str]
|
|
108
|
+
The docker params for this selector
|
|
109
|
+
"""
|
|
110
|
+
return []
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class RAMSelector(BaseSelector):
|
|
114
|
+
"""A selector is a way to describe a flavours preference for a
|
|
115
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
type: Literal["ram"]
|
|
119
|
+
min: int
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class CPUSelector(BaseSelector):
|
|
123
|
+
"""A selector is a way to describe a flavours preference for a
|
|
124
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
type: Literal["cpu"]
|
|
128
|
+
min: int
|
|
129
|
+
frequency: Optional[int] = None
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class CudaSelector(BaseSelector):
|
|
133
|
+
"""A selector is a way to describe a flavours preference for a
|
|
134
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
type: Literal["cuda"]
|
|
138
|
+
frequency: Optional[int] = Field(default=None, description="The frequency in MHz")
|
|
139
|
+
memory: Optional[int] = Field(default=None, description="The memory in MB")
|
|
140
|
+
architecture: Optional[str] = Field(
|
|
141
|
+
default=None, description="The architecture of the GPU"
|
|
142
|
+
)
|
|
143
|
+
compute_capability: str = Field(
|
|
144
|
+
default="3.5", description="The minimum compute capability"
|
|
145
|
+
)
|
|
146
|
+
cuda_cores: Optional[int] = None
|
|
147
|
+
cuda_version: str = Field(default="10.2", description="The minimum cuda version")
|
|
148
|
+
|
|
149
|
+
def build_docker_params(self) -> List[str]:
|
|
150
|
+
"""Builds the docker params for this selector
|
|
151
|
+
|
|
152
|
+
Should return a list of strings that can be used as docker params
|
|
153
|
+
If the selector is not required, it should return an empty list
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
List[str]
|
|
158
|
+
The docker params for this selector
|
|
159
|
+
"""
|
|
160
|
+
return [
|
|
161
|
+
"--gpus",
|
|
162
|
+
"all",
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class RocmSelector(BaseSelector):
|
|
167
|
+
"""A selector is a way to describe a flavours preference for a
|
|
168
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
type: Literal["rocm"]
|
|
172
|
+
min: int
|
|
173
|
+
frequency: Optional[int] = None
|
|
174
|
+
memory: Optional[int] = None
|
|
175
|
+
architecture: Optional[str] = None
|
|
176
|
+
compute_capability: Optional[str] = None
|
|
177
|
+
cuda_cores: Optional[int] = None
|
|
178
|
+
cuda_version: Optional[str] = None
|
|
179
|
+
|
|
180
|
+
def build_docker_params(self) -> List[str]:
|
|
181
|
+
"""Builds the docker params for this selector"""
|
|
182
|
+
|
|
183
|
+
return [
|
|
184
|
+
"--device=/dev/kfd",
|
|
185
|
+
"--device=/dev/dri",
|
|
186
|
+
"--group-add",
|
|
187
|
+
"video",
|
|
188
|
+
"--cap-add=SYS_PTRACE",
|
|
189
|
+
"--security-opt",
|
|
190
|
+
"seccomp=unconfined",
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class LabelSelector(BaseSelector):
|
|
195
|
+
"""A selector is a way to describe a flavours preference for a
|
|
196
|
+
compute node. It contains the node_id, the selector and the flavour_id.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
type: Literal["label"]
|
|
200
|
+
key: str
|
|
201
|
+
value: str
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class ServiceSelector(BaseSelector):
|
|
205
|
+
"""A service selector is a way to describe a flavours preference for a
|
|
206
|
+
service. It contains the service_id,
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
type: Literal["service"]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
Selector = Union[
|
|
213
|
+
RAMSelector, CPUSelector, CudaSelector, RocmSelector, LabelSelector, ServiceSelector
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Inspection(BaseModel):
|
|
218
|
+
definitions: List[DefinitionInput]
|
|
219
|
+
size: int
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class Flavour(BaseModel):
|
|
223
|
+
selectors: List[Selector]
|
|
224
|
+
description: str = Field(default="")
|
|
225
|
+
dockerfile: str = Field(default="Dockerfile")
|
|
226
|
+
build_command: List[str] = Field(
|
|
227
|
+
default_factory=lambda: [
|
|
228
|
+
"docker",
|
|
229
|
+
"build",
|
|
230
|
+
"-t",
|
|
231
|
+
"{tag}",
|
|
232
|
+
"-f",
|
|
233
|
+
"{dockerfile}",
|
|
234
|
+
".",
|
|
235
|
+
]
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
@validator("build_command", each_item=True, always=True)
|
|
239
|
+
def check_valid_template_name(cls, v):
|
|
240
|
+
"""Checks that the template name is valid"""
|
|
241
|
+
for literal_text, field_name, format_spec, conversion in Formatter().parse(v):
|
|
242
|
+
if field_name is not None:
|
|
243
|
+
assert (
|
|
244
|
+
field_name in ALLOWED_BUILDER_KEYS
|
|
245
|
+
), f"Invalid template key {field_name}. Allowed keys are {ALLOWED_BUILDER_KEYS}"
|
|
246
|
+
|
|
247
|
+
return v
|
|
248
|
+
|
|
249
|
+
def generate_build_command(self, tag: str, relative_dir: str):
|
|
250
|
+
"""Generates the build command for this flavour"""
|
|
251
|
+
|
|
252
|
+
dockerfile = os.path.join(relative_dir, self.dockerfile)
|
|
253
|
+
|
|
254
|
+
return [v.format(tag=tag, dockerfile=dockerfile) for v in self.build_command]
|
|
255
|
+
|
|
256
|
+
def check_relative_paths(self, flavour_folder: str):
|
|
257
|
+
"""Checks that the paths are relative to the flavour folder"""
|
|
258
|
+
|
|
259
|
+
dockerfile_path = os.path.join(flavour_folder, self.dockerfile)
|
|
260
|
+
|
|
261
|
+
if not os.path.exists(dockerfile_path):
|
|
262
|
+
raise Exception(
|
|
263
|
+
f"Could not find Dockerfile {self.dockerfile} in flavour {flavour_folder}"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class Deployment(BaseModel):
|
|
268
|
+
"""A deployment is a Release of a Build.
|
|
269
|
+
It contains the build_id, the manifest, the builder, the definitions, the image and the deployed_at timestamp.
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
deployment_run: str = Field(
|
|
276
|
+
default_factory=lambda: str(uuid.uuid4()),
|
|
277
|
+
description="The unique identifier of the deployment run",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
deployment_id: str = Field(
|
|
281
|
+
default_factory=lambda: str(uuid.uuid4()),
|
|
282
|
+
description="The unique identifier of the deployment",
|
|
283
|
+
)
|
|
284
|
+
manifest: Manifest = Field(description="The manifest of the app that was deployed")
|
|
285
|
+
selectors: List[Selector] = Field(
|
|
286
|
+
description="The selectors are used to place this image on the nodes",
|
|
287
|
+
default_factory=list,
|
|
288
|
+
)
|
|
289
|
+
flavour: str = Field(
|
|
290
|
+
description="The flavour that was used to build this deployment",
|
|
291
|
+
default="vanilla",
|
|
292
|
+
)
|
|
293
|
+
build_id: str = Field(
|
|
294
|
+
description="The build_id of the build that was deployed. Is referenced in the build.yaml file."
|
|
295
|
+
)
|
|
296
|
+
inspection: Optional[Inspection] = Field(
|
|
297
|
+
description="The inspection of the build that was deployed"
|
|
298
|
+
)
|
|
299
|
+
image: str = Field(
|
|
300
|
+
description="The docker image that was built for this deployment"
|
|
301
|
+
)
|
|
302
|
+
deployed_at: datetime.datetime = Field(
|
|
303
|
+
default_factory=datetime.datetime.now,
|
|
304
|
+
description="The timestamp of the deployment",
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class DeploymentsConfigFile(BaseModel):
|
|
309
|
+
"""The ConfigFile is a pydantic model that represents the deployments.yaml file
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
BaseModel : _type_
|
|
315
|
+
_description_
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
deployments: List[Deployment] = []
|
|
319
|
+
latest_deployment_run: Optional[str] = None
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
class Build(BaseModel):
|
|
323
|
+
build_run: str
|
|
324
|
+
build_id: str
|
|
325
|
+
inspection: Optional[Inspection] = None
|
|
326
|
+
description: str = Field(default="")
|
|
327
|
+
selectors: List[Selector] = Field(default_factory=list)
|
|
328
|
+
flavour: str = Field(default="vanilla")
|
|
329
|
+
manifest: Manifest
|
|
330
|
+
build_at: datetime.datetime = Field(default_factory=datetime.datetime.now)
|
|
331
|
+
base_docker_command: List[str] = Field(
|
|
332
|
+
default_factory=lambda: ["docker", "run", "-it", "--net", "host"]
|
|
333
|
+
)
|
|
334
|
+
base_arkitekt_next_command: List[str] = Field(
|
|
335
|
+
default_factory=lambda: ["arkitekt_next", "run", "prod", "--headless"]
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def build_docker_command(self) -> List[str]:
|
|
339
|
+
"""Builds the docker command for this build"""
|
|
340
|
+
|
|
341
|
+
base_command = self.base_docker_command
|
|
342
|
+
|
|
343
|
+
for selector in self.selectors:
|
|
344
|
+
base_command = base_command + selector.build_docker_params()
|
|
345
|
+
|
|
346
|
+
base_command = base_command + [self.build_id]
|
|
347
|
+
|
|
348
|
+
return base_command
|
|
349
|
+
|
|
350
|
+
def build_arkitekt_next_command(self, fakts_url: str):
|
|
351
|
+
"""Builds the arkitekt_next command for this build"""
|
|
352
|
+
|
|
353
|
+
base_command = self.base_arkitekt_next_command
|
|
354
|
+
|
|
355
|
+
for selector in self.selectors:
|
|
356
|
+
base_command = base_command + selector.build_arkitekt_next_params()
|
|
357
|
+
|
|
358
|
+
base_command = base_command + ["--url", fakts_url]
|
|
359
|
+
|
|
360
|
+
return base_command
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class BuildsConfigFile(BaseModel):
|
|
364
|
+
builds: List[Build] = Field(default_factory=list)
|
|
365
|
+
latest_build_run: Optional[str] = None
|
arkitekt_next/cli/ui.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from rich.console import Group
|
|
2
|
+
from rich.tree import Tree
|
|
3
|
+
from rich.panel import Panel
|
|
4
|
+
from arkitekt_next.apps import App
|
|
5
|
+
from typing import MutableSet, Tuple, Any, Dict
|
|
6
|
+
import os
|
|
7
|
+
from .texts import LOGO, WELCOME_MESSAGE
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def construct_codegen_welcome_panel() -> Panel:
|
|
11
|
+
md = Panel(
|
|
12
|
+
LOGO
|
|
13
|
+
+ "[white]"
|
|
14
|
+
+ WELCOME_MESSAGE
|
|
15
|
+
+ "\n\n"
|
|
16
|
+
+ "[bold green]Let's setup your codegen environment",
|
|
17
|
+
title="Welcome to ArkitektNext Codegen",
|
|
18
|
+
title_align="center",
|
|
19
|
+
border_style="green",
|
|
20
|
+
style="green",
|
|
21
|
+
)
|
|
22
|
+
return md
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def construct_changes_group(changes: MutableSet[Tuple[Any, str]]) -> Group:
|
|
26
|
+
"""Construct a rich panel group for the detected changes
|
|
27
|
+
|
|
28
|
+
This panel is displayed if the app has detected changes before
|
|
29
|
+
running the app. This can be caused by a change in the code or
|
|
30
|
+
a change in the environment (e.g. a new package was installed)"""
|
|
31
|
+
panel_header = "Detected changes\n"
|
|
32
|
+
|
|
33
|
+
actor_tree = Tree("Changes in", style="white not bold")
|
|
34
|
+
panel_group = Group(panel_header, actor_tree)
|
|
35
|
+
for change, path in changes:
|
|
36
|
+
actor_tree.add(os.path.normpath(path))
|
|
37
|
+
|
|
38
|
+
return panel_group
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def construct_app_group(app: App) -> Group:
|
|
42
|
+
"""Construct a rich panel group for the app
|
|
43
|
+
|
|
44
|
+
It displays the registered definitions
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
app : App
|
|
49
|
+
The app to construct the panel for
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
Group
|
|
54
|
+
A rich panel group
|
|
55
|
+
"""
|
|
56
|
+
panel_header = f"Running App \n\n{app.manifest.identifier}:{app.manifest.version}\n"
|
|
57
|
+
|
|
58
|
+
actor_tree = Tree("Registered Definitions", style="white not bold")
|
|
59
|
+
panel_group = Group(panel_header, actor_tree)
|
|
60
|
+
|
|
61
|
+
rekuest = app.services.get("rekuest")
|
|
62
|
+
|
|
63
|
+
for key, extension in rekuest.agent.extensions.items():
|
|
64
|
+
tree = actor_tree.add(key)
|
|
65
|
+
for template in extension.definition_registry.templates.values():
|
|
66
|
+
tree.add(template.interface + "-" + template.definition.name)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
return panel_group
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def construct_leaking_group(variables: Dict[str, Any]) -> Group:
|
|
73
|
+
"""Construct a rich panel group for the leaking variables
|
|
74
|
+
|
|
75
|
+
This panel is displayed if the app has leaking variables
|
|
76
|
+
and is therefore considered to be not safe to run
|
|
77
|
+
as an ArkitektNext plugin
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
variables : Dict[str, Any]
|
|
82
|
+
The leaking variables
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Group
|
|
87
|
+
The rich panel group
|
|
88
|
+
"""
|
|
89
|
+
panel_header = (
|
|
90
|
+
"Detected leaking Variables\n\n"
|
|
91
|
+
"[white]Your app is leaking variables. "
|
|
92
|
+
"Leaking variables can cause memory leaks and other issues."
|
|
93
|
+
"Please make sure you are not defining variables in the"
|
|
94
|
+
"global scope (outside of functions). "
|
|
95
|
+
"If you want to define constants please make them all UPPERCASE\n\n"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
actor_tree = Tree("Leaking Variables", style="white not bold")
|
|
99
|
+
panel_group = Group(panel_header, actor_tree)
|
|
100
|
+
for key, value in variables.items():
|
|
101
|
+
actor_tree.add("key: " + key + " value: " + str(value))
|
|
102
|
+
|
|
103
|
+
return panel_group
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def construct_run_panel(app: App) -> Panel:
|
|
107
|
+
app_group = construct_app_group(app)
|
|
108
|
+
|
|
109
|
+
return Panel(
|
|
110
|
+
app_group, style="bold green", border_style="green", title="ArkitektNext Run"
|
|
111
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from importlib import import_module
|
|
2
|
+
from typing import Callable
|
|
3
|
+
from arkitekt_next.apps.types import App
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def import_builder(builder: str) -> Callable[..., App]:
|
|
8
|
+
module_path, function_name = builder.rsplit(".", 1)
|
|
9
|
+
module = import_module(module_path)
|
|
10
|
+
function = getattr(module, function_name)
|
|
11
|
+
return function
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def build_relative_dir(*paths):
|
|
15
|
+
return os.path.join(os.path.dirname(os.path.abspath(__file__)), *paths)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import semver
|
|
2
|
+
from .errors import ValidationError
|
|
3
|
+
import rich_click as click
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def is_valid_semver(param: str, loaded=False) -> semver.VersionInfo:
|
|
7
|
+
"""Checks if the param is a valid semver version and returns it as a semver object"""
|
|
8
|
+
if not semver.Version.is_valid(param):
|
|
9
|
+
if loaded:
|
|
10
|
+
raise click.ClickException(
|
|
11
|
+
"Manifest version incorrect, please update your manifest to a valid semver version [link=https://semver.org]semver[/link]."
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
raise ValidationError(
|
|
15
|
+
"ArkitektNext versions need to follow semantic versioning. Please choose a correct format (examples: 0.0.0, 0.1.0, 0.0.0-alpha.1)"
|
|
16
|
+
)
|
|
17
|
+
return semver.Version.parse(param)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import contextvars
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
import rich_click as click
|
|
4
|
+
from .types import Manifest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
current_console: contextvars.ContextVar[Console] = contextvars.ContextVar(
|
|
8
|
+
"current_console"
|
|
9
|
+
)
|
|
10
|
+
current_manifest: contextvars.ContextVar[Manifest] = contextvars.ContextVar(
|
|
11
|
+
"current_manifest"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_console(ctx) -> Console:
|
|
16
|
+
try:
|
|
17
|
+
return ctx.obj["console"]
|
|
18
|
+
except LookupError:
|
|
19
|
+
raise click.ClickException(
|
|
20
|
+
"No Current Console. Probably you are not running this command from the CLI."
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def set_console(ctx, console):
|
|
25
|
+
ctx.obj["console"] = console
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_manifest(ctx) -> Manifest:
|
|
29
|
+
"""Get the current manifest or raise an exception."""
|
|
30
|
+
try:
|
|
31
|
+
return ctx.obj["manifest"]
|
|
32
|
+
except LookupError:
|
|
33
|
+
raise click.ClickException(
|
|
34
|
+
"No manifest found. You need to run the `arkitekt_next init` command first."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_manifest(ctx, manifest):
|
|
39
|
+
ctx.obj["manifest"] = manifest
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version: v1
|