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.
Files changed (126) hide show
  1. ciocore/VERSION +1 -1
  2. ciocore/__init__.py +23 -1
  3. ciocore/api_client.py +655 -160
  4. ciocore/auth/__init__.py +5 -3
  5. ciocore/cli.py +501 -0
  6. ciocore/common.py +15 -13
  7. ciocore/conductor_submit.py +77 -60
  8. ciocore/config.py +127 -13
  9. ciocore/data.py +162 -77
  10. ciocore/docsite/404.html +746 -0
  11. ciocore/docsite/apidoc/api_client/index.html +3605 -0
  12. ciocore/docsite/apidoc/apidoc/index.html +909 -0
  13. ciocore/docsite/apidoc/config/index.html +1652 -0
  14. ciocore/docsite/apidoc/data/index.html +1553 -0
  15. ciocore/docsite/apidoc/hardware_set/index.html +2460 -0
  16. ciocore/docsite/apidoc/package_environment/index.html +1507 -0
  17. ciocore/docsite/apidoc/package_tree/index.html +2386 -0
  18. ciocore/docsite/assets/_mkdocstrings.css +16 -0
  19. ciocore/docsite/assets/images/favicon.png +0 -0
  20. ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js +29 -0
  21. ciocore/docsite/assets/javascripts/bundle.471ce7a9.min.js.map +7 -0
  22. ciocore/docsite/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
  23. ciocore/docsite/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
  24. ciocore/docsite/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
  25. ciocore/docsite/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
  26. ciocore/docsite/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
  27. ciocore/docsite/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
  28. ciocore/docsite/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
  29. ciocore/docsite/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
  30. ciocore/docsite/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
  31. ciocore/docsite/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
  32. ciocore/docsite/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
  33. ciocore/docsite/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
  34. ciocore/docsite/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
  35. ciocore/docsite/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
  36. ciocore/docsite/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
  37. ciocore/docsite/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
  38. ciocore/docsite/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
  39. ciocore/docsite/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
  40. ciocore/docsite/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
  41. ciocore/docsite/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
  42. ciocore/docsite/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
  43. ciocore/docsite/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
  44. ciocore/docsite/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
  45. ciocore/docsite/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
  46. ciocore/docsite/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
  47. ciocore/docsite/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
  48. ciocore/docsite/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
  49. ciocore/docsite/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
  50. ciocore/docsite/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
  51. ciocore/docsite/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
  52. ciocore/docsite/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
  53. ciocore/docsite/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
  54. ciocore/docsite/assets/javascripts/lunr/tinyseg.js +206 -0
  55. ciocore/docsite/assets/javascripts/lunr/wordcut.js +6708 -0
  56. ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js +42 -0
  57. ciocore/docsite/assets/javascripts/workers/search.b8dbb3d2.min.js.map +7 -0
  58. ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css +1 -0
  59. ciocore/docsite/assets/stylesheets/main.3cba04c6.min.css.map +1 -0
  60. ciocore/docsite/assets/stylesheets/palette.06af60db.min.css +1 -0
  61. ciocore/docsite/assets/stylesheets/palette.06af60db.min.css.map +1 -0
  62. ciocore/docsite/cmdline/docs/index.html +871 -0
  63. ciocore/docsite/cmdline/downloader/index.html +934 -0
  64. ciocore/docsite/cmdline/packages/index.html +878 -0
  65. ciocore/docsite/cmdline/uploader/index.html +995 -0
  66. ciocore/docsite/how-to-guides/index.html +869 -0
  67. ciocore/docsite/index.html +895 -0
  68. ciocore/docsite/logo.png +0 -0
  69. ciocore/docsite/objects.inv +0 -0
  70. ciocore/docsite/search/search_index.json +1 -0
  71. ciocore/docsite/sitemap.xml +3 -0
  72. ciocore/docsite/sitemap.xml.gz +0 -0
  73. ciocore/docsite/stylesheets/extra.css +26 -0
  74. ciocore/docsite/stylesheets/tables.css +167 -0
  75. ciocore/downloader/base_downloader.py +644 -0
  76. ciocore/downloader/download_runner_base.py +47 -0
  77. ciocore/downloader/job_downloader.py +119 -0
  78. ciocore/{downloader.py → downloader/legacy_downloader.py} +12 -9
  79. ciocore/downloader/log.py +73 -0
  80. ciocore/downloader/logging_download_runner.py +87 -0
  81. ciocore/downloader/perpetual_downloader.py +63 -0
  82. ciocore/downloader/registry.py +97 -0
  83. ciocore/downloader/reporter.py +135 -0
  84. ciocore/exceptions.py +8 -2
  85. ciocore/file_utils.py +51 -50
  86. ciocore/hardware_set.py +449 -0
  87. ciocore/loggeria.py +89 -20
  88. ciocore/package_environment.py +110 -48
  89. ciocore/package_query.py +182 -0
  90. ciocore/package_tree.py +319 -258
  91. ciocore/retry.py +0 -0
  92. ciocore/uploader/_uploader.py +547 -364
  93. ciocore/uploader/thread_queue_job.py +176 -0
  94. ciocore/uploader/upload_stats/__init__.py +3 -4
  95. ciocore/uploader/upload_stats/stats_formats.py +10 -4
  96. ciocore/validator.py +34 -2
  97. ciocore/worker.py +174 -151
  98. ciocore-10.0.0b3.dist-info/METADATA +928 -0
  99. ciocore-10.0.0b3.dist-info/RECORD +128 -0
  100. {ciocore-5.1.1.dist-info → ciocore-10.0.0b3.dist-info}/WHEEL +1 -1
  101. ciocore-10.0.0b3.dist-info/entry_points.txt +2 -0
  102. tests/instance_type_fixtures.py +175 -0
  103. tests/package_fixtures.py +205 -0
  104. tests/test_api_client.py +297 -12
  105. tests/test_base_downloader.py +104 -0
  106. tests/test_cli.py +149 -0
  107. tests/test_common.py +1 -7
  108. tests/test_config.py +40 -18
  109. tests/test_data.py +162 -173
  110. tests/test_downloader.py +118 -0
  111. tests/test_hardware_set.py +139 -0
  112. tests/test_job_downloader.py +213 -0
  113. tests/test_package_query.py +38 -0
  114. tests/test_package_tree.py +91 -291
  115. tests/test_submit.py +44 -18
  116. tests/test_uploader.py +1 -4
  117. ciocore/__about__.py +0 -10
  118. ciocore/cli/conductor.py +0 -191
  119. ciocore/compat.py +0 -15
  120. ciocore-5.1.1.data/scripts/conductor +0 -19
  121. ciocore-5.1.1.data/scripts/conductor.bat +0 -13
  122. ciocore-5.1.1.dist-info/METADATA +0 -408
  123. ciocore-5.1.1.dist-info/RECORD +0 -47
  124. tests/mocks/api_client_mock.py +0 -51
  125. /ciocore/{cli → downloader}/__init__.py +0 -0
  126. {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
- """This module represents the list of software packages as a tree.
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
- It has methods and functions to traverse the tree to find
4
- packages and build paths etc.
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
- See tests in /tests/test_package_tree.py for additional usage info.
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
- from ciocore.package_environment import PackageEnvironment
18
+ WINDOWS = "windows"
19
+ LINUX = "linux"
14
20
 
15
- WINDOWS="windows"
16
- LINUX="linux"
21
+ #: The set of supported platforms, currently windows and linux.
22
+ PLATFORMS = {WINDOWS, LINUX}
17
23
 
18
- PLATFORMS={WINDOWS, LINUX}
24
+ class PackageTree(object):
25
+ def __init__(self, packages, *host_products, **kwargs):
26
+ """Build the tree with a list of packages.
19
27
 
20
- def remove_unreachable(paths):
21
- """Remove unreachable paths.
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
- Given some paths, remove those for which there are not additional paths present at each level up
24
- the hierarchy to the host. It is possible to have orphaned paths if a package can be reached by
25
- more than one path. For example, a shader library may be compatible with 2 versions of a
26
- renderer, but only one of those renderers is compatible with this host. We need to remove the
27
- entry that references a different host.
28
- """
29
- results = []
30
- previous = ""
31
- for path in sorted(paths):
32
- parts = path.split("/")
33
- valid_subpath = path == "%s/%s" % (previous, parts[-1])
34
- if len(parts) == 1 or valid_subpath:
35
- results.append(path)
36
- previous = path
37
- return results
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
- """Name like `houdini 16.5.323` or `maya 2016.SP3`.
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. It's purpose
45
- is to enable path construction to uniquely identify a package. For example, houdini
46
- 16.0.736/arnold-houdini 2.0.2.2/al-shaders 1.0
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
- platform = pkg.get("platform")
56
- return " ".join(filter(None, [pkg["product"], version_string, platform]))
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 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
- if _is_product(branch, name=name):
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