tests.test_partial_functions   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 164
Duplicated Lines 8.54 %

Importance

Changes 0
Metric Value
eloc 95
dl 14
loc 164
rs 10
c 0
b 0
f 0
wmc 26

21 Functions

Rating   Name   Duplication   Size   Complexity  
A test_if_a_typed_argument_cant_be_constructed_a_helpful_exception_is_returned() 0 5 2
A test_partial_application_can_be_applied_to_functions_with_named_args() 0 3 1
A test_a_decorator_can_be_used_to_bind_as_well() 0 2 1
A failing_to_construct_function() 0 5 1
A test_partial_application_can_be_applied_to_generators() 0 4 1
A test_missing_call_arguments_results_in_sensible_error_messages() 0 5 2
A test_partial_application_can_be_applied_to_functions_with_positional_args_first() 0 3 1
A example_generator() 0 5 1
A another_example_function() 0 6 1
A test_partial_application_returns_something_that_is_considered_a_function() 0 3 1
A example_function_with_to_injectables() 0 4 1
A test_passed_in_arguments_are_used_over_container_generated_ones_when_positional() 0 3 1
A test_passed_in_arguments_are_used_over_container_generated_ones_when_named() 0 3 1
A test_a_decorator_can_be_used_to_bind_and_with_positional_arguments() 0 2 1
A test_injected_arguments_can_be_skipped() 0 3 1
A test_deps_are_loaded_at_call_time_not_definition_time() 0 8 1
A test_incorrect_arguments_are_handled_well() 0 4 2
A example_function() 0 2 1
A test_container_values_can_be_overridden() 0 4 1
A test_name_and_docs_are_kept() 0 5 1
A test_partials_can_be_provided_with_an_update_method() 14 14 1

2 Methods

Rating   Name   Duplication   Size   Complexity  
A MyDep.__init__() 0 3 1
A CantBeAutoConstructed.__init__() 0 2 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
import inspect
2
from typing import Generator, Any, ClassVar
3
4
import pytest
5
6
from lagom import Container, bind_to_container, injectable
7
from lagom.exceptions import UnresolvableType
8
from lagom.interfaces import WriteableContainer
9
10
11
class MyDep:
12
    loaded: ClassVar[bool] = False
13
    value: str
14
15
    def __init__(self, value="testing"):
16
        MyDep.loaded = True
17
        self.value = value
18
19
20
class CantBeAutoConstructed:
21
    def __init__(self, something):
22
        pass
23
24
25
container = Container()
26
27
28
def example_function(message: str, resolved: MyDep = injectable) -> str:
29
    return resolved.value + message
30
31
32
def example_function_with_to_injectables(
33
    one: MyDep = injectable, two: MyDep = injectable
34
) -> str:
35
    return one.value + two.value
36
37
38
def example_generator(
39
    message: str, resolved: MyDep = injectable
40
) -> Generator[str, Any, Any]:
41
    yield resolved.value + message
42
    yield resolved.value + " finished"
43
44
45
@bind_to_container(container)
46
def another_example_function(message: str, resolved: MyDep = injectable) -> str:
47
    """
48
    I am DOCS
49
    """
50
    return resolved.value + message
51
52
53
@bind_to_container(container)
54
def failing_to_construct_function(
55
    try_to_resolve: CantBeAutoConstructed = injectable,
56
) -> str:
57
    return "doesnt matter"
58
59
60
def test_partial_application_can_be_applied_to_functions_with_named_args():
61
    partial = container.partial(example_function)
62
    assert partial(message=" world") == "testing world"
63
64
65
def test_partial_application_returns_something_that_is_considered_a_function():
66
    partial = container.partial(example_function)
67
    inspect.isfunction(partial)
68
69
70
def test_partial_application_can_be_applied_to_functions_with_positional_args_first():
71
    partial = container.partial(example_function)
72
    assert partial(" world") == "testing world"
73
74
75
def test_passed_in_arguments_are_used_over_container_generated_ones_when_positional():
76
    partial = container.partial(example_function)
77
    assert partial(" world", MyDep("overridden")) == "overridden world"
78
79
80
def test_passed_in_arguments_are_used_over_container_generated_ones_when_named():
81
    partial = container.partial(example_function)
82
    assert partial(message=" world", resolved=MyDep("overridden")) == "overridden world"
83
84
85
def test_injected_arguments_can_be_skipped():
86
    partial = container.partial(example_function_with_to_injectables)
87
    assert partial(two=MyDep(" 2nd")) == "testing 2nd"
88
89
90
def test_a_decorator_can_be_used_to_bind_as_well():
91
    assert another_example_function(message=" hello") == "testing hello"
92
93
94
def test_a_decorator_can_be_used_to_bind_and_with_positional_arguments():
95
    assert another_example_function(" hello") == "testing hello"
96
97
98
def test_container_values_can_be_overridden():
99
    assert (
100
        another_example_function(resolved=MyDep("replaced"), message=" hello")
101
        == "replaced hello"
102
    )
103
104
105
def test_missing_call_arguments_results_in_sensible_error_messages():
106
    with pytest.raises(TypeError) as err:
107
        another_example_function()
108
    assert "another_example_function() missing 1 required positional argument" in str(
109
        err.value
110
    )
111
112
113
def test_incorrect_arguments_are_handled_well():
114
    with pytest.raises(TypeError) as err:
115
        another_example_function(not_the_message=" no")
116
    assert "unexpected keyword argument 'not_the_message'" in str(err.value)
117
118
119
def test_if_a_typed_argument_cant_be_constructed_a_helpful_exception_is_returned():
120
    with pytest.raises(UnresolvableType) as err:
121
        failing_to_construct_function()
122
    assert "Unable to construct dependency of type CantBeAutoConstructed" in str(
123
        err.value
124
    )
125
126
127
def test_partial_application_can_be_applied_to_generators():
128
    partial = container.partial(example_generator)
129
    results = [result for result in partial(message=" world")]
130
    assert results == ["testing world", "testing finished"]
131
132
133
def test_deps_are_loaded_at_call_time_not_definition_time():
134
    MyDep.loaded = False
135
136
    @bind_to_container(container)
137
    def some_random_unused_function(message: str, resolved: MyDep = injectable) -> str:
138
        return resolved.value + message
139
140
    assert not MyDep.loaded
141
142
143
def test_name_and_docs_are_kept():
144
    assert another_example_function.__name__ == "another_example_function"
145
    # Note: whitespace stripping because some versions of python format the docs differently
146
    #       and we don't care too much about that. just that content is kept.
147
    assert another_example_function.__doc__.strip() == "I am DOCS"  # type: ignore[union-attr]
148
149
150 View Code Duplication
def test_partials_can_be_provided_with_an_update_method(container: Container):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
151
    def _my_func(a, b: MyDep = injectable):
152
        return a, b
153
154
    def _dep_two_is_dep_one(c: WriteableContainer, a, k):
155
        # We'll do something a bit weird and say the container
156
        # always injects the first supplied argument when asked for
157
        # a MyDep. Don't do this for real.
158
        c[MyDep] = a[0]
159
160
    weird = container.partial(_my_func, container_updater=_dep_two_is_dep_one)
161
162
    arg_1, arg_2 = weird("hello")
163
    assert arg_1 == arg_2
164