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/auth/__init__.py CHANGED
@@ -1,12 +1,14 @@
1
+ import os
1
2
  import webbrowser
2
3
  from ciocore.auth import server
3
4
  import logging
4
5
 
5
6
 
6
7
  def run(creds_file, base_url):
8
+ port = int(os.environ.get('CONDUCTOR_AUTH_PORT', 8085))
7
9
  logging.debug("Base URL is %s" % base_url)
8
- url = "{}/api/oauth_jwt?redirect_uri=http://localhost:8085/index.html&scope=user&response_type=client_token".format(base_url)
10
+ url = "{}/api/oauth_jwt?redirect_uri=http://localhost:{}/index.html&scope=user&response_type=client_token".format(base_url, port)
9
11
  if webbrowser.open_new(url):
10
- server.run(port=8085, creds_file=creds_file)
12
+ server.run(port=port, creds_file=creds_file)
11
13
  else:
12
- raise RuntimeError("Unable to open web browser. Please contact Conductor support for help configuring Conductor Client")
14
+ raise RuntimeError("Unable to open web browser on port {}. Try setting the CONDUCTOR_AUTH_PORT environment variable to a different port.".format(port))
ciocore/cli.py ADDED
@@ -0,0 +1,501 @@
1
+ import os
2
+ import logging
3
+ import click
4
+ import webbrowser
5
+ import http.server
6
+ import socketserver
7
+
8
+ from ciocore import loggeria, config, api_client, package_query
9
+
10
+ from ciocore import version as VERSION
11
+
12
+ from ciocore.uploader import Uploader
13
+ from ciocore.downloader.legacy_downloader import Downloader
14
+ from ciocore.downloader.logging_download_runner import LoggingDownloadRunner
15
+ from ciocore.downloader.log import LOGGER_NAME, LEVEL_MAP
16
+
17
+
18
+ LOG_LEVEL_HELP = """The logging level to display"""
19
+
20
+ LOG_DIR_HELP = """
21
+ Write a log file to the given directory. The log rotates, creating a new log file every day, while
22
+ storing logs for the last 7 days.
23
+ """
24
+ LOG_TO_CONSOLE_HELP = """
25
+ If set, logging will be output to the console as well as the logging file."""
26
+
27
+ THREADS_HELP = """The number of threads that should download simultaneously"""
28
+
29
+ UPLOADER_DATABASE_FILEPATH_HELP = (
30
+ "Specify a filepath to the local md5 caching database."
31
+ )
32
+ LOCATION_HELP = """
33
+ Specify a location tag to associate with uploads, downloads, and submissions. A location tag allows
34
+ you to limit the scope of your uploads and downloads to jobs sharing the same location tag. This is
35
+ useful while using the uploader or downloader in daemon mode.
36
+ """
37
+ UPLOADER_MD5_CACHING_HELP = """
38
+ Use cached md5s. This can dramatically improve the uploading times, as md5 checking can be very
39
+ time consuming. Caching md5s allows subsequent uploads (of the same files) to skip the md5
40
+ generation process (if the files appear to not have been modified since the last time they were
41
+ submitted). The cache is stored locally and uses a file's modification time and file size to
42
+ intelligently guess whether the file has changed. Set this flag to False if there is concern that
43
+ files may not be getting re-uploaded properly
44
+ """
45
+
46
+ UPLOADER_PATHS_HELP = """
47
+ A list of paths to upload. Use quotes if paths contain spaces or special characters"""
48
+
49
+ DOWNLOADER_JOB_ID_HELP = """
50
+ The job id(s) to download. When specified will only download those jobs and terminate afterwards
51
+ """
52
+
53
+ DOWNLOADER_OUTPUT_PATH_HELP = """
54
+ Override the output directory"""
55
+
56
+ PACKAGES_FORMAT_OPTIONS = ["text", "markdown", "html"]
57
+ PACKAGES_FORMAT_HELP = """
58
+ text: The output is a simple list of software names and versions, with nesting to indicate plugin
59
+ compatibility. Output is sent to stdout.
60
+
61
+ markdown: Designed for use in other markdown documentation systems where it benefits from consistent
62
+ styling. Output is sent to stdout and can be piped to a file.
63
+
64
+ html: Opens a browser window and displays the output in a simple html page.
65
+ """
66
+
67
+ DOCS_PORT_HELP = """
68
+ The port to serve the documentation on. Defaults to 8025.
69
+ """
70
+
71
+ DOWNLOADER_PAGE_SIZE_HELP = """
72
+ The number of files to request from the Conductor API at a time. Defaults to 50.
73
+ """
74
+
75
+ PROGRESS_INTERVAL_HELP = """
76
+ The number of milliseconds to pass before printing a emitting a progress message. Defaults to 500.
77
+ """
78
+ DEFAULT_PROGRESS_INTERVAL = 500
79
+
80
+ DOWNLOADER_FORCE_DOWNLOAD_HELP = """
81
+ Force download of files even if they already locally. MD5s will not be calculated or checked for this mode. The default is False.
82
+ """
83
+
84
+ DOWNLOAD_REGEX_HELP = """
85
+ A regular expression to filter the files to download. Only files whose names match the regex are downloaded. If a task has multiple files and the regex matches only a subset of them, that subset is considered to be the entire task. This means that by default, tasks are reported to the server as downloaded, even though they weren't all downloaded.
86
+ """
87
+
88
+ DOWNLOAD_DISABLE_REPORTING_HELP = """
89
+ Decide whether to report back to the server with the status of the download. If you use this flag the state of tasks on the server remains unaffected, whether it is downloaded or pernding.
90
+ """
91
+
92
+ SEQ_FORMAT_HELP = """
93
+ The format of the graph to display. The default is text.
94
+ """
95
+
96
+
97
+ DEFAULT_DOCS_PORT = 8025
98
+
99
+ LOG_FORMATTER = logging.Formatter(
100
+ "%(asctime)s %(name)s%(levelname)9s %(threadName)s: %(message)s"
101
+ )
102
+
103
+ logger = logging.getLogger("conductor.cli")
104
+
105
+
106
+ cfg = config.get()
107
+ DEFAULT_CONFIG_MD5_CACHING = cfg["md5_caching"]
108
+ DEFAULT_CONFIG_THREAD_COUNT = cfg["thread_count"]
109
+ DEFAULT_CONFIG_LOG_LEVEL = cfg["log_level"]
110
+ DEFAULT_DOWNLOADER_PAGE_SIZE = cfg["downloader_page_size"]
111
+
112
+
113
+ def _set_logging(log_level, log_to_console=True, log_dir=None, log_filename=None):
114
+ level = loggeria.LEVEL_MAP.get(log_level)
115
+ loggeria.setup_conductor_logging(
116
+ logger_level=level,
117
+ log_dirpath=log_dir,
118
+ log_filename=log_filename,
119
+ console_formatter=loggeria.FORMATTER_VERBOSE,
120
+ disable_console_logging= not log_to_console,
121
+ use_system_log=True,
122
+ )
123
+
124
+ print("Logging to %s" % loggeria.LOG_PATH)
125
+
126
+ def _register(client):
127
+ api_client.ApiClient.register_client(
128
+ client_name=client.CLIENT_NAME, client_version=VERSION
129
+ )
130
+
131
+ ########################### MAIN #################################
132
+ @click.group(invoke_without_command=True)
133
+ @click.pass_context
134
+ @click.option("-v", "--version", is_flag=True, help="Print the version and exit.")
135
+ def main(ctx, version):
136
+ """
137
+ Conductor Command-line interface.
138
+
139
+ To get help on subcommands, use the --help flag after the subcommand.
140
+
141
+ Example:
142
+ conductor download --help
143
+
144
+ """
145
+ if not ctx.invoked_subcommand:
146
+ if version:
147
+ click.echo(VERSION)
148
+ ctx.exit()
149
+ click.echo(ctx.get_help())
150
+ ctx.exit()
151
+
152
+ ############################# UPLOAD #############################
153
+ @main.command()
154
+ @click.option("-db", "--database_filepath", help=UPLOADER_DATABASE_FILEPATH_HELP)
155
+ @click.option(
156
+ "-md5",
157
+ "--md5_caching",
158
+ help=UPLOADER_MD5_CACHING_HELP,
159
+ type=bool,
160
+ default=DEFAULT_CONFIG_MD5_CACHING,
161
+ )
162
+ @click.option(
163
+ "-lv",
164
+ "--log_level",
165
+ help=LOG_LEVEL_HELP,
166
+ type=click.Choice(choices=loggeria.LEVELS, case_sensitive=False),
167
+ show_choices=True,
168
+ default=DEFAULT_CONFIG_LOG_LEVEL,
169
+ )
170
+ @click.option("-ld", "--log_dir", help=LOG_DIR_HELP)
171
+ @click.option("-lcl", "--log_to_console", is_flag=True, help=LOG_TO_CONSOLE_HELP, default=False)
172
+ @click.option(
173
+ "-tc",
174
+ "--thread_count",
175
+ type=int,
176
+ help=THREADS_HELP,
177
+ default=DEFAULT_CONFIG_THREAD_COUNT,
178
+ )
179
+ @click.option("-lc", "--location", help=LOCATION_HELP)
180
+ @click.argument("paths", nargs=-1, type=click.Path(exists=True, resolve_path=True))
181
+ def upload(
182
+ database_filepath, location, md5_caching, log_level, log_to_console, log_dir, thread_count, paths
183
+ ):
184
+ """Upload files to Conductor.
185
+
186
+ With no arguments, the uploader runs in daemon mode, watching for files to upload for submitted
187
+ jobs.
188
+
189
+ Alternatively, specify a list of paths to upload.
190
+
191
+ Example:
192
+ conductor upload file1 file2 file3
193
+ """
194
+
195
+ _set_logging(log_level, log_to_console=log_to_console, log_dir=log_dir, log_filename="conductor_uploader.log")
196
+
197
+ args_dict = {
198
+ "database_filepath": database_filepath,
199
+ "location": location,
200
+ "md5_caching": md5_caching,
201
+ "thread_count": thread_count,
202
+ }
203
+
204
+ up = Uploader(args_dict)
205
+
206
+ if paths:
207
+ up.assets_only(*paths)
208
+ return
209
+
210
+ up.main()
211
+
212
+
213
+ ########################### PACKAGES #############################
214
+ @main.command()
215
+ @click.option(
216
+ "-f",
217
+ "--fmt",
218
+ "--format",
219
+ default="text",
220
+ help=PACKAGES_FORMAT_HELP,
221
+ type=click.Choice(choices=PACKAGES_FORMAT_OPTIONS, case_sensitive=False),
222
+ )
223
+ def packages(fmt):
224
+ """List the software packages available on the render nodes in the cloud."""
225
+ package_query.pq(format=fmt)
226
+
227
+
228
+ ########################### DOCS #############################
229
+ @main.command()
230
+ @click.option(
231
+ "-p",
232
+ "--port",
233
+ help=DOCS_PORT_HELP,
234
+ type=int,
235
+ default=DEFAULT_DOCS_PORT,
236
+ )
237
+ def docs(port):
238
+ """Open the Conductor Core documentation in a web browser."""
239
+
240
+ handler = http.server.SimpleHTTPRequestHandler
241
+ socketserver.TCPServer.allow_reuse_address = True
242
+ with socketserver.TCPServer(("", port), handler) as httpd:
243
+ print(f"Server started at http://localhost:{port}")
244
+ print("Press Ctrl+C to stop the server")
245
+
246
+ # Change to the directory containing your site
247
+ current_dir = os.path.dirname(os.path.abspath(__file__))
248
+ site_dir = os.path.join(current_dir, "docsite")
249
+ os.chdir(site_dir)
250
+
251
+ # Start the server
252
+ localhost_url = f"http://localhost:{port}"
253
+ webbrowser.open(localhost_url)
254
+ httpd.serve_forever()
255
+
256
+
257
+ ########################### DEPRECATIONS #############################
258
+ ############################# UPLOADER #############################
259
+ DEPRECATED_PATHS_HELP = "Specify a list of paths to upload."
260
+
261
+
262
+ @main.command(deprecated=True)
263
+ @click.option("-db", "--database_filepath", help=UPLOADER_DATABASE_FILEPATH_HELP)
264
+ @click.option(
265
+ "-md5",
266
+ "--md5_caching",
267
+ help=UPLOADER_MD5_CACHING_HELP,
268
+ type=bool,
269
+ default=DEFAULT_CONFIG_MD5_CACHING,
270
+ )
271
+ @click.option(
272
+ "-lv",
273
+ "--log_level",
274
+ help=LOG_LEVEL_HELP,
275
+ type=click.Choice(choices=loggeria.LEVELS, case_sensitive=False),
276
+ show_choices=True,
277
+ default=DEFAULT_CONFIG_LOG_LEVEL,
278
+ )
279
+ @click.option("-ld", "--log_dir", help=LOG_DIR_HELP)
280
+ @click.option(
281
+ "-tc",
282
+ "--thread_count",
283
+ type=int,
284
+ help=THREADS_HELP,
285
+ default=DEFAULT_CONFIG_THREAD_COUNT,
286
+ )
287
+ @click.option("-lcl", "--log_to_console", is_flag=True, help=LOG_TO_CONSOLE_HELP, default=False)
288
+ @click.option("-lc", "--location", help=LOCATION_HELP)
289
+ @click.option(
290
+ "-p",
291
+ "--paths",
292
+ help=DEPRECATED_PATHS_HELP,
293
+ multiple=True,
294
+ type=click.Path(exists=True, resolve_path=True),
295
+ )
296
+ def uploader(
297
+ database_filepath, location, md5_caching, log_level, log_dir, log_to_console, thread_count, paths
298
+ ):
299
+ """Upload files to Conductor.
300
+
301
+ With no arguments, the uploader runs in daemon mode, watching for files to upload for submitted
302
+ jobs.
303
+
304
+ Alternatively, specify a list of paths to upload with the --paths option.
305
+
306
+ Example:
307
+ conductor upload file1 file2 file3
308
+ """
309
+ _set_logging(log_level, log_to_console=log_to_console, log_dir=log_dir, log_filename="conductor_uploader.log")
310
+
311
+ args_dict = {
312
+ "database_filepath": database_filepath,
313
+ "location": location,
314
+ "md5_caching": md5_caching,
315
+ "thread_count": thread_count,
316
+ }
317
+
318
+ up = Uploader(args_dict)
319
+
320
+ if paths:
321
+ up.assets_only(*paths)
322
+ return
323
+
324
+ up.main()
325
+
326
+
327
+ ########################### DOWNLOADER #############################
328
+ DEPRECATED_JOBID_HELP = (
329
+ "Download all the files from completed tasks for the given jobs."
330
+ )
331
+ DEPRECATED_TASKID_HELP = "Download the files from the given tasks in the specified job."
332
+
333
+
334
+ @main.command(deprecated=True)
335
+ @click.option("-o", "--output", help=DOWNLOADER_OUTPUT_PATH_HELP)
336
+ @click.option("-j", "--job_id", help=DEPRECATED_JOBID_HELP)
337
+ @click.option("-t", "--task_id", help=DEPRECATED_TASKID_HELP)
338
+ @click.option(
339
+ "-lv",
340
+ "--log_level",
341
+ help=LOG_LEVEL_HELP,
342
+ type=click.Choice(choices=loggeria.LEVELS, case_sensitive=False),
343
+ show_choices=True,
344
+ default=DEFAULT_CONFIG_LOG_LEVEL,
345
+ )
346
+ @click.option("-ld", "--log_dir", help=LOG_DIR_HELP)
347
+ @click.option(
348
+ "-tc",
349
+ "--thread_count",
350
+ type=int,
351
+ help=THREADS_HELP,
352
+ default=DEFAULT_CONFIG_THREAD_COUNT,
353
+ )
354
+ @click.option("-lc", "--location", help=LOCATION_HELP)
355
+ @click.pass_context
356
+ def downloader(
357
+ ctx, job_id, task_id, output, location, log_level, log_dir, thread_count
358
+ ):
359
+ """
360
+ Download renders and other output files from Conductor. You can specify a job id and optional task id to
361
+ download, or you can omit all options and the downloader will run in daemon mode.
362
+
363
+ In daemon mode, the downloader polls for new jobs to download. You can specify a location tag to
364
+ limit the scope of the downloader to only download jobs that were submitted with the same
365
+ location tag.
366
+
367
+ Examples:
368
+ conductor downloader # daemon mode
369
+ conductor downloader --job_id --task_id 01234
370
+ conductor downloader --task_id --task_id 123
371
+ """
372
+ click.secho("This command has been deprecated. Please use conductor download instead.", fg='red')
373
+ logfile = log_dir and os.path.join(log_dir, "conductor_dl.log")
374
+ _set_logging(log_level, logfile)
375
+ _register(Downloader)
376
+
377
+ if not job_id and not task_id:
378
+ Downloader.start_daemon(
379
+ thread_count=thread_count, location=location, output_dir=output
380
+ )
381
+ ctx.exit(0)
382
+
383
+ Downloader.download_jobs(
384
+ (job_id,),
385
+ task_id=task_id,
386
+ thread_count=thread_count,
387
+ output_dir=output,
388
+ )
389
+
390
+
391
+ ########################### PAGED DOWNLOAD #############################
392
+ @main.command()
393
+ @click.option("-f", "--force", is_flag=True, help=DOWNLOADER_FORCE_DOWNLOAD_HELP)
394
+ @click.option(
395
+ "-ps",
396
+ "--page_size",
397
+ type=int,
398
+ help=DOWNLOADER_PAGE_SIZE_HELP,
399
+ default=DEFAULT_DOWNLOADER_PAGE_SIZE,
400
+ )
401
+ @click.option(
402
+ "-o", "--output-path",
403
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
404
+ help=DOWNLOADER_OUTPUT_PATH_HELP,
405
+ )
406
+ @click.option(
407
+ "-pi",
408
+ "--progress_interval",
409
+ type=int,
410
+ help=PROGRESS_INTERVAL_HELP,
411
+ default=DEFAULT_PROGRESS_INTERVAL,
412
+ )
413
+ @click.option(
414
+ "-lv",
415
+ "--log_level",
416
+ help=LOG_LEVEL_HELP,
417
+ type=click.Choice(choices=loggeria.LEVELS, case_sensitive=False),
418
+ show_choices=True,
419
+ default=DEFAULT_CONFIG_LOG_LEVEL,
420
+ )
421
+ @click.option(
422
+ "-tc",
423
+ "--thread_count",
424
+ type=int,
425
+ help=THREADS_HELP,
426
+ default=DEFAULT_CONFIG_THREAD_COUNT,
427
+ )
428
+ @click.option(
429
+ "-dr",
430
+ "--disable-reporting",
431
+ help=DOWNLOAD_DISABLE_REPORTING_HELP,
432
+ is_flag=True,
433
+ default=False,
434
+ )
435
+ @click.option("-r", "--regex", help=DOWNLOAD_REGEX_HELP)
436
+ @click.option("-lc", "--location", help=LOCATION_HELP)
437
+ @click.argument("jobids", nargs=-1)
438
+ def download(
439
+ jobids,
440
+ page_size,
441
+ output_path,
442
+ location,
443
+ progress_interval,
444
+ log_level,
445
+ thread_count,
446
+ force,
447
+ regex,
448
+ disable_reporting,
449
+ ):
450
+
451
+ """
452
+ Download renders and other output files from Conductor. You can give a list of job ids to
453
+ download, or you can omit jobids and the downloader will run in daemon mode.
454
+
455
+ If you provide jobids, the default behavior is to download all the files from completed tasks
456
+ for those jobs. You can however specify an explicit set of tasks to downloade by providing a
457
+ task range spec after each job id. To do so, append a colon to the job id and then a compact
458
+ task specification. See the examples.
459
+
460
+ In daemon mode, the downloader polls for new jobs to download. You can specify a location tag to
461
+ limit the scope of the downloader to only download jobs that were submitted with the same
462
+ location tag.
463
+
464
+ Examples:
465
+
466
+ conductor download # daemon mode
467
+
468
+ conductor download 1234 1235
469
+
470
+ conductor download 1234:1-10
471
+
472
+ conductor download 1234:1-5x2,10,12-14
473
+
474
+ conductor download 1234:1-5 1235:5-10
475
+
476
+ """
477
+
478
+ _register(LoggingDownloadRunner)
479
+
480
+ # No longer uses Loggeria.
481
+ # Instead, it gets the logger with broken-pipe handling from the downloader.log module.
482
+ logging.getLogger(LOGGER_NAME).setLevel(LEVEL_MAP[log_level])
483
+
484
+ kwargs = {
485
+ "output_path": output_path,
486
+ "num_threads": thread_count,
487
+ "progress_interval": progress_interval,
488
+ "page_size": page_size,
489
+ "force": force,
490
+ "regex": regex,
491
+ "disable_reporting": disable_reporting,
492
+ }
493
+
494
+ dl = LoggingDownloadRunner(
495
+ jobids,
496
+ location,
497
+ **kwargs
498
+ )
499
+
500
+ dl.run()
501
+
ciocore/common.py CHANGED
@@ -14,17 +14,10 @@ import sys
14
14
  import time
