ciocore 6.1.0__py2.py3-none-any.whl → 6.3.0__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.

Potentially problematic release.


This version of ciocore might be problematic. Click here for more details.

ciocore/VERSION CHANGED
@@ -1 +1 @@
1
- 6.1.0
1
+ 6.3.0
ciocore/hardware_set.py CHANGED
@@ -1,31 +1,46 @@
1
1
 
2
2
  import copy
3
+ import logging
3
4
 
4
- MISC_CATEGORY_LABEL = "-- Misc --"
5
+ logger = logging.getLogger(__name__)
5
6
 
6
7
  class HardwareSet(object):
7
8
  """A class to manage the categorized instance types.
8
9
 
9
10
  It takes flat instance types array and builds a nested structure where instance types exist in
10
11
  categories.
12
+
13
+ If after categorization there is only one category and it is the misc category, then we
14
+ recategorize by cpu and gpu.
11
15
  """
12
16
 
13
17
  def __init__(self, instance_types):
18
+
14
19
  self.instance_types = self.build_unique(instance_types)
15
20
  self.categories = self.build_categories()
16
-
21
+
17
22
  @staticmethod
18
23
  def build_unique(instance_types):
19
24
  """Build a dictionary of instance types using name as key.
20
25
 
21
26
  Remove any instance types whose description has already been seen. We can't have duplicate
22
27
  descriptions.
28
+
29
+ If categories exist, then remove any instance types that don't have a category. Otherwise,
30
+ since there are no categories, we can't remove any instance types.
23
31
  """
32
+ categories = [category for it in instance_types for category in (it.get("categories") or [])]
24
33
  result = {}
25
34
  seen_descriptions = set()
26
35
  for it in instance_types:
27
36
  if it["description"] in seen_descriptions:
28
37
  continue
38
+ if categories:
39
+ if it.get("categories") in [[], None]:
40
+ continue
41
+ else:
42
+ # make our own categories GPU/CPU
43
+ it["categories"] = [{'label': 'GPU', 'order': 2}] if "gpu" in it and it["gpu"] else [{'label': 'CPU', 'order': 1}]
29
44
  result[it["name"]] = it
30
45
  seen_descriptions.add(it["description"])
31
46
  return result
@@ -37,9 +52,7 @@ class HardwareSet(object):
37
52
  dikt = {}
38
53
  for key in self.instance_types:
39
54
  it = self.instance_types[key]
40
- categories = it.get("categories") or []
41
- if not categories:
42
- categories.append({"label": MISC_CATEGORY_LABEL, "order": 999})
55
+ categories = it["categories"]
43
56
  for category in categories:
44
57
  label = category["label"]
45
58
  if label not in dikt:
@@ -53,29 +66,78 @@ class HardwareSet(object):
53
66
  result.append(category)
54
67
  return sorted(result, key=lambda k: k["order"])
55
68
 
56
-
69
+ def recategorize(self, partitioner):
70
+ """Recategorize the instance types.
71
+
72
+ Partitioner is a function that takes an instance type and returns a list of categories.
73
+
74
+ If for example you want to append to the existing categories, then you can use a function like this:
75
+ lambda x: x["categories"] + [{'label': 'Low cores', 'order': 10}] if x["cores"] < 16 else [{'label': 'High cores', 'order': 20}]
76
+
77
+ Rebuilds the categories structure after assigning the new categories.
78
+ """
79
+ for key in self.instance_types:
80
+ self.instance_types[key]["categories"] = partitioner(self.instance_types[key])
81
+ self.categories = self.build_categories()
82
+
83
+
57
84
  def get_model(self, with_misc=False):
58
- """Returns the categories structure with filtering and renaming ready for some UI.
85
+ """Returns the categories structure with renaming ready for some UI.
59
86
 
