ciocore 6.3.2rc1__py2.py3-none-any.whl → 6.4.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.

Files changed (91) hide show
  1. ciocore/VERSION +1 -1
  2. ciocore/__about__.py +3 -20
  3. ciocore/api_client.py +116 -236
  4. ciocore/cli/__init__.py +3 -0
  5. ciocore/cli/conductor.py +206 -0
  6. ciocore/config.py +44 -50
  7. ciocore/data.py +131 -85
  8. ciocore/downloader.py +64 -46
  9. ciocore/hardware_set.py +99 -326
  10. ciocore/package_environment.py +74 -48
  11. ciocore/package_tree.py +377 -300
  12. ciocore/uploader/_uploader.py +2 -6
  13. ciocore-6.4.0.data/scripts/conductor +19 -0
  14. ciocore-6.4.0.data/scripts/conductor.bat +13 -0
  15. {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/METADATA +12 -35
  16. ciocore-6.4.0.dist-info/RECORD +51 -0
  17. {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/WHEEL +1 -1
  18. tests/instance_type_fixtures.py +9 -43
  19. tests/mocks/api_client_mock.py +31 -0
  20. tests/test_hardware_set.py +4 -50
  21. ciocore/cli.py +0 -336
  22. ciocore/dev_inst_tagger.py +0 -120
  23. ciocore/docsite/404.html +0 -723
  24. ciocore/docsite/apidoc/api_client/index.html +0 -2590
  25. ciocore/docsite/apidoc/apidoc/index.html +0 -868
  26. ciocore/docsite/apidoc/config/index.html +0 -1562
  27. ciocore/docsite/apidoc/data/index.html +0 -1550
  28. ciocore/docsite/apidoc/hardware_set/index.html +0 -2324
  29. ciocore/docsite/apidoc/package_environment/index.html +0 -1430
  30. ciocore/docsite/apidoc/package_tree/index.html +0 -2310
  31. ciocore/docsite/assets/_mkdocstrings.css +0 -16
  32. ciocore/docsite/assets/images/favicon.png +0 -0
  33. ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js +0 -29
  34. ciocore/docsite/assets/javascripts/bundle.4e31edb1.min.js.map +0 -8
  35. ciocore/docsite/assets/javascripts/lunr/min/lunr.ar.min.js +0 -1
  36. ciocore/docsite/assets/javascripts/lunr/min/lunr.da.min.js +0 -18
  37. ciocore/docsite/assets/javascripts/lunr/min/lunr.de.min.js +0 -18
  38. ciocore/docsite/assets/javascripts/lunr/min/lunr.du.min.js +0 -18
  39. ciocore/docsite/assets/javascripts/lunr/min/lunr.es.min.js +0 -18
  40. ciocore/docsite/assets/javascripts/lunr/min/lunr.fi.min.js +0 -18
  41. ciocore/docsite/assets/javascripts/lunr/min/lunr.fr.min.js +0 -18
  42. ciocore/docsite/assets/javascripts/lunr/min/lunr.hi.min.js +0 -1
  43. ciocore/docsite/assets/javascripts/lunr/min/lunr.hu.min.js +0 -18
  44. ciocore/docsite/assets/javascripts/lunr/min/lunr.hy.min.js +0 -1
  45. ciocore/docsite/assets/javascripts/lunr/min/lunr.it.min.js +0 -18
  46. ciocore/docsite/assets/javascripts/lunr/min/lunr.ja.min.js +0 -1
  47. ciocore/docsite/assets/javascripts/lunr/min/lunr.jp.min.js +0 -1
  48. ciocore/docsite/assets/javascripts/lunr/min/lunr.kn.min.js +0 -1
  49. ciocore/docsite/assets/javascripts/lunr/min/lunr.ko.min.js +0 -1
  50. ciocore/docsite/assets/javascripts/lunr/min/lunr.multi.min.js +0 -1
  51. ciocore/docsite/assets/javascripts/lunr/min/lunr.nl.min.js +0 -18
  52. ciocore/docsite/assets/javascripts/lunr/min/lunr.no.min.js +0 -18
  53. ciocore/docsite/assets/javascripts/lunr/min/lunr.pt.min.js +0 -18
  54. ciocore/docsite/assets/javascripts/lunr/min/lunr.ro.min.js +0 -18
  55. ciocore/docsite/assets/javascripts/lunr/min/lunr.ru.min.js +0 -18
  56. ciocore/docsite/assets/javascripts/lunr/min/lunr.sa.min.js +0 -1
  57. ciocore/docsite/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -1
  58. ciocore/docsite/assets/javascripts/lunr/min/lunr.sv.min.js +0 -18
  59. ciocore/docsite/assets/javascripts/lunr/min/lunr.ta.min.js +0 -1
  60. ciocore/docsite/assets/javascripts/lunr/min/lunr.te.min.js +0 -1
  61. ciocore/docsite/assets/javascripts/lunr/min/lunr.th.min.js +0 -1
  62. ciocore/docsite/assets/javascripts/lunr/min/lunr.tr.min.js +0 -18
  63. ciocore/docsite/assets/javascripts/lunr/min/lunr.vi.min.js +0 -1
  64. ciocore/docsite/assets/javascripts/lunr/min/lunr.zh.min.js +0 -1
  65. ciocore/docsite/assets/javascripts/lunr/tinyseg.js +0 -206
  66. ciocore/docsite/assets/javascripts/lunr/wordcut.js +0 -6708
  67. ciocore/docsite/assets/javascripts/workers/search.dfff1995.min.js +0 -42
  68. ciocore/docsite/assets/javascripts/workers/search.dfff1995.min.js.map +0 -8
  69. ciocore/docsite/assets/stylesheets/main.83068744.min.css +0 -1
  70. ciocore/docsite/assets/stylesheets/main.83068744.min.css.map +0 -1
  71. ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css +0 -1
  72. ciocore/docsite/assets/stylesheets/palette.ecc896b0.min.css.map +0 -1
  73. ciocore/docsite/cmdline/docs/index.html +0 -834
  74. ciocore/docsite/cmdline/downloader/index.html +0 -897
  75. ciocore/docsite/cmdline/packages/index.html +0 -841
  76. ciocore/docsite/cmdline/uploader/index.html +0 -950
  77. ciocore/docsite/how-to-guides/index.html +0 -831
  78. ciocore/docsite/index.html +0 -853
  79. ciocore/docsite/logo.png +0 -0
  80. ciocore/docsite/objects.inv +0 -0
  81. ciocore/docsite/search/search_index.json +0 -1
  82. ciocore/docsite/sitemap.xml +0 -3
  83. ciocore/docsite/sitemap.xml.gz +0 -0
  84. ciocore/docsite/stylesheets/extra.css +0 -26
  85. ciocore/docsite/stylesheets/tables.css +0 -170
  86. ciocore/package_query.py +0 -171
  87. ciocore-6.3.2rc1.dist-info/RECORD +0 -115
  88. ciocore-6.3.2rc1.dist-info/entry_points.txt +0 -2
  89. tests/test_cli.py +0 -161
  90. tests/test_downloader.py +0 -56
  91. {ciocore-6.3.2rc1.dist-info → ciocore-6.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env python
2
+ # Use absolute imports to avoid a "conductor" name clash (this module name vs conductor package).
3
+ from __future__ import absolute_import
4
+
5
+ import argparse
6
+ import os
7
+ import sys
8
+
9
+ sys.path.append(os.path.dirname(os.path.dirname(__file__)))
10
+
11
+ from ciocore import downloader, uploader, loggeria, config
12
+
13
+ def build_parser():
14
+ cfg = config.config().config
15
+
16
+ # Create a parent parser. Arguments that are common across all subparsers can be added to this parser
17
+ parent_parser = argparse.ArgumentParser(add_help=False)
18
+
19
+ # create the main parser. Not sure why this parser is required, but got parsing tracebacks when excluding it (it gets confused about the arguments provided)
20
+ parser = argparse.ArgumentParser(description="description")
21
+ subparsers = parser.add_subparsers(title="actions")
22
+
23
+
24
+ #############################
25
+ # UPLOADER PARSER
26
+ #############################
27
+ uploader_parser_desciption = "parse uploader arguments"
28
+ uploader_parser_help = ("Starts the Uploader in a continous running mode, polling Conductor for "
29
+ "paths to upload unless a list of paths are provided."
30
+ )
31
+
32
+ uploader_parser = subparsers.add_parser("uploader", parents=[parent_parser],
33
+ help=uploader_parser_help,
34
+ description=uploader_parser_desciption,
35
+ formatter_class=argparse.RawTextHelpFormatter)
36
+
37
+ uploader_parser.add_argument("--database_filepath",
38
+ help=("The filepath to the local md5 caching database. If no filepath "
39
+ "is specified, the database will be created in a temp directory"))
40
+
41
+ uploader_parser.add_argument("--location",
42
+ help=('An optional string to indicate which location this uploader '
43
+ 'executable should register as. This option is only relevant '
44
+ 'for conductor accounts which submits jobs from different locations '
45
+ '(e.g. differing geographic locations or office locations that have differing file systems).'
46
+ ' Typically each location would have its own conductor uploader process running. This location '
47
+ 'string allows each uploader to target specific upload jobs (files to upload) that are appropriate '
48
+ 'for it. This is potentially useful as each location may have differing file systems '
49
+ 'available to it (e.g. uploader1 has /filesystem1 available to it, but uploader2 only '
50
+ 'has /filesystem2 available to it). In this case uploader1 should only upload files '
51
+ 'that exist on /filesystem1 and uploader2 should only upload files that exist on /filesystem2. '
52
+ 'This is achieved by including a location argument (such as "location1" or "location2") '
53
+ 'when submitting jobs, as well as when launching this uploader command.'))
54
+
55
+ uploader_parser.add_argument("--md5_caching",
56
+ help=("Use cached md5s. This can dramatically improve the uploading "
57
+ "times, as md5 checking can be very time consuming. Caching md5s "
58
+ "allows subsequent uploads (of the same files) to skip the "
59
+ "md5 generation process (if the files appear to not have been "
60
+ "modified since the last time they were submitted). The cache is "
61
+ "stored locally and uses a file's modification time and file size "
62
+ "to intelligently guess whether the file has changed. Set this "
63
+ "flag to False if there is concern that files may not be getting "
64
+ "re-uploaded properly"),
65
+ choices=[False, True],
66
+ type=cast_to_bool,
67
+ default=None)
68
+
69
+ uploader_parser.add_argument("--log_level",
70
+ choices=loggeria.LEVELS,
71
+ help="The logging level to display")
72
+
73
+ uploader_parser.add_argument("--log_dir",
74
+ help=("When provided, will write a log file to "
75
+ "the provided directory. This will be a "
76
+ "rotating log, creating a new log file "
77
+ "everyday, while storing the last 7 days "
78
+ "of logs"))
79
+
80
+ uploader_parser.add_argument("--thread_count",
81
+ type=int,
82
+ default=cfg["thread_count"],
83
+ help=('The number of threads that should download simultaneously'))
84
+
85
+ uploader_parser.add_argument("--paths",
86
+ type=str,
87
+ action="append",
88
+ nargs="+",
89
+ help=('A list of explicit paths to upload. Paths with spaces and/or special characters should be encapsulated in quotes'))
90
+
91
+ uploader_parser.set_defaults(func=run_uploader)
92
+
93
+ #############################
94
+ # DOWNLOADER PARSER
95
+ #############################
96
+
97
+ downloader_parser_desciption = "parse downloader arguments"
98
+ downloader_parser_help = ""
99
+
100
+ downloader_parser = subparsers.add_parser("downloader", parents=[parent_parser],
101
+ help=downloader_parser_help,
102
+ description=downloader_parser_desciption,
103
+ formatter_class=argparse.RawTextHelpFormatter)
104
+
105
+ downloader_parser.add_argument("--job_id",
106
+ help=("The job id(s) to download. When specified "
107
+ "will only download those jobs and terminate "
108
+ "afterwards"),
109
+ action='append')
110
+
111
+ downloader_parser.add_argument("--task_id",
112
+ help="Manually download output for specific tasks - use a comma-separated list of tasks if you wish")
113
+
114
+ downloader_parser.add_argument("--output",
115
+ help="Override for the output directory")
116
+
117
+ downloader_parser.add_argument("--location",
118
+ help=('An optional string to indicate which location this downloader '
119
+ 'executable should register as. This option is only relevant for '
120
+ 'conductor accounts which submits jobs from different locations '
121
+ '(e.g. differing geographic locations or office locations that '
122
+ 'have differing file systems). Typically each location would '
123
+ 'have its own conductor downloader process running. This location '
124
+ 'argument allows each downloader to target specific jobs (to '
125
+ 'download upon job-completion) that match its appropriate location. '
126
+ 'Essentially this allows the location of which a job was submitted '
127
+ 'from to also be the destination in which to deliver completed '
128
+ 'renders to (which would typically be the desired behavior).'))
129
+
130
+ downloader_parser.add_argument("--project",
131
+ help=('An optional string to indicate which project that this downloader executable should register as.'))
132
+
133
+ downloader_parser.add_argument("--log_level",
134
+ choices=loggeria.LEVELS,
135
+ default=cfg["log_level"],
136
+ help="The logging level to display")
137
+
138
+ downloader_parser.add_argument("--log_dir",
139
+ help=("When provided, will write a log file to "
140
+ "the provided directory. This will be a "
141
+ "rotating log, creating a new log file "
142
+ "everyday, while storing the last 7 days "
143
+ "of logs"))
144
+
145
+ downloader_parser.add_argument("--thread_count",
146
+ type=int,
147
+ default=cfg["thread_count"],
148
+ help=('The number of threads that should download simultaneously'))
149
+
150
+ downloader_parser.add_argument("--alt",
151
+ help=('Run an alternative version of the downloader'),
152
+ action='store_true')
153
+
154
+ downloader_parser.set_defaults(func=run_downloader)
155
+
156
+ return parser
157
+
158
+
159
+ def cast_to_bool(string):
160
+ '''
161
+ Ensure that the argument provided is either "True" or "False (or "true" or
162
+ "false") and convert that argument to an actual bool value (True or False).
163
+ '''
164
+ string_lower = string.lower()
165
+ if string_lower == "true":
166
+ return True
167
+ elif string_lower == "false":
168
+ return False
169
+ raise argparse.ArgumentTypeError('Argument must be True or False')
170
+
171
+ def run_uploader(args):
172
+ uploader.run_uploader(args)
173
+
174
+
175
+ def run_downloader(args):
176
+ '''
177
+ Convert the argparse Namespace object to a dictionary and run the downloader
178
+ with the given args.
179
+ '''
180
+ # Convert Namespace args object to args dict
181
+ args_dict = vars(args)
182
+
183
+ if args_dict.get("task_id") and not args_dict.get("job_id"):
184
+ raise argparse.ArgumentTypeError('Must supply a job_id with task_id.')
185
+
186
+ # New downloader broke in python 3. It was used only for linux and in
187
+ # daemon mode, so for now we'll use the old downloader for everything.
188
+
189
+ return downloader.run_downloader(args_dict)
190
+
191
+
192
+ def main():
193
+ parser = build_parser()
194
+ args = parser.parse_args()
195
+ # Handle calling the script without an argument, fixes argparse issue
196
+ # https://bugs.python.org/issue16308
197
+ try:
198
+ func = args.func
199
+ except AttributeError:
200
+ parser.error("too few arguments")
201
+ func(args)
202
+
203
+
204
+ if __name__ == '__main__':
205
+ main()
206
+
ciocore/config.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
- Config is a configuration object implemented as a module-level singleton.
2
+ A configuration object implemented as a module-level singleton.
3
3
 
4
- Configuration variables can be shared by importing the module. If there are changes in environment variables or other sources, the config can be refreshed.
4
+ Configuration variables can be shared by importing the module. If there are changes in environment variables or other sources, the config can be refreshed. See [config()](#config)
5
5
  """
6
6
 
7
7
  import logging
@@ -24,16 +24,17 @@ __config__ = None
24
24
 
25
25
  def config(force=False):
26
26
  """
27
+ DEPRECATED! Use [get()](#get) instead.
28
+
27
29
  Instantiate a config object if necessary.
28
30
 
29
- Deprecated:
30
- Use [get()](#get) instead.
31
+ Keyword Arguments:
31
32
 
32
- Args:
33
- force (bool): Discards any existing config object and instantiate a new one -- Defaults to `False`.
33
+ * **`force`** -- Discards any existing config object and instantiate a new one -- Defaults to `False`.
34
34
 
35
35
  Returns:
36
- dict: A dictionary containing configuration values.
36
+
37
+ * [Config()](#Config) object containing a dictionary of configuration variables.
37
38
  """
38
39
 
39
40
  global __config__
@@ -45,25 +46,32 @@ def get(force=False):
45
46
  """
46
47
  Instantiate a config object if necessary and return the dictionary.
47
48
 
48
- Args:
49
- force (bool): Discards any existing config object and instantiate a new one -- Defaults to `False`.
49
+ Keyword Arguments:
50
+
51
+ * **`force`** -- Discards any existing config object and instantiate a new one -- Defaults to `False`.
50
52
 
51
53
  Returns:
52
- dict: A dictionary containing configuration values.
53
-
54
- Example:
55
- >>> from ciocore import config
56
- >>> config.get()
57
- {
58
- 'thread_count': 16,
59
- 'priority': 5,
60
- 'md5_caching': True,
61
- 'log_level': 'INFO',
62
- 'url': 'https://dashboard.conductortech.com',
63
- 'auth_url': 'https://dashboard.conductortech.com',
64
- 'api_url': 'https://api.conductortech.com',
65
- 'api_key': None
66
- }
54
+
55
+ * A dictionary containing configuration.
56
+ \
57
+ ???+ example
58
+ ``` python
59
+ from ciocore import config
60
+
61
+ print(config.get())
62
+
63
+ # Result:
64
+ # {
65
+ # 'thread_count': 16,
66
+ # 'priority': 5,
67
+ # 'md5_caching': True,
68
+ # 'log_level': 'INFO',
69
+ # 'url': 'https://dashboard.conductortech.com',
70
+ # 'auth_url': 'https://dashboard.conductortech.com',
71
+ # 'api_url': 'https://api.conductortech.com',
72
+ # 'api_key': None
73
+ # }
74
+ ```
67
75
  """
68
76
  global __config__
69
77
  if force or not __config__:
@@ -73,28 +81,12 @@ def get(force=False):
73
81
  class Config(object):
74
82
  def __init__(self):
75
83
  """
76
- Initialize the config object.
77
-
78
- A config object is a dictionary containing configuration values. It is a singleton, so there is only one instance of it. It is instantiated the first time it is needed. It can be refreshed by calling get() with the `force` keyword argument set to `True`.
79
-
80
- A Config object has the following properties:
81
-
82
- * `thread_count` The number of threads to use for downloading files. Defaults to the number of CPUs on the system times 2. It can be overridden by the `CONDUCTOR_THREAD_COUNT` environment variable.
83
- * `priority` Set the priority for submissions. Defaults to 5. It can be overridden by the `CONDUCTOR_PRIORITY` environment variable.
84
- * `md5_caching` Whether to cache MD5s. Defaults to `True`. It can be overridden by the `CONDUCTOR_MD5_CACHING` environment variable. Cachine MD5s significantly improves submission performance, but on rare occasions it can cause submissions to fail. If you experience this, set `md5_caching` to `False`.
85
- * `log_level` The logging level. Defaults to `INFO`. It can be overridden by the `CONDUCTOR_LOG_LEVEL` environment variable.
86
- * `url` The URL of the Conductor dashboard. Defaults to `https://dashboard.conductortech.com`. It can be overridden by the `CONDUCTOR_URL` environment variable.
87
- * `auth_url` The URL of the Conductor dashboard. Defaults to `https://dashboard.conductortech.com`. It can be overridden by the `CONDUCTOR_AUTH_URL` environment variable. This is deprecated. Use `url` instead.
88
- * `api_url` The URL of the Conductor API. Defaults to `https://api.conductortech.com`. It can be overridden by the `CONDUCTOR_API_URL` environment variable.
89
- * `api_key` The API key. The API key can be acquired from the Conductor dashboard, and can be stored in an environment variable or a file. In both cases the API KEY can be a JSON object or a base64 encoded JSON object. If it is base64 encoded, it can be a string or bytes. If it is a string, it will be decoded as ASCII. If it is bytes, it will be decoded as UTF-8.
90
- * Environment variable: The `CONDUCTOR_API_KEY` variable can hold the API KEY directly.
91
- * File: The `CONDUCTOR_API_KEY_PATH` variable can hold the path to a file containing the API KEY.
84
+ Create a configuration dictionary.
92
85
 
93
- Returns:
94
- Config: A config object.
95
86
 
96
87
  Raises:
97
- ValueError -- Invalid inputs, such as badly formed URLs.
88
+
89
+ * **`ValueError`** -- Invalid inputs, such as badly formed URLs.
98
90
  """
99
91
 
100
92
  try:
@@ -136,14 +128,15 @@ class Config(object):
136
128
  @staticmethod
137
129
  def get_api_key_from_variable():
138
130
  """
139
- Attempt to get an API key from the `CONDUCTOR_API_KEY` environment variable.
131
+ Attempt to get an API key from the CONDUCTOR_API_KEY environment variable.
140
132
 
141
133
  Raises:
142
- ValueError: An error occurred while reading or loading the key into JSON.
134
+
135
+ * **`ValueError`** -- An error occurred while loading the key into JSON.
143
136
 
144
137
  Returns:
145
- str: JSON object containing the key - base 64 decoded if necessary.
146
-
138
+
139
+ * JSON object containing the key - base 64 decoded if necessary.
147
140
  """
148
141
  api_key = os.environ.get("CONDUCTOR_API_KEY")
149
142
  if not api_key:
@@ -169,11 +162,12 @@ class Config(object):
169
162
  Attempt to get an API key from the file in the CONDUCTOR_API_KEY_PATH environment variable.
170
163
 
171
164
  Raises:
172
- ValueError: An error occurred while reading or loading the key into JSON.
165
+
166
+ * **`ValueError`** -- An error occurred while reading or loading the key into JSON.
173
167
 
174
168
  Returns:
175
- str: JSON object containing the key - base 64 decoded if necessary.
176
-
169
+
170
+ * JSON object containing the key - base 64 decoded if necessary.
177
171
  """
178
172
  api_key_path = os.environ.get("CONDUCTOR_API_KEY_PATH")
179
173
  if not api_key_path: