コード例 #1
0
ファイル: func_task_decorator.py プロジェクト: lbtanh/dbnd
def _task_decorator(*decorator_args, **decorator_kwargs):
    task_type = decorator_kwargs.pop(
        "_task_type")  # type: Type[_DecoratedTask]
    task_default_result = decorator_kwargs.pop(
        "_task_default_result")  # ParameterFactory
    task_defaults = decorator_kwargs.pop("defaults", None)
    if not is_databand_enabled():
        # simple `@task` decorator, no options were (probably) given.
        if len(decorator_args) == 1 and callable(decorator_args[0]):
            return decorator_args[0]
        return __passthrough_decorator

    def decorated(item):
        try:

            func_spec = build_task_decorator_spec(
                item=item,
                decorator_kwargs=decorator_kwargs,
                default_result=task_default_result,
            )

            class DynamicFuncAsTask(task_type):
                _conf__decorator_spec = func_spec
                _callable_item = None
                __doc__ = item.__doc__
                __module__ = item.__module__

                defaults = task_defaults

            # we can't create class dynamically because of python2/3
            # __name__ is not overridable

            task_cls = DynamicFuncAsTask
        except Exception as ex:
            logger.error(
                "Failed to create task %s: %s\n%s\n",
                item.__name__,
                str(ex),
                user_side_code(context=5),
                exc_info=show_exc_info(ex),
            )
            raise

        if func_spec.is_class:
            callable_item = six.add_metaclass(_DecoratedUserClassMeta)(item)
        else:
            callable_item = _decorated_user_func(task_cls=task_cls)
        task_cls._callable_item = callable_item

        callable_item.func = item
        callable_item.task_cls = task_cls
        callable_item.task = task_cls
        callable_item.t = task_cls
        return task_cls._callable_item

    # simple `@task` decorator, no options were (probably) given.
    if len(decorator_args) == 1 and callable(decorator_args[0]):
        return decorated(decorator_args[0])

    return decorated
コード例 #2
0
ファイル: dbnd_decorator.py プロジェクト: databand-ai/dbnd
def build_task_decorator(*decorator_args, **decorator_kwargs):
    # this code creates a new decorator that can be applied on any User Code

    if not is_databand_enabled():
        # simple `@task` decorator, no options were (probably) given.
        if len(decorator_args) == 1 and callable(decorator_args[0]):
            return decorator_args[0]
        return _do_nothing_decorator

    def class_or_func_decorator(class_or_func):
        # this code will run during compile time, when we apply dbnd decorator (for example: @task)
        task_decorator = TaskDecorator(class_or_func,
                                       decorator_kwargs=decorator_kwargs)
        tp = task_decorator.task_passport

        # we need to manually register the task here, since in regular flow
        # this happens in TaskMetaclass, but it's not invoked here due to lazy
        # evaluation task_cls
        r = get_task_registry()
        r.register_task_cls_factory(
            task_cls_factory=task_decorator.get_task_cls,
            full_task_family=tp.full_task_family,
            task_family=tp.task_family,
        )
        if task_decorator.is_class:
            # we will change metaclass for UserClass so we will process all UserClass calls
            #
            # @task
            # class UserClass():
            #     pass
            # so the the moment user call UserClass(), -> _DecoratedUserClassMeta.__call__ will be called
            dbnd_decorated_class = six.add_metaclass(
                _UserClassWithTaskDecoratorMetaclass)(class_or_func)
            dbnd_decorated_class.task_decorator = task_decorator
            task_decorator.class_or_func = dbnd_decorated_class
            return dbnd_decorated_class
        else:
            # @task
            # def user_func():
            #     pass
            # we will return our wrapper, that will be called during a runtime,
            # when user calls his own code.
            return build_dbnd_decorated_func(task_decorator)

    # simple `@task` decorator in opposite to @task(...), no options were (probably) given.
    if len(decorator_args) == 1 and callable(decorator_args[0]):
        return class_or_func_decorator(decorator_args[0])

    return class_or_func_decorator
