scalable-pypeline 2.1.9__py2.py3-none-any.whl → 2.1.11__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "2.1.9"
1
+ __version__ = "2.1.11"
pypeline/dramatiq.py CHANGED
@@ -1,3 +1,4 @@
1
+ import copy
1
2
  import typing
2
3
  import pika
3
4
  import logging
@@ -101,45 +102,50 @@ def register_actors_for_workers(broker: Broker):
101
102
 
102
103
  if not service:
103
104
  return
104
- for task in service.get("registeredTasks", []):
105
- pipeline_meta = None
106
- for pipeline_key, pipeline in pypeline_config["pipelines"].items():
107
- pipeline_config = pipeline["config"]
105
+
106
+ worker_registered_tasks = [
107
+ task_handler["handler"] for task_handler in service.get("registeredTasks")
108
+ ]
109
+
110
+ # Loop over the pipelines to get metadata and other information about the task for registration
111
+ for pipeline_key, pipeline in pypeline_config["pipelines"].items():
112
+ for task, task_handler_meta in pipeline["config"]["taskDefinitions"].items():
108
113
  if pipeline["schemaVersion"] == 1:
109
- pipeline_tasks = [
110
- t["handler"] for t in pipeline_config["taskDefinitions"].values()
111
- ]
114
+ # Check if any task in this pipeline is registered
115
+ task_handlers = [task_handler_meta["handler"]]
112
116
  elif pipeline["schemaVersion"] == 2:
113
- pipeline_tasks = [
114
- handler
115
- for key in pipeline_config["taskDefinitions"]
116
- for handler in pipeline_config["taskDefinitions"][key].get(
117
- "handlers", []
118
- )
119
- ]
120
- if task["handler"] in pipeline_tasks:
121
- pipeline_meta = pipeline_config["metadata"]
122
- break
123
-
124
- if pipeline_meta is None:
125
- for job in scheduled_jobs_config:
126
- config = job["config"]
127
- if config["task"] == task["handler"]:
128
- pipeline_meta = {"queue": config.get("queue", "default")}
129
-
130
- if pipeline_meta is None:
131
- raise ValueError(
132
- f"Registered task {task['handler']} is not defined in a pipeline or scheduled task"
133
- )
134
-
135
- try:
136
- worker_path = task["handler"] # Required, no default
137
- tmp_handler = get_callable(worker_path)
138
- if pipeline_meta and pipeline_meta.get("maxRetry", 0) >= 0:
139
- pipeline_meta["store_results"] = True
140
- _ = register_lazy_actor(broker, tmp_handler, pipeline_meta)
141
- except Exception as e:
142
- logger.exception(f"Unable to add a task to dramatiq: {e}")
117
+ task_handlers = [t for t in task_handler_meta["handlers"]]
118
+
119
+ for task_handler in task_handlers:
120
+ if task_handler in worker_registered_tasks:
121
+ server_type = task_handler_meta.get("serverType", None)
122
+
123
+ try:
124
+ pipeline_metadata = copy.deepcopy(
125
+ pipeline["config"]["metadata"]
126
+ )
127
+ tmp_handler = get_callable(task_handler)
128
+ if pipeline_metadata.get("maxRetry", 0) >= 0:
129
+ pipeline_metadata["store_results"] = True
130
+ _ = register_lazy_actor(
131
+ broker, tmp_handler, pipeline_metadata, server_type
132
+ )
133
+ except Exception as e:
134
+ logger.exception(
135
+ f"Unable to add a task {task_handler} to dramatiq: {e}"
136
+ )
137
+ # Loop over the scheduled jobs and create metadata and other information about the task for registration
138
+ for job in scheduled_jobs_config:
139
+ config = job["config"]
140
+ if config["task"] in worker_registered_tasks:
141
+ pipeline_meta = {"queue": config.get("queue", "default")}
142
+ try:
143
+ tmp_handler = get_callable(config["task"])
144
+ if pipeline_meta and pipeline_meta.get("maxRetry", 0) >= 0:
145
+ pipeline_meta["store_results"] = True
146
+ _ = register_lazy_actor(broker, tmp_handler, pipeline_meta, None)
147
+ except Exception as e:
148
+ logger.exception(f"Unable to add a task to dramatiq: {e}")
143
149
 
144
150
 
145
151
  class Dramatiq:
@@ -4,6 +4,7 @@
4
4
  import yaml
