prefect-client 2.19.2__py3-none-any.whl → 2.19.4__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.
- prefect/client/schemas/objects.py +4 -0
- prefect/deployments/base.py +7 -1
- prefect/flows.py +66 -1
- prefect/runner/runner.py +12 -8
- prefect/utilities/callables.py +261 -2
- prefect/utilities/importtools.py +71 -0
- {prefect_client-2.19.2.dist-info → prefect_client-2.19.4.dist-info}/METADATA +1 -1
- {prefect_client-2.19.2.dist-info → prefect_client-2.19.4.dist-info}/RECORD +11 -11
- {prefect_client-2.19.2.dist-info → prefect_client-2.19.4.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-2.19.4.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-2.19.4.dist-info}/top_level.txt +0 -0
@@ -97,6 +97,10 @@ class WorkPoolStatus(AutoEnum):
|
|
97
97
|
NOT_READY = AutoEnum.auto()
|
98
98
|
PAUSED = AutoEnum.auto()
|
99
99
|
|
100
|
+
@property
|
101
|
+
def display_name(self):
|
102
|
+
return self.name.replace("_", " ").capitalize()
|
103
|
+
|
100
104
|
|
101
105
|
class WorkerStatus(AutoEnum):
|
102
106
|
"""Enumeration of worker statuses."""
|
prefect/deployments/base.py
CHANGED
@@ -396,7 +396,13 @@ async def _find_flow_functions_in_file(filename: str) -> List[Dict]:
|
|
396
396
|
return decorated_functions
|
397
397
|
|
398
398
|
for node in ast.walk(tree):
|
399
|
-
if isinstance(
|
399
|
+
if isinstance(
|
400
|
+
node,
|
401
|
+
(
|
402
|
+
ast.FunctionDef,
|
403
|
+
ast.AsyncFunctionDef,
|
404
|
+
),
|
405
|
+
):
|
400
406
|
for decorator in node.decorator_list:
|
401
407
|
# handles @flow
|
402
408
|
is_name_match = (
|
prefect/flows.py
CHANGED
@@ -5,7 +5,9 @@ Module containing the base workflow class and decorator - for most use cases, us
|
|
5
5
|
# This file requires type-checking with pyright because mypy does not yet support PEP612
|
6
6
|
# See https://github.com/python/mypy/issues/8645
|
7
7
|
|
8
|
+
import ast
|
8
9
|
import datetime
|
10
|
+
import importlib.util
|
9
11
|
import inspect
|
10
12
|
import os
|
11
13
|
import tempfile
|
@@ -1640,7 +1642,9 @@ def load_flow_from_script(path: str, flow_name: str = None) -> Flow:
|
|
1640
1642
|
)
|
1641
1643
|
|
1642
1644
|
|
1643
|
-
def load_flow_from_entrypoint(
|
1645
|
+
def load_flow_from_entrypoint(
|
1646
|
+
entrypoint: str,
|
1647
|
+
) -> Flow:
|
1644
1648
|
"""
|
1645
1649
|
Extract a flow object from a script at an entrypoint by running all of the code in the file.
|
1646
1650
|
|
@@ -1793,3 +1797,64 @@ async def serve(
|
|
1793
1797
|
)
|
1794
1798
|
|
1795
1799
|
await runner.start()
|
1800
|
+
|
1801
|
+
|
1802
|
+
def load_flow_argument_from_entrypoint(
|
1803
|
+
entrypoint: str, arg: str = "name"
|
1804
|
+
) -> Optional[str]:
|
1805
|
+
"""
|
1806
|
+
Extract a flow argument from an entrypoint string.
|
1807
|
+
|
1808
|
+
Loads the source code of the entrypoint and extracts the flow argument from the
|
1809
|
+
`flow` decorator.
|
1810
|
+
|
1811
|
+
Args:
|
1812
|
+
entrypoint: a string in the format `<path_to_script>:<flow_func_name>` or a module path
|
1813
|
+
to a flow function
|
1814
|
+
|
1815
|
+
Returns:
|
1816
|
+
The flow argument value
|
1817
|
+
"""
|
1818
|
+
if ":" in entrypoint:
|
1819
|
+
# split by the last colon once to handle Windows paths with drive letters i.e C:\path\to\file.py:do_stuff
|
1820
|
+
path, func_name = entrypoint.rsplit(":", maxsplit=1)
|
1821
|
+
source_code = Path(path).read_text()
|
1822
|
+
else:
|
1823
|
+
path, func_name = entrypoint.rsplit(".", maxsplit=1)
|
1824
|
+
spec = importlib.util.find_spec(path)
|
1825
|
+
if not spec or not spec.origin:
|
1826
|
+
raise ValueError(f"Could not find module {path!r}")
|
1827
|
+
source_code = Path(spec.origin).read_text()
|
1828
|
+
parsed_code = ast.parse(source_code)
|
1829
|
+
func_def = next(
|
1830
|
+
(
|
1831
|
+
node
|
1832
|
+
for node in ast.walk(parsed_code)
|
1833
|
+
if isinstance(
|
1834
|
+
node,
|
1835
|
+
(
|
1836
|
+
ast.FunctionDef,
|
1837
|
+
ast.AsyncFunctionDef,
|
1838
|
+
),
|
1839
|
+
)
|
1840
|
+
and node.name == func_name
|
1841
|
+
),
|
1842
|
+
None,
|
1843
|
+
)
|
1844
|
+
if not func_def:
|
1845
|
+
raise ValueError(f"Could not find flow {func_name!r} in {path!r}")
|
1846
|
+
for decorator in func_def.decorator_list:
|
1847
|
+
if (
|
1848
|
+
isinstance(decorator, ast.Call)
|
1849
|
+
and getattr(decorator.func, "id", "") == "flow"
|
1850
|
+
):
|
1851
|
+
for keyword in decorator.keywords:
|
1852
|
+
if keyword.arg == arg:
|
1853
|
+
return (
|
1854
|
+
keyword.value.value
|
1855
|
+
) # Return the string value of the argument
|
1856
|
+
|
1857
|
+
if arg == "name":
|
1858
|
+
return func_name.replace(
|
1859
|
+
"_", "-"
|
1860
|
+
) # If no matching decorator or keyword argument is found
|
prefect/runner/runner.py
CHANGED
@@ -78,9 +78,7 @@ from prefect.deployments.runner import (
|
|
78
78
|
)
|
79
79
|
from prefect.deployments.schedules import FlexibleScheduleList
|
80
80
|
from prefect.events import DeploymentTriggerTypes, TriggerTypes
|
81
|
-
from prefect.exceptions import
|
82
|
-
Abort,
|
83
|
-
)
|
81
|
+
from prefect.exceptions import Abort, ObjectNotFound
|
84
82
|
from prefect.flows import Flow
|
85
83
|
from prefect.logging.loggers import PrefectLogAdapter, flow_run_logger, get_logger
|
86
84
|
from prefect.runner.server import start_webserver
|
@@ -1130,12 +1128,18 @@ class Runner:
|
|
1130
1128
|
Run the hooks for a flow.
|
1131
1129
|
"""
|
1132
1130
|
if state.is_cancelling():
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1131
|
+
try:
|
1132
|
+
flow = await load_flow_from_flow_run(
|
1133
|
+
flow_run, client=self._client, storage_base_path=str(self._tmp_dir)
|
1134
|
+
)
|
1135
|
+
hooks = flow.on_cancellation or []
|
1137
1136
|
|
1138
|
-
|
1137
|
+
await _run_hooks(hooks, flow_run, flow, state)
|
1138
|
+
except ObjectNotFound:
|
1139
|
+
run_logger = self._get_flow_run_logger(flow_run)
|
1140
|
+
run_logger.warning(
|
1141
|
+
f"Runner cannot retrieve flow to execute cancellation hooks for flow run {flow_run.id!r}."
|
1142
|
+
)
|
1139
1143
|
|
1140
1144
|
async def _run_on_crashed_hooks(
|
1141
1145
|
self,
|
prefect/utilities/callables.py
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
Utilities for working with Python callables.
|
3
3
|
"""
|
4
4
|
|
5
|
+
import ast
|
6
|
+
import importlib.util
|
5
7
|
import inspect
|
6
8
|
from functools import partial
|
9
|
+
from pathlib import Path
|
7
10
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
8
11
|
|
9
12
|
import cloudpickle
|
@@ -31,7 +34,10 @@ from prefect.exceptions import (
|
|
31
34
|
ReservedArgumentError,
|
32
35
|
SignatureMismatchError,
|
33
36
|
)
|
34
|
-
from prefect.logging.loggers import disable_logger
|
37
|
+
from prefect.logging.loggers import disable_logger, get_logger
|
38
|
+
from prefect.utilities.importtools import safe_load_namespace
|
39
|
+
|
40
|
+
logger = get_logger(__name__)
|
35
41
|
|
36
42
|
|
37
43
|
def get_call_parameters(
|
@@ -318,9 +324,62 @@ def parameter_schema(fn: Callable) -> ParameterSchema:
|
|
318
324
|
# `eval_str` is not available in Python < 3.10
|
319
325
|
signature = inspect.signature(fn)
|
320
326
|
|
327
|
+
docstrings = parameter_docstrings(inspect.getdoc(fn))
|
328
|
+
|
329
|
+
return generate_parameter_schema(signature, docstrings)
|
330
|
+
|
331
|
+
|
332
|
+
def parameter_schema_from_entrypoint(entrypoint: str) -> ParameterSchema:
|
333
|
+
"""
|
334
|
+
Generate a parameter schema from an entrypoint string.
|
335
|
+
|
336
|
+
Will load the source code of the function and extract the signature and docstring
|
337
|
+
to generate the schema.
|
338
|
+
|
339
|
+
Useful for generating a schema for a function when instantiating the function may
|
340
|
+
not be possible due to missing imports or other issues.
|
341
|
+
|
342
|
+
Args:
|
343
|
+
entrypoint: A string representing the entrypoint to a function. The string
|
344
|
+
should be in the format of `module.path.to.function:do_stuff`.
|
345
|
+
|
346
|
+
Returns:
|
347
|
+
ParameterSchema: The parameter schema for the function.
|
348
|
+
"""
|
349
|
+
if ":" in entrypoint:
|
350
|
+
# split by the last colon once to handle Windows paths with drive letters i.e C:\path\to\file.py:do_stuff
|
351
|
+
path, func_name = entrypoint.rsplit(":", maxsplit=1)
|
352
|
+
source_code = Path(path).read_text()
|
353
|
+
else:
|
354
|
+
path, func_name = entrypoint.rsplit(".", maxsplit=1)
|
355
|
+
spec = importlib.util.find_spec(path)
|
356
|
+
if not spec or not spec.origin:
|
357
|
+
raise ValueError(f"Could not find module {path!r}")
|
358
|
+
source_code = Path(spec.origin).read_text()
|
359
|
+
signature = _generate_signature_from_source(source_code, func_name)
|
360
|
+
docstring = _get_docstring_from_source(source_code, func_name)
|
361
|
+
return generate_parameter_schema(signature, parameter_docstrings(docstring))
|
362
|
+
|
363
|
+
|
364
|
+
def generate_parameter_schema(
|
365
|
+
signature: inspect.Signature, docstrings: Dict[str, str]
|
366
|
+
) -> ParameterSchema:
|
367
|
+
"""
|
368
|
+
Generate a parameter schema from a function signature and docstrings.
|
369
|
+
|
370
|
+
To get a signature from a function, use `inspect.signature(fn)` or
|
371
|
+
`_generate_signature_from_source(source_code, func_name)`.
|
372
|
+
|
373
|
+
Args:
|
374
|
+
signature: The function signature.
|
375
|
+
docstrings: A dictionary mapping parameter names to docstrings.
|
376
|
+
|
377
|
+
Returns:
|
378
|
+
ParameterSchema: The parameter schema.
|
379
|
+
"""
|
380
|
+
|
321
381
|
model_fields = {}
|
322
382
|
aliases = {}
|
323
|
-
docstrings = parameter_docstrings(inspect.getdoc(fn))
|
324
383
|
|
325
384
|
class ModelConfig:
|
326
385
|
arbitrary_types_allowed = True
|
@@ -362,3 +421,203 @@ def raise_for_reserved_arguments(fn: Callable, reserved_arguments: Iterable[str]
|
|
362
421
|
raise ReservedArgumentError(
|
363
422
|
f"{argument!r} is a reserved argument name and cannot be used."
|
364
423
|
)
|
424
|
+
|
425
|
+
|
426
|
+
def _generate_signature_from_source(
|
427
|
+
source_code: str, func_name: str
|
428
|
+
) -> inspect.Signature:
|
429
|
+
"""
|
430
|
+
Extract the signature of a function from its source code.
|
431
|
+
|
432
|
+
Will ignore missing imports and exceptions while loading local class definitions.
|
433
|
+
|
434
|
+
Args:
|
435
|
+
source_code: The source code where the function named `func_name` is declared.
|
436
|
+
func_name: The name of the function.
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
The signature of the function.
|
440
|
+
"""
|
441
|
+
# Load the namespace from the source code. Missing imports and exceptions while
|
442
|
+
# loading local class definitions are ignored.
|
443
|
+
namespace = safe_load_namespace(source_code)
|
444
|
+
# Parse the source code into an AST
|
445
|
+
parsed_code = ast.parse(source_code)
|
446
|
+
|
447
|
+
func_def = next(
|
448
|
+
(
|
449
|
+
node
|
450
|
+
for node in ast.walk(parsed_code)
|
451
|
+
if isinstance(
|
452
|
+
node,
|
453
|
+
(
|
454
|
+
ast.FunctionDef,
|
455
|
+
ast.AsyncFunctionDef,
|
456
|
+
),
|
457
|
+
)
|
458
|
+
and node.name == func_name
|
459
|
+
),
|
460
|
+
None,
|
461
|
+
)
|
462
|
+
if func_def is None:
|
463
|
+
raise ValueError(f"Function {func_name} not found in source code")
|
464
|
+
parameters = []
|
465
|
+
|
466
|
+
# Handle annotations for positional only args e.g. def func(a, /, b, c)
|
467
|
+
for arg in func_def.args.posonlyargs:
|
468
|
+
name = arg.arg
|
469
|
+
annotation = arg.annotation
|
470
|
+
if annotation is not None:
|
471
|
+
try:
|
472
|
+
ann_code = compile(ast.Expression(annotation), "<string>", "eval")
|
473
|
+
annotation = eval(ann_code, namespace)
|
474
|
+
except Exception as e:
|
475
|
+
logger.debug("Failed to evaluate annotation for %s: %s", name, e)
|
476
|
+
annotation = inspect.Parameter.empty
|
477
|
+
else:
|
478
|
+
annotation = inspect.Parameter.empty
|
479
|
+
|
480
|
+
param = inspect.Parameter(
|
481
|
+
name, inspect.Parameter.POSITIONAL_ONLY, annotation=annotation
|
482
|
+
)
|
483
|
+
parameters.append(param)
|
484
|
+
|
485
|
+
# Determine the annotations for args e.g. def func(a: int, b: str, c: float)
|
486
|
+
for arg in func_def.args.args:
|
487
|
+
name = arg.arg
|
488
|
+
annotation = arg.annotation
|
489
|
+
if annotation is not None:
|
490
|
+
try:
|
491
|
+
# Compile and evaluate the annotation
|
492
|
+
ann_code = compile(ast.Expression(annotation), "<string>", "eval")
|
493
|
+
annotation = eval(ann_code, namespace)
|
494
|
+
except Exception as e:
|
495
|
+
# Don't raise an error if the annotation evaluation fails. Set the
|
496
|
+
# annotation to `inspect.Parameter.empty` instead which is equivalent to
|
497
|
+
# not having an annotation.
|
498
|
+
logger.debug("Failed to evaluate annotation for %s: %s", name, e)
|
499
|
+
annotation = inspect.Parameter.empty
|
500
|
+
else:
|
501
|
+
annotation = inspect.Parameter.empty
|
502
|
+
|
503
|
+
param = inspect.Parameter(
|
504
|
+
name, inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=annotation
|
505
|
+
)
|
506
|
+
parameters.append(param)
|
507
|
+
|
508
|
+
# Handle default values for args e.g. def func(a=1, b="hello", c=3.14)
|
509
|
+
defaults = [None] * (
|
510
|
+
len(func_def.args.args) - len(func_def.args.defaults)
|
511
|
+
) + func_def.args.defaults
|
512
|
+
for param, default in zip(parameters, defaults):
|
513
|
+
if default is not None:
|
514
|
+
try:
|
515
|
+
def_code = compile(ast.Expression(default), "<string>", "eval")
|
516
|
+
default = eval(def_code, namespace)
|
517
|
+
except Exception as e:
|
518
|
+
logger.debug(
|
519
|
+
"Failed to evaluate default value for %s: %s", param.name, e
|
520
|
+
)
|
521
|
+
default = None # Set to None if evaluation fails
|
522
|
+
parameters[parameters.index(param)] = param.replace(default=default)
|
523
|
+
|
524
|
+
# Handle annotations for keyword only args e.g. def func(*, a: int, b: str)
|
525
|
+
for kwarg in func_def.args.kwonlyargs:
|
526
|
+
name = kwarg.arg
|
527
|
+
annotation = kwarg.annotation
|
528
|
+
if annotation is not None:
|
529
|
+
try:
|
530
|
+
ann_code = compile(ast.Expression(annotation), "<string>", "eval")
|
531
|
+
annotation = eval(ann_code, namespace)
|
532
|
+
except Exception as e:
|
533
|
+
logger.debug("Failed to evaluate annotation for %s: %s", name, e)
|
534
|
+
annotation = inspect.Parameter.empty
|
535
|
+
else:
|
536
|
+
annotation = inspect.Parameter.empty
|
537
|
+
|
538
|
+
param = inspect.Parameter(
|
539
|
+
name, inspect.Parameter.KEYWORD_ONLY, annotation=annotation
|
540
|
+
)
|
541
|
+
parameters.append(param)
|
542
|
+
|
543
|
+
# Handle default values for keyword only args e.g. def func(*, a=1, b="hello")
|
544
|
+
defaults = [None] * (
|
545
|
+
len(func_def.args.kwonlyargs) - len(func_def.args.kw_defaults)
|
546
|
+
) + func_def.args.kw_defaults
|
547
|
+
for param, default in zip(parameters[-len(func_def.args.kwonlyargs) :], defaults):
|
548
|
+
if default is not None:
|
549
|
+
try:
|
550
|
+
def_code = compile(ast.Expression(default), "<string>", "eval")
|
551
|
+
default = eval(def_code, namespace)
|
552
|
+
except Exception as e:
|
553
|
+
logger.debug(
|
554
|
+
"Failed to evaluate default value for %s: %s", param.name, e
|
555
|
+
)
|
556
|
+
default = None
|
557
|
+
parameters[parameters.index(param)] = param.replace(default=default)
|
558
|
+
|
559
|
+
# Handle annotations for varargs and kwargs e.g. def func(*args: int, **kwargs: str)
|
560
|
+
if func_def.args.vararg:
|
561
|
+
parameters.append(
|
562
|
+
inspect.Parameter(
|
563
|
+
func_def.args.vararg.arg, inspect.Parameter.VAR_POSITIONAL
|
564
|
+
)
|
565
|
+
)
|
566
|
+
if func_def.args.kwarg:
|
567
|
+
parameters.append(
|
568
|
+
inspect.Parameter(func_def.args.kwarg.arg, inspect.Parameter.VAR_KEYWORD)
|
569
|
+
)
|
570
|
+
|
571
|
+
# Handle return annotation e.g. def func() -> int
|
572
|
+
return_annotation = func_def.returns
|
573
|
+
if return_annotation is not None:
|
574
|
+
try:
|
575
|
+
ret_ann_code = compile(
|
576
|
+
ast.Expression(return_annotation), "<string>", "eval"
|
577
|
+
)
|
578
|
+
return_annotation = eval(ret_ann_code, namespace)
|
579
|
+
except Exception as e:
|
580
|
+
logger.debug("Failed to evaluate return annotation: %s", e)
|
581
|
+
return_annotation = inspect.Signature.empty
|
582
|
+
|
583
|
+
return inspect.Signature(parameters, return_annotation=return_annotation)
|
584
|
+
|
585
|
+
|
586
|
+
def _get_docstring_from_source(source_code: str, func_name: str) -> Optional[str]:
|
587
|
+
"""
|
588
|
+
Extract the docstring of a function from its source code.
|
589
|
+
|
590
|
+
Args:
|
591
|
+
source_code (str): The source code of the function.
|
592
|
+
func_name (str): The name of the function.
|
593
|
+
|
594
|
+
Returns:
|
595
|
+
The docstring of the function. If the function has no docstring, returns None.
|
596
|
+
"""
|
597
|
+
parsed_code = ast.parse(source_code)
|
598
|
+
|
599
|
+
func_def = next(
|
600
|
+
(
|
601
|
+
node
|
602
|
+
for node in ast.walk(parsed_code)
|
603
|
+
if isinstance(
|
604
|
+
node,
|
605
|
+
(
|
606
|
+
ast.FunctionDef,
|
607
|
+
ast.AsyncFunctionDef,
|
608
|
+
),
|
609
|
+
)
|
610
|
+
and node.name == func_name
|
611
|
+
),
|
612
|
+
None,
|
613
|
+
)
|
614
|
+
if func_def is None:
|
615
|
+
raise ValueError(f"Function {func_name} not found in source code")
|
616
|
+
|
617
|
+
if (
|
618
|
+
func_def.body
|
619
|
+
and isinstance(func_def.body[0], ast.Expr)
|
620
|
+
and isinstance(func_def.body[0].value, ast.Constant)
|
621
|
+
):
|
622
|
+
return func_def.body[0].value.value
|
623
|
+
return None
|
prefect/utilities/importtools.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import ast
|
1
2
|
import importlib
|
2
3
|
import importlib.util
|
3
4
|
import inspect
|
@@ -14,8 +15,11 @@ from typing import Any, Callable, Dict, Iterable, NamedTuple, Optional, Union
|
|
14
15
|
import fsspec
|
15
16
|
|
16
17
|
from prefect.exceptions import ScriptError
|
18
|
+
from prefect.logging.loggers import get_logger
|
17
19
|
from prefect.utilities.filesystem import filename, is_local_path, tmpchdir
|
18
20
|
|
21
|
+
logger = get_logger(__name__)
|
22
|
+
|
19
23
|
|
20
24
|
def to_qualified_name(obj: Any) -> str:
|
21
25
|
"""
|
@@ -356,3 +360,70 @@ class AliasedModuleLoader(Loader):
|
|
356
360
|
if self.callback is not None:
|
357
361
|
self.callback(self.alias)
|
358
362
|
sys.modules[self.alias] = root_module
|
363
|
+
|
364
|
+
|
365
|
+
def safe_load_namespace(source_code: str):
|
366
|
+
"""
|
367
|
+
Safely load a namespace from source code.
|
368
|
+
|
369
|
+
This function will attempt to import all modules and classes defined in the source
|
370
|
+
code. If an import fails, the error is caught and the import is skipped. This function
|
371
|
+
will also attempt to compile and evaluate class and function definitions locally.
|
372
|
+
|
373
|
+
Args:
|
374
|
+
source_code: The source code to load
|
375
|
+
|
376
|
+
Returns:
|
377
|
+
The namespace loaded from the source code. Can be used when evaluating source
|
378
|
+
code.
|
379
|
+
"""
|
380
|
+
parsed_code = ast.parse(source_code)
|
381
|
+
|
382
|
+
namespace = {"__name__": "prefect_safe_namespace_loader"}
|
383
|
+
|
384
|
+
# Walk through the AST and find all import statements
|
385
|
+
for node in ast.walk(parsed_code):
|
386
|
+
if isinstance(node, ast.Import):
|
387
|
+
for alias in node.names:
|
388
|
+
module_name = alias.name
|
389
|
+
as_name = alias.asname if alias.asname else module_name
|
390
|
+
try:
|
391
|
+
# Attempt to import the module
|
392
|
+
namespace[as_name] = importlib.import_module(module_name)
|
393
|
+
logger.debug("Successfully imported %s", module_name)
|
394
|
+
except ImportError as e:
|
395
|
+
logger.debug(f"Failed to import {module_name}: {e}")
|
396
|
+
elif isinstance(node, ast.ImportFrom):
|
397
|
+
module_name = node.module
|
398
|
+
if module_name is None:
|
399
|
+
continue
|
400
|
+
try:
|
401
|
+
module = importlib.import_module(module_name)
|
402
|
+
for alias in node.names:
|
403
|
+
name = alias.name
|
404
|
+
asname = alias.asname if alias.asname else name
|
405
|
+
try:
|
406
|
+
# Get the specific attribute from the module
|
407
|
+
attribute = getattr(module, name)
|
408
|
+
namespace[asname] = attribute
|
409
|
+
except AttributeError as e:
|
410
|
+
logger.debug(
|
411
|
+
"Failed to retrieve %s from %s: %s", name, module_name, e
|
412
|
+
)
|
413
|
+
except ImportError as e:
|
414
|
+
logger.debug("Failed to import from %s: %s", node.module, e)
|
415
|
+
|
416
|
+
# Handle local definitions
|
417
|
+
for node in ast.walk(parsed_code):
|
418
|
+
if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.Assign)):
|
419
|
+
try:
|
420
|
+
# Compile and execute each class and function definition and assignment
|
421
|
+
code = compile(
|
422
|
+
ast.Module(body=[node], type_ignores=[]),
|
423
|
+
filename="<ast>",
|
424
|
+
mode="exec",
|
425
|
+
)
|
426
|
+
exec(code, namespace)
|
427
|
+
except Exception as e:
|
428
|
+
logger.debug("Failed to compile: %s", e)
|
429
|
+
return namespace
|
@@ -9,7 +9,7 @@ prefect/engine.py,sha256=hGaxEyJB0OSb_fc2sRsoL550DGOeuwttWOY3dQR6wZw,90418
|
|
9
9
|
prefect/exceptions.py,sha256=Fyl-GXvF9OuKHtsyn5EhWg81pkU1UG3DFHsI1JzhOQE,10851
|
10
10
|
prefect/filesystems.py,sha256=XniPSdBAqywj43X7GyfuWJQIbz07QJ5Y3cVNLhIF3lQ,35260
|
11
11
|
prefect/flow_runs.py,sha256=mFHLavZk1yZ62H3UazuNDBZWAF7AqKttA4rMcHgsVSw,3119
|
12
|
-
prefect/flows.py,sha256=
|
12
|
+
prefect/flows.py,sha256=2CRt3E0THxqriT9ZwMCaPBUMxwkRHU4UNg3iYjvQaTE,75292
|
13
13
|
prefect/futures.py,sha256=RaWfYIXtH7RsWxQ5QWTTlAzwtVV8XWpXaZT_hLq35vQ,12590
|
14
14
|
prefect/manifests.py,sha256=sTM7j8Us5d49zaydYKWsKb7zJ96v1ChkLkLeR0GFYD8,683
|
15
15
|
prefect/new_flow_engine.py,sha256=A1adTWTBAwPCn6ay003Jsoc2SdYgHV4AcJo1bmpa_7Y,16039
|
@@ -170,7 +170,7 @@ prefect/client/utilities.py,sha256=7V4IkfC8x_OZuPXGvtIMmwZCOW63hSY8iVQkuRYTR6g,3
|
|
170
170
|
prefect/client/schemas/__init__.py,sha256=KlyqFV-hMulMkNstBn_0ijoHoIwJZaBj6B1r07UmgvE,607
|
171
171
|
prefect/client/schemas/actions.py,sha256=npka1_fiJ_Y_GU6R_uv8wS2kGiJAu5XXqK5b6S0zy-8,27957
|
172
172
|
prefect/client/schemas/filters.py,sha256=gv57m0bHJqL7Ifsc_vAdRODFomaMVcrGXKAahOSBU4w,35598
|
173
|
-
prefect/client/schemas/objects.py,sha256=
|
173
|
+
prefect/client/schemas/objects.py,sha256=Ie82ck5vXXgs0Q_luVe_NJkg_SAYN68d0nTxxFKt31M,53130
|
174
174
|
prefect/client/schemas/responses.py,sha256=XAc95g3PRL9UIkH9_VMuv0ECHKdc19guBLmdt5KefkI,15325
|
175
175
|
prefect/client/schemas/schedules.py,sha256=ZF7fFbkcc8rVdx2MxE8DR0av3FUE9qDPjLreEuV8HfM,12193
|
176
176
|
prefect/client/schemas/sorting.py,sha256=EIQ6FUjUWMwk6fn6ckVLQLXOP-GI5kce7ftjUkDFWV0,2490
|
@@ -180,7 +180,7 @@ prefect/concurrency/events.py,sha256=agci0Y5S0SwZhENgXIG_lbsqh4om9oeU6E_GbtZ55wM
|
|
180
180
|
prefect/concurrency/services.py,sha256=qk4y4H5UsUKz67BufZBJ3WXt2y8UEndXn6TxDkqPzeA,2549
|
181
181
|
prefect/concurrency/sync.py,sha256=gXyiiA0bul7jjpHWPm6su8pFdBiMOekhu9FHnjiPWBQ,3339
|
182
182
|
prefect/deployments/__init__.py,sha256=dM866rOEz3BbAN_xaFMHj3Hw1oOFemBTZ2yxVE6IGoY,394
|
183
|
-
prefect/deployments/base.py,sha256=
|
183
|
+
prefect/deployments/base.py,sha256=0l2D_laMc3q2Q5nvh-WANv3iDy4Ih5BqcPMNJJbHuP0,16391
|
184
184
|
prefect/deployments/deployments.py,sha256=bYNmxU0yn2jzluGIr2tUkgRi73WGQ6gGbjb0GlD4EIk,41656
|
185
185
|
prefect/deployments/runner.py,sha256=a2dxc84zCofZFXV47M2zfntqUaoAhGWvf7o0s3MjPws,44772
|
186
186
|
prefect/deployments/schedules.py,sha256=23GDCAKOP-aAEKGappwTrM4HU67ndVH7NR4Dq0neU_U,1884
|
@@ -235,7 +235,7 @@ prefect/logging/logging.yml,sha256=UkEewf0c3_dURI2uCU4RrxkhI5Devoa1s93fl7hilcg,3
|
|
235
235
|
prefect/pydantic/__init__.py,sha256=BsW32X7fvl44J1JQer1tkEpfleMtL2kL5Uy1KmwWvso,2714
|
236
236
|
prefect/pydantic/main.py,sha256=ups_UULBhCPhB-E7X7-Qgbpor1oJdqChRzpD0ZYQH8A,839
|
237
237
|
prefect/runner/__init__.py,sha256=7U-vAOXFkzMfRz1q8Uv6Otsvc0OrPYLLP44srwkJ_8s,89
|
238
|
-
prefect/runner/runner.py,sha256=
|
238
|
+
prefect/runner/runner.py,sha256=45sZR2kDvhTODyGmeiRe-bgVWq5JHsmZvFPBsiiyKxA,45147
|
239
239
|
prefect/runner/server.py,sha256=mgjH5SPlj3xu0_pZHg15zw59OSJ5lIzTIZ101s281Oo,10655
|
240
240
|
prefect/runner/storage.py,sha256=iZey8Am51c1fZFpS9iVXWYpKiM_lSocvaJEOZVExhvA,22428
|
241
241
|
prefect/runner/submit.py,sha256=w53VdsqfwjW-M3e8hUAAoVlNrXsvGuuyGpEN0wi3vX0,8537
|
@@ -255,7 +255,7 @@ prefect/types/__init__.py,sha256=aZvlQ2uXl949sJ_khmxSVkRH3o6edo-eJ_GBGMBN5Yg,313
|
|
255
255
|
prefect/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
256
256
|
prefect/utilities/annotations.py,sha256=bXB43j5Zsq5gaBcJe9qnszBlnNwCTwqSTgcu2OkkRLo,2776
|
257
257
|
prefect/utilities/asyncutils.py,sha256=1xpjGFs72vQTPXfG0ww1mfNBwp0-UbRICLGVeRuiqOg,17010
|
258
|
-
prefect/utilities/callables.py,sha256
|
258
|
+
prefect/utilities/callables.py,sha256=-Ccr5JmDoafth46MPPQCRStBMSG_ickCDfBztpd_MAs,21119
|
259
259
|
prefect/utilities/collections.py,sha256=0v-NNXxYYzkUTCCNDMNB44AnDv9yj35UYouNraCqlo8,15449
|
260
260
|
prefect/utilities/compat.py,sha256=mNQZDnzyKaOqy-OV-DnmH_dc7CNF5nQgW_EsA4xMr7g,906
|
261
261
|
prefect/utilities/context.py,sha256=BThuUW94-IYgFYTeMIM9KMo8ShT3oiI7w5ajZHzU1j0,1377
|
@@ -264,7 +264,7 @@ prefect/utilities/dockerutils.py,sha256=O5lIgCej5KGRYU2TC1NzNuIK595uOIWJilhZXYEV
|
|
264
264
|
prefect/utilities/engine.py,sha256=TKiYqpfgt4zopuI8yvh2e-V9GgLcRrh3TpKRhvLuHdw,25669
|
265
265
|
prefect/utilities/filesystem.py,sha256=M_TeZ1MftjBf7hDLWk-Iphir369TpJ1binMsBKiO9YE,4449
|
266
266
|
prefect/utilities/hashing.py,sha256=EOwZLmoIZImuSTxAvVqInabxJ-4RpEfYeg9e2EDQF8o,1752
|
267
|
-
prefect/utilities/importtools.py,sha256=
|
267
|
+
prefect/utilities/importtools.py,sha256=1rPTqlUY3qWP4GeX4wYrSE9pUtKrD8-ngnDz0uqT2ZI,14600
|
268
268
|
prefect/utilities/math.py,sha256=wLwcKVidpNeWQi1TUIWWLHGjlz9UgboX9FUGhx_CQzo,2821
|
269
269
|
prefect/utilities/names.py,sha256=x-stHcF7_tebJPvB1dz-5FvdXJXNBTg2kFZXSnIBBmk,1657
|
270
270
|
prefect/utilities/processutils.py,sha256=yo_GO48pZzgn4A0IK5irTAoqyUCYvWKDSqHXCrtP8c4,14547
|
@@ -285,8 +285,8 @@ prefect/workers/block.py,sha256=5bdCuqT-4I-et_8ZLG2y1AODzYiCQwFiivhdt5NMEog,7635
|
|
285
285
|
prefect/workers/process.py,sha256=pPtCdA7fKQ4OsvoitT-cayZeh5HgLX4xBUYlb2Zad-Q,9475
|
286
286
|
prefect/workers/server.py,sha256=WVZJxR8nTMzK0ov0BD0xw5OyQpT26AxlXbsGQ1OrxeQ,1551
|
287
287
|
prefect/workers/utilities.py,sha256=VfPfAlGtTuDj0-Kb8WlMgAuOfgXCdrGAnKMapPSBrwc,2483
|
288
|
-
prefect_client-2.19.
|
289
|
-
prefect_client-2.19.
|
290
|
-
prefect_client-2.19.
|
291
|
-
prefect_client-2.19.
|
292
|
-
prefect_client-2.19.
|
288
|
+
prefect_client-2.19.4.dist-info/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
|
289
|
+
prefect_client-2.19.4.dist-info/METADATA,sha256=jp5x5csGV9bneDyiyGLFtL_KR0Kmbvm-K3lzNIZ5psU,7401
|
290
|
+
prefect_client-2.19.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
291
|
+
prefect_client-2.19.4.dist-info/top_level.txt,sha256=MJZYJgFdbRc2woQCeB4vM6T33tr01TmkEhRcns6H_H4,8
|
292
|
+
prefect_client-2.19.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|