ciocore 5.1.1__py2.py3-none-any.whl → 10.0.0b3__py2.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.
- ciocore/VERSION +1 -1
- ciocore/__init__.py +23 -1
- ciocore/api_client.py +655 -160
- ciocore/auth/__init__.py +5 -3
- ciocore/cli.py +501 -0
- ciocore/common.py +15 -13
- ciocore/conductor_submit.py +77 -60
- ciocore/config.py +127 -13
- ciocore/data.py +162 -77
- ciocore/docsite/404.html +746 -0
- ciocore/docsite/apidoc/api_client/index.html +3605 -0
- ciocore/docsite/apidoc/apidoc/index.html +909 -0
- ciocore/docsite/apidoc/config/index.html +1652 -0
- ciocore/docsite/apidoc/data/index.html +1553 -0
- ciocore/docsite/apidoc/hardware_set/index.html +2460 -0
- ciocore/docsite/apidoc/package_environment/index.html +1507 -0
- ciocore/docsite/apidoc/package_tree/index.html +2386 -0
- ciocore/docsite/assets/_mkdocstrings.css +16 -0
- ciocore/docsite/assets/images/favicon.png +0 -0
- ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js +29 -0
- ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js.map +7 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- ciocore/docsite/assets/javascripts/lunr/tinyseg.js +206 -0
- ciocore/docsite/assets/javascripts/lunr/wordcut.js +6708 -0
- ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js +42 -0
- ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js.map +7 -0
- ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css +1 -0
- ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
- ciocore/docsite/assets/stylesheets/palette.06af60db.min.css +1 -0
- ciocore/docsite/assets/stylesheets/palette.06af60db.min.css.map +1 -0
- ciocore/docsite/cmdline/docs/index.html +871 -0
- ciocore/docsite/cmdline/downloader/index.html +934 -0
- ciocore/docsite/cmdline/packages/index.html +878 -0
- ciocore/docsite/cmdline/uploader/index.html +995 -0
- ciocore/docsite/how-to-guides/index.html +869 -0
- ciocore/docsite/index.html +895 -0
- ciocore/docsite/logo.png +0 -0
- ciocore/docsite/objects.inv +0 -0
- ciocore/docsite/search/search_index.json +1 -0
- ciocore/docsite/sitemap.xml +3 -0
- ciocore/docsite/sitemap.xml.gz +0 -0
- ciocore/docsite/stylesheets/extra.css +26 -0
- ciocore/docsite/stylesheets/tables.css +167 -0
- ciocore/downloader/base_downloader.py +644 -0
- ciocore/downloader/download_runner_base.py +47 -0
- ciocore/downloader/job_downloader.py +119 -0
- ciocore/{downloader.py → downloader/legacy_downloader.py} +12 -9
- ciocore/downloader/log.py +73 -0
- ciocore/downloader/logging_download_runner.py +87 -0
- ciocore/downloader/perpetual_downloader.py +63 -0
- ciocore/downloader/registry.py +97 -0
- ciocore/downloader/reporter.py +135 -0
- ciocore/exceptions.py +8 -2
- ciocore/file_utils.py +51 -50
- ciocore/hardware_set.py +449 -0
- ciocore/loggeria.py +89 -20
- ciocore/package_environment.py +110 -48
- ciocore/package_query.py +182 -0
- ciocore/package_tree.py +319 -258
- ciocore/retry.py +0 -0
- ciocore/uploader/_uploader.py +547 -364
- ciocore/uploader/thread_queue_job.py +176 -0
- ciocore/uploader/upload_stats/__init__.py +3 -4
- ciocore/uploader/upload_stats/stats_formats.py +10 -4
- ciocore/validator.py +34 -2
- ciocore/worker.py +174 -151
- ciocore-10.0.0b3.dist-info/METADATA +928 -0
- ciocore-10.0.0b3.dist-info/RECORD +128 -0
- {ciocore-5.1.1.dist-info → ciocore-10.0.0b3.dist-info}/WHEEL +1 -1
- ciocore-10.0.0b3.dist-info/entry_points.txt +2 -0
- tests/instance_type_fixtures.py +175 -0
- tests/package_fixtures.py +205 -0
- tests/test_api_client.py +297 -12
- tests/test_base_downloader.py +104 -0
- tests/test_cli.py +149 -0
- tests/test_common.py +1 -7
- tests/test_config.py +40 -18
- tests/test_data.py +162 -173
- tests/test_downloader.py +118 -0
- tests/test_hardware_set.py +139 -0
- tests/test_job_downloader.py +213 -0
- tests/test_package_query.py +38 -0
- tests/test_package_tree.py +91 -291
- tests/test_submit.py +44 -18
- tests/test_uploader.py +1 -4
- ciocore/__about__.py +0 -10
- ciocore/cli/conductor.py +0 -191
- ciocore/compat.py +0 -15
- ciocore-5.1.1.data/scripts/conductor +0 -19
- ciocore-5.1.1.data/scripts/conductor.bat +0 -13
- ciocore-5.1.1.dist-info/METADATA +0 -408
- ciocore-5.1.1.dist-info/RECORD +0 -47
- tests/mocks/api_client_mock.py +0 -51
- /ciocore/{cli → downloader}/__init__.py +0 -0
- {ciocore-5.1.1.dist-info → ciocore-10.0.0b3.dist-info}/top_level.txt +0 -0
ciocore/package_tree.py
CHANGED
|
@@ -1,50 +1,325 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
A class to provide available packages as DAG structure. In reality however, the structure is just two levels deep: **hosts** and **plugins**.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
* DCCs such as **Maya** and **Cinema4D** are top-level host packages.
|
|
5
|
+
* Renderers and other plugins are children of those hosts.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
"""
|
|
7
|
+
Methods are provided to traverse the tree to find packages by name, version, platform and so on. If you are writing submission tools there's no need to create a Package tree directly. It is recommended to use the singleton module: [ciocore.data](/data/).
|
|
8
8
|
|
|
9
|
+
The only functions you should need from this module are:
|
|
10
|
+
|
|
11
|
+
* [supported_host_names()](/package_tree/#ciocore.package_tree.PackageTree.supported_host_names)
|
|
12
|
+
* [supported_plugins()](/package_tree/#ciocore.package_tree.PackageTree.supported_plugins)
|
|
13
|
+
"""
|
|
9
14
|
|
|
10
15
|
import copy
|
|
11
16
|
import json
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
WINDOWS = "windows"
|
|
19
|
+
LINUX = "linux"
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
LINUX
|
|
21
|
+
#: The set of supported platforms, currently windows and linux.
|
|
22
|
+
PLATFORMS = {WINDOWS, LINUX}
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
class PackageTree(object):
|
|
25
|
+
def __init__(self, packages, *host_products, **kwargs):
|
|
26
|
+
"""Build the tree with a list of packages.
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
Args:
|
|
29
|
+
packages (list): List of packages direct from the [Conductor packages endpoint](https://dashboard.conductortech.com/api/v1/ee/packages).
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
*host_products: Filter the tree to contain only top-level host packages of products specified in this list and their plugins. If there are no host_products specified, and the product keyword is omitted, the tree contains all packages.
|
|
32
|
+
|
|
33
|
+
Keyword Args:
|
|
34
|
+
product (str): Build the tree from versions of a single product and its compatible plugins. Defaults to `None`, in which case the tree is built from all packages. It is an error to specify both host_products and product. If a nonexistent product is given, the PackageTree is empty. By specifying `product`, you can build the object based on a single plugin product.
|
|
35
|
+
platforms (set): Build the tree from versions for a specific platform. Defaults to the set `{"linux", "windows"}`.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
KeyError: An invalid platform was provided.
|
|
39
|
+
ValueError: Cannot choose both product and host_products.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> from ciocore import api_client, package_tree
|
|
43
|
+
# Request packages as a flat list from Conductor.
|
|
44
|
+
>>> packages = api_client.request_software_packages()
|
|
45
|
+
# Build tree of dependencies from packages list
|
|
46
|
+
>>> pt = package_tree.PackageTree(packages, "cinema4d", "maya-io")
|
|
47
|
+
>>> for path in pt.to_path_list():
|
|
48
|
+
>>> print(path)
|
|
49
|
+
cinema4d 22.118.RB320081 linux
|
|
50
|
+
cinema4d 22.118.RB320081 linux/redshift-cinema4d 3.0.43 linux
|
|
51
|
+
cinema4d 22.118.RB320081 linux/redshift-cinema4d 3.0.45 linux
|
|
52
|
+
maya-io 2022.SP3 linux
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
platforms = kwargs.get("platforms",PLATFORMS)
|
|
57
|
+
product=kwargs.get("product")
|
|
58
|
+
|
|
59
|
+
unknown_platforms = set(platforms) - PLATFORMS
|
|
60
|
+
if unknown_platforms:
|
|
61
|
+
raise KeyError("Unrecognized platform {}".format(" ".join(unknown_platforms)))
|
|
62
|
+
|
|
63
|
+
if product and host_products:
|
|
64
|
+
raise ValueError("You cannot choose both product and host_products.")
|
|
65
|
+
|
|
66
|
+
packages = [_clean_package(p) for p in packages if p["platform"] in platforms]
|
|
67
|
+
|
|
68
|
+
root_ids = []
|
|
69
|
+
if product:
|
|
70
|
+
root_ids = [p["package_id"] for p in packages if p["product"] == product]
|
|
71
|
+
else:
|
|
72
|
+
for p in packages:
|
|
73
|
+
if not p["plugin_host_product"]:
|
|
74
|
+
if p["product"] in host_products or not host_products:
|
|
75
|
+
root_ids.append(p["package_id"])
|
|
76
|
+
|
|
77
|
+
self._tree = _build_tree(packages, {"children": [], "plugins": root_ids})
|
|
78
|
+
|
|
79
|
+
def supported_host_names(self):
|
|
80
|
+
"""
|
|
81
|
+
All host names from the software tree.
|
|
82
|
+
|
|
83
|
+
These names can be used to populate a dropdown menu. Then a single selection from that menu
|
|
84
|
+
can be used to retrieve the complete package in order to generate an environment dictionary
|
|
85
|
+
and get package IDs.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
list(str): Fully qualified DCC hosts of the form: `product version platform`.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> from ciocore import api_client, package_tree
|
|
92
|
+
>>> packages = api_client.request_software_packages()
|
|
93
|
+
>>> pt = package_tree.PackageTree(packages, product="cinema4d")
|
|
94
|
+
>>> pt.supported_host_names()
|
|
95
|
+
cinema4d 21.209.RB305619 linux
|
|
96
|
+
cinema4d 22.116.RB316423 linux
|
|
97
|
+
cinema4d 22.118.RB320081 linux
|
|
98
|
+
cinema4d 23.110.RB330286 linux
|
|
99
|
+
cinema4d 24.111 linux
|
|
100
|
+
cinema4d 24.111 windows
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
paths = []
|
|
104
|
+
for pkg in self._tree["children"]:
|
|
105
|
+
paths.append(to_name(pkg))
|
|
106
|
+
return sorted(paths)
|
|
107
|
+
|
|
108
|
+
def supported_plugins(self, host):
|
|
109
|
+
"""
|
|
110
|
+
Find the plugins that are children of the given DCC host.
|
|
111
|
+
|
|
112
|
+
The result does not contain platform information since we assume that plugins are compatible with the DCC host that was used to request them.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
host (str): Name of the DCC host, typically one of the entries returned by [supported_host_names()](/package_tree/#ciocore.package_tree.PackageTree.supported_host_names).
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
list(dict): Each entry contains a plugin product and a list of versions.
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
>>> from ciocore import api_client, package_tree
|
|
122
|
+
>>> packages = api_client.request_software_packages()
|
|
123
|
+
>>> pt = package_tree.PackageTree(packages, product="cinema4d")
|
|
124
|
+
>>> name = pt.supported_host_names()[0]
|
|
125
|
+
>>> pt.supported_plugins(name)
|
|
126
|
+
[
|
|
127
|
+
{
|
|
128
|
+
"plugin": "arnold-cinema4d",
|
|
129
|
+
"versions": [
|
|
130
|
+
"3.3.2.100",
|
|
131
|
+
"3.3.3.0"
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"plugin": "redshift-cinema4d",
|
|
136
|
+
"versions": [
|
|
137
|
+
"2.6.54",
|
|
138
|
+
"2.6.56",
|
|
139
|
+
"3.0.21",
|
|
140
|
+
"3.0.22",
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
]
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
subtree = self.find_by_name(host)
|
|
148
|
+
plugin_versions = _to_path_list(subtree)
|
|
149
|
+
except TypeError:
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
if not plugin_versions:
|
|
153
|
+
return []
|
|
154
|
+
|
|
155
|
+
plugin_dict = {}
|
|
156
|
+
for plugin, version, _ in [pv.split(" ") for pv in plugin_versions]:
|
|
157
|
+
if plugin not in plugin_dict:
|
|
158
|
+
plugin_dict[plugin] = []
|
|
159
|
+
plugin_dict[plugin].append(version)
|
|
160
|
+
|
|
161
|
+
# convert to list so it can be sorted
|
|
162
|
+
plugins = []
|
|
163
|
+
for key in plugin_dict:
|
|
164
|
+
plugins.append({"plugin": key, "versions": sorted(plugin_dict[key])})
|
|
165
|
+
|
|
166
|
+
return sorted(plugins, key=lambda k: k["plugin"])
|
|
167
|
+
|
|
168
|
+
def find_by_name(self, name, limit=None):
|
|
169
|
+
"""
|
|
170
|
+
Search the tree for a product with the given name.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
name (str): The name constructed from the package using to_name(). It must be an exact match with product, version, and platform. For example: `maya 2018.0 windows`
|
|
174
|
+
|
|
175
|
+
Keyword Args:
|
|
176
|
+
limit (int): Limit the search depth. Defaults to `None`.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
object: The package that matches.
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> from ciocore import api_client, package_tree
|
|
183
|
+
>>> packages = api_client.request_software_packages()
|
|
184
|
+
>>> pt = package_tree.PackageTree(packages, product="cinema4d")
|
|
185
|
+
>>> pt.find_by_name("redshift-cinema4d 3.0.64 linux")
|
|
186
|
+
{
|
|
187
|
+
'platform': 'linux',
|
|
188
|
+
'plugin_host_product': 'cinema4d',
|
|
189
|
+
'product': 'redshift-cinema4d',
|
|
190
|
+
'major_version': '3',
|
|
191
|
+
'release_version': '64',
|
|
192
|
+
'vendor': 'maxon',
|
|
193
|
+
'children': [],
|
|
194
|
+
...
|
|
195
|
+
}
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
return _find_by_name(self._tree, name, limit, 0)
|
|
199
|
+
|
|
200
|
+
def find_by_path(self, path):
|
|
201
|
+
"""
|
|
202
|
+
Find the package uniquely described by the given path.
|
|
203
|
+
|
|
204
|
+
The path is of the form returned by the to_path_list() method.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
path (str): The path
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
object: The package or None if no package exists with the given path.
|
|
211
|
+
|
|
212
|
+
Example:
|
|
213
|
+
>>> from ciocore import api_client, package_tree, package_environment
|
|
214
|
+
>>> packages = api_client.request_software_packages()
|
|
215
|
+
>>> pt = package_tree.PackageTree(packages, product="cinema4d")
|
|
216
|
+
>>> pt.find_by_path("cinema4d 24.111 linux/redshift-cinema4d 3.0.62 linux")
|
|
217
|
+
{
|
|
218
|
+
'platform': 'linux',
|
|
219
|
+
'plugin_host_product': 'cinema4d',
|
|
220
|
+
'product': 'redshift-cinema4d',
|
|
221
|
+
'major_version': '3',
|
|
222
|
+
'release_version': '62',
|
|
223
|
+
'vendor': 'maxon',
|
|
224
|
+
'children': [],
|
|
225
|
+
'plugin_host_version': "24",
|
|
226
|
+
...
|
|
227
|
+
}
|
|
228
|
+
"""
|
|
229
|
+
return _find_by_path(self._tree, path)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def to_path_list(self, name=None):
|
|
233
|
+
"""
|
|
234
|
+
Get paths to all nodes.
|
|
235
|
+
|
|
236
|
+
This is useful for populating a chooser to choose packages fully qualified by path.
|
|
237
|
+
Houdini's tree widget, for example, takes the below format unchanged and generates the
|
|
238
|
+
appropriate UI.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
name (str): Get paths below the tree represented by the name. Defaults to None (root node).
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
list(str): Paths to all nodes in the tree.
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
>>> from ciocore import api_client, package_tree
|
|
248
|
+
>>> packages = api_client.request_software_packages()
|
|
249
|
+
>>> pt = package_tree.PackageTree(packages, product="cinema4d")
|
|
250
|
+
>>> pt.to_path_list()
|
|
251
|
+
cinema4d 22.118.RB320081 linux
|
|
252
|
+
cinema4d 22.118.RB320081 linux/redshift-cinema4d 3.0.43 linux
|
|
253
|
+
cinema4d 22.118.RB320081 linux/redshift-cinema4d 3.0.45 linux
|
|
254
|
+
cinema4d 22.118.RB320081 linux/redshift-cinema4d 3.0.22 linux
|
|
255
|
+
cinema4d 22.118.RB320081 linux/arnold-cinema4d 3.3.2.100 linux
|
|
256
|
+
...
|
|
257
|
+
|
|
258
|
+
>>> pt.to_path_list(name="cinema4d 24.111 linux")
|
|
259
|
+
redshift-cinema4d 3.0.57 linux
|
|
260
|
+
redshift-cinema4d 3.0.62 linux
|
|
261
|
+
redshift-cinema4d 3.0.45 linux
|
|
262
|
+
redshift-cinema4d 3.0.64 linux
|
|
263
|
+
"""
|
|
264
|
+
if name:
|
|
265
|
+
subtree = self.find_by_name(name)
|
|
266
|
+
return _to_path_list(subtree)
|
|
267
|
+
return _to_path_list(self._tree)
|
|
268
|
+
|
|
269
|
+
def platforms(self):
|
|
270
|
+
"""
|
|
271
|
+
Get the platforms represented by packages in the tree.
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
set: The set of platforms.
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
# No need to recurse. Plugins are assumed to be compatible with the host.
|
|
278
|
+
return set([host["platform"] for host in self._tree["children"]])
|
|
279
|
+
|
|
280
|
+
def json(self):
|
|
281
|
+
"""
|
|
282
|
+
The whole tree of softwware as json.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
str: JSON.
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
return json.dumps(self._tree)
|
|
289
|
+
|
|
290
|
+
def __bool__(self):
|
|
291
|
+
return True if self._tree["children"] else False
|
|
292
|
+
|
|
293
|
+
def as_dict(self):
|
|
294
|
+
"""
|
|
295
|
+
Returns:
|
|
296
|
+
dict: The underlying software dictionary.
|
|
297
|
+
|
|
298
|
+
"""
|
|
299
|
+
return self._tree
|
|
38
300
|
|
|
39
301
|
|
|
40
302
|
def to_name(pkg):
|
|
41
|
-
"""
|
|
303
|
+
"""
|
|
304
|
+
Generate a name like `houdini 16.5.323 linux` or `maya 2016.SP3 linux`.
|
|
42
305
|
|
|
43
306
|
This name is derived from the product and version fields in a package. Note: It is not
|
|
44
|
-
necessarily possible to go the other way and extract version fields from the name.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
307
|
+
necessarily possible to go the other way and extract version fields from the name.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
pkg (object): An object with product, platform, and all version fields.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
str: The package name.
|
|
314
|
+
|
|
315
|
+
Examples:
|
|
316
|
+
>>> from ciocore import api_client, package_tree
|
|
317
|
+
>>> packages = api_client.request_software_packages()
|
|
318
|
+
>>> package_tree.to_name(packages[0])
|
|
319
|
+
redshift-maya 3.0.64 linux
|
|
320
|
+
|
|
321
|
+
"""
|
|
322
|
+
|
|
48
323
|
version_parts = [
|
|
49
324
|
pkg["major_version"],
|
|
50
325
|
pkg["minor_version"],
|
|
@@ -52,15 +327,15 @@ def to_name(pkg):
|
|
|
52
327
|
pkg["build_version"],
|
|
53
328
|
]
|
|
54
329
|
version_string = (".").join([p for p in version_parts if p])
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
330
|
+
if pkg["platform"] not in PLATFORMS:
|
|
331
|
+
raise KeyError("Invalid platform: {}".format(pkg["platform"]))
|
|
332
|
+
return " ".join(filter(None, [pkg["product"], version_string, pkg["platform"]]))
|
|
58
333
|
|
|
59
334
|
|
|
60
335
|
def _build_tree(packages, package):
|
|
61
336
|
"""Build a tree of dependent software plugins.
|
|
62
337
|
|
|
63
|
-
Add a children key, and For each ID in the `plugins` key, add the package it refers to
|
|
338
|
+
Add a children key, and For each ID in the `plugins` key, add the package to which it refers to
|
|
64
339
|
children. Recurse until no more plugins are left.
|
|
65
340
|
"""
|
|
66
341
|
|
|
@@ -73,41 +348,6 @@ def _build_tree(packages, package):
|
|
|
73
348
|
return package
|
|
74
349
|
|
|
75
350
|
|
|
76
|
-
def _is_product(pkg, **kwargs):
|
|
77
|
-
"""Is this pkg the product described in kwargs.
|
|
78
|
-
|
|
79
|
-
Works in one of 2 ways. Match against either 1. display name, or 2. the raw keys (product,
|
|
80
|
-
major_version, minor_version, release_version, build_version, platform). The root node has no `product`
|
|
81
|
-
key because it is a collection of host packages.
|
|
82
|
-
"""
|
|
83
|
-
if not pkg.get("product"):
|
|
84
|
-
return False
|
|
85
|
-
|
|
86
|
-
name = kwargs.get("name")
|
|
87
|
-
if name:
|
|
88
|
-
if name == to_name(pkg):
|
|
89
|
-
return True
|
|
90
|
-
return False
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
for key in kwargs:
|
|
94
|
-
value = kwargs[key]
|
|
95
|
-
pkg_value = pkg[key]
|
|
96
|
-
if value and value != pkg_value:
|
|
97
|
-
return False
|
|
98
|
-
return True
|
|
99
|
-
|
|
100
|
-
def _find_by_keys(tree, **kw):
|
|
101
|
-
"""Given product and version keys find the package."""
|
|
102
|
-
if not tree:
|
|
103
|
-
return None
|
|
104
|
-
if _is_product(tree, **kw):
|
|
105
|
-
return tree
|
|
106
|
-
for child_tree in tree["children"]:
|
|
107
|
-
result = _find_by_keys(child_tree, **kw)
|
|
108
|
-
if result:
|
|
109
|
-
return result
|
|
110
|
-
return None
|
|
111
351
|
|
|
112
352
|
|
|
113
353
|
def _find_by_name(branch, name, limit=None, depth=0):
|
|
@@ -118,7 +358,8 @@ def _find_by_name(branch, name, limit=None, depth=0):
|
|
|
118
358
|
"""
|
|
119
359
|
if not branch:
|
|
120
360
|
return None
|
|
121
|
-
|
|
361
|
+
|
|
362
|
+
if branch.get("product") and name == to_name(branch):
|
|
122
363
|
return branch
|
|
123
364
|
depth += 1
|
|
124
365
|
|
|
@@ -140,7 +381,8 @@ def _find_by_path(tree, path):
|
|
|
140
381
|
|
|
141
382
|
if not path:
|
|
142
383
|
return None
|
|
143
|
-
|
|
384
|
+
|
|
385
|
+
|
|
144
386
|
result = None
|
|
145
387
|
for name in [p for p in path.split("/") if p]:
|
|
146
388
|
tree = _find_by_name(tree, name, 1)
|
|
@@ -153,10 +395,12 @@ def _to_path_list(tree, **kw):
|
|
|
153
395
|
|
|
154
396
|
This means starting at the level of the given tree, get all the paths to intermediate and leaf
|
|
155
397
|
nodes. This is useful for populating a chooser to choose packages fully qualified by path.
|
|
398
|
+
Houdini's tree widget for example, takes the below format unchanged and generates the
|
|
399
|
+
appropriate UI.
|
|
156
400
|
|
|
157
|
-
* 'houdini 16.0.736'
|
|
158
|
-
* 'houdini 16.0.736/arnold-houdini 2.0.1'
|
|
159
|
-
* 'houdini 16.0.736/arnold-houdini 2.0.1/al-shaders 1.0'
|
|
401
|
+
* 'houdini 16.0.736 linux'
|
|
402
|
+
* 'houdini 16.0.736 linux/arnold-houdini 2.0.1 linux'
|
|
403
|
+
* 'houdini 16.0.736 linux/arnold-houdini 2.0.1 linux/al-shaders 1.0 linux'
|
|
160
404
|
"""
|
|
161
405
|
parent_name = kw.get("parent_name", "")
|
|
162
406
|
paths = kw.get("paths", [])
|
|
@@ -168,21 +412,6 @@ def _to_path_list(tree, **kw):
|
|
|
168
412
|
return paths
|
|
169
413
|
|
|
170
414
|
|
|
171
|
-
def to_all_paths(path):
|
|
172
|
-
"""Extract all ancestor paths from a path.
|
|
173
|
-
|
|
174
|
-
This can be useful if the user selects a plugin from a chooser, because we know we'll want its
|
|
175
|
-
host ancestors. Just split the string and work up the parts.
|
|
176
|
-
"""
|
|
177
|
-
result = []
|
|
178
|
-
parts = path.split("/")
|
|
179
|
-
while parts:
|
|
180
|
-
result.append(("/").join(parts))
|
|
181
|
-
parts.pop()
|
|
182
|
-
result.reverse()
|
|
183
|
-
return result
|
|
184
|
-
|
|
185
|
-
|
|
186
415
|
def _clean_package(package):
|
|
187
416
|
"""Remove some unwanted keys.
|
|
188
417
|
|
|
@@ -203,171 +432,3 @@ def _clean_package(package):
|
|
|
203
432
|
pkg["children"] = []
|
|
204
433
|
return pkg
|
|
205
434
|
|
|
206
|
-
def _get_platform_filter(**kwargs):
|
|
207
|
-
"""
|
|
208
|
-
Get the set of desired platforms from kwargs
|
|
209
|
-
If none given, assume all platforms
|
|
210
|
-
Raise on unrecognized input
|
|
211
|
-
"""
|
|
212
|
-
platforms = set(kwargs.get("platforms", PLATFORMS))
|
|
213
|
-
unknown_platforms = platforms - PLATFORMS
|
|
214
|
-
if unknown_platforms:
|
|
215
|
-
raise KeyError("Unrecognized platform {}".format(" ".join(unknown_platforms)))
|
|
216
|
-
return platforms
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
class PackageTree(object):
|
|
220
|
-
"""Class to represent available packages as a tree.
|
|
221
|
-
|
|
222
|
-
Data structure is really a DAG because a tool may be compatible with more than one host product.
|
|
223
|
-
"""
|
|
224
|
-
|
|
225
|
-
def __init__(self, packages, **kwargs):
|
|
226
|
-
"""Initialize based on a product.
|
|
227
|
-
|
|
228
|
-
If product kwarg given then build the tree containing packages below versions of that product
|
|
229
|
-
only. e.g. "houdini" or "maya-io"
|
|
230
|
-
|
|
231
|
-
If platforms kwarg given, filter out non-matching platforms.
|
|
232
|
-
"""
|
|
233
|
-
product = kwargs.get("product")
|
|
234
|
-
|
|
235
|
-
platforms = _get_platform_filter(**kwargs)
|
|
236
|
-
|
|
237
|
-
packages = [_clean_package(p) for p in packages if p["platform"] in platforms]
|
|
238
|
-
|
|
239
|
-
if product:
|
|
240
|
-
root_ids = [p["package_id"] for p in packages if p["product"] == product]
|
|
241
|
-
else:
|
|
242
|
-
root_ids = [p["package_id"] for p in packages if not p["plugin_host_product"]]
|
|
243
|
-
|
|
244
|
-
self.tree = _build_tree(packages, {"children": [], "plugins": root_ids})
|
|
245
|
-
|
|
246
|
-
def find_by_name(self, name, limit=None, depth=0):
|
|
247
|
-
"""Search the tree for a product with the given name.
|
|
248
|
-
|
|
249
|
-
This name is the name originally constructed from the package using to_name.
|
|
250
|
-
|
|
251
|
-
It must be an exact match with product version platform, example: "maya 2018.0 windows".
|
|
252
|
-
"""
|
|
253
|
-
return _find_by_name(self.tree, name, limit, depth)
|
|
254
|
-
|
|
255
|
-
def find_by_keys(self, **kw):
|
|
256
|
-
"""Search the tree for a product with the given keys.
|
|
257
|
-
|
|
258
|
-
Whichever keys from the following are given, must match, product, major_version,
|
|
259
|
-
minor_version, release_version, build_version, platform
|
|
260
|
-
"""
|
|
261
|
-
return _find_by_keys(self.tree, **kw)
|
|
262
|
-
|
|
263
|
-
def find_by_path(self, path):
|
|
264
|
-
"""Find the package uniquely described by this path."""
|
|
265
|
-
return _find_by_path(self.tree, path)
|
|
266
|
-
|
|
267
|
-
def to_path_list(self, **kw):
|
|
268
|
-
"""Get paths to all nodes.
|
|
269
|
-
|
|
270
|
-
Example:
|
|
271
|
-
|
|
272
|
-
'maya-io 2018.SP2 linux',
|
|
273
|
-
'maya-io 2018.SP2 linux/yeti 3.8 linux',
|
|
274
|
-
'maya-io 2018.SP2 linux/yeti 3.1.15 linux',
|
|
275
|
-
'maya-io 2018.SP4 linux',
|
|
276
|
-
'maya-io 2018.SP4 linux/yeti 2.2.2 linux',
|
|
277
|
-
'maya-io 2018.SP4 linux/arnold-maya 2.0.2.3 linux',
|
|
278
|
-
'maya-io 2018.SP4 linux/v-ray-maya 3.60.01.0 linux',
|
|
279
|
-
|
|
280
|
-
If the name keyword is given, get paths below the tree represented by that name.
|
|
281
|
-
|
|
282
|
-
Example: to_path_list(name='maya-io 2018.SP4') 'yeti 2.2.2', 'arnold-maya 2.0.2.3',
|
|
283
|
-
'v-ray-maya 3.60.01.0',
|
|
284
|
-
|
|
285
|
-
A TypeError will be thrown if the name is invalid.
|
|
286
|
-
"""
|
|
287
|
-
name = kw.get("name")
|
|
288
|
-
if name:
|
|
289
|
-
subtree = self.find_by_name(name)
|
|
290
|
-
return _to_path_list(subtree)
|
|
291
|
-
return _to_path_list(self.tree)
|
|
292
|
-
|
|
293
|
-
def get_all_paths_to(self, **kw):
|
|
294
|
-
"""All paths to the package described in kw.
|
|
295
|
-
|
|
296
|
-
Its possible there is more than one path to a given node. For now we just get all paths
|
|
297
|
-
through the tree and then select the ones whose leaf matches.
|
|
298
|
-
"""
|
|
299
|
-
all_paths = _to_path_list(self.tree)
|
|
300
|
-
name = to_name(kw)
|
|
301
|
-
return [p for p in all_paths if p.endswith(name)]
|
|
302
|
-
|
|
303
|
-
def platforms(self):
|
|
304
|
-
"""
|
|
305
|
-
Get the set of platforms represented by packages in the tree.
|
|
306
|
-
|
|
307
|
-
Returns a set
|
|
308
|
-
"""
|
|
309
|
-
|
|
310
|
-
# No need to recurse. Plugins are assumed to be compatible with the host.
|
|
311
|
-
return set([host["platform"] for host in self.tree["children"]])
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
def get_environment(self, paths):
|
|
315
|
-
"""Get merged environment from paths."""
|
|
316
|
-
package_env = PackageEnvironment()
|
|
317
|
-
for path in paths:
|
|
318
|
-
package = _find_by_path(self.tree, path)
|
|
319
|
-
if package:
|
|
320
|
-
package_env.extend(package)
|
|
321
|
-
return package_env
|
|
322
|
-
|
|
323
|
-
def json(self):
|
|
324
|
-
"""The whole tree as json."""
|
|
325
|
-
return json.dumps(self.tree)
|
|
326
|
-
|
|
327
|
-
# TODO write tests for this
|
|
328
|
-
def supported_host_names(self):
|
|
329
|
-
"""Pluck host paths from the available software tree.
|
|
330
|
-
|
|
331
|
-
Dont get children
|
|
332
|
-
|
|
333
|
-
Get for all platforms, or platforms list if given.
|
|
334
|
-
"""
|
|
335
|
-
pathlist = _to_path_list(self.tree)
|
|
336
|
-
return sorted([p for p in pathlist if "/" not in p])
|
|
337
|
-
|
|
338
|
-
def supported_plugins(self, host):
|
|
339
|
-
"""Make sorted list of plugins.
|
|
340
|
-
|
|
341
|
-
Each entry is an object with a 'plugin' and a 'versions' key.
|
|
342
|
-
|
|
343
|
-
Example: plugins = [
|
|
344
|
-
{"plugin": "arnold", "versions": ["1","2","3"]},
|
|
345
|
-
{"plugin": "vray", "versions": ["1","2","3"]}
|
|
346
|
-
]
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
We don't provide platform (3rd element) for plugins since the host software was used to
|
|
350
|
-
request the plugins and consumers can derive the platform from that.
|
|
351
|
-
"""
|
|
352
|
-
|
|
353
|
-
try:
|
|
354
|
-
subtree = self.find_by_name(host)
|
|
355
|
-
plugin_versions = _to_path_list(subtree)
|
|
356
|
-
except TypeError:
|
|
357
|
-
return []
|
|
358
|
-
|
|
359
|
-
if not plugin_versions:
|
|
360
|
-
return []
|
|
361
|
-
|
|
362
|
-
plugin_dict = {}
|
|
363
|
-
for plugin, version, _ in [pv.split(" ") for pv in plugin_versions]:
|
|
364
|
-
if plugin not in plugin_dict:
|
|
365
|
-
plugin_dict[plugin] = []
|
|
366
|
-
plugin_dict[plugin].append(version)
|
|
367
|
-
|
|
368
|
-
# convert to list so it can be sorted
|
|
369
|
-
plugins = []
|
|
370
|
-
for key in plugin_dict:
|
|
371
|
-
plugins.append({"plugin": key, "versions": sorted(plugin_dict[key])})
|
|
372
|
-
|
|
373
|
-
return sorted(plugins, key=lambda k: k["plugin"])
|
ciocore/retry.py
ADDED
|
File without changes
|