60
- with_misc: Include misc category. The misc category is always included if it's the only one.
87
+ NOTE: THIS METHOD WILL BE DEPRECATED. with_misc is no longer used, which means that this
88
+ function just renames a few keys. What's more, the init function ensures that every instance
89
+ type has a (non-misc) category. So this function is no longer needed. Submitters that use
90
+ it will work fine, but should be updated to use the categories structure directly.
61
91
  """
92
+ if with_misc:
93
+ logger.warning("with_misc is no longer used")
62
94
  result = []
63
- should_remove_misc = not with_misc and len( self.categories) > 1
64
-
65
95
  for category in self.categories:
66
- if should_remove_misc and category["label"] == MISC_CATEGORY_LABEL:
67
- continue
68
-
69
96
  result.append({
70
97
  "label": category["label"],
71
- "content": list(map(lambda k: {"label": k["description"], "value": k["name"]} , category["content"]))
98
+ "content": [{"label": k["description"], "value": k["name"]} for k in category["content"]]
72
99
  })
73
100
 
74
101
  return result
75
-
102
+
76
103
  def find(self, name):
104
+ """Find an instance type by name (sku).
105
+
106
+ Example: find("p3.2xlarge")
107
+
108
+ Returns and instance-type or None if not found.
109
+ """
77
110
  return self.instance_types.get(name)
78
111
 
112
+ def find_category(self, label):
113
+ """Find a category by label.
114
+
115
+ Example: find_category("GPU")
116
+
117
+ Returns the entire category and its contents or None if not found.
118
+ """
119
+ return next((c for c in self.categories if c["label"] == label), None)
120
+
121
+
122
+ def find_all(self, condition):
123
+ """Find all instance types that match a condition.
124
+
125
+ Example: find_all(lambda x: x["gpu"])
126
+ """
127
+ result = []
128
+ for key in self.instance_types:
129
+ if condition(self.instance_types[key]):
130
+ result.append(self.instance_types[key])
131
+ return result
132
+
133
+ def find_first(self, condition):
134
+ """Find the first instance type that matches a condition.
135
+
136
+ Example: find_first(lambda x: x["cores"] == 4)"])
137
+ """
138
+ return next(iter(self.find_all(condition)), None)
139
+
79
140
  def number_of_categories(self):
141
+ """Return the number of categories in the data."""
80
142
  return len(self.categories)
81
143
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ciocore
3
- Version: 6.1.0
3
+ Version: 6.3.0
4
4
  Summary: Core functionality for Conductor's client tools
5
5
  Home-page: https://github.com/ConductorTechnologies/ciocore
6
6
  Author: conductor
@@ -26,11 +26,6 @@ Use the command-line uploader and downloader or develop your own tools using the
26
26
  pip install --upgrade ciocore
27
27
  ```
28
28
 
29
- **To install a specific version, for example 0.1.0.**
30
- ```bash
31
- pip install --upgrade ciocore==0.1.0
32
- ```
33
-
34
29
  Run the conductor command to confirm the package was installed.
35
30
  ```bash
36
31
  conductor --help
@@ -48,13 +43,23 @@ See [CONTRIBUTING](CONTRIBUTING.md)
48
43
 
49
44
  ## Changelog
50
45
 
46
+ ## Version:6.3.0 -- 05 Jul 2023
47
+
48
+ * In the absence of instance-type categories provided by the endpoint, the client generates default
49
+ categories CPU and GPU which at least splits the instance types into two groups, thereby making
50
+ dropdown menus more manageable, especially for CoreWeave's array of machines.
51
+ * update circleci config and related documentation
52
+
51
53
  ## Version:6.1.0 -- 09 Jun 2023
52
54
 
53
- Add the ability to specify which platforms to get resources for. This helps consumers of the API to avoid fetching resources for Windows when the client is not ready.
55
+ Add the ability to specify which platforms to get resources for. This helps consumers of the API to
56
+ avoid fetching resources for Windows when the client is not ready.
54
57
 
55
58
  ## Version:6.0.0 -- 07 Jun 2023
56
59
 
57
- This is a breaking change due instance types being provided as an instance of HardwareSet with support for categories as opposed to a simple list. Consumers of the API will need to be updated to support this change.
60
+ This is a breaking change due instance types being provided as an instance of HardwareSet with
61
+ support for categories as opposed to a simple list. Consumers of the API will need to be updated to
62
+ support this change.
58
63
 
59
64
  ## Version:6.0.0-rc.4 -- 06 Jun 2023
60
65
 
@@ -1,4 +1,4 @@
1
- ciocore/VERSION,sha256=ldru8b_Gwjy37St6Co1rJJjrYxhdPXqLQJkIrzMNpO8,5
1
+ ciocore/VERSION,sha256=AcBmZzP8VI6iOQK01krLRTlT4Tu0al4tBEsVA7A-nUo,5
2
2
  ciocore/__about__.py,sha256=nTb4Xx0r9QtGROSFKgwDZ-Mr2LKjm2wVt1OkMQAkRAQ,241
3
3
  ciocore/__init__.py,sha256=zB_e7gBW2QloqbrGzcvccfpZhOhd6o9KACnScrwylm8,25
4
4
  ciocore/api_client.py,sha256=O9uvtnqgmE2IRkqlP9DoPn6CX_HAIuSLo9je5Om-800,17913
@@ -11,7 +11,7 @@ ciocore/data.py,sha256=RQZEYGkZrVFgyyPqzBat-L99sq49h-SQUnSdGIPDI18,8938
11
11
  ciocore/downloader.py,sha256=oRmRePdIx_GdQwM39ipl9_e2c2ZkcGJRanuHOZmqpTM,51147
12
12
  ciocore/exceptions.py,sha256=c1K48hWcD8VOORfNoSf5qlQkLLEqwjryH8FUwUYAYU4,1431
13
13
  ciocore/file_utils.py,sha256=bAlL31B4YkRgX-yT8kF8UXBFktQlsE1PvxbKqTeAeOU,17174
14
- ciocore/hardware_set.py,sha256=5qQg28OfuwG3NCfSjDxqcZPByW95PHwShsFhV2Qkb_Q,2771
14
+ ciocore/hardware_set.py,sha256=txcSLrVSNewRGxKLye-wuM8szGMVloU29ktL8WHdUtM,5401
15
15
  ciocore/loggeria.py,sha256=dKKJC8ZtRZdghqD5R5XrA6eDoy8gKacfeTA-zNzXvDE,13482
16
16
  ciocore/package_environment.py,sha256=oEbNKXRtPSPzKR-yCoKtvgzu4OCmr-zaqAcNoLAN9Uk,7238
17
17
  ciocore/package_tree.py,sha256=kH03HVfjomj7nsaxJJtr-1KSQ_9ZSQY5msG_l9btvg8,16277
@@ -23,16 +23,16 @@ ciocore/auth/__init__.py,sha256=cdS-xZzMq41yXM5cz8sUlcYgo8CJYh8HcCCWmhbDgf0,606
23
23
  ciocore/auth/server.py,sha256=8btX9-EokUl6q55V8muDmEV2tvvbTBD0BHeWFbwkzUc,3892
24
24
  ciocore/cli/__init__.py,sha256=RmZKWJaMpzNyMdyYc2W3VXglaJiC8vyR2cgUlA-9Qmw,26
25
25
  ciocore/cli/conductor.py,sha256=snmlICkMgP94ZPKl6J7g299deB75QwGDWI0oCnZPfSI,10861
26
- ciocore-6.1.0.data/scripts/conductor,sha256=Nk3QsLQqbUUrtaKDp4b5mr9__4tz-xnssENpQe5vuIo,409
27
- ciocore-6.1.0.data/scripts/conductor.bat,sha256=T1_9ByheubBczgQZn8_LwfvMtWgE7Bt64EsEScnSXMs,447
26
+ ciocore-6.3.0.data/scripts/conductor,sha256=Nk3QsLQqbUUrtaKDp4b5mr9__4tz-xnssENpQe5vuIo,409
27
+ ciocore-6.3.0.data/scripts/conductor.bat,sha256=T1_9ByheubBczgQZn8_LwfvMtWgE7Bt64EsEScnSXMs,447
28
28
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- tests/instance_type_fixtures.py,sha256=ajOfz_AO39Er1mKNN913oVcBVrW_El1ADBHQoYU6Pp4,2935
29
+ tests/instance_type_fixtures.py,sha256=gR6ordfghp34I94oM2eDg6jfIe5HAwE644GKIwspuW8,3469
30
30
  tests/package_fixtures.py,sha256=CsJnhB7oYzIxJH7b1tCOPyvnnVSCqEbSPhtCnsHL-nA,5070
31
31
  tests/test_api_client.py,sha256=dWOmAIKmX0gLaDGsT9VfAq9Hcs9HIQf2P5IMM_Bt5dE,1284
32
32
  tests/test_common.py,sha256=lJpzRdL-7u4McXFbLuwPQQoUnuEOnCVQtZEt6e_dIYs,638
33
33
  tests/test_config.py,sha256=nSmpinX2SmDNAprIcxs9UHdB0VakJB0snXaZmAoKJSc,12863
34
34
  tests/test_data.py,sha256=YdP1kZJivQ6yb9z96UK6oMDaOfJAl4YMJqzKvlCQaes,5744
35
- tests/test_hardware_set.py,sha256=9MAXI2sTziTddfSnE4uIJmMfIAf5ijukPsO-q7XE4JY,2400
35
+ tests/test_hardware_set.py,sha256=TcBh63rOxf1rKXxKlCPSnHueBFlz7rNP6BcoJjgVvPs,3065
36
36
  tests/test_imports_2and3.py,sha256=ehqpRYPVY7djBcb8OT_cnh86iCJJ9wuMWnfSR9RHxmY,507
37
37
  tests/test_package_environment.py,sha256=CdiC2PDVSnbcwTb4fsDTWqGYSzs1n5ca2KMoyISckGA,5893
38
38
  tests/test_package_tree.py,sha256=xCwNwYUmJrfmgCP2FGoHRFG-L0JPy8s4-66icxAls4o,6780
@@ -42,7 +42,7 @@ tests/test_validator.py,sha256=2fY66ayNc08PGyj2vTI-V_1yeCWJDngkj2zkUM5TTCI,1526
42
42
  tests/mocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  tests/mocks/api_client_mock.py,sha256=Wfv2JPFSZfyHftVqsqxcpXWJn136pEHx26I_esz567E,943
44
44
  tests/mocks/glob.py,sha256=J2MH7nqi6NJOHuGdVWxhfeBd700_Ckj6cLh_8jSNkfg,215
45
- ciocore-6.1.0.dist-info/METADATA,sha256=aU4R-bY3WcjkrDCqv08xp6PkfAKZ9FXtuNzZcPv-M4g,14026
46
- ciocore-6.1.0.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
47
- ciocore-6.1.0.dist-info/top_level.txt,sha256=SvlM5JlqULzAz00JZWfiUhfjhqDzYzSWssA87zdJl0o,14
48
- ciocore-6.1.0.dist-info/RECORD,,
45
+ ciocore-6.3.0.dist-info/METADATA,sha256=Jf9qxoQUVb1qnmIgguLei22xyWZTXtoHIEYWBf9pe4w,14285
46
+ ciocore-6.3.0.dist-info/WHEEL,sha256=a-zpFRIJzOq5QfuhBzbhiA1eHTzNCJn8OdRvhdNX0Rk,110
47
+ ciocore-6.3.0.dist-info/top_level.txt,sha256=SvlM5JlqULzAz00JZWfiUhfjhqDzYzSWssA87zdJl0o,14
48
+ ciocore-6.3.0.dist-info/RECORD,,
@@ -105,7 +105,7 @@ CW_INSTANCE_TYPES = [
105
105
  "operating_system": "linux",
106
106
  "description": "Desc 8 C 32 M F",
107
107
  },
108
- {
108
+ {
109
109
  "cores": 8,
110
110
  "memory": 64,
111
111
  "name": "g-8-32",
@@ -114,3 +114,24 @@ CW_INSTANCE_TYPES = [
114
114
  "description": "Desc 8 C 32 M G",
115
115
  },
116
116
  ]
117
+
118
+ CW_INSTANCE_TYPES_WITH_GPUS = [
119
+ {
120
+ "cores": 8,
121
+ "memory": 64,
122
+ "name": "f-8-32-gpu",
123
+ "categories": [{"label": "high", "order": 3}],
124
+ "operating_system": "linux",
125
+ "description": "Desc 8 C 32 M F gpu",
126
+ "gpu": 1,
127
+ },
128
+ {
129
+ "cores": 8,
130
+ "memory": 64,
131
+ "name": "g-8-32-gpu",
132
+ "categories": [{"label": "high", "order": 3}],
133
+ "operating_system": "linux",
134
+ "description": "Desc 8 C 32 M G gpu",
135
+ "gpu": 1,
136
+ },
137
+ ] + CW_INSTANCE_TYPES
@@ -5,7 +5,7 @@
5
5
  import unittest
6
6
  from unittest.mock import patch
7
7
 
8
- from ciocore.hardware_set import MISC_CATEGORY_LABEL, HardwareSet
8
+ from ciocore.hardware_set import HardwareSet
9
9
 
10
10
  PROJECTS = [
11
11
  "Deadpool",
@@ -15,7 +15,7 @@ PROJECTS = [
15
15
  ]
16
16
 
17
17
  from package_fixtures import *
18
- from instance_type_fixtures import *
18
+ from instance_type_fixtures import CW_INSTANCE_TYPES, CW_INSTANCE_TYPES_WITH_GPUS, ALL_INSTANCE_TYPES
19
19
 
20
20
 
21
21
  class TestCategorizedInstanceTypes(unittest.TestCase):
@@ -23,7 +23,7 @@ class TestCategorizedInstanceTypes(unittest.TestCase):
23
23
  self.hs = HardwareSet(CW_INSTANCE_TYPES)
24
24
 
25
25
  def test_number_of_categories(self):
26
- self.assertEqual(self.hs.number_of_categories(), 5)
26
+ self.assertEqual(self.hs.number_of_categories(), 4)
27
27
 
28
28
  def test_categories_sorted_on_order(self):
29
29
  labels = [i["label"] for i in self.hs.get_model()]
@@ -39,17 +39,31 @@ class TestCategorizedInstanceTypes(unittest.TestCase):
39
39
  self.assertIn("a-4-16", low_category_values)
40
40
  self.assertIn("a-4-16", extra_category_values)
41
41
 
42
- def test_model_removes_misc_by_default(self):
43
- model = self.hs.get_model()
44
- labels = [c["label"] for c in model]
45
- self.assertNotIn(MISC_CATEGORY_LABEL, labels)
46
-
47
- def test_model_adds_misc_if_specified(self):
48
- model = self.hs.get_model(with_misc=True)
49
- labels = [c["label"] for c in model]
50
- self.assertIn(MISC_CATEGORY_LABEL, labels)
51
-
52
-
42
+ class TestRecategorizeInstanceTypes(unittest.TestCase):
43
+ def setUp(self):
44
+ self.hs = HardwareSet(CW_INSTANCE_TYPES_WITH_GPUS)
45
+
46
+ def test_recategorize_cpu_gpu(self):
47
+ test_func = lambda x: [{'label': 'GPU', 'order': 2}] if "gpu" in x and x["gpu"] else [{'label': 'CPU', 'order': 1}]
48
+ self.hs.recategorize(test_func)
49
+ self.assertEqual(self.hs.number_of_categories(), 2)
50
+
51
+ def test_find_all_by_condition(self):
52
+ test_func = lambda x: "gpu" in x and x["gpu"]
53
+ result = self.hs.find_all(test_func)
54
+ self.assertEqual(len(result), 2)
55
+
56
+ def test_find_first_by_condition(self):
57
+ test_func = lambda x: x[ "memory"] < 32
58
+ result = self.hs.find_first(test_func)
59
+ self.assertTrue(result["memory"] < 32)
60
+
61
+ def test_find_category(self):
62
+ label = "mid"
63
+ result = self.hs.find_category(label)
64
+ self.assertEqual(result["label"] , label)
65
+ self.assertEqual(result["order"] , 2)
66
+
53
67
  class TestUncategorizedInstanceTypes(unittest.TestCase):
54
68
  def setUp(self):
55
69
  self.hs = HardwareSet(ALL_INSTANCE_TYPES)