def test_plan_non_annotated_args(): class WithNonAnnArgs: def __init__(self, a: A, b: B, non_ann, non_ann_def=0, *, non_ann_kw, non_ann_kw_def=1): pass plan = andi.plan( WithNonAnnArgs.__init__, is_injectable=ALL, externally_provided={A} ) assert dict(plan.dependencies) == {A: {}, B: {}} assert _final_kwargs_spec(plan) == {'a': A, 'b': B} assert not plan.full_final_kwargs plan_class = andi.plan(WithNonAnnArgs, is_injectable=ALL, externally_provided=[A]) assert plan_class.dependencies == plan.dependencies assert _final_kwargs_spec(plan_class) == _final_kwargs_spec(plan) assert not plan.full_final_kwargs with pytest.raises(TypeError): build(plan) instances = build(plan.dependencies, instances_stock={A: ""}) o = WithNonAnnArgs(non_ann=None, non_ann_kw=None, **plan.final_kwargs(instances)) assert isinstance(o, WithNonAnnArgs) with pytest.raises(andi.NonProvidableError) as ex_info: andi.plan(WithNonAnnArgs, is_injectable=ALL, externally_provided=[A], full_final_kwargs=True) assert error_causes(ex_info) == [ ('non_ann', [LackingAnnotationErrCase('non_ann', WithNonAnnArgs)]), ('non_ann_def', [LackingAnnotationErrCase('non_ann_def', WithNonAnnArgs)]), ('non_ann_kw', [LackingAnnotationErrCase('non_ann_kw', WithNonAnnArgs)]), ('non_ann_kw_def', [LackingAnnotationErrCase('non_ann_kw_def', WithNonAnnArgs)]), ]
def test_plan_no_args(full_final_kwargs): def fn(): return True plan = andi.plan(fn, is_injectable=[], full_final_kwargs=full_final_kwargs) assert plan == [(fn, {})] assert plan.full_final_kwargs instances = build(plan) assert instances[fn] assert fn(**plan.final_kwargs(instances))
def test_plan_similar_for_class_or_func(cls, is_injectable, externally_provided): is_injectable = is_injectable + [cl.__init__ for cl in is_injectable] externally_provided = externally_provided + [cl.__init__ for cl in externally_provided] external_deps = {cl: "external" for cl in externally_provided} plan_cls = andi.plan(cls, is_injectable=is_injectable, externally_provided=externally_provided) plan_func = andi.plan(cls.__init__, is_injectable=is_injectable, externally_provided=externally_provided) plan_func[-1] = (cls, plan_func[-1][1]) # To make plans compatible assert plan_cls == plan_func assert plan_cls.full_final_kwargs assert plan_cls.full_final_kwargs == plan_func.full_final_kwargs instances = build(plan_cls, external_deps) assert type(instances[cls]) == cls or instances[cls] == "external" instances = build(plan_func, external_deps) assert type(instances[cls]) == cls or instances[cls] == "external"
def test_plan_for_func(): def fn(other: str, e: E, c: C): assert other == 'yeah!' assert type(e) == E assert type(c) == C plan = andi.plan(fn, is_injectable=ALL, externally_provided={A}) assert _final_kwargs_spec(plan) == {'e': E, 'c': C} assert not plan.full_final_kwargs instances = build(plan.dependencies, {A: ""}) fn(other="yeah!", **plan.final_kwargs(instances)) with pytest.raises(TypeError): build(plan, {A: ""}) with pytest.raises(andi.NonProvidableError) as ex_info: andi.plan(fn, is_injectable=ALL, externally_provided=[A], full_final_kwargs=True) assert error_causes(ex_info) == [ ('other', [NonInjectableOrExternalErrCase('other', fn, [str])])]
def test_plan_and_build(): plan = andi.plan(E, is_injectable=lambda x: True, externally_provided={A}) assert dict(plan[:2]).keys() == {A, B} assert list(dict(plan[:2]).values()) == [{}, {}] assert plan[2:] == [ (C, {'a': A, 'b': B}), (D, {'a': A, 'c': C}), (E, {'b': B, 'c': C, 'd': D}) ] assert plan.full_final_kwargs instances = build(plan, {A: ""}) assert type(instances[E]) == E
def test_plan_use_fn_as_annotations(full_final_kwargs): def fn_ann(b: B): setattr(b, "modified", True) return b def fn(b: fn_ann): return b plan = andi.plan(fn, is_injectable=[fn_ann, B], full_final_kwargs=full_final_kwargs) assert plan.full_final_kwargs instances = build(plan) assert instances[fn].modified
def test_plan_with_optionals_and_union(): def fn(str_or_b_or_None: Optional[Union[str, B]]): return str_or_b_or_None plan = andi.plan(fn, is_injectable={str, B, type(None)}) assert type(build(plan)[fn]) == str plan = andi.plan(fn, is_injectable={B, type(None)}) assert type(build(plan)[fn]) == B plan = andi.plan(fn, is_injectable={B, type(None)}, externally_provided={str}) assert type(build(plan)[fn]) == str plan = andi.plan(fn, is_injectable={type(None)}) assert build(plan)[fn] is None plan = andi.plan(fn, is_injectable={type(None)}, externally_provided={str}) assert type(build(plan)[fn]) == str plan = andi.plan(fn, is_injectable={type(None)}, externally_provided={str, B}) assert type(build(plan)[fn]) == str plan = andi.plan(fn, is_injectable={type(None)}, externally_provided={B}) assert type(build(plan)[fn]) == B plan = andi.plan(fn, is_injectable={}) assert plan == [(fn, {})] assert not plan.full_final_kwargs with pytest.raises(NonProvidableError) as ex_info: andi.plan(fn, is_injectable={}, full_final_kwargs=True) assert error_causes(ex_info) == [ ('str_or_b_or_None', [ NonInjectableOrExternalErrCase('str_or_b_or_None', fn, [str, B, type(None)]) ]) ]
def test_plan_with_optionals(): def fn(a: Optional[str]): assert a is None return "invoked!" plan = andi.plan(fn, is_injectable={type(None), str}, externally_provided={str}) assert plan == [(str, {}), (fn, {'a': str})] assert plan.full_final_kwargs plan = andi.plan(fn, is_injectable={type(None)}) assert plan.dependencies == [(type(None), {})] assert _final_kwargs_spec(plan) == {'a': type(None)} assert plan.full_final_kwargs instances = build(plan) assert instances[type(None)] is None assert instances[fn] == "invoked!" with pytest.raises(andi.NonProvidableError) as ex_info: andi.plan(fn, is_injectable={}, full_final_kwargs=True) assert error_causes(ex_info) == [ ('a', [NonInjectableOrExternalErrCase('a', fn, [str, type(None)])])]