15
15
  import traceback
16
16
 
17
- try:
18
- from importlib.reload import reload
19
- except ImportError:
20
- from imp import reload
21
- except ImportError:
22
- reload = reload
23
-
24
17
  BYTES_1KB = 1024
25
- BYTES_1MB = BYTES_1KB ** 2
26
- BYTES_1GB = BYTES_1KB ** 3
27
- BYTES_1TB = BYTES_1KB ** 4
18
+ BYTES_1MB = BYTES_1KB**2
19
+ BYTES_1GB = BYTES_1KB**3
20
+ BYTES_1TB = BYTES_1KB**4
28
21
 
29
22
  CONDUCTOR_LOGGER_NAME = "conductor"
30
23
 
@@ -283,7 +276,7 @@ class DecRetry(object):
283
276
  sleep_time = self.static_sleep
284
277
  else:
285
278
  # use random for jitter.
286
- sleep_time = random.randrange(0, 2 ** try_num)
279
+ sleep_time = random.randrange(0, 2**try_num)
287
280
  msg = "%s, Retrying in %d seconds..." % (str(e), sleep_time)
288
281
  logger.warning(msg)
289
282
  self.sleep(sleep_time)
@@ -371,7 +364,16 @@ def generate_md5(
371
364
 
372
365
  while len(file_buffer) > 0:
373
366
  hash_obj.update(file_buffer)
374
- file_buffer = file_obj.read(MD5_BLOCKSIZE)
367
+ try:
368
+ file_buffer = file_obj.read(MD5_BLOCKSIZE)
369
+ except OSError:
370
+ logger.log(
371
+ log_level,
372
+ "Cannot read file '%s'. Is it corrupted or unavailable?",
373
+ filepath,
374
+ )
375
+ raise
376
+
375
377
  curtime = time.time()
376
378
  bytes_processed = buffer_count * MD5_BLOCKSIZE
377
379
  percentage_processed = int((bytes_processed / float(file_size)) * 100)
@@ -435,4 +437,4 @@ def get_human_timestamp(seconds_since_epoch):
435
437
  """
436
438
  convert the given seconds since epoch (float)
437
439
  """
438
- return str(datetime.datetime.fromtimestamp(int(seconds_since_epoch)))
440
+ return str(datetime.datetime.fromtimestamp(int(seconds_since_epoch)))