5
5
  from marshmallow import Schema, fields, EXCLUDE, validates_schema
6
6
  from marshmallow.exceptions import ValidationError
7
+ from marshmallow.validate import OneOf
7
8
 
8
9
  from pypeline.pipeline_settings_schema import PipelineSettingsSchema
9
10
 
@@ -116,6 +117,14 @@ class TaskDefinitionsSchemaV1(ExcludeUnknownSchema):
116
117
  example="custom-queue-name",
117
118
  )
118
119
 
120
+ serverType = fields.String(
121
+ required=False,
122
+ validate=OneOf(
123
+ ["xs", "s", "m", "l", "xl", "xxl", "xxxl"],
124
+ error="Invalid serverType. Available options: 'xs', 's', 'm', 'l', 'xl', 'xxl', 'xxxl'.",
125
+ ),
126
+ )
127
+
119
128
 
120
129
  class TaskDefinitionsSchemaV2(ExcludeUnknownSchema):
121
130
  """Schema for a single task's configuration"""
@@ -140,6 +149,14 @@ class TaskDefinitionsSchemaV2(ExcludeUnknownSchema):
140
149
  example="custom-queue-name",
141
150
  )
142
151
 
152
+ serverType = fields.String(
153
+ required=False,
154
+ validate=OneOf(
155
+ ["xs", "s", "m", "l", "xl", "xxl", "xxxl"],
156
+ error="Invalid serverType. Available options: 'xs', 's', 'm', 'l', 'xl', 'xxl', 'xxxl'.",
157
+ ),
158
+ )
159
+
143
160
 
144
161
  class PipelineConfigSchemaBase(Schema):
145
162
  """Overall pipeline configuration schema"""
@@ -19,7 +19,6 @@ class Pypeline:
19
19
  pipeline: dict,
20
20
  scenarios: dict = {},
21
21
  broker=None,
22
- execution_id=None,
23
22
  ):
24
23
  # Construct initial properties
25
24
  self.pipeline = pipeline
@@ -78,10 +77,12 @@ class Pypeline:
78
77
  handler = task_definitions[first_task]["handlers"][
79
78
  scenario["taskReplacements"].get(first_task, 0)
80
79
  ]
80
+ server_type = task_definitions[first_task].get("serverType", None)
81
81
  lazy_actor = register_lazy_actor(
82
82
  self.broker,
83
83
  get_callable(handler),
84
84
  pipeline_config["metadata"],
85
+ server_type,
85
86
  )
86
87
  message = lazy_actor.message()
87
88
  message.options["pipeline"] = pipeline
@@ -111,10 +112,12 @@ class Pypeline:
111
112
  handler = task_definitions[first_task]["handlers"][
112
113
  first_scenario_task_replacements.get(first_task, 0)
113
114
  ]
115
+ server_type = task_definitions[first_task].get("serverType", None)
114
116
  lazy_actor = register_lazy_actor(
115
117
  self.broker,
116
118
  get_callable(handler),
117
119
  pipeline_config["metadata"],
120
+ server_type,
118
121
  )
119
122
  message = lazy_actor.message()
120
123
  message.options["pipeline"] = pipeline