コード例 #3
0
ファイル: decorated_task.py プロジェクト: cxz/dbnd
    def _call_handler(cls, call_user_code, call_args, call_kwargs):
        """
        -= Use "Step into My Code"" to get back from Databand code! =-

        decorated object call/creation  ( my_func(), MyDecoratedTask()
        """
        force_invoke = call_kwargs.pop("__force_invoke", False)
        if force_invoke or not is_databand_enabled():
            # 1. Databand is not enabled
            # 2. we have this call coming from Task.run / Task.band direct invocation
            return call_user_code(*call_args, **call_kwargs)
        func_call = FuncCall(
            task_cls=cls,
            call_args=call_args,
            call_kwargs=call_kwargs,
            call_user_code=call_user_code,
        )

        if is_in_airflow_dag_build_context(
        ):  # we are in Airflow DAG building mode
            return build_task_at_airflow_dag_context(task_cls=cls,
                                                     call_args=call_args,
                                                     call_kwargs=call_kwargs)

        airflow_task_context = try_get_airflow_context()
        if airflow_task_context:
            return track_airflow_dag_run_operator_run(
                func_call=func_call, airflow_task_context=airflow_task_context)

        current = try_get_current_task()
        if not current and is_inplace_run():
            from dbnd._core.inplace_run.inplace_run_manager import dbnd_run_start

            task_run = dbnd_run_start()
            if task_run:
                current = task_run.task

        if not current:  # direct call to the function
            return func_call.invoke()

        ######
        # DBND HANDLING OF CALL
        # now we can make some decisions what we do with the call
        # it's not coming from _invoke_func
        # but from   user code ...   some_func()  or SomeTask()
        phase = current_phase()
        if phase is TaskContextPhase.BUILD:
            # we are in the @pipeline context, we are building execution plan
            t = cls(*call_args, **call_kwargs)

            # we are in inline debug mode -> we are going to execute the task
            # we are in the band
            # and want to return result of the object
            if t.task_definition.single_result_output:
                return t.result

            # we have multiple outputs ( result, another output.. )
            # -> just return task object
            return t

        if phase is TaskContextPhase.RUN:
            # we are in the run function!
            if (current.settings.dynamic_task.enabled
                    and current.task_supports_dynamic_tasks):
                # isinstance() check required to prevent infinite recursion when @task is on
                # class and not on func (example: see test_task_decorated_class.py)
                # and the current task supports inline calls
                # that's extra mechanism in addition to __force_invoke
                # on pickle/unpickle isinstance fails to run.
                return create_and_run_dynamic_task_safe(func_call=func_call)

        # we can not call it in"databand" way, fallback to normal execution
        return func_call.invoke()
コード例 #4
0
ファイル: task_cls_builder.py プロジェクト: kalebinn/dbnd
def _task_decorator(*decorator_args, **decorator_kwargs):
    if not is_databand_enabled():
        # simple `@task` decorator, no options were (probably) given.
        if len(decorator_args) == 1 and callable(decorator_args[0]):
            return decorator_args[0]
        return _passthrough_decorator

    task_type = decorator_kwargs.pop(
        "_task_type")  # type: Type[_DecoratedTask]
    task_default_result = decorator_kwargs.pop(
        "_task_default_result")  # ParameterFactory
    task_defaults = decorator_kwargs.pop("defaults", None)

    def decorated(class_or_func):
        try:
            func_spec = build_task_decorator_spec(
                class_or_func=class_or_func,
                decorator_kwargs=decorator_kwargs,
                default_result=task_default_result,
            )
        except Exception as ex:
            logger.error(
                "Failed to create task %s: %s\n%s\n",
                class_or_func.__name__,
                str(ex),
                user_side_code(context=5),
                exc_info=show_exc_info(ex),
            )
            raise

        fp = TaskClsBuilder(func_spec, task_type, task_defaults)

        if func_spec.is_class:
            wrapper = six.add_metaclass(_DecoratedUserClassMeta)(class_or_func)
            fp._callable_item = wrapper

        else:

            @functools.wraps(class_or_func)
            def wrapper(*args, **kwargs):
                if in_tracking_mode():
                    with fp.tracking_context(args,
                                             kwargs) as track_result_callback:
                        return track_result_callback(fp.func(*args, **kwargs))

                return _call_handler(
                    fp.get_task_cls(),
                    call_user_code=fp.func,
                    call_args=args,
                    call_kwargs=kwargs,
                )

            wrapper.dbnd_run = fp.dbnd_run

        wrapper.__is_dbnd_task__ = True
        wrapper.func = class_or_func

        # we're using CallableLazyObjectProxy to have lazy evaluation for creating task_cls
        # this is only orchestration scenarios
        task_cls = CallableLazyObjectProxy(fp.get_task_cls)
        wrapper.task_cls = task_cls
        wrapper.task = task_cls
        wrapper.t = task_cls

        # we need lazy task_definition here, for example for dbnd_task_as_bash_operator
        wrapper.task_definition = CallableLazyObjectProxy(
            fp.get_task_definition)

        # we need to manually register the task here, since in regular flow
        # this happens in TaskMetaclass, but it's not invoked here due to lazy
        # evaluation using CallableLazyObjectProxy
        tp = TaskPassport.from_func_spec(func_spec, decorator_kwargs)

        # TODO: we can use CallableLazyObjectProxy object (task_cls) instead of task_cls_factory
        r = get_task_registry()
        r.register_task_cls_factory(
            task_cls_factory=fp.get_task_cls,
            full_task_family=tp.full_task_family,
            task_family=tp.task_family,
        )

        return wrapper

    # simple `@task` decorator, no options were (probably) given.
    if len(decorator_args) == 1 and callable(decorator_args[0]):
        return decorated(decorator_args[0])

    return decorated