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.

Files changed (119) hide show
  1. arkitekt_next/__init__.py +43 -0
  2. arkitekt_next/apps/__init__.py +3 -0
  3. arkitekt_next/apps/easy.py +99 -0
  4. arkitekt_next/apps/next.py +40 -0
  5. arkitekt_next/apps/qt.py +97 -0
  6. arkitekt_next/apps/service/__init__.py +3 -0
  7. arkitekt_next/apps/service/fakts.py +88 -0
  8. arkitekt_next/apps/service/fakts_next.py +79 -0
  9. arkitekt_next/apps/service/fakts_qt.py +82 -0
  10. arkitekt_next/apps/service/fluss_next.py +31 -0
  11. arkitekt_next/apps/service/grant_registry.py +27 -0
  12. arkitekt_next/apps/service/herre.py +24 -0
  13. arkitekt_next/apps/service/herre_qt.py +57 -0
  14. arkitekt_next/apps/service/kabinet.py +31 -0
  15. arkitekt_next/apps/service/mikro_next.py +81 -0
  16. arkitekt_next/apps/service/rekuest_next.py +53 -0
  17. arkitekt_next/apps/service/unlok_next.py +32 -0
  18. arkitekt_next/apps/types.py +53 -0
  19. arkitekt_next/builders.py +264 -0
  20. arkitekt_next/cli/__init__.py +0 -0
  21. arkitekt_next/cli/commands/call/__init__.py +0 -0
  22. arkitekt_next/cli/commands/call/local.py +132 -0
  23. arkitekt_next/cli/commands/call/main.py +22 -0
  24. arkitekt_next/cli/commands/call/remote.py +90 -0
  25. arkitekt_next/cli/commands/gen/__init__.py +0 -0
  26. arkitekt_next/cli/commands/gen/compile.py +45 -0
  27. arkitekt_next/cli/commands/gen/init.py +122 -0
  28. arkitekt_next/cli/commands/gen/main.py +29 -0
  29. arkitekt_next/cli/commands/gen/watch.py +32 -0
  30. arkitekt_next/cli/commands/init/__init__.py +0 -0
  31. arkitekt_next/cli/commands/init/main.py +194 -0
  32. arkitekt_next/cli/commands/inspect/__init__.py +0 -0
  33. arkitekt_next/cli/commands/inspect/definitions.py +53 -0
  34. arkitekt_next/cli/commands/inspect/main.py +22 -0
  35. arkitekt_next/cli/commands/inspect/variables.py +92 -0
  36. arkitekt_next/cli/commands/manifest/__init__.py +0 -0
  37. arkitekt_next/cli/commands/manifest/inspect.py +42 -0
  38. arkitekt_next/cli/commands/manifest/main.py +25 -0
  39. arkitekt_next/cli/commands/manifest/scopes.py +155 -0
  40. arkitekt_next/cli/commands/manifest/version.py +147 -0
  41. arkitekt_next/cli/commands/manifest/wizard.py +94 -0
  42. arkitekt_next/cli/commands/port/__init__.py +0 -0
  43. arkitekt_next/cli/commands/port/build.py +231 -0
  44. arkitekt_next/cli/commands/port/init.py +82 -0
  45. arkitekt_next/cli/commands/port/main.py +31 -0
  46. arkitekt_next/cli/commands/port/publish.py +102 -0
  47. arkitekt_next/cli/commands/port/stage.py +59 -0
  48. arkitekt_next/cli/commands/port/utils.py +47 -0
  49. arkitekt_next/cli/commands/port/validate.py +78 -0
  50. arkitekt_next/cli/commands/port/wizard.py +329 -0
  51. arkitekt_next/cli/commands/run/__init__.py +0 -0
  52. arkitekt_next/cli/commands/run/dev.py +349 -0
  53. arkitekt_next/cli/commands/run/main.py +22 -0
  54. arkitekt_next/cli/commands/run/prod.py +57 -0
  55. arkitekt_next/cli/commands/run/utils.py +10 -0
  56. arkitekt_next/cli/commands/server/__init__.py +0 -0
  57. arkitekt_next/cli/commands/server/down.py +56 -0
  58. arkitekt_next/cli/commands/server/init.py +74 -0
  59. arkitekt_next/cli/commands/server/inspect.py +59 -0
  60. arkitekt_next/cli/commands/server/main.py +33 -0
  61. arkitekt_next/cli/commands/server/open.py +66 -0
  62. arkitekt_next/cli/commands/server/remove.py +60 -0
  63. arkitekt_next/cli/commands/server/stop.py +56 -0
  64. arkitekt_next/cli/commands/server/up.py +70 -0
  65. arkitekt_next/cli/commands/server/utils.py +33 -0
  66. arkitekt_next/cli/configs/base.yaml +867 -0
  67. arkitekt_next/cli/constants.py +63 -0
  68. arkitekt_next/cli/dockerfiles/vanilla.dockerfile +8 -0
  69. arkitekt_next/cli/errors.py +4 -0
  70. arkitekt_next/cli/inspect.py +1 -0
  71. arkitekt_next/cli/io.py +255 -0
  72. arkitekt_next/cli/main.py +83 -0
  73. arkitekt_next/cli/options.py +166 -0
  74. arkitekt_next/cli/schemas/fluss.schema.graphql +2446 -0
  75. arkitekt_next/cli/schemas/gucker.schema.graphql +8908 -0
  76. arkitekt_next/cli/schemas/kabinet.schema.graphql +515 -0
  77. arkitekt_next/cli/schemas/kluster.schema.graphql +109 -0
  78. arkitekt_next/cli/schemas/konviktion.schema.graphql +70 -0
  79. arkitekt_next/cli/schemas/kuay.schema.graphql +356 -0
  80. arkitekt_next/cli/schemas/mikro.schema.graphql +8908 -0
  81. arkitekt_next/cli/schemas/mikro_next.schema.graphql +1639 -0
  82. arkitekt_next/cli/schemas/napari.schema.graphql +8908 -0
  83. arkitekt_next/cli/schemas/omero_ark.schema.graphql +100 -0
  84. arkitekt_next/cli/schemas/port.schema.graphql +356 -0
  85. arkitekt_next/cli/schemas/rekuest.schema.graphql +4630 -0
  86. arkitekt_next/cli/schemas/rekuest_next.schema.graphql +1159 -0
  87. arkitekt_next/cli/schemas/unlok.schema.graphql +1013 -0
  88. arkitekt_next/cli/templates/filter.py +26 -0
  89. arkitekt_next/cli/templates/simple.py +67 -0
  90. arkitekt_next/cli/texts.py +20 -0
  91. arkitekt_next/cli/types.py +365 -0
  92. arkitekt_next/cli/ui.py +111 -0
  93. arkitekt_next/cli/utils.py +15 -0
  94. arkitekt_next/cli/validators.py +17 -0
  95. arkitekt_next/cli/vars.py +39 -0
  96. arkitekt_next/cli/versions/v1.yaml +1 -0
  97. arkitekt_next/constants.py +6 -0
  98. arkitekt_next/model.py +110 -0
  99. arkitekt_next/qt/__init__.py +9 -0
  100. arkitekt_next/qt/assets/dark/gear.png +0 -0
  101. arkitekt_next/qt/assets/dark/green pulse.gif +0 -0
  102. arkitekt_next/qt/assets/dark/orange pulse.gif +0 -0
  103. arkitekt_next/qt/assets/dark/pink pulse.gif +0 -0
  104. arkitekt_next/qt/assets/dark/red pulse.gif +0 -0
  105. arkitekt_next/qt/assets/light/gear.png +0 -0
  106. arkitekt_next/qt/assets/light/green pulse.gif +0 -0
  107. arkitekt_next/qt/assets/light/orange pulse.gif +0 -0
  108. arkitekt_next/qt/assets/light/pink pulse.gif +0 -0
  109. arkitekt_next/qt/assets/light/red pulse.gif +0 -0
  110. arkitekt_next/qt/magic_bar.py +545 -0
  111. arkitekt_next/qt/utils.py +30 -0
  112. arkitekt_next/service_registry.py +51 -0
  113. arkitekt_next/tqdm.py +43 -0
  114. arkitekt_next/utils.py +38 -0
  115. arkitekt_next-0.7.8.dist-info/LICENSE +21 -0
  116. arkitekt_next-0.7.8.dist-info/METADATA +155 -0
  117. arkitekt_next-0.7.8.dist-info/RECORD +119 -0
  118. arkitekt_next-0.7.8.dist-info/WHEEL +4 -0
  119. 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
@@ -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
@@ -0,0 +1,6 @@
1
+ REPO_URL = (
2
+ "https://arkitekt_next.live/repo.json"
3
+ )
4
+
5
+
6
+ DEFAULT_ARKITEKT_URL = "http://127.0.0.1:12000"