ciocore 7.0.2b4__py2.py3-none-any.whl → 8.0.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 +1 -1
- ciocore/__init__.py +23 -1
- ciocore/api_client.py +422 -156
- ciocore/cli.py +503 -0
- ciocore/common.py +10 -1
- ciocore/config.py +86 -54
- ciocore/data.py +23 -70
- ciocore/docsite/404.html +723 -0
- ciocore/docsite/apidoc/api_client/index.html +3203 -0
- ciocore/docsite/apidoc/apidoc/index.html +868 -0
- ciocore/docsite/apidoc/config/index.html +1591 -0
- ciocore/docsite/apidoc/data/index.html +1480 -0
- ciocore/docsite/apidoc/hardware_set/index.html +2367 -0
- ciocore/docsite/apidoc/package_environment/index.html +1450 -0
- ciocore/docsite/apidoc/package_tree/index.html +2310 -0
- ciocore/docsite/assets/_mkdocstrings.css +16 -0
- ciocore/docsite/assets/images/favicon.png +0 -0
- ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js +29 -0
- ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js.map +8 -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.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.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.dfff1995.min.js +42 -0
- ciocore/docsite/assets/javascripts/workers/search.dfff1995.min.js.map +8 -0
- ciocore/docsite/assets/stylesheets/main.83068744.min.css +1 -0
- ciocore/docsite/assets/stylesheets/main.83068744.min.css.map +1 -0
- ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css +1 -0
- ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css.map +1 -0
- ciocore/docsite/cmdline/docs/index.html +834 -0
- ciocore/docsite/cmdline/downloader/index.html +897 -0
- ciocore/docsite/cmdline/packages/index.html +841 -0
- ciocore/docsite/cmdline/uploader/index.html +950 -0
- ciocore/docsite/how-to-guides/index.html +831 -0
- ciocore/docsite/index.html +853 -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/__init__.py +0 -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} +0 -1
- 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/file_utils.py +3 -3
- ciocore/hardware_set.py +0 -4
- ciocore/package_environment.py +67 -75
- ciocore/package_query.py +171 -0
- ciocore/package_tree.py +300 -377
- ciocore/retry.py +0 -0
- ciocore/uploader/_uploader.py +205 -152
- {ciocore-7.0.2b4.dist-info → ciocore-8.0.0.dist-info}/METADATA +33 -12
- ciocore-8.0.0.dist-info/RECORD +127 -0
- {ciocore-7.0.2b4.dist-info → ciocore-8.0.0.dist-info}/WHEEL +1 -1
- ciocore-8.0.0.dist-info/entry_points.txt +2 -0
- tests/extra_env_fixtures.py +57 -0
- tests/instance_type_fixtures.py +42 -8
- tests/project_fixtures.py +8 -0
- tests/test_api_client.py +125 -4
- tests/test_base_downloader.py +104 -0
- tests/test_cli.py +163 -0
- tests/test_common.py +8 -8
- tests/test_config.py +23 -9
- tests/test_data.py +148 -160
- tests/test_downloader.py +118 -0
- tests/test_hardware_set.py +70 -20
- tests/test_job_downloader.py +213 -0
- tests/test_submit.py +9 -2
- ciocore/__about__.py +0 -10
- ciocore/cli/__init__.py +0 -3
- ciocore/cli/conductor.py +0 -210
- ciocore-7.0.2b4.data/scripts/conductor +0 -19
- ciocore-7.0.2b4.data/scripts/conductor.bat +0 -13
- ciocore-7.0.2b4.dist-info/RECORD +0 -51
- tests/mocks/api_client_mock.py +0 -31
- {ciocore-7.0.2b4.dist-info → ciocore-8.0.0.dist-info}/top_level.txt +0 -0
tests/test_downloader.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
from ciocore.downloader.job_downloader import JobDownloader, flatten
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest import mock
|
|
5
|
+
from ciocore.downloader.base_downloader import temp_file
|
|
6
|
+
import tempfile
|
|
7
|
+
import shutil
|
|
8
|
+
import os
|
|
9
|
+
import stat
|
|
10
|
+
import contextlib
|
|
11
|
+
|
|
12
|
+
class TempFileTests(unittest.TestCase):
|
|
13
|
+
|
|
14
|
+
def test_temp_file_creation(self):
|
|
15
|
+
# Create a temporary file and test if it exists
|
|
16
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
17
|
+
self.assertTrue(os.path.exists(temp_path))
|
|
18
|
+
self.assertTrue(os.access(temp_path, os.W_OK & os.R_OK))
|
|
19
|
+
|
|
20
|
+
def test_temp_file_overwrite_existing(self):
|
|
21
|
+
# Test if existing file is overwritten by the temporary file
|
|
22
|
+
# Create a dummy file at the specified path
|
|
23
|
+
os.makedirs("tests/files", exist_ok=True)
|
|
24
|
+
with open("tests/files/file.txt", "w") as f:
|
|
25
|
+
f.write("dummy content")
|
|
26
|
+
|
|
27
|
+
# Replace the file with a temporary file and check the content
|
|
28
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
29
|
+
with open(temp_path, "r") as tf:
|
|
30
|
+
content = tf.read()
|
|
31
|
+
self.assertEqual(content, "")
|
|
32
|
+
|
|
33
|
+
def test_temp_file_cleanup(self):
|
|
34
|
+
with temp_file("tests/files/file.txt") as temp_path:
|
|
35
|
+
pass # Do nothing
|
|
36
|
+
self.assertFalse(os.path.exists(temp_path))
|
|
37
|
+
|
|
38
|
+
class TestJobDownloaderFlatten(unittest.TestCase):
|
|
39
|
+
|
|
40
|
+
def test_pad_job_id(self):
|
|
41
|
+
input = ("1234",)
|
|
42
|
+
result = flatten(input)
|
|
43
|
+
self.assertEqual(result, [{"job_id": "01234", "task_ids":None}])
|
|
44
|
+
|
|
45
|
+
def test_several_job_ids(self):
|
|
46
|
+
input = ("1234","1235","1236")
|
|
47
|
+
result = flatten(input)
|
|
48
|
+
self.assertEqual(result, [
|
|
49
|
+
{"job_id": "01234", "task_ids":None},
|
|
50
|
+
{"job_id": "01235", "task_ids":None},
|
|
51
|
+
{"job_id": "01236", "task_ids":None}
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
def test_job_and_tasks(self):
|
|
55
|
+
input = ("1234:1-7x2,10",)
|
|
56
|
+
result = flatten(input)
|
|
57
|
+
self.assertEqual(result, [{"job_id": "01234", "task_ids":["001","003","005","007","010"]}])
|
|
58
|
+
|
|
59
|
+
def test_several_job_and_tasks(self):
|
|
60
|
+
input = ("1234:1-7x2,10","1235:12-15")
|
|
61
|
+
result = flatten(input)
|
|
62
|
+
self.assertEqual(result, [
|
|
63
|
+
{"job_id": "01234", "task_ids":["001","003","005","007","010"]},
|
|
64
|
+
{"job_id": "01235", "task_ids":["012","013","014","015"]}
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
def test_mix_job_and_job_with_tasks(self):
|
|
68
|
+
input = ("1234","1235:12-15")
|
|
69
|
+
result = flatten(input)
|
|
70
|
+
self.assertEqual(result, [
|
|
71
|
+
{"job_id": "01234", "task_ids":None},
|
|
72
|
+
{"job_id": "01235", "task_ids":["012","013","014","015"]}
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
def test_invalid_range_downloads_whole_job(self):
|
|
76
|
+
# Someone might have a bunch of stuff queued up and made a mistake and left for the night.
|
|
77
|
+
# We should download the whole job in this case so they don't have to restart the dl in the
|
|
78
|
+
# morning.
|
|
79
|
+
input = ("1234:badrange",)
|
|
80
|
+
result = flatten(input)
|
|
81
|
+
self.assertEqual(result, [
|
|
82
|
+
{"job_id": "01234", "task_ids":None}
|
|
83
|
+
])
|
|
84
|
+
|
|
85
|
+
class TestJobDownloaderGetSomeTasks(unittest.TestCase):
|
|
86
|
+
def setUp(self):
|
|
87
|
+
# Create a mock instance of JobDownloader
|
|
88
|
+
self.obj = JobDownloader(jobs=["00000"])
|
|
89
|
+
|
|
90
|
+
@mock.patch("ciocore.downloader.job_downloader.logger.error")
|
|
91
|
+
def test_get_some_tasks_error(self, mock_error):
|
|
92
|
+
# Mock the api client to return an error
|
|
93
|
+
mock_api_client = mock.Mock()
|
|
94
|
+
response = '{"error": "some error message"}'
|
|
95
|
+
code = 404
|
|
96
|
+
mock_api_client.make_request.return_value = (response, code)
|
|
97
|
+
self.obj.client = mock_api_client
|
|
98
|
+
# call get_some_tasks
|
|
99
|
+
tasks, locator = self.obj.get_some_tasks({})
|
|
100
|
+
|
|
101
|
+
self.assertEqual(tasks, [])
|
|
102
|
+
self.assertIsNone(locator)
|
|
103
|
+
mock_error.assert_called_with(
|
|
104
|
+
'Error fetching download info for job ID: %s : %s : %s', '00000', '/jobs/00000/downloads', mock.ANY)
|
|
105
|
+
|
|
106
|
+
def test_get_some_tasks_success(self):
|
|
107
|
+
mock_api_client = mock.Mock()
|
|
108
|
+
# Mock the api client to return a batch of tasks and a next cursor
|
|
109
|
+
response = '{"downloads": [{"id": 1, "name": "task1"}, {"id": 2, "name": "task2"}], "next_cursor": "1234"}'
|
|
110
|
+
code = 201
|
|
111
|
+
mock_api_client.make_request.return_value = (response, code)
|
|
112
|
+
self.obj.client = mock_api_client
|
|
113
|
+
self.obj.jobs = [{"job_id": "00000", "task_ids": [1, 2]}]
|
|
114
|
+
# call get_some_tasks with a locator
|
|
115
|
+
tasks, locator = self.obj.get_some_tasks({"job_index": 0, "cursor": None})
|
|
116
|
+
|
|
117
|
+
self.assertEqual(tasks, [{"id": 1, "name": "task1"}, {"id": 2, "name": "task2"}])
|
|
118
|
+
self.assertEqual(locator, {"job_index": 0, "cursor": "1234"})
|
tests/test_hardware_set.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
isort:skip_file
|
|
4
4
|
"""
|
|
5
5
|
import unittest
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
|
|
8
8
|
from ciocore.hardware_set import HardwareSet
|
|
9
9
|
|
|
@@ -15,7 +15,12 @@ PROJECTS = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
from package_fixtures import *
|
|
18
|
-
from instance_type_fixtures import
|
|
18
|
+
from instance_type_fixtures import (
|
|
19
|
+
CW_INSTANCE_TYPES,
|
|
20
|
+
AWS_INSTANCE_TYPES,
|
|
21
|
+
CW_INSTANCE_TYPES_WITH_GPUS,
|
|
22
|
+
ALL_INSTANCE_TYPES,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class TestCategorizedInstanceTypes(unittest.TestCase):
|
|
@@ -31,20 +36,29 @@ class TestCategorizedInstanceTypes(unittest.TestCase):
|
|
|
31
36
|
|
|
32
37
|
def test_content_count(self):
|
|
33
38
|
low_category_values = [c["value"] for c in self.hs.get_model()[0]["content"]]
|
|
34
|
-
self.assertEqual(low_category_values, ["a-4-16", "b-8-16"])
|
|
39
|
+
self.assertEqual(low_category_values, ["cw-a-4-16", "cw-b-8-16"])
|
|
35
40
|
|
|
36
41
|
def test_in_several_categories(self):
|
|
37
42
|
low_category_values = [c["value"] for c in self.hs.get_model()[0]["content"]]
|
|
38
43
|
extra_category_values = [c["value"] for c in self.hs.get_model()[3]["content"]]
|
|
39
|
-
self.assertIn("a-4-16", low_category_values)
|
|
40
|
-
self.assertIn("a-4-16", extra_category_values)
|
|
44
|
+
self.assertIn("cw-a-4-16", low_category_values)
|
|
45
|
+
self.assertIn("cw-a-4-16", extra_category_values)
|
|
46
|
+
|
|
47
|
+
def test_category_names(self):
|
|
48
|
+
names = self.hs.labels()
|
|
49
|
+
self.assertEqual(names, ["low", "mid", "high", "extra"])
|
|
50
|
+
|
|
41
51
|
|
|
42
52
|
class TestRecategorizeInstanceTypes(unittest.TestCase):
|
|
43
53
|
def setUp(self):
|
|
44
54
|
self.hs = HardwareSet(CW_INSTANCE_TYPES_WITH_GPUS)
|
|
45
|
-
|
|
55
|
+
|
|
46
56
|
def test_recategorize_cpu_gpu(self):
|
|
47
|
-
test_func =
|
|
57
|
+
test_func = (
|
|
58
|
+
lambda x: [{"label": "GPU", "order": 2}]
|
|
59
|
+
if "gpu" in x and x["gpu"]
|
|
60
|
+
else [{"label": "CPU", "order": 1}]
|
|
61
|
+
)
|
|
48
62
|
self.hs.recategorize(test_func)
|
|
49
63
|
self.assertEqual(self.hs.number_of_categories(), 2)
|
|
50
64
|
|
|
@@ -53,18 +67,40 @@ class TestRecategorizeInstanceTypes(unittest.TestCase):
|
|
|
53
67
|
result = self.hs.find_all(test_func)
|
|
54
68
|
self.assertEqual(len(result), 2)
|
|
55
69
|
print(self.hs)
|
|
56
|
-
|
|
70
|
+
|
|
57
71
|
def test_find_first_by_condition(self):
|
|
58
|
-
test_func = lambda x: x[
|
|
72
|
+
test_func = lambda x: x["memory"] < 32
|
|
59
73
|
result = self.hs.find_first(test_func)
|
|
60
74
|
self.assertTrue(result["memory"] < 32)
|
|
61
|
-
|
|
75
|
+
|
|
62
76
|
def test_find_category(self):
|
|
63
77
|
label = "mid"
|
|
64
78
|
result = self.hs.find_category(label)
|
|
65
|
-
self.assertEqual(result["label"]
|
|
66
|
-
self.assertEqual(result["order"]
|
|
67
|
-
|
|
79
|
+
self.assertEqual(result["label"], label)
|
|
80
|
+
self.assertEqual(result["order"], 2)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestFind(unittest.TestCase):
|
|
84
|
+
def setUp(self):
|
|
85
|
+
self.hs = HardwareSet(CW_INSTANCE_TYPES_WITH_GPUS)
|
|
86
|
+
|
|
87
|
+
def test_find_unspecified_category(self):
|
|
88
|
+
result = self.hs.find("cw-e-4-32")
|
|
89
|
+
self.assertIsNotNone(result)
|
|
90
|
+
|
|
91
|
+
def test_category_that_exists(self):
|
|
92
|
+
result = self.hs.find("cw-e-4-32", category="high")
|
|
93
|
+
self.assertIsNotNone(result)
|
|
94
|
+
|
|
95
|
+
def test_returns_none_if_not_in_category(self):
|
|
96
|
+
result = self.hs.find("cw-e-4-32", category="low")
|
|
97
|
+
self.assertIsNone(result)
|
|
98
|
+
|
|
99
|
+
def test_returns_none_if_non_existent_category(self):
|
|
100
|
+
result = self.hs.find("cw-e-4-32", category="foo")
|
|
101
|
+
self.assertIsNone(result)
|
|
102
|
+
|
|
103
|
+
|
|
68
104
|
class TestUncategorizedInstanceTypes(unittest.TestCase):
|
|
69
105
|
def setUp(self):
|
|
70
106
|
self.hs = HardwareSet(ALL_INSTANCE_TYPES)
|
|
@@ -78,12 +114,26 @@ class TestUncategorizedInstanceTypes(unittest.TestCase):
|
|
|
78
114
|
self.assertEqual(
|
|
79
115
|
result,
|
|
80
116
|
[
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
117
|
+
"windows 4 core 26.0GB Mem",
|
|
118
|
+
"linux 4 core 27.0GB Mem",
|
|
119
|
+
"linux 8 core 30.0GB Mem",
|
|
120
|
+
"windows 32 core 208.0GB Mem",
|
|
121
|
+
"linux 32 core 208.0GB Mem",
|
|
122
|
+
"windows 64 core 416.0GB Mem",
|
|
123
|
+
"linux 64 core 416.0GB Mem",
|
|
88
124
|
],
|
|
89
125
|
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TestProvider(unittest.TestCase):
|
|
129
|
+
def test_gcp_provider(self):
|
|
130
|
+
hs = HardwareSet(ALL_INSTANCE_TYPES)
|
|
131
|
+
self.assertEqual(hs.provider, "gcp")
|
|
132
|
+
|
|
133
|
+
def test_cw_provider(self):
|
|
134
|
+
hs = HardwareSet(CW_INSTANCE_TYPES_WITH_GPUS)
|
|
135
|
+
self.assertEqual(hs.provider, "cw")
|
|
136
|
+
|
|
137
|
+
def test_aws_provider(self):
|
|
138
|
+
hs = HardwareSet(AWS_INSTANCE_TYPES)
|
|
139
|
+
self.assertEqual(hs.provider, "aws")
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
from ciocore.downloader.job_downloader import JobDownloader, flatten
|
|
2
|
+
import unittest
|
|
3
|
+
from unittest import mock
|
|
4
|
+
import re
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
DOWNLOAD_BATCHES = {
|
|
8
|
+
"00000": [
|
|
9
|
+
{
|
|
10
|
+
"next_cursor": "00000_1",
|
|
11
|
+
"downloads": [
|
|
12
|
+
{
|
|
13
|
+
"download_id": 1,
|
|
14
|
+
"files": [
|
|
15
|
+
{"url": "https://example.com/file1", "size": 100},
|
|
16
|
+
{"url": "https://example.com/file2", "size": 200},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"download_id": 2,
|
|
21
|
+
"files": [
|
|
22
|
+
{"url": "https://example.com/file3", "size": 100},
|
|
23
|
+
{"url": "https://example.com/file4", "size": 200},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"next_cursor": "00000_2",
|
|
30
|
+
"downloads": [
|
|
31
|
+
{
|
|
32
|
+
"download_id": 3,
|
|
33
|
+
"files": [
|
|
34
|
+
{"url": "https://example.com/file5", "size": 100},
|
|
35
|
+
{"url": "https://example.com/file6", "size": 200},
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"download_id": 4,
|
|
40
|
+
"files": [
|
|
41
|
+
{"url": "https://example.com/file7", "size": 100},
|
|
42
|
+
{"url": "https://example.com/file8", "size": 200},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"next_cursor": None,
|
|
49
|
+
"downloads": [
|
|
50
|
+
{
|
|
51
|
+
"download_id": 5,
|
|
52
|
+
"files": [
|
|
53
|
+
{"url": "https://example.com/file9", "size": 100},
|
|
54
|
+
{"url": "https://example.com/file10", "size": 200},
|
|
55
|
+
],
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
"00001": [
|
|
61
|
+
{
|
|
62
|
+
"next_cursor": "00001_1",
|
|
63
|
+
"downloads": [
|
|
64
|
+
{
|
|
65
|
+
"download_id": 101,
|
|
66
|
+
"files": [
|
|
67
|
+
{"url": "https://example.com/file101", "size": 100},
|
|
68
|
+
{"url": "https://example.com/file102", "size": 200},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"download_id": 102,
|
|
73
|
+
"files": [
|
|
74
|
+
{"url": "https://example.com/file103", "size": 100},
|
|
75
|
+
{"url": "https://example.com/file104", "size": 200},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"next_cursor": "00001_2",
|
|
82
|
+
"downloads": [
|
|
83
|
+
{
|
|
84
|
+
"download_id": 103,
|
|
85
|
+
"files": [
|
|
86
|
+
{"url": "https://example.com/file105", "size": 100},
|
|
87
|
+
{"url": "https://example.com/file106", "size": 200},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"download_id": 104,
|
|
92
|
+
"files": [
|
|
93
|
+
{"url": "https://example.com/file107", "size": 100},
|
|
94
|
+
{"url": "https://example.com/file108", "size": 200},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"next_cursor": None,
|
|
101
|
+
"downloads": [
|
|
102
|
+
{
|
|
103
|
+
"download_id": 105,
|
|
104
|
+
"files": [
|
|
105
|
+
{"url": "https://example.com/file109", "size": 100},
|
|
106
|
+
{"url": "https://example.com/file110", "size": 200},
|
|
107
|
+
],
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class MockedApiClient:
|
|
115
|
+
def _get_batch(self, cursor):
|
|
116
|
+
if not cursor:
|
|
117
|
+
return json.dumps(DOWNLOAD_BATCHES["00000"][0])
|
|
118
|
+
|
|
119
|
+
job_id, index = cursor.split("_")
|
|
120
|
+
index = int(index)
|
|
121
|
+
batch = json.dumps(DOWNLOAD_BATCHES[job_id][index])
|
|
122
|
+
return batch
|
|
123
|
+
|
|
124
|
+
def make_request(self, url, verb="POST", params=None, data=None, use_api_key=True):
|
|
125
|
+
m = re.match("/jobs/(\d{5})/downloads", url)
|
|
126
|
+
if m:
|
|
127
|
+
job_id = m.group(1)
|
|
128
|
+
# This is a request for a download batch
|
|
129
|
+
if not job_id in DOWNLOAD_BATCHES:
|
|
130
|
+
print(f"Job {job_id} not found")
|
|
131
|
+
return None, 204
|
|
132
|
+
else:
|
|
133
|
+
print(f"Job {job_id} found")
|
|
134
|
+
|
|
135
|
+
cursor = params.get("start_cursor", None)
|
|
136
|
+
batch = self._get_batch(cursor)
|
|
137
|
+
return batch, 201
|
|
138
|
+
else:
|
|
139
|
+
# This is not a request for a download batch
|
|
140
|
+
raise NotImplementedError("This is not a request for a download batch")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestJobDownloader(unittest.TestCase):
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def setUp(self):
|
|
147
|
+
download_tasks_patcher = mock.patch.object(JobDownloader,"download_tasks")
|
|
148
|
+
self.mock_download_tasks = download_tasks_patcher.start()
|
|
149
|
+
self.addCleanup(download_tasks_patcher.stop)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_batch_all_batches(self):
|
|
153
|
+
mocked_jobs = DOWNLOAD_BATCHES.keys()
|
|
154
|
+
|
|
155
|
+
# instantiate
|
|
156
|
+
jdl = JobDownloader(mocked_jobs)
|
|
157
|
+
|
|
158
|
+
# set the mocked api client
|
|
159
|
+
jdl.client = MockedApiClient()
|
|
160
|
+
|
|
161
|
+
jdl.run()
|
|
162
|
+
self.assertEqual(self.mock_download_tasks.call_count, 6)
|
|
163
|
+
|
|
164
|
+
# def test_no_calls_if_non_existent_job(self):
|
|
165
|
+
# mocked_jobs = ["00002"]
|
|
166
|
+
# ``
|
|
167
|
+
# # instantiate
|
|
168
|
+
# jdl = JobDownloader(mocked_jobs)
|
|
169
|
+
|
|
170
|
+
# # set the mocked api client
|
|
171
|
+
# jdl.client = MockedApiClient()
|
|
172
|
+
|
|
173
|
+
# jdl.run()
|
|
174
|
+
# self.assertEqual(self.mock_download_tasks.call_count, 0)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# mock_get_some_tasks.side_effect = get_some_tasks_side_effect
|
|
178
|
+
|
|
179
|
+
# tasks, locator = jdl.get_some_tasks(None)
|
|
180
|
+
# self.assertEqual(tasks, ["t1,t2,t3"])
|
|
181
|
+
# self.assertEqual(locator, {"job_index": 0, "cursor": "a"})
|
|
182
|
+
|
|
183
|
+
# print(jdl.get_some_tasks(2))
|
|
184
|
+
# print(jdl.get_some_tasks(3))
|
|
185
|
+
|
|
186
|
+
# with mock.patch('requests.get') as mock_get:
|
|
187
|
+
# # Construct the expected JSON response
|
|
188
|
+
# json_response = {
|
|
189
|
+
# "next_cursor": "AA",
|
|
190
|
+
# "downloads": [
|
|
191
|
+
# {
|
|
192
|
+
# "download_id": 1,
|
|
193
|
+
# "files": [
|
|
194
|
+
# {
|
|
195
|
+
# "url": "https://example.com/file1",
|
|
196
|
+
# "size": 100
|
|
197
|
+
# },
|
|
198
|
+
# {
|
|
199
|
+
# "url": "https://example.com/file2",
|
|
200
|
+
# "size": 200
|
|
201
|
+
# }
|
|
202
|
+
# ]
|
|
203
|
+
# }
|
|
204
|
+
# ],
|
|
205
|
+
# "job_id": "00000",
|
|
206
|
+
# "output_dir": "/some/path",
|
|
207
|
+
# "size": 300,
|
|
208
|
+
# "task_id": "001"
|
|
209
|
+
# }
|
|
210
|
+
# # Create a mock response object
|
|
211
|
+
# mock_response = mock_get.return_value
|
|
212
|
+
# # Set the JSON content of the response
|
|
213
|
+
# mock_response.json.return_value = json_response
|
tests/test_submit.py
CHANGED
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import unittest
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from unittest.mock import MagicMock, patch
|
|
10
|
+
except ImportError:
|
|
11
|
+
from mock import MagicMock, patch
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
8
15
|
def get_required_args():
|
|
9
16
|
return {
|
|
10
17
|
"instance_type": "inst",
|
|
@@ -136,4 +143,4 @@ class uploaderTest():
|
|
|
136
143
|
up = uploader.Uploader(uploader_args)
|
|
137
144
|
manager = up.create_manager("some_project")
|
|
138
145
|
filemap = {"/a/b1": None,"/a/b2": None}
|
|
139
|
-
self.
|
|
146
|
+
# self.assertEqual(uploader_args["thread_count"], 4)
|
ciocore/__about__.py
DELETED
ciocore/cli/__init__.py
DELETED