@@ -63,9 +63,10 @@ def dag_generator(
63
63
  message_group = []
64
64
  for task in task_group:
65
65
  module_path = task_definitions[task]["handler"]
66
+ server_type = task_definitions[task].get("serverType", None)
66
67
  tmp_handler = get_callable(module_path)
67
68
  lazy_actor = register_lazy_actor(
68
- broker, tmp_handler, pipeline_config["metadata"]
69
+ broker, tmp_handler, pipeline_config["metadata"], server_type
69
70
  )
70
71
  registered_actors[task] = lazy_actor
71
72
  if args and not kwargs:
@@ -31,16 +31,14 @@ class PypelineMiddleware(Middleware):
31
31
  task_name = message.options["task_name"]
32
32
  task_key = f"{execution_id}-{task_name}"
33
33
 
34
- # Signal to other jobs that current job is finished
34
+ # Signal to other jobs that current task is finished
35
35
  locking_parallel_barrier = LockingParallelBarrier(
36
36
  self.redis_url,
37
37
  task_key=task_key,
38
38
  lock_key=f"{message.options['base_case_execution_id']}-lock",
39
39
  )
40
40
  try:
41
- locking_parallel_barrier.acquire_lock(
42
- timeout=PARALLEL_PIPELINE_CALLBACK_BARRIER_TTL
43
- )
41
+ locking_parallel_barrier.acquire_lock(timeout=10)
44
42
  _ = locking_parallel_barrier.decrement_task_count()
45
43
  finally:
46
44
  locking_parallel_barrier.release_lock()
@@ -50,16 +48,16 @@ class PypelineMiddleware(Middleware):
50
48
 
51
49
  messages = []
52
50
  for child in children_tasks:
53
- child_ancestors = sorted(nx.ancestors(graph, child))
51
+ child_ancestors = sorted(graph.predecessors(child))
54
52
 
55
53
  ancestor_tasks_complete = True
56
54
 
57
55
  for ancestor in child_ancestors:
58
- task_key = f"{execution_id}-{ancestor}"
56
+ ancestor_task_key = f"{execution_id}-{ancestor}"
59
57
 
60
58
  locking_parallel_barrier = LockingParallelBarrier(
61
59
  self.redis_url,
62
- task_key=task_key,
60
+ task_key=ancestor_task_key,
63
61
  lock_key=f"{message.options['base_case_execution_id']}-lock",
64
62
  )
65
63
  try:
@@ -74,12 +72,17 @@ class PypelineMiddleware(Middleware):
74
72
  finally:
75
73
  locking_parallel_barrier.release_lock()
76
74
 
77
- if not remaining_tasks:
78
- task_key = f"{message.options['base_case_execution_id']}-{ancestor}"
75
+ # If the lock didn't exist for the current tasks execution id then it would indicate
76
+ # that this is the start of a new scenario. Therefore we need to find the ancestor
77
+ # that is executed in the base case execution id and make sure it has completed
78
+ if remaining_tasks is None:
79
+ ancestor_task_key = (
80
+ f"{message.options['base_case_execution_id']}-{ancestor}"
81
+ )
79
82
 
80
83
  locking_parallel_barrier = LockingParallelBarrier(
81
84
  self.redis_url,
82
- task_key=task_key,
85
+ task_key=ancestor_task_key,
83
86
  lock_key=f"{message.options['base_case_execution_id']}-lock",
84
87
  )
85
88
  try:
@@ -95,7 +98,7 @@ class PypelineMiddleware(Middleware):
95
98
  )
96
99
  finally:
97
100
  locking_parallel_barrier.release_lock()
98
- if remaining_tasks >= 1:
101
+ if remaining_tasks is None or remaining_tasks >= 1:
99
102
  ancestor_tasks_complete = False
100
103
  break
101
104
 
@@ -103,6 +106,7 @@ class PypelineMiddleware(Middleware):
103
106
  if not ancestor_tasks_complete:
104
107
  break
105
108
 
109
+ # Handle situation where base case kicks off new scenario
106
110
  if (
107
111
  message.options["base_case_execution_id"]
108
112
  == message.options["execution_id"]
@@ -124,11 +128,13 @@ class PypelineMiddleware(Middleware):
124
128
  handler = task_definitions[child]["handlers"][
125
129
  task_replacements.get(child, 0)
126
130
  ]
131
+ server_type = task_definitions[child].get("serverType", None)
127
132
 
128
133
  lazy_actor = register_lazy_actor(
129
134
  broker,
130
135
  get_callable(handler),
131
136
  pipeline_config["metadata"],
137
+ server_type,
132
138
  )
133
139
  scenario_message = lazy_actor.message()
134
140
  scenario_message.options["pipeline"] = pipeline
@@ -154,6 +160,8 @@ class PypelineMiddleware(Middleware):
154
160
  scenario["execution_id"]
155
161
  )
156
162
  messages.append(scenario_message)
163
+
164
+ # Kick off child task for current scenario
157
165
  task_key = f"{execution_id}-{child}"
