scalable-pypeline 1.2.2__py2.py3-none-any.whl → 2.0.1__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.
- pypeline/__init__.py +1 -1
- pypeline/barrier.py +34 -0
- pypeline/composition.py +348 -0
- pypeline/constants.py +51 -84
- pypeline/dramatiq.py +470 -0
- pypeline/extensions.py +9 -8
- pypeline/flask/__init__.py +3 -5
- pypeline/flask/api/pipelines.py +109 -148
- pypeline/flask/api/schedules.py +14 -39
- pypeline/flask/decorators.py +18 -53
- pypeline/flask/flask_pypeline.py +156 -0
- pypeline/middleware.py +61 -0
- pypeline/pipeline_config_schema.py +104 -91
- pypeline/pypeline_yaml.py +458 -0
- pypeline/schedule_config_schema.py +35 -120
- pypeline/utils/config_utils.py +52 -310
- pypeline/utils/module_utils.py +35 -71
- pypeline/utils/pipeline_utils.py +161 -0
- scalable_pypeline-2.0.1.dist-info/METADATA +217 -0
- scalable_pypeline-2.0.1.dist-info/RECORD +27 -0
- scalable_pypeline-2.0.1.dist-info/entry_points.txt +3 -0
- tests/fixtures/__init__.py +0 -1
- pypeline/celery.py +0 -206
- pypeline/celery_beat.py +0 -254
- pypeline/flask/api/utils.py +0 -35
- pypeline/flask/flask_sermos.py +0 -156
- pypeline/generators.py +0 -196
- pypeline/logging_config.py +0 -171
- pypeline/pipeline/__init__.py +0 -0
- pypeline/pipeline/chained_task.py +0 -70
- pypeline/pipeline/generator.py +0 -254
- pypeline/sermos_yaml.py +0 -442
- pypeline/utils/graph_utils.py +0 -144
- pypeline/utils/task_utils.py +0 -552
- scalable_pypeline-1.2.2.dist-info/METADATA +0 -163
- scalable_pypeline-1.2.2.dist-info/RECORD +0 -33
- scalable_pypeline-1.2.2.dist-info/entry_points.txt +0 -2
- tests/fixtures/s3_fixtures.py +0 -52
- {scalable_pypeline-1.2.2.dist-info → scalable_pypeline-2.0.1.dist-info}/LICENSE +0 -0
- {scalable_pypeline-1.2.2.dist-info → scalable_pypeline-2.0.1.dist-info}/WHEEL +0 -0
- {scalable_pypeline-1.2.2.dist-info → scalable_pypeline-2.0.1.dist-info}/top_level.txt +0 -0
pypeline/utils/config_utils.py
CHANGED
@@ -1,326 +1,68 @@
|
|
1
|
-
""" General utilities used frequently in configuration-related tasks.
|
2
|
-
|
3
|
-
More specifically, these are methods that help interact with Pipeline and
|
4
|
-
Schedule configurations that originate from your `sermos.yaml` file. These
|
5
|
-
utility functions make it easy to switch between `local` and `cloud` modes
|
6
|
-
based on the value of `DEFAULT_BASE_URL` in your environment.
|
7
|
-
|
8
|
-
- If the base url is `local`, then all config tasks will read directly from
|
9
|
-
your local `sermos.yaml` file. Update operations will *not* do anything (that
|
10
|
-
is, your sermos.yaml file will not be updated).
|
11
|
-
|
12
|
-
- If the base url is anything other than `local`, this will assume a cloud
|
13
|
-
api url was provided (if None is set in environment, Sermos will default to
|
14
|
-
the Sermos Cloud base API assuming this is a Sermos Cloud deployment). You can
|
15
|
-
provide your own cloud API endpoints if desired, look to documentation for best
|
16
|
-
practices.
|
17
|
-
|
18
|
-
TODO Need to remove the dependency on Redis and make caching behavior optional.
|
19
|
-
"""
|
20
|
-
import os
|
21
1
|
import logging
|
22
|
-
import
|
23
|
-
from typing import Union, Any
|
24
|
-
from urllib.parse import urljoin
|
25
|
-
import requests
|
26
|
-
from rhodb.redis_conf import RedisConnector
|
27
|
-
from pypeline.constants import DEFAULT_BASE_URL, PIPELINE_CONFIG_CACHE_KEY, \
|
28
|
-
SCHEDULE_CONFIG_CACHE_KEY, CONFIG_REFRESH_RATE, USING_SERMOS_CLOUD, \
|
29
|
-
LOCAL_DEPLOYMENT_VALUE, DEFAULT_CONFIG_RETRIEVAL_PAGE_SIZE
|
30
|
-
from pypeline.sermos_yaml import load_sermos_config
|
31
|
-
from pypeline.schedule_config_schema import BaseScheduleSchema
|
32
|
-
|
33
|
-
logger = logging.getLogger(__name__)
|
34
|
-
redis_conn = RedisConnector().get_connection()
|
35
|
-
|
36
|
-
|
37
|
-
def get_access_key(access_key: Union[str, None] = None,
|
38
|
-
env_var_name: str = 'SERMOS_ACCESS_KEY'):
|
39
|
-
""" Simple helper to get admin server access key in a standard fashion. If
|
40
|
-
one is provided, return it back. If not, look in environment for
|
41
|
-
`env_var_name`. If that doesn't exist, raise useful error.
|
42
|
-
|
43
|
-
If this is a local deployment, no access key is required/relevant,
|
44
|
-
so simply return 'local'
|
45
|
-
"""
|
46
|
-
if access_key is not None:
|
47
|
-
return access_key
|
48
|
-
|
49
|
-
if not USING_SERMOS_CLOUD:
|
50
|
-
return LOCAL_DEPLOYMENT_VALUE # e.g. 'local'
|
51
|
-
|
52
|
-
try:
|
53
|
-
return os.environ[env_var_name]
|
54
|
-
except KeyError:
|
55
|
-
raise KeyError(
|
56
|
-
f"{env_var_name} not found in this environment. Find a valid "
|
57
|
-
"access key in your Sermos Cloud administration console.")
|
58
|
-
|
59
|
-
|
60
|
-
# TODO cast to UUID?
|
61
|
-
def get_deployment_id(deployment_id: Union[str, None] = None,
|
62
|
-
env_var_name: str = 'SERMOS_DEPLOYMENT_ID'):
|
63
|
-
""" Simple helper to get the deployment id in a standard fashion. Look in
|
64
|
-
the environment for `env_var_name`. If that doesn't exist, raise useful
|
65
|
-
error.
|
66
|
-
|
67
|
-
If this is a local deployment, no deployment id is required/relevant,
|
68
|
-
so this will simply return 'local' in the event the DEFAULT_BASE_URL is
|
69
|
-
set to the LOCAL_DEPLOYMENT_VALUE ('local' by default) in the environment.
|
70
|
-
"""
|
71
|
-
if deployment_id is not None:
|
72
|
-
return deployment_id
|
73
|
-
|
74
|
-
if not USING_SERMOS_CLOUD:
|
75
|
-
return LOCAL_DEPLOYMENT_VALUE # e.g. 'local'
|
76
|
-
|
77
|
-
try:
|
78
|
-
return os.environ[env_var_name]
|
79
|
-
except KeyError:
|
80
|
-
raise KeyError(
|
81
|
-
f"{env_var_name} not found in this environment. Note: this is "
|
82
|
-
"required when running a Celery worker as `beat`. Find this ID "
|
83
|
-
"in your administration console. For local development, this can "
|
84
|
-
"be any arbitrary string.")
|
85
|
-
|
86
|
-
|
87
|
-
def load_json_config_from_redis(key: str) -> Any:
|
88
|
-
""" Load a json key from redis. Special carve out for keys explicitly set
|
89
|
-
to "none".
|
90
|
-
"""
|
91
|
-
val = redis_conn.get(key)
|
92
|
-
if val is None or val.decode('utf-8').lower() == 'none':
|
93
|
-
return None
|
94
|
-
return json.loads(val)
|
95
|
-
|
96
|
-
|
97
|
-
def set_json_config_to_redis(key: str,
|
98
|
-
data: Union[dict, None],
|
99
|
-
refresh_rate: int = CONFIG_REFRESH_RATE):
|
100
|
-
""" For Admin API actions (e.g. schedules/pipelines), deployments cache
|
101
|
-
results. The standard method for doing this is through a refresh key, which
|
102
|
-
is set in redis to expire after the CONFIG_REFRESH_RATE. This will set
|
103
|
-
the cached key.
|
104
|
-
|
105
|
-
Rationale for manually setting a "None" key instead of simply skipping
|
106
|
-
is to protect against case of a spammed config request for an unknown
|
107
|
-
pipeline, for example. This will still limit our requests to Sermos Cloud
|
108
|
-
based on the refresh rate even in that scenario.
|
109
|
-
"""
|
110
|
-
if data is None:
|
111
|
-
data = 'None'
|
112
|
-
else:
|
113
|
-
data = json.dumps(data)
|
114
|
-
|
115
|
-
redis_conn.setex(key, refresh_rate, data)
|
116
|
-
|
117
|
-
|
118
|
-
def _generate_api_url(endpoint: str = ''):
|
119
|
-
""" Provide a normalized url based on the base url and endpoint and add in
|
120
|
-
the deployment_id to the url, which is required for all default
|
121
|
-
pipeline/schedule endpoints if using Sermos Cloud.
|
122
|
-
|
123
|
-
The Sermos Cloud API spec bases everything on the notion of `deployments`,
|
124
|
-
so if you are rolling your own 'non-local' API, you will need to mock this
|
125
|
-
concept in order to use the built in helper functions for retrieving
|
126
|
-
pipelines and schedules from an API source.
|
127
|
-
"""
|
128
|
-
deployment_id = get_deployment_id() # From env if None
|
129
|
-
return urljoin(DEFAULT_BASE_URL, f'deployments/{deployment_id}/{endpoint}')
|
130
|
-
|
2
|
+
from typing import Union
|
131
3
|
|
132
|
-
|
133
|
-
|
134
|
-
access_key: str,
|
135
|
-
refresh_rate: int = CONFIG_REFRESH_RATE) -> Any:
|
136
|
-
""" Attempt to load a configuration (pipeline/schedule) from cache If not available,
|
137
|
-
retrieve API response from Sermos Config Server and cache the response for
|
138
|
-
CONFIG_REFRESH_RATE seconds in local Redis.
|
139
|
-
"""
|
140
|
-
conf = load_json_config_from_redis(key)
|
141
|
-
if conf is not None:
|
142
|
-
return conf
|
143
|
-
|
144
|
-
# Ask Sermos Cloud (Note: Sermos Cloud's API expects `apikey`)
|
145
|
-
headers = {
|
146
|
-
'apikey': access_key,
|
147
|
-
}
|
148
|
-
|
149
|
-
params = {
|
150
|
-
'page_size': DEFAULT_CONFIG_RETRIEVAL_PAGE_SIZE,
|
151
|
-
'page': 1
|
152
|
-
}
|
153
|
-
|
154
|
-
r = requests.get(admin_api_endpoint, headers=headers, verify=True,
|
155
|
-
params=params)
|
4
|
+
from pypeline.constants import WORKER_NAME
|
5
|
+
from pypeline.pypeline_yaml import load_pypeline_config
|
156
6
|
|
157
|
-
|
158
|
-
if r.status_code == 200:
|
159
|
-
data = r.json()
|
160
|
-
else:
|
161
|
-
logger.warning(f"Non-200 response retrieving {admin_api_endpoint}: "
|
162
|
-
f"{r.status_code}, {r.reason}")
|
163
|
-
|
164
|
-
# There's a chance we need to request ALL schedule configs from sermos cloud
|
165
|
-
# for the scheduled tasks. Lets loop and grab all of them.
|
166
|
-
while key == SCHEDULE_CONFIG_CACHE_KEY and \
|
167
|
-
len(data['data']['results']) < data['data']['count']:
|
168
|
-
params['page'] += 1
|
169
|
-
r = requests.get(admin_api_endpoint, headers=headers, verify=True,
|
170
|
-
params=params)
|
171
|
-
if r.status_code == 200:
|
172
|
-
paginated_data = r.json()
|
173
|
-
data['data']['results'] = data['data']['results'] + \
|
174
|
-
paginated_data['data']['results']
|
175
|
-
else:
|
176
|
-
logger.warning(f"Non-200 response retrieving {admin_api_endpoint}: "
|
177
|
-
f"{r.status_code}, {r.reason}")
|
178
|
-
break
|
179
|
-
|
180
|
-
# Cache result
|
181
|
-
if data is not None:
|
182
|
-
set_json_config_to_redis(key, data, refresh_rate)
|
183
|
-
|
184
|
-
return data
|
7
|
+
logger = logging.getLogger(__name__)
|
185
8
|
|
186
9
|
|
187
10
|
def retrieve_latest_pipeline_config(
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
match what Sermos Cloud expects for seamless Sermos Cloud deployments.
|
201
|
-
However, you can provide any base url and stand up your own API if desired!
|
202
|
-
|
203
|
-
This utilizes redis (required for Sermos-based pipelines/scheduled tasks)
|
204
|
-
to cache the result for a predetermined amount of time before requesting an
|
205
|
-
update. This is because pipelines/tasks can be invoked rapidly but do not
|
206
|
-
change frequently.
|
207
|
-
"""
|
208
|
-
# If this is a LOCAL deployment, look to sermos.yaml directly
|
209
|
-
if not USING_SERMOS_CLOUD:
|
210
|
-
sermos_config = load_sermos_config()
|
211
|
-
if 'pipelines' in sermos_config:
|
212
|
-
pipelines = []
|
213
|
-
found_pipeline = None
|
214
|
-
for p_id, config in sermos_config['pipelines'].items():
|
215
|
-
config['sermosPipelineId'] = p_id
|
216
|
-
if pipeline_id == p_id:
|
217
|
-
found_pipeline = config
|
218
|
-
break
|
219
|
-
pipelines.append(config)
|
220
|
-
|
221
|
-
if pipeline_id:
|
222
|
-
if found_pipeline:
|
223
|
-
return found_pipeline
|
224
|
-
raise ValueError(f'Invalid pipeline {pipeline_id}')
|
225
|
-
|
226
|
-
return pipelines
|
227
|
-
return None
|
228
|
-
|
229
|
-
# If this is a CLOUD deployment, generate a valid API url and ask the API
|
230
|
-
# service for pipeline configuration. If this deployment is set up to
|
231
|
-
# cache results, do so.
|
232
|
-
cache_key = PIPELINE_CONFIG_CACHE_KEY.format(pipeline_id)
|
233
|
-
access_key = get_access_key(access_key) # From env if None
|
11
|
+
pipeline_id: Union[str, None] = None
|
12
|
+
) -> Union[dict, list]:
|
13
|
+
"""Retrieve the 'latest' pipeline configuration for a given pipeline."""
|
14
|
+
pypeline_config = load_pypeline_config()
|
15
|
+
if "pipelines" in pypeline_config:
|
16
|
+
pipelines = []
|
17
|
+
found_pipeline = None
|
18
|
+
for p_id, config in pypeline_config["pipelines"].items():
|
19
|
+
if pipeline_id == p_id:
|
20
|
+
found_pipeline = config
|
21
|
+
break
|
22
|
+
pipelines.append(config)
|
234
23
|
|
235
|
-
# Generate pipeline specific API endpoint. If pipeline_id
|
236
|
-
# is None, then we're asking for 'all' pipelines.
|
237
|
-
api_url = _generate_api_url('pipelines')
|
238
|
-
if pipeline_id is not None:
|
239
|
-
api_url = urljoin(api_url + '/', pipeline_id) # Add pipeline ID
|
240
|
-
|
241
|
-
# Retrieve (and cache) result - this will be the exact result from the
|
242
|
-
# API response.
|
243
|
-
data = _retrieve_and_cache_config(cache_key, api_url, access_key,
|
244
|
-
refresh_rate)
|
245
|
-
if data:
|
246
24
|
if pipeline_id:
|
247
|
-
|
248
|
-
|
249
|
-
|
25
|
+
if found_pipeline:
|
26
|
+
return found_pipeline
|
27
|
+
raise ValueError(f"Invalid pipeline {pipeline_id}")
|
250
28
|
|
29
|
+
return pipelines
|
30
|
+
return None
|
251
31
|
|
252
|
-
def retrieve_latest_schedule_config(access_key: Union[str, None] = None,
|
253
|
-
refresh_rate: int = CONFIG_REFRESH_RATE):
|
254
|
-
""" Retrieve the 'latest' scheduled tasks configuration.
|
255
32
|
|
256
|
-
|
257
|
-
|
258
|
-
|
33
|
+
def retrieve_latest_schedule_config():
|
34
|
+
"""Retrieve the 'latest' scheduled tasks configuration."""
|
35
|
+
pypeline_config = load_pypeline_config()
|
36
|
+
if "scheduledTasks" in pypeline_config:
|
37
|
+
tasks = []
|
38
|
+
for task_id, config in pypeline_config["scheduledTasks"].items():
|
39
|
+
tasks.append(config)
|
40
|
+
return tasks
|
41
|
+
return None
|
259
42
|
|
260
|
-
If the DEFAULT_BASE_URL is anything else, this will assume that it is a valid
|
261
|
-
API base url and make a request. The request will be formatted to match what
|
262
|
-
Sermos Cloud expects for seamless Sermos Cloud deployments. However, you can
|
263
|
-
provide any base url and stand up your own API if desired!
|
264
43
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
44
|
+
def get_service_config_for_worker(
|
45
|
+
pypeline_config: dict, worker_name: str = None
|
46
|
+
) -> Union[dict, None]:
|
47
|
+
"""For the current WORKER_NAME (which must be present in the environment
|
48
|
+
of this worker instance for a valid deployment), return the worker's
|
49
|
+
serviceConfig object.
|
269
50
|
"""
|
270
|
-
if
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
config['sermosScheduledTasksId'] = task_id
|
276
|
-
tasks.append(config)
|
277
|
-
return tasks
|
51
|
+
if pypeline_config is None:
|
52
|
+
raise ValueError("Pypeline config was not provided")
|
53
|
+
if worker_name is None:
|
54
|
+
worker_name = WORKER_NAME
|
55
|
+
if worker_name is None:
|
278
56
|
return None
|
279
57
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
BaseScheduleSchema.get_by_version(schedule['schemaVersion'])
|
292
|
-
schema = ScheduleSchema()
|
293
|
-
_schedule = schema.load(schedule)
|
294
|
-
_schedule['id'] = schedule['id']
|
295
|
-
schedules.append(_schedule)
|
296
|
-
|
297
|
-
return schedules
|
298
|
-
|
299
|
-
|
300
|
-
def update_schedule_config(new_schedule_config: dict,
|
301
|
-
access_key: Union[str, None] = None,
|
302
|
-
schedule_config_endpoint: Union[str, None] = None):
|
303
|
-
""" Tell Sermos to update a deployment's schedule with new version.
|
304
|
-
"""
|
305
|
-
|
306
|
-
# Don't send status to sermos-cloud if we're running in local mode
|
307
|
-
if not USING_SERMOS_CLOUD:
|
308
|
-
return True
|
309
|
-
|
310
|
-
access_key = get_access_key(access_key) # From env if None
|
311
|
-
api_url = _generate_api_url('scheduled_tasks')
|
312
|
-
|
313
|
-
# Ask Sermos Cloud (Note: Sermos Cloud's API expects `apikey`)
|
314
|
-
headers = {'apikey': access_key}
|
315
|
-
|
316
|
-
for scheduled_task in new_schedule_config['schedules']:
|
317
|
-
copy_task = dict(scheduled_task)
|
318
|
-
task_id = copy_task.pop('id')
|
319
|
-
url = f"{api_url}/{task_id}"
|
320
|
-
r = requests.put(url, json=copy_task, headers=headers, verify=True)
|
321
|
-
if r.status_code != 200:
|
322
|
-
logger.error("Unable to update schedule task in sermos cloud")
|
323
|
-
logger.error(r.json())
|
324
|
-
return False
|
325
|
-
|
326
|
-
return True
|
58
|
+
service_config = pypeline_config.get("serviceConfig", [])
|
59
|
+
for service in service_config:
|
60
|
+
if service["name"] == worker_name:
|
61
|
+
return service
|
62
|
+
|
63
|
+
raise ValueError(
|
64
|
+
"Could not find a service config for worker "
|
65
|
+
f"`{worker_name}`. Make sure you have added the service in"
|
66
|
+
f" your pypeline.yaml with `name: {worker_name}` and "
|
67
|
+
"`type: celery-worker`."
|
68
|
+
)
|
pypeline/utils/module_utils.py
CHANGED
@@ -5,32 +5,32 @@ import re
|
|
5
5
|
import logging
|
6
6
|
import importlib
|
7
7
|
from typing import Callable
|
8
|
-
from pypeline.constants import
|
8
|
+
from pypeline.constants import API_ACCESS_KEY, PYPELINE_CLIENT_PKG_NAME
|
9
9
|
|
10
10
|
logger = logging.getLogger(__name__)
|
11
11
|
|
12
12
|
|
13
|
-
class
|
14
|
-
"""
|
15
|
-
|
13
|
+
class PypelineModuleLoader(object):
|
14
|
+
"""Helper class to load modules / classes / methods based on a path string."""
|
15
|
+
|
16
16
|
def get_module(self, resource_dot_path: str):
|
17
|
-
"""
|
18
|
-
|
17
|
+
"""Retrieve the module based on a 'resource dot path'.
|
18
|
+
e.g. package.subdir.feature_file.MyCallable
|
19
19
|
"""
|
20
|
-
module_path =
|
20
|
+
module_path = ".".join(resource_dot_path.split(".")[:-1])
|
21
21
|
module = importlib.import_module(module_path)
|
22
22
|
return module
|
23
23
|
|
24
24
|
def get_callable_name(self, resource_dot_path: str) -> str:
|
25
|
-
"""
|
26
|
-
|
25
|
+
"""Retrieve the callable based on config string.
|
26
|
+
e.g. package.subdir.feature_file.MyCallable
|
27
27
|
"""
|
28
|
-
callable_name = resource_dot_path.split(
|
28
|
+
callable_name = resource_dot_path.split(".")[-1]
|
29
29
|
return callable_name
|
30
30
|
|
31
31
|
def get_callable(self, resource_dot_path: str) -> Callable:
|
32
|
-
"""
|
33
|
-
|
32
|
+
"""Retrieve the actual handler class based on config string.
|
33
|
+
e.g. package.subdir.feature_file.MyCallable
|
34
34
|
"""
|
35
35
|
module = self.get_module(resource_dot_path)
|
36
36
|
callable_name = self.get_callable_name(resource_dot_path)
|
@@ -38,82 +38,46 @@ class SermosModuleLoader(object):
|
|
38
38
|
|
39
39
|
|
40
40
|
def normalized_pkg_name(pkg_name: str, dashed: bool = False):
|
41
|
-
"""
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
41
|
+
"""We maintain consistency by always specifying the package name as
|
42
|
+
the "dashed version".
|
43
|
+
|
44
|
+
Python/setuptools will replace "_" with "-" but resource_filename()
|
45
|
+
expects the exact directory name, essentially. In order to keep it
|
46
|
+
simple upstream and *always* provide package name as the dashed
|
47
|
+
version, we do replacement here to 'normalize' both versions to
|
48
|
+
whichever convention you need at the time.
|
49
|
+
|
50
|
+
if `dashed`:
|
51
|
+
my-package-name --> my-package-name
|
52
|
+
my_package_name --> my-package-name
|
53
|
+
|
54
|
+
else:
|
55
|
+
my-package-name --> my_package_name
|
56
|
+
my_package_name --> my_package_name
|
57
57
|
"""
|
58
58
|
if dashed:
|
59
|
-
return str(pkg_name).replace(
|
60
|
-
return str(pkg_name).replace(
|
61
|
-
|
62
|
-
|
63
|
-
def get_client_pkg_name(pkg_name: str = None):
|
64
|
-
""" Verify the package name provided and get from environment if None.
|
65
|
-
|
66
|
-
Raise if neither provided nor found.
|
67
|
-
|
68
|
-
Arguments:
|
69
|
-
pkg_name (optional): Directory name for your Python
|
70
|
-
package. e.g. my_package_name . If none provided, will check
|
71
|
-
environment for `SERMOS_CLIENT_PKG_NAME`. If not found,
|
72
|
-
will exit.
|
73
|
-
"""
|
74
|
-
pkg_name = pkg_name if pkg_name else SERMOS_CLIENT_PKG_NAME
|
75
|
-
if pkg_name is None:
|
76
|
-
msg = "Unable to find `pkg-name` in CLI arguments nor in "\
|
77
|
-
"environment under `{}`".format('SERMOS_CLIENT_PKG_NAME')
|
78
|
-
logger.error(msg)
|
79
|
-
raise ValueError(msg)
|
80
|
-
return pkg_name
|
59
|
+
return str(pkg_name).replace("_", "-")
|
60
|
+
return str(pkg_name).replace("-", "_")
|
81
61
|
|
82
62
|
|
83
63
|
def match_prefix(string: str, prefix_p: str) -> bool:
|
84
|
-
"""
|
85
|
-
"""
|
86
|
-
pattern = re.compile('^(' + prefix_p + ').*')
|
64
|
+
"""For given string, determine whether it begins with provided prefix_p."""
|
65
|
+
pattern = re.compile("^(" + prefix_p + ").*")
|
87
66
|
if pattern.match(string):
|
88
67
|
return True
|
89
68
|
return False
|
90
69
|
|
91
70
|
|
92
71
|
def match_suffix(string: str, suffix_p: str) -> bool:
|
93
|
-
"""
|
94
|
-
"""
|
95
|
-
pattern = re.compile('.*(' + suffix_p + ')$')
|
72
|
+
"""For given string, determine whether it ends with provided suffix_p."""
|
73
|
+
pattern = re.compile(".*(" + suffix_p + ")$")
|
96
74
|
if pattern.match(string):
|
97
75
|
return True
|
98
76
|
return False
|
99
77
|
|
100
78
|
|
101
79
|
def match_prefix_suffix(string: str, prefix_p: str, suffix_p: str) -> bool:
|
102
|
-
"""
|
103
|
-
"""
|
80
|
+
"""For given string, determine whether it starts w/ prefix & ends w/ suffix"""
|
104
81
|
if match_prefix(string, prefix_p) and match_suffix(string, suffix_p):
|
105
82
|
return True
|
106
83
|
return False
|
107
|
-
|
108
|
-
|
109
|
-
def find_from_environment(prefix_p: str, suffix_p: str) -> list:
|
110
|
-
""" Find all envirionment variables that match prefix and suffix.
|
111
|
-
|
112
|
-
Can provide any regex compatible string as values.
|
113
|
-
"""
|
114
|
-
matching_vars = []
|
115
|
-
environment_vars = os.environ
|
116
|
-
for var in environment_vars:
|
117
|
-
if match_prefix_suffix(var, prefix_p, suffix_p):
|
118
|
-
matching_vars.append(var)
|
119
|
-
return matching_vars
|