album-solution-api 0.6.0__tar.gz
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.
- album-solution-api-0.6.0/LICENSE +21 -0
- album-solution-api-0.6.0/PKG-INFO +29 -0
- album-solution-api-0.6.0/README.md +3 -0
- album-solution-api-0.6.0/pyproject.toml +6 -0
- album-solution-api-0.6.0/setup.cfg +44 -0
- album-solution-api-0.6.0/setup.py +3 -0
- album-solution-api-0.6.0/src/album/__init__.py +1 -0
- album-solution-api-0.6.0/src/album/runner/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/album_logging.py +196 -0
- album-solution-api-0.6.0/src/album/runner/api.py +187 -0
- album-solution-api-0.6.0/src/album/runner/core/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/core/api/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/core/api/model/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/core/api/model/coordinates.py +18 -0
- album-solution-api-0.6.0/src/album/runner/core/api/model/solution.py +103 -0
- album-solution-api-0.6.0/src/album/runner/core/default_values_runner.py +20 -0
- album-solution-api-0.6.0/src/album/runner/core/model/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/core/model/coordinates.py +31 -0
- album-solution-api-0.6.0/src/album/runner/core/model/solution.py +112 -0
- album-solution-api-0.6.0/src/album/runner/core/model/solution_script.py +173 -0
- album-solution-api-0.6.0/src/album/runner/core/schema/__init__.py +0 -0
- album-solution-api-0.6.0/src/album/runner/core/schema/solution_schema.json +287 -0
- album-solution-api-0.6.0/src/album_solution_api.egg-info/PKG-INFO +29 -0
- album-solution-api-0.6.0/src/album_solution_api.egg-info/SOURCES.txt +25 -0
- album-solution-api-0.6.0/src/album_solution_api.egg-info/dependency_links.txt +1 -0
- album-solution-api-0.6.0/src/album_solution_api.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021, album developers.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: album-solution-api
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Album is a framework for unifying computational tools across frameworks, languages, and environments.
|
|
5
|
+
Home-page: https://gitlab.com/album-app/album-solution-api
|
|
6
|
+
Author: Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
7
|
+
Maintainer: Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
8
|
+
Maintainer-email: album@kyleharrington.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Project-URL: Source, https://gitlab.com/album-app/album-solution-api
|
|
11
|
+
Project-URL: Tracker, https://gitlab.com/album-app/album-solution-api/issues
|
|
12
|
+
Keywords: virtual,environments,imaging
|
|
13
|
+
Platform: any
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.6
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
|
|
27
|
+
# album-solution-api
|
|
28
|
+
|
|
29
|
+
Package installed in any Album solution environment. Provides the API for the solution script.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = album-solution-api
|
|
3
|
+
version = 0.6.0
|
|
4
|
+
description = Album is a framework for unifying computational tools across frameworks, languages, and environments.
|
|
5
|
+
long_description = file: README.md
|
|
6
|
+
long_description_content_type = text/markdown
|
|
7
|
+
url = https://gitlab.com/album-app/album-solution-api
|
|
8
|
+
author = Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
9
|
+
maintainer = Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
10
|
+
maintainer_email = album@kyleharrington.com
|
|
11
|
+
license = MIT
|
|
12
|
+
license_file = LICENSE
|
|
13
|
+
platforms = any
|
|
14
|
+
classifiers =
|
|
15
|
+
Development Status :: 3 - Alpha
|
|
16
|
+
License :: OSI Approved :: MIT License
|
|
17
|
+
Operating System :: MacOS :: MacOS X
|
|
18
|
+
Operating System :: Microsoft :: Windows
|
|
19
|
+
Operating System :: POSIX
|
|
20
|
+
Programming Language :: Python :: 3.8
|
|
21
|
+
Programming Language :: Python :: 3.9
|
|
22
|
+
Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Topic :: Utilities
|
|
24
|
+
keywords = virtual, environments, imaging
|
|
25
|
+
project_urls =
|
|
26
|
+
Source=https://gitlab.com/album-app/album-solution-api
|
|
27
|
+
Tracker=https://gitlab.com/album-app/album-solution-api/issues
|
|
28
|
+
|
|
29
|
+
[options]
|
|
30
|
+
package_dir =
|
|
31
|
+
=src
|
|
32
|
+
packages = find_namespace:
|
|
33
|
+
python_requires = >=3.6
|
|
34
|
+
|
|
35
|
+
[options.package_data]
|
|
36
|
+
* = *.json
|
|
37
|
+
|
|
38
|
+
[options.packages.find]
|
|
39
|
+
where = src
|
|
40
|
+
|
|
41
|
+
[egg_info]
|
|
42
|
+
tag_build =
|
|
43
|
+
tag_date = 0
|
|
44
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__import__('pkg_resources').declare_namespace(__name__)
|
|
File without changes
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
from enum import Enum, unique
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Global variable for tracking the currently active logger. Do not use this
|
|
7
|
+
directly instead use get_active_logger()
|
|
8
|
+
"""
|
|
9
|
+
_active_logger = {}
|
|
10
|
+
|
|
11
|
+
DEBUG = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def thread_stack():
|
|
15
|
+
global _active_logger
|
|
16
|
+
thread_id = threading.current_thread().ident
|
|
17
|
+
if thread_id not in _active_logger:
|
|
18
|
+
_active_logger[thread_id] = []
|
|
19
|
+
return _active_logger.get(thread_id)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def push_active_logger(logger):
|
|
23
|
+
"""Insert a logger to the _active_logger stack."""
|
|
24
|
+
thread_stack().insert(0, logger)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_active_logger():
|
|
28
|
+
"""Return the currently active logger, which is defined globally."""
|
|
29
|
+
stack = thread_stack()
|
|
30
|
+
if len(stack) > 0:
|
|
31
|
+
return stack[0]
|
|
32
|
+
return logging.getLogger() # root logger
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_active_logger_in_thread(thread_ident):
|
|
36
|
+
"""Return the currently active logger, which is defined globally."""
|
|
37
|
+
if thread_ident in _active_logger:
|
|
38
|
+
stack = _active_logger.get(thread_ident)
|
|
39
|
+
if len(stack) > 0:
|
|
40
|
+
return stack[0]
|
|
41
|
+
return logging.getLogger() # root logger
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def pop_active_logger():
|
|
45
|
+
"""Pop the currently active logger from the _active_solution stack."""
|
|
46
|
+
|
|
47
|
+
stack = thread_stack()
|
|
48
|
+
if len(stack) > 0:
|
|
49
|
+
logger = stack.pop(0)
|
|
50
|
+
logger.handlers.clear()
|
|
51
|
+
return logger
|
|
52
|
+
else:
|
|
53
|
+
return logging.getLogger() # root logger
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@unique
|
|
57
|
+
class LogLevel(Enum):
|
|
58
|
+
"""LogLevel album allows.
|
|
59
|
+
|
|
60
|
+
Notes:
|
|
61
|
+
Only add Names available in python standard logging module.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
DEBUG = 1
|
|
65
|
+
INFO = 0
|
|
66
|
+
WARNING = 2
|
|
67
|
+
ERROR = 3
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def to_loglevel(value):
|
|
71
|
+
"""Converts a string value to a @LogLevel.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
value:
|
|
75
|
+
The string value
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The LovLevel class enum
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
KeyError when loglevel unknown.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
return LogLevel[value]
|
|
86
|
+
except KeyError as err:
|
|
87
|
+
logger = get_active_logger()
|
|
88
|
+
logger.error('Loglevel %s not allowed or unknown!' % value)
|
|
89
|
+
raise err
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def configure_logging(name, loglevel=None, stream_handler=None, formatter_string=None, parent_thread_id=None,
|
|
93
|
+
parent_name=None):
|
|
94
|
+
"""Configures a logger with a certain name and loglevel.
|
|
95
|
+
|
|
96
|
+
loglevel:
|
|
97
|
+
The Loglevel to use. Either DEBUG or INFO.
|
|
98
|
+
name:
|
|
99
|
+
The name of the logger.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The logger object.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
# create or get currently active logger
|
|
106
|
+
if parent_thread_id is None:
|
|
107
|
+
parent = get_active_logger()
|
|
108
|
+
else:
|
|
109
|
+
parent = get_active_logger_in_thread(parent_thread_id)
|
|
110
|
+
|
|
111
|
+
if not parent_name:
|
|
112
|
+
parent_name = parent.name
|
|
113
|
+
|
|
114
|
+
# if currently active logger has the same name, just return it
|
|
115
|
+
if parent_name == name:
|
|
116
|
+
return parent
|
|
117
|
+
|
|
118
|
+
# make sure the new logger is registered as a child of the currently active logger in order to propagate logs
|
|
119
|
+
if parent_name.endswith("." + name):
|
|
120
|
+
name = parent_name
|
|
121
|
+
else:
|
|
122
|
+
if not name.startswith(parent_name + "."):
|
|
123
|
+
name = parent_name + "." + name
|
|
124
|
+
|
|
125
|
+
logger = logging.getLogger(name)
|
|
126
|
+
if loglevel is None:
|
|
127
|
+
logger.setLevel(parent.level)
|
|
128
|
+
else:
|
|
129
|
+
logger.setLevel(loglevel.name)
|
|
130
|
+
|
|
131
|
+
if stream_handler:
|
|
132
|
+
# create formatter
|
|
133
|
+
if not formatter_string:
|
|
134
|
+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
135
|
+
else:
|
|
136
|
+
formatter = logging.Formatter(formatter_string)
|
|
137
|
+
# create console handler and set level to debug
|
|
138
|
+
# ToDo: different handlers necessary? e.g. logging additional into a file?
|
|
139
|
+
ch = logging.StreamHandler(stream_handler)
|
|
140
|
+
ch.setLevel(loglevel.name)
|
|
141
|
+
ch.setFormatter(formatter)
|
|
142
|
+
logger.addHandler(ch)
|
|
143
|
+
|
|
144
|
+
# push logger if not already active
|
|
145
|
+
if get_active_logger() != logger:
|
|
146
|
+
push_active_logger(logger)
|
|
147
|
+
|
|
148
|
+
return logger
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_loglevel():
|
|
152
|
+
"""Returns the loglevel of the current active logger."""
|
|
153
|
+
return get_active_logger().level
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_loglevel_name():
|
|
157
|
+
"""Returns the Name of the loglevel of the current active logger."""
|
|
158
|
+
logger = get_active_logger()
|
|
159
|
+
return logging.getLevelName(logger.level)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def set_loglevel(loglevel):
|
|
163
|
+
""" Sets logLevel for a logger with a certain name for ALL available handlers.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
loglevel:
|
|
167
|
+
The Loglevel to use. Either DEBUG or INFO.
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
# logger loglevel
|
|
171
|
+
active_logger = get_active_logger()
|
|
172
|
+
active_logger.debug('Set loglevel to %s...' % loglevel.name)
|
|
173
|
+
|
|
174
|
+
active_logger.setLevel(loglevel.name)
|
|
175
|
+
|
|
176
|
+
# set handler loglevel
|
|
177
|
+
for handler in active_logger.handlers:
|
|
178
|
+
handler_name = handler.stream.name if hasattr(handler, "stream") and hasattr(handler.stream,
|
|
179
|
+
active_logger.name) else "default handler"
|
|
180
|
+
active_logger.debug('Set loglevel for handler %s to %s...' % (handler_name, loglevel.name))
|
|
181
|
+
handler.setLevel(loglevel.name)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class LogEntry:
|
|
185
|
+
name = None
|
|
186
|
+
level = None
|
|
187
|
+
message = None
|
|
188
|
+
|
|
189
|
+
def __init__(self, name, level, message):
|
|
190
|
+
self.name = name
|
|
191
|
+
self.level = level
|
|
192
|
+
self.message = message
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def debug_settings():
|
|
196
|
+
return DEBUG
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import tarfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from urllib.request import urlretrieve
|
|
7
|
+
|
|
8
|
+
from album.runner import album_logging
|
|
9
|
+
from album.runner.album_logging import get_active_logger, configure_logging
|
|
10
|
+
from album.runner.core.api.model.solution import ISolution
|
|
11
|
+
from album.runner.core.default_values_runner import DefaultValuesRunner
|
|
12
|
+
from album.runner.core.model.solution import Solution
|
|
13
|
+
from album.runner.core.model.solution_script import SolutionScript
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def download_if_not_exists(url, file_name):
|
|
17
|
+
"""Downloads resource if not already cached and returns local resource path.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
url: The URL of the download.
|
|
21
|
+
file_name: The local filename of the download.
|
|
22
|
+
|
|
23
|
+
Returns: The path to the downloaded resource.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
download_dir = get_cache_path()
|
|
27
|
+
download_path = download_dir.joinpath(file_name)
|
|
28
|
+
|
|
29
|
+
if download_path.exists():
|
|
30
|
+
get_active_logger().info(f"Found cache of {url}: {download_path}...")
|
|
31
|
+
return download_path
|
|
32
|
+
if not download_dir.exists():
|
|
33
|
+
download_dir.mkdir(parents=True)
|
|
34
|
+
|
|
35
|
+
get_active_logger().info(f"Downloading {url} to {download_path}...")
|
|
36
|
+
|
|
37
|
+
urlretrieve(url, download_path)
|
|
38
|
+
|
|
39
|
+
return download_path
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def extract_tar(in_tar, out_dir):
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
out_dir: Directory where the TAR file should be extracted to
|
|
47
|
+
in_tar: TAR file to be extracted
|
|
48
|
+
"""
|
|
49
|
+
out_path = Path(out_dir)
|
|
50
|
+
|
|
51
|
+
if not out_path.exists():
|
|
52
|
+
out_path.mkdir(parents=True)
|
|
53
|
+
|
|
54
|
+
get_active_logger().info(f"Extracting {in_tar} to {out_dir}...")
|
|
55
|
+
|
|
56
|
+
my_tar = tarfile.open(in_tar)
|
|
57
|
+
my_tar.extractall(out_dir)
|
|
58
|
+
my_tar.close()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# todo: extract_zip
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_environment_path() -> Path:
|
|
65
|
+
"""Returns the path of the environment the solution runs in."""
|
|
66
|
+
active_solution = get_active_solution()
|
|
67
|
+
res = active_solution.installation().environment_path()
|
|
68
|
+
return Path(res) if res else res
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_data_path() -> Path:
|
|
72
|
+
"""Returns the data path provided for the solution."""
|
|
73
|
+
active_solution = get_active_solution()
|
|
74
|
+
res = active_solution.installation().data_path()
|
|
75
|
+
return Path(res) if res else res
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_package_path() -> Path:
|
|
79
|
+
"""Returns the package path provided for the solution."""
|
|
80
|
+
active_solution = get_active_solution()
|
|
81
|
+
res = active_solution.installation().package_path()
|
|
82
|
+
return Path(res) if res else res
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_app_path() -> Path:
|
|
86
|
+
"""Returns the app path provided for the solution."""
|
|
87
|
+
active_solution = get_active_solution()
|
|
88
|
+
res = active_solution.installation().app_path()
|
|
89
|
+
return Path(res) if res else res
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_cache_path() -> Path:
|
|
93
|
+
"""Returns the cache path provided for the solution."""
|
|
94
|
+
active_solution = get_active_solution()
|
|
95
|
+
res = active_solution.installation().user_cache_path()
|
|
96
|
+
return Path(res) if res else res
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def in_target_environment() -> bool:
|
|
100
|
+
"""Gives the boolean information whether or not current python is the python from the album target environment.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
True when current active python is the album target environment else False.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
active_solution = get_active_solution()
|
|
107
|
+
|
|
108
|
+
return True if sys.executable.startswith(
|
|
109
|
+
active_solution.installation().environment_path()) else False
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_args():
|
|
113
|
+
"""Get the parsed argument from the solution call.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
The namespace object of the parsed arguments.
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
active_solution = get_active_solution()
|
|
120
|
+
|
|
121
|
+
return active_solution.args()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
Global variable for tracking the currently active solution. Do not use this
|
|
126
|
+
directly instead use get_active_solution()
|
|
127
|
+
"""
|
|
128
|
+
_active_solution = []
|
|
129
|
+
|
|
130
|
+
enc = sys.getfilesystemencoding()
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def setup(**attrs):
|
|
134
|
+
"""This configures a solution for the use by the main album tool."""
|
|
135
|
+
global _active_solution
|
|
136
|
+
loglevel = os.getenv(DefaultValuesRunner.env_variable_logger_level.value, "INFO")
|
|
137
|
+
configure_logging("script", loglevel=album_logging.to_loglevel(loglevel),
|
|
138
|
+
stream_handler=sys.stdout, formatter_string=SolutionScript.get_script_logging_formatter_str())
|
|
139
|
+
next_solution = Solution(attrs)
|
|
140
|
+
push_active_solution(next_solution)
|
|
141
|
+
goal = os.getenv(DefaultValuesRunner.env_variable_action.value, None)
|
|
142
|
+
if goal:
|
|
143
|
+
goal = Solution.Action[goal]
|
|
144
|
+
package_path = os.getenv(DefaultValuesRunner.env_variable_package.value, os.path.abspath(__file__))
|
|
145
|
+
if package_path:
|
|
146
|
+
package_path = Path(package_path)
|
|
147
|
+
installation_base_path = os.getenv(DefaultValuesRunner.env_variable_installation.value, None)
|
|
148
|
+
if installation_base_path:
|
|
149
|
+
installation_base_path = Path(installation_base_path)
|
|
150
|
+
environment_path = os.getenv(DefaultValuesRunner.env_variable_environment.value, None)
|
|
151
|
+
if environment_path:
|
|
152
|
+
environment_path = Path(environment_path)
|
|
153
|
+
if goal:
|
|
154
|
+
next_solution.installation().set_package_path(package_path)
|
|
155
|
+
SolutionScript.trigger_solution_goal(next_solution, goal, package_path, installation_base_path, environment_path)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def push_active_solution(solution_object: ISolution):
|
|
159
|
+
"""Pop a solution to the _active_solution stack."""
|
|
160
|
+
global _active_solution
|
|
161
|
+
_active_solution.insert(0, solution_object)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_parent_solution() -> Optional[ISolution]:
|
|
165
|
+
"""Return the parent solution of the currently active solution."""
|
|
166
|
+
global _active_solution
|
|
167
|
+
if len(_active_solution) > 1:
|
|
168
|
+
return _active_solution[1]
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_active_solution() -> Optional[ISolution]:
|
|
173
|
+
"""Return the currently active solution, which is defined globally."""
|
|
174
|
+
global _active_solution
|
|
175
|
+
if len(_active_solution) > 0:
|
|
176
|
+
return _active_solution[0]
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def pop_active_solution():
|
|
181
|
+
"""Pop the currently active solution from the _active_solution stack."""
|
|
182
|
+
global _active_solution
|
|
183
|
+
|
|
184
|
+
if len(_active_solution) > 0:
|
|
185
|
+
return _active_solution.pop(0)
|
|
186
|
+
else:
|
|
187
|
+
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from abc import ABCMeta, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ICoordinates:
|
|
5
|
+
|
|
6
|
+
__metaclass__ = ABCMeta
|
|
7
|
+
|
|
8
|
+
@abstractmethod
|
|
9
|
+
def name(self):
|
|
10
|
+
raise NotImplementedError
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def group(self):
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def version(self):
|
|
18
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from abc import ABCMeta, abstractmethod
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from album.runner.core.api.model.coordinates import ICoordinates
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ISolution:
|
|
10
|
+
|
|
11
|
+
__metaclass__ = ABCMeta
|
|
12
|
+
|
|
13
|
+
class Action(Enum):
|
|
14
|
+
NO_ACTION = 0
|
|
15
|
+
INSTALL = 1
|
|
16
|
+
RUN = 2
|
|
17
|
+
TEST = 3
|
|
18
|
+
UNINSTALL = 4
|
|
19
|
+
|
|
20
|
+
class ISetup(dict):
|
|
21
|
+
__metaclass__ = ABCMeta
|
|
22
|
+
__getattr__ = dict.get
|
|
23
|
+
__setattr__ = dict.__setitem__
|
|
24
|
+
__delattr__ = dict.__delitem__
|
|
25
|
+
|
|
26
|
+
class IInstallation:
|
|
27
|
+
__metaclass__ = ABCMeta
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def environment_path(self) -> Path:
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def user_cache_path(self) -> Path:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def internal_cache_path(self) -> Path:
|
|
39
|
+
raise NotImplementedError
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def package_path(self) -> Path:
|
|
43
|
+
raise NotImplementedError
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def data_path(self) -> Path:
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def app_path(self) -> Path:
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def installation_path(self) -> Path:
|
|
55
|
+
raise NotImplementedError
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def set_environment_path(self, path: Path):
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def set_package_path(self, package_path: Path):
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def set_installation_path(self, solution_base_path: Path):
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def setup(self) -> ISetup:
|
|
71
|
+
raise NotImplementedError
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def installation(self) -> IInstallation:
|
|
75
|
+
raise NotImplementedError
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def coordinates(self) -> ICoordinates:
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def script(self) -> Path:
|
|
83
|
+
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def args(self) -> List:
|
|
87
|
+
raise NotImplementedError
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def get_arg(self, name: str):
|
|
91
|
+
raise NotImplementedError
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def get_identifier(self) -> str:
|
|
95
|
+
raise NotImplementedError
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def set_script(self, script: str):
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def set_args(self, args: List):
|
|
103
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class DefaultValuesRunner(Enum):
|
|
5
|
+
"""Add an entry here to initialize default attributes for a album runner instance."""
|
|
6
|
+
|
|
7
|
+
solution_app_prefix = "app" # solution specific app files
|
|
8
|
+
solution_data_prefix = "data" # solution specific data files
|
|
9
|
+
solution_internal_cache_prefix = (
|
|
10
|
+
"icache" # solution specific album internal cache files
|
|
11
|
+
)
|
|
12
|
+
solution_user_cache_prefix = (
|
|
13
|
+
"ucache" # solution specific user cache files, accessible via runner API
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
env_variable_action = "ALBUM_SOLUTION_ACTION"
|
|
17
|
+
env_variable_logger_level = "ALBUM_LOGGER_LEVEL"
|
|
18
|
+
env_variable_package = "ALBUM_SOLUTION_PACKAGE"
|
|
19
|
+
env_variable_installation = "ALBUM_SOLUTION_INSTALLATION"
|
|
20
|
+
env_variable_environment = "ALBUM_SOLUTION_ENVIRONMENT"
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from album.runner.core.api.model.coordinates import ICoordinates
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Coordinates(ICoordinates):
|
|
5
|
+
"""Class for the Coordinates of a solution."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, group: str, name: str, version: str) -> None:
|
|
8
|
+
self._group = group
|
|
9
|
+
self._name = name
|
|
10
|
+
self._version = version
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def name(self):
|
|
14
|
+
return self._name
|
|
15
|
+
|
|
16
|
+
def group(self):
|
|
17
|
+
return self._group
|
|
18
|
+
|
|
19
|
+
def version(self):
|
|
20
|
+
return self._version
|
|
21
|
+
|
|
22
|
+
def __str__(self) -> str:
|
|
23
|
+
return f"{self._group}:{self._name}:{self._version}"
|
|
24
|
+
|
|
25
|
+
def __eq__(self, o: object) -> bool:
|
|
26
|
+
return isinstance(
|
|
27
|
+
o, ICoordinates
|
|
28
|
+
) and o.group() == self._group and o.name() == self._name and o.version() == self._version
|
|
29
|
+
|
|
30
|
+
def __hash__(self):
|
|
31
|
+
return hash(self.__str__())
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from album.runner.core.api.model.coordinates import ICoordinates
|
|
5
|
+
from album.runner.core.api.model.solution import ISolution
|
|
6
|
+
from album.runner.core.default_values_runner import DefaultValuesRunner
|
|
7
|
+
from album.runner.core.model.coordinates import Coordinates
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Solution(ISolution):
|
|
11
|
+
"""Encapsulates a album solution configuration."""
|
|
12
|
+
|
|
13
|
+
class Setup(ISolution.ISetup):
|
|
14
|
+
|
|
15
|
+
def __init__(self, attrs=None):
|
|
16
|
+
"""sets object attributes
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
attrs:
|
|
20
|
+
Dictionary containing the attributes.
|
|
21
|
+
"""
|
|
22
|
+
if attrs:
|
|
23
|
+
super().__init__(attrs)
|
|
24
|
+
else:
|
|
25
|
+
super().__init__()
|
|
26
|
+
|
|
27
|
+
def __str__(self, indent=2):
|
|
28
|
+
s = '\n'
|
|
29
|
+
for attr in self.__dict__:
|
|
30
|
+
for i in range(0, indent):
|
|
31
|
+
s += '\t'
|
|
32
|
+
s += (attr + ':\t' + str(getattr(self, attr))) + '\n'
|
|
33
|
+
return s
|
|
34
|
+
|
|
35
|
+
class Installation (ISolution.IInstallation):
|
|
36
|
+
def __init__(self):
|
|
37
|
+
super().__init__()
|
|
38
|
+
# API keywords
|
|
39
|
+
self._installation_path = None
|
|
40
|
+
self._environment_path = None
|
|
41
|
+
self._package_path = None
|
|
42
|
+
|
|
43
|
+
def environment_path(self) -> Path:
|
|
44
|
+
return self._environment_path
|
|
45
|
+
|
|
46
|
+
def user_cache_path(self) -> Path:
|
|
47
|
+
return self._installation_path.joinpath(DefaultValuesRunner.solution_user_cache_prefix.value)
|
|
48
|
+
|
|
49
|
+
def internal_cache_path(self) -> Path:
|
|
50
|
+
return self._installation_path.joinpath(DefaultValuesRunner.solution_internal_cache_prefix.value)
|
|
51
|
+
|
|
52
|
+
def package_path(self) -> Path:
|
|
53
|
+
return self._package_path
|
|
54
|
+
|
|
55
|
+
def installation_path(self) -> Path:
|
|
56
|
+
return self._installation_path
|
|
57
|
+
|
|
58
|
+
def data_path(self) -> Path:
|
|
59
|
+
return self._installation_path.joinpath(DefaultValuesRunner.solution_data_prefix.value)
|
|
60
|
+
|
|
61
|
+
def app_path(self) -> Path:
|
|
62
|
+
return self._installation_path.joinpath(DefaultValuesRunner.solution_app_prefix.value)
|
|
63
|
+
|
|
64
|
+
def set_environment_path(self, path: Path):
|
|
65
|
+
self._environment_path = path
|
|
66
|
+
|
|
67
|
+
def set_package_path(self, package_path: Path):
|
|
68
|
+
self._package_path = package_path
|
|
69
|
+
|
|
70
|
+
def set_installation_path(self, solution_base_path: Path):
|
|
71
|
+
self._installation_path = solution_base_path
|
|
72
|
+
|
|
73
|
+
def __init__(self, attrs=None):
|
|
74
|
+
self._installation = Solution.Installation()
|
|
75
|
+
self._setup = Solution.Setup(attrs)
|
|
76
|
+
self._coordinates = Coordinates(attrs['group'], attrs['name'], attrs['version'])
|
|
77
|
+
self._args = None
|
|
78
|
+
self._script = None
|
|
79
|
+
|
|
80
|
+
def setup(self) -> ISolution.ISetup:
|
|
81
|
+
return self._setup
|
|
82
|
+
|
|
83
|
+
def installation(self) -> ISolution.IInstallation:
|
|
84
|
+
return self._installation
|
|
85
|
+
|
|
86
|
+
def coordinates(self) -> ICoordinates:
|
|
87
|
+
return self._coordinates
|
|
88
|
+
|
|
89
|
+
def script(self) -> Path:
|
|
90
|
+
return self._script
|
|
91
|
+
|
|
92
|
+
def get_arg(self, k):
|
|
93
|
+
"""Get a specific named argument for this album if it exists."""
|
|
94
|
+
matches = [arg for arg in self._setup.args if arg['name'] == k]
|
|
95
|
+
return matches[0]
|
|
96
|
+
|
|
97
|
+
def get_identifier(self) -> str:
|
|
98
|
+
identifier = '_'.join([self._setup.group, self._setup.name, self._setup.version])
|
|
99
|
+
return identifier
|
|
100
|
+
|
|
101
|
+
def set_script(self, script: str):
|
|
102
|
+
self._script = script
|
|
103
|
+
|
|
104
|
+
def __eq__(self, other):
|
|
105
|
+
return isinstance(other, Solution) and \
|
|
106
|
+
other.coordinates() == self._coordinates
|
|
107
|
+
|
|
108
|
+
def set_args(self, args: List):
|
|
109
|
+
self._args = args
|
|
110
|
+
|
|
111
|
+
def args(self) -> List:
|
|
112
|
+
return self._args
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
from argparse import ArgumentError
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from album.runner import album_logging
|
|
7
|
+
from album.runner.album_logging import get_active_logger, configure_logging
|
|
8
|
+
from album.runner.core.api.model.solution import ISolution
|
|
9
|
+
from album.runner.core.model.solution import Solution
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SolutionScript:
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def make_action(solution, mydest):
|
|
16
|
+
class CustomAction(argparse.Action):
|
|
17
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
18
|
+
setattr(namespace, mydest, solution.get_arg(mydest)['action'](values))
|
|
19
|
+
|
|
20
|
+
return CustomAction
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def get_script_logging_formatter_str():
|
|
24
|
+
return '%(levelname)-7s %(name)s - %(message)s'
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def get_script_logging_formatter_regex():
|
|
28
|
+
regex_log_level = 'DEBUG|INFO|WARNING|ERROR'
|
|
29
|
+
return r'(%s)\s+([\s\S]+) - ([\s\S]+)?' % regex_log_level
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def trigger_solution_goal(solution, goal, package_path=None, installation_base_path=None, environment_path=None):
|
|
33
|
+
SolutionScript.api_access(solution, package_path, installation_base_path, environment_path)
|
|
34
|
+
parser = None
|
|
35
|
+
if package_path:
|
|
36
|
+
sys.path.append(package_path)
|
|
37
|
+
if solution.setup().args:
|
|
38
|
+
append_arguments = (goal == Solution.Action.RUN) or (goal == Solution.Action.TEST)
|
|
39
|
+
if append_arguments:
|
|
40
|
+
parser = SolutionScript.append_arguments(solution)
|
|
41
|
+
if goal == Solution.Action.INSTALL:
|
|
42
|
+
solution.setup().install()
|
|
43
|
+
if goal == Solution.Action.UNINSTALL:
|
|
44
|
+
solution.setup().uninstall()
|
|
45
|
+
if goal == Solution.Action.RUN:
|
|
46
|
+
SolutionScript.execute_run_action(solution)
|
|
47
|
+
if goal == Solution.Action.TEST:
|
|
48
|
+
if 'pre_test' in solution.setup():
|
|
49
|
+
d = solution.setup().pre_test()
|
|
50
|
+
else:
|
|
51
|
+
d = {}
|
|
52
|
+
if d is None:
|
|
53
|
+
d = {}
|
|
54
|
+
sys.argv = sys.argv + ["=".join([c, d[c]]) for c in d]
|
|
55
|
+
|
|
56
|
+
# parse args again after pre_test() routine if necessary.
|
|
57
|
+
if parser and "args" in solution.setup().keys():
|
|
58
|
+
args = parser.parse_args()
|
|
59
|
+
solution.set_args(args)
|
|
60
|
+
|
|
61
|
+
SolutionScript.execute_run_action(solution)
|
|
62
|
+
solution.setup().test()
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def execute_run_action(solution):
|
|
66
|
+
get_active_logger().info("Starting %s" % solution.setup().name)
|
|
67
|
+
if solution.setup().run and callable(solution.setup().run):
|
|
68
|
+
solution.setup().run()
|
|
69
|
+
else:
|
|
70
|
+
get_active_logger().warn(
|
|
71
|
+
"No \"run\" routine configured for solution \"%s\"." % solution.setup().name)
|
|
72
|
+
if solution.setup().close and callable(solution.setup().close):
|
|
73
|
+
solution.setup().close()
|
|
74
|
+
get_active_logger().info("Finished %s" % solution.setup().name)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def init_logging():
|
|
78
|
+
configure_logging("script", loglevel=album_logging.to_loglevel(album_logging.get_loglevel_name()),
|
|
79
|
+
stream_handler=sys.stdout,
|
|
80
|
+
formatter_string=SolutionScript.get_script_logging_formatter_str())
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def api_access(solution: ISolution, package_path, installation_base_path, environment_path):
|
|
84
|
+
if package_path:
|
|
85
|
+
solution.installation().set_package_path(package_path)
|
|
86
|
+
sys.path.insert(0, solution.installation().package_path())
|
|
87
|
+
if installation_base_path:
|
|
88
|
+
solution.installation().set_installation_path(installation_base_path)
|
|
89
|
+
# add app_path to syspath
|
|
90
|
+
sys.path.insert(0, solution.installation().app_path())
|
|
91
|
+
if environment_path:
|
|
92
|
+
solution.installation().set_environment_path(environment_path)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def append_arguments(solution: ISolution):
|
|
96
|
+
parser = None
|
|
97
|
+
if isinstance(solution.setup().args, str):
|
|
98
|
+
SolutionScript._handle_args_string(solution.setup().args)
|
|
99
|
+
else:
|
|
100
|
+
parser = SolutionScript._handle_args_list(solution)
|
|
101
|
+
return parser
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _handle_args_string(args):
|
|
105
|
+
# pass through to module
|
|
106
|
+
if args == 'pass-through':
|
|
107
|
+
get_active_logger().info(
|
|
108
|
+
'Argument parsing not specified in album solution. Passing arguments through...'
|
|
109
|
+
)
|
|
110
|
+
else:
|
|
111
|
+
message = 'Argument keyword \'%s\' not supported!' % args
|
|
112
|
+
get_active_logger().error(message)
|
|
113
|
+
raise ArgumentError(argument=args, message=message)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _handle_args_list(solution: ISolution):
|
|
117
|
+
parser = argparse.ArgumentParser(description='album run %s' % solution.setup().name)
|
|
118
|
+
for arg in solution.setup().args:
|
|
119
|
+
SolutionScript._add_parser_argument(solution, parser, arg)
|
|
120
|
+
args = parser.parse_args()
|
|
121
|
+
solution.set_args(args)
|
|
122
|
+
return parser
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _add_parser_argument(solution, parser, arg):
|
|
126
|
+
keys = arg.keys()
|
|
127
|
+
|
|
128
|
+
if 'default' in keys and 'action' in keys:
|
|
129
|
+
get_active_logger().warning("Default values cannot be automatically set when an action is provided! "
|
|
130
|
+
"Ignoring default values...")
|
|
131
|
+
|
|
132
|
+
args = {}
|
|
133
|
+
if 'action' in keys:
|
|
134
|
+
args['action'] = SolutionScript.make_action(solution, arg['name'])
|
|
135
|
+
if 'default' in keys:
|
|
136
|
+
args['default'] = arg['default']
|
|
137
|
+
if 'description' in keys:
|
|
138
|
+
args['help'] = arg['description']
|
|
139
|
+
if 'type' in keys:
|
|
140
|
+
args['type'] = SolutionScript._parse_type(arg['type'])
|
|
141
|
+
if 'required' in keys:
|
|
142
|
+
args['required'] = arg['required']
|
|
143
|
+
parser.add_argument('--%s' % arg['name'], **args)
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _get_action_class_name(name):
|
|
147
|
+
class_name = '%sAction' % name.capitalize()
|
|
148
|
+
return class_name
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def strtobool(val):
|
|
152
|
+
val = val.lower()
|
|
153
|
+
if val in ('y', 'yes', 't', 'true', 'on', '1'):
|
|
154
|
+
return True
|
|
155
|
+
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
|
|
156
|
+
return False
|
|
157
|
+
else:
|
|
158
|
+
raise ValueError("invalid truth value %r" % (val,))
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _parse_type(type_str):
|
|
162
|
+
if type_str == 'string':
|
|
163
|
+
return str
|
|
164
|
+
if type_str == 'file':
|
|
165
|
+
return Path
|
|
166
|
+
if type_str == 'directory':
|
|
167
|
+
return Path
|
|
168
|
+
if type_str == 'integer':
|
|
169
|
+
return int
|
|
170
|
+
if type_str == 'float':
|
|
171
|
+
return float
|
|
172
|
+
if type_str == 'boolean':
|
|
173
|
+
return SolutionScript.strtobool
|
|
File without changes
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$ref": "#/definitions/RDF",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
|
+
"definitions": {
|
|
5
|
+
"Parent": {
|
|
6
|
+
"anyOf": [
|
|
7
|
+
{
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"group": {
|
|
11
|
+
"title": "group",
|
|
12
|
+
"type": "string"
|
|
13
|
+
},
|
|
14
|
+
"name": {
|
|
15
|
+
"title": "name",
|
|
16
|
+
"type": "string"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"title": "version",
|
|
20
|
+
"type": "string"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"required": [
|
|
24
|
+
"group",
|
|
25
|
+
"name",
|
|
26
|
+
"version"
|
|
27
|
+
]
|
|
28
|
+
}, {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"resolve_solution": {
|
|
32
|
+
"title": "resolve_solution",
|
|
33
|
+
"type": "string"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": [
|
|
37
|
+
"resolve_solution"
|
|
38
|
+
]
|
|
39
|
+
}, {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"doi": {
|
|
43
|
+
"title": "doi",
|
|
44
|
+
"type": "string"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"doi"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
"Argument": {
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"name": {
|
|
57
|
+
"title": "name",
|
|
58
|
+
"type": "string"
|
|
59
|
+
},
|
|
60
|
+
"description": {
|
|
61
|
+
"title": "description",
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"type": {
|
|
65
|
+
"title": "type",
|
|
66
|
+
"type": "string",
|
|
67
|
+
"enum": ["string", "file", "directory", "boolean", "integer", "float"]
|
|
68
|
+
},
|
|
69
|
+
"default": {
|
|
70
|
+
"title": "default"
|
|
71
|
+
},
|
|
72
|
+
"required": {
|
|
73
|
+
"title": "required",
|
|
74
|
+
"type": "boolean"
|
|
75
|
+
},
|
|
76
|
+
"action": {
|
|
77
|
+
"title": "action"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"name"
|
|
82
|
+
],
|
|
83
|
+
"type": "object"
|
|
84
|
+
},
|
|
85
|
+
"Dependencies": {
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"properties": {
|
|
88
|
+
"parent": {
|
|
89
|
+
"title": "parent",
|
|
90
|
+
"$ref": "#/definitions/Parent"
|
|
91
|
+
},
|
|
92
|
+
"environment_file": {
|
|
93
|
+
"title": "environment_file"
|
|
94
|
+
},
|
|
95
|
+
"plugins": {
|
|
96
|
+
"items": {
|
|
97
|
+
"$ref": "#/definitions/Plugin"
|
|
98
|
+
},
|
|
99
|
+
"title": "plugins",
|
|
100
|
+
"type": "array"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"required": [],
|
|
104
|
+
"type": "object"
|
|
105
|
+
},
|
|
106
|
+
"Plugin": {
|
|
107
|
+
"additionalProperties": false,
|
|
108
|
+
"properties": {
|
|
109
|
+
"id": {
|
|
110
|
+
"title": "id",
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"args": {
|
|
114
|
+
"title": "args",
|
|
115
|
+
"type": "object"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"required": [
|
|
119
|
+
"id"
|
|
120
|
+
],
|
|
121
|
+
"type": "object"
|
|
122
|
+
},
|
|
123
|
+
"Citation": {
|
|
124
|
+
"additionalProperties": false,
|
|
125
|
+
"properties": {
|
|
126
|
+
"doi": {
|
|
127
|
+
"title": "doi",
|
|
128
|
+
"type": "string"
|
|
129
|
+
},
|
|
130
|
+
"text": {
|
|
131
|
+
"title": "text",
|
|
132
|
+
"type": "string"
|
|
133
|
+
},
|
|
134
|
+
"url": {
|
|
135
|
+
"title": "url",
|
|
136
|
+
"type": "string"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"required": [
|
|
140
|
+
"text"
|
|
141
|
+
],
|
|
142
|
+
"type": "object"
|
|
143
|
+
},
|
|
144
|
+
"Cover": {
|
|
145
|
+
"additionalProperties": false,
|
|
146
|
+
"properties": {
|
|
147
|
+
"source": {
|
|
148
|
+
"title": "source",
|
|
149
|
+
"type": "string"
|
|
150
|
+
},
|
|
151
|
+
"description": {
|
|
152
|
+
"title": "description",
|
|
153
|
+
"type": "string"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"type": "object"
|
|
157
|
+
},
|
|
158
|
+
"RDF": {
|
|
159
|
+
"additionalProperties": false,
|
|
160
|
+
"properties": {
|
|
161
|
+
"solution_creators": {
|
|
162
|
+
"type": "array",
|
|
163
|
+
"title": "solution_creators",
|
|
164
|
+
"items": {
|
|
165
|
+
"type": "string"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"args": {
|
|
169
|
+
"items": {
|
|
170
|
+
"$ref": "#/definitions/Argument"
|
|
171
|
+
},
|
|
172
|
+
"type": "array"
|
|
173
|
+
},
|
|
174
|
+
"cite": {
|
|
175
|
+
"items": {
|
|
176
|
+
"$ref": "#/definitions/Citation"
|
|
177
|
+
},
|
|
178
|
+
"type": "array"
|
|
179
|
+
},
|
|
180
|
+
"covers": {
|
|
181
|
+
"items": {
|
|
182
|
+
"$ref": "#/definitions/Cover"
|
|
183
|
+
},
|
|
184
|
+
"title": "covers",
|
|
185
|
+
"type": "array"
|
|
186
|
+
},
|
|
187
|
+
"description": {
|
|
188
|
+
"title": "description",
|
|
189
|
+
"type": "string"
|
|
190
|
+
},
|
|
191
|
+
"documentation": {
|
|
192
|
+
"title": "documentation",
|
|
193
|
+
"anyOf": [
|
|
194
|
+
{
|
|
195
|
+
"type": "string"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"type": "array",
|
|
199
|
+
"items": {
|
|
200
|
+
"type": "string"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
"acknowledgement": {
|
|
206
|
+
"title": "acknowledgement",
|
|
207
|
+
"type": "string"
|
|
208
|
+
},
|
|
209
|
+
"license": {
|
|
210
|
+
"title": "license",
|
|
211
|
+
"type": "string"
|
|
212
|
+
},
|
|
213
|
+
"name": {
|
|
214
|
+
"title": "name",
|
|
215
|
+
"type": "string"
|
|
216
|
+
},
|
|
217
|
+
"group": {
|
|
218
|
+
"title": "group",
|
|
219
|
+
"type": "string"
|
|
220
|
+
},
|
|
221
|
+
"title": {
|
|
222
|
+
"title": "title",
|
|
223
|
+
"type": "string"
|
|
224
|
+
},
|
|
225
|
+
"album_api_version": {
|
|
226
|
+
"title": "album_api_version",
|
|
227
|
+
"type": "string"
|
|
228
|
+
},
|
|
229
|
+
"tags": {
|
|
230
|
+
"items": {
|
|
231
|
+
"type": "string"
|
|
232
|
+
},
|
|
233
|
+
"title": "tags",
|
|
234
|
+
"type": "array"
|
|
235
|
+
},
|
|
236
|
+
"version": {
|
|
237
|
+
"title": "version",
|
|
238
|
+
"type": "string"
|
|
239
|
+
},
|
|
240
|
+
"changelog": {
|
|
241
|
+
"title": "changelog",
|
|
242
|
+
"type": "string"
|
|
243
|
+
},
|
|
244
|
+
"doi": {
|
|
245
|
+
"title": "doi",
|
|
246
|
+
"type": "string"
|
|
247
|
+
},
|
|
248
|
+
"dependencies": {
|
|
249
|
+
"title": "dependencies",
|
|
250
|
+
"type": "object",
|
|
251
|
+
"$ref": "#/definitions/Dependencies"
|
|
252
|
+
},
|
|
253
|
+
"steps": {
|
|
254
|
+
"title": "steps"
|
|
255
|
+
},
|
|
256
|
+
"custom": {
|
|
257
|
+
"title": "custom",
|
|
258
|
+
"type": "object"
|
|
259
|
+
},
|
|
260
|
+
"install": {
|
|
261
|
+
"title": "install"
|
|
262
|
+
},
|
|
263
|
+
"uninstall": {
|
|
264
|
+
"title": "uninstall"
|
|
265
|
+
},
|
|
266
|
+
"run": {
|
|
267
|
+
"title": "run"
|
|
268
|
+
},
|
|
269
|
+
"test": {
|
|
270
|
+
"title": "test"
|
|
271
|
+
},
|
|
272
|
+
"pre_test": {
|
|
273
|
+
"title": "pre_test"
|
|
274
|
+
},
|
|
275
|
+
"close": {
|
|
276
|
+
"title": "close"
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"required": [
|
|
280
|
+
"group",
|
|
281
|
+
"name",
|
|
282
|
+
"version"
|
|
283
|
+
],
|
|
284
|
+
"type": "object"
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: album-solution-api
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Album is a framework for unifying computational tools across frameworks, languages, and environments.
|
|
5
|
+
Home-page: https://gitlab.com/album-app/album-solution-api
|
|
6
|
+
Author: Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
7
|
+
Maintainer: Kyle Harrington, Jan Philipp Albrecht, Deborah Schmidt
|
|
8
|
+
Maintainer-email: album@kyleharrington.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Project-URL: Source, https://gitlab.com/album-app/album-solution-api
|
|
11
|
+
Project-URL: Tracker, https://gitlab.com/album-app/album-solution-api/issues
|
|
12
|
+
Keywords: virtual,environments,imaging
|
|
13
|
+
Platform: any
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Operating System :: POSIX
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.6
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
|
|
27
|
+
# album-solution-api
|
|
28
|
+
|
|
29
|
+
Package installed in any Album solution environment. Provides the API for the solution script.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.cfg
|
|
5
|
+
setup.py
|
|
6
|
+
src/album/__init__.py
|
|
7
|
+
src/album/runner/__init__.py
|
|
8
|
+
src/album/runner/album_logging.py
|
|
9
|
+
src/album/runner/api.py
|
|
10
|
+
src/album/runner/core/__init__.py
|
|
11
|
+
src/album/runner/core/default_values_runner.py
|
|
12
|
+
src/album/runner/core/api/__init__.py
|
|
13
|
+
src/album/runner/core/api/model/__init__.py
|
|
14
|
+
src/album/runner/core/api/model/coordinates.py
|
|
15
|
+
src/album/runner/core/api/model/solution.py
|
|
16
|
+
src/album/runner/core/model/__init__.py
|
|
17
|
+
src/album/runner/core/model/coordinates.py
|
|
18
|
+
src/album/runner/core/model/solution.py
|
|
19
|
+
src/album/runner/core/model/solution_script.py
|
|
20
|
+
src/album/runner/core/schema/__init__.py
|
|
21
|
+
src/album/runner/core/schema/solution_schema.json
|
|
22
|
+
src/album_solution_api.egg-info/PKG-INFO
|
|
23
|
+
src/album_solution_api.egg-info/SOURCES.txt
|
|
24
|
+
src/album_solution_api.egg-info/dependency_links.txt
|
|
25
|
+
src/album_solution_api.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
album
|