158
166
  locking_parallel_barrier = LockingParallelBarrier(
159
167
  self.redis_url,
@@ -164,10 +172,12 @@ class PypelineMiddleware(Middleware):
164
172
  handler = task_definitions[child]["handlers"][
165
173
  task_replacements.get(child, 0)
166
174
  ]
175
+ server_type = task_definitions[child].get("serverType", None)
167
176
  lazy_actor = register_lazy_actor(
168
177
  broker,
169
178
  get_callable(handler),
170
179
  pipeline_config["metadata"],
180
+ server_type,
171
181
  )
172
182
 
173
183
  child_message = lazy_actor.message()
@@ -45,9 +45,13 @@ def register_lazy_actor(
45
45
  broker: Broker,
46
46
  fn: Optional[Callable[P, Union[Awaitable[R], R]]] = None,
47
47
  pipeline_meta: typing.Dict = {},
48
+ server_type: Optional[str] = None,
48
49
  **kwargs,
49
50
  ) -> typing.Type["LazyActor"]:
50
- kwargs["queue_name"] = pipeline_meta.get("queue", "default")
51
+ if server_type:
52
+ kwargs["queue_name"] = server_type + "-" + pipeline_meta.get("queue", "default")
53
+ else:
54
+ kwargs["queue_name"] = pipeline_meta.get("queue", "default")
51
55
  kwargs["max_retries"] = pipeline_meta.get("maxRetry", DEFAULT_TASK_MAX_RETRY)
52
56
  # Convert from seconds to milliseconds
53
57
  kwargs["min_backoff"] = (
@@ -110,7 +114,7 @@ class LazyActor(object):
110
114
 
111
115
  def register(self, broker):
112
116
  self.actor = register_actor(
113
- actor_name=f"{self.fn.__module__}.{self.fn.__name__}",
117
+ actor_name=f"{self.fn.__module__}.{self.fn.__name__}-{self.kw['queue_name']}",
114
118
  broker=broker,
115
119
  **self.kw,
116
120
  )(ensure_return_value(default_value=True)(self.fn))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scalable-pypeline
3
- Version: 2.1.9
3
+ Version: 2.1.11
4
4
  Summary: PypeLine - Python pipelines for the Real World
5
5
  Home-page: https://gitlab.com/bravos2/pypeline
6
6
  Author: Bravos Power Corporation
@@ -1,9 +1,9 @@
1
- pypeline/__init__.py,sha256=Gf0zUMmdRLkPmvwnoP7oCpWn7Ep93jlNIlBD6nifugI,22
1
+ pypeline/__init__.py,sha256=zcHMV6HbszEZFZ1gDEpbHxTYg8dJ5fCYdeDd-O6IPP4,23
2
2
  pypeline/barrier.py,sha256=oO964l9qOCOibweOHyNivmAvufdXOke9nz2tdgclouo,1172
3
3
  pypeline/constants.py,sha256=EGSuLq4KhZ4bxrbtnUgKclELRyya5ipvv0WeybCzNAs,3049
4
- pypeline/dramatiq.py,sha256=5whdOQjzknLjl9lYNj5-f2jRw5ysRPhQExi5NN4KeOY,12837
4
+ pypeline/dramatiq.py,sha256=nnNxm2akL6hjqfjwQglK76LspZrdrL_2E_rDN5ThwZ8,13432
5
5
  pypeline/extensions.py,sha256=BzOTnXhNxap3N7uIUUh_hO6dDwx08Vc_RJDE93_K0Lo,610
6
- pypeline/pipeline_config_schema.py,sha256=hK2_egtg-YFx_XJDs_NyrOTGKkel7W83X-G0sic52sM,10592
6
+ pypeline/pipeline_config_schema.py,sha256=ApgDqgINFDcmErVP7jJUJgkaVfwNRanj4q12ehWVJUU,11143
7
7
  pypeline/pipeline_settings_schema.py,sha256=IBBtWqxrRA0MrUwNZEnGhYJbmag-sus1Ln6VCrUvuyY,20313
8
8
  pypeline/pypeline_yaml.py,sha256=Og08sUKwOjq7JYPnkg-NIcGbHravYCkC5Arz22rZEtA,16981
9
9
  pypeline/schedule_config_schema.py,sha256=vtZV-5wpGcAiYcXxdBPRkrjsbR6x_9E-1PC2elrKKbE,3611
@@ -14,24 +14,24 @@ pypeline/flask/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
14
14
  pypeline/flask/api/pipelines.py,sha256=lw1ggsjp_Iha5MhyQGHtVW0akpVJnxIk0hn6NkC3c8s,9314
15
15
  pypeline/flask/api/schedules.py,sha256=8PKCMdPucaer8opchNlI5aDssK2UqT79hHpeg5BMtTA,1210
16
16
  pypeline/pipelines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- pypeline/pipelines/factory.py,sha256=356v1S0WPvDkd9f0fKk0H9aXVbOqQYSWt47aOl66EKk,3172
17
+ pypeline/pipelines/factory.py,sha256=p2b1XhhMYt_f5h2FAr6Zze1FfWz8SCyO9DEIS_EDDKE,3258
18
18
  pypeline/pipelines/composition/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  pypeline/pipelines/composition/parallel_pipeline_composition.py,sha256=pTw9Xb9h4JnV4siFc3JStm5lB-i9djUADo3Kh5K3s7g,12976
20
- pypeline/pipelines/composition/pypeline_composition.py,sha256=UBuDKEfRoIbL-9c-HH2ZTVbzfkwFSlNoFH-AVNqt0QE,7965
20
+ pypeline/pipelines/composition/pypeline_composition.py,sha256=G6hptAN3LaVGm6DE7yhjGs-LDbw7m94-z08pLZCqNXA,8154
21
21
  pypeline/pipelines/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  pypeline/pipelines/middleware/get_active_worker_id_middleware.py,sha256=X4ZfRk3L8MD00DTsGHth7oOdy-W7LQV96T8vu5UC42A,755
23
23
  pypeline/pipelines/middleware/parallel_pipeline_middleware.py,sha256=kTp6niYoe2nXIiN6EGRfdpxrJyioo0GPxDkfefbGlEk,2821
24
- pypeline/pipelines/middleware/pypeline_middleware.py,sha256=IXVqzcOlSJ43lsn-i298RkaeygB-PTJjsvdTDtpgfwg,8141
24
+ pypeline/pipelines/middleware/pypeline_middleware.py,sha256=wdqcnya9SrcBs2VOfBOIXY03VC0uadMcS5SD4hGOk8U,8843
25
25
  pypeline/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  pypeline/utils/config_utils.py,sha256=rAIATyoW7kGETZ_Z2DqiXtGd7bJp5uPfcLtfNPOYsNs,2167
27
- pypeline/utils/dramatiq_utils.py,sha256=5GDcOvKY-8S8r---wb6Q8QAywhbKVJ-qILjcYNHei8Y,3658
27
+ pypeline/utils/dramatiq_utils.py,sha256=DUdgVywm1182A4i69XzH9EIh1EJ9zAHmJLtOaVSW7pw,3844
28
28
  pypeline/utils/module_utils.py,sha256=-yEJIukDCoXnmlZVXB6Dww25tH6GdPE5SoFqv6pfdVU,3682
29
29
  pypeline/utils/pipeline_utils.py,sha256=kGP1QwCJikGC5QNRtzRXCDVewyRMpWIqERTNnxGLlSY,4795
30
30
  pypeline/utils/schema_utils.py,sha256=Fgl0y9Cuo_TZeEx_S3gaSVnLjn6467LTkjb2ek7Ms98,851
31
31
  tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- scalable_pypeline-2.1.9.dist-info/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
33
- scalable_pypeline-2.1.9.dist-info/METADATA,sha256=H96u1H3mZdGCb1yfmo52azC_BiVq0rzo2nBYTViHElk,5926
34
- scalable_pypeline-2.1.9.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
35
- scalable_pypeline-2.1.9.dist-info/entry_points.txt,sha256=uWs10ODfHSBKo2Cx_QaUjPHQTpZ3e77j9VlAdRRmMyg,119
36
- scalable_pypeline-2.1.9.dist-info/top_level.txt,sha256=C7dpkEOc_-nnsAQb28BfQknjD6XHRyS9ZrvVeoIbV7s,15
37
- scalable_pypeline-2.1.9.dist-info/RECORD,,
32
+ scalable_pypeline-2.1.11.dist-info/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
33
+ scalable_pypeline-2.1.11.dist-info/METADATA,sha256=HdDTGiEP9ELgzK4H1HrIWem1ztzABQ5SmIv-Y3NPpm8,5927
34
+ scalable_pypeline-2.1.11.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
35
+ scalable_pypeline-2.1.11.dist-info/entry_points.txt,sha256=uWs10ODfHSBKo2Cx_QaUjPHQTpZ3e77j9VlAdRRmMyg,119
36
+ scalable_pypeline-2.1.11.dist-info/top_level.txt,sha256=C7dpkEOc_-nnsAQb28BfQknjD6XHRyS9ZrvVeoIbV7s,15
37
+ scalable_pypeline-2.1.11.dist-info/RECORD,,