decoy 2.1.2__py3-none-any.whl → 2.2.1__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.
decoy/__init__.py CHANGED
@@ -1,15 +1,16 @@
1
1
  """Decoy stubbing and spying library."""
2
+
2
3
  from typing import Any, Callable, Coroutine, Generic, Optional, Union, overload
3
4
 
4
5
  from . import errors, matchers, warnings
5
- from .core import DecoyCore, StubCore, PropCore
6
- from .types import ClassT, ContextValueT, FuncT, ReturnT
7
6
  from .context_managers import (
8
- ContextManager,
9
7
  AsyncContextManager,
10
- GeneratorContextManager,
11
8
  AsyncGeneratorContextManager,
9
+ ContextManager,
10
+ GeneratorContextManager,
12
11
  )
12
+ from .core import DecoyCore, PropCore, StubCore
13
+ from .types import ClassT, ContextValueT, FuncT, ReturnT
13
14
 
14
15
  # ensure decoy does not pollute pytest tracebacks
15
16
  __tracebackhide__ = True
@@ -40,16 +41,13 @@ class Decoy:
40
41
  self._core = DecoyCore()
41
42
 
42
43
  @overload
43
- def mock(self, *, cls: Callable[..., ClassT]) -> ClassT:
44
- ...
44
+ def mock(self, *, cls: Callable[..., ClassT]) -> ClassT: ...
45
45
 
46
46
  @overload
47
- def mock(self, *, func: FuncT) -> FuncT:
48
- ...
47
+ def mock(self, *, func: FuncT) -> FuncT: ...
49
48
 
50
49
  @overload
51
- def mock(self, *, name: str, is_async: bool = False) -> Any:
52
- ...
50
+ def mock(self, *, name: str, is_async: bool = False) -> Any: ...
53
51
 
54
52
  def mock(
55
53
  self,
@@ -84,7 +82,7 @@ class Decoy:
84
82
  spec = cls or func
85
83
 
86
84
  if spec is None and name is None:
87
- raise errors.MockNameRequiredError()
85
+ raise errors.MockNameRequiredError.create()
88
86
 
89
87
  return self._core.mock(spec=spec, name=name, is_async=is_async)
90
88
 
@@ -255,29 +253,25 @@ class Stub(Generic[ReturnT]):
255
253
  def then_enter_with(
256
254
  self: "Stub[ContextManager[ContextValueT]]",
257
255
  value: ContextValueT,
258
- ) -> None:
259
- ...
256
+ ) -> None: ...
260
257
 
261
258
  @overload
262
259
  def then_enter_with(
263
260
  self: "Stub[AsyncContextManager[ContextValueT]]",
264
261
  value: ContextValueT,
265
- ) -> None:
266
- ...
262
+ ) -> None: ...
267
263
 
268
264
  @overload
269
265
  def then_enter_with(
270
266
  self: "Stub[GeneratorContextManager[ContextValueT]]",
271
267
  value: ContextValueT,
272
- ) -> None:
273
- ...
268
+ ) -> None: ...
274
269
 
275
270
  @overload
276
271
  def then_enter_with(
277
272
  self: "Stub[AsyncGeneratorContextManager[ContextValueT]]",
278
273
  value: ContextValueT,
279
- ) -> None:
280
- ...
274
+ ) -> None: ...
281
275
 
282
276
  def then_enter_with(
283
277
  self: Union[
@@ -347,4 +341,4 @@ class Prop(Generic[ReturnT]):
347
341
  self._core.delete()
348
342
 
349
343
 
350
- __all__ = ["Decoy", "Stub", "Prop", "matchers", "warnings", "errors"]
344
+ __all__ = ["Decoy", "Prop", "Stub", "errors", "matchers", "warnings"]
decoy/call_handler.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Spy call handling."""
2
+
2
3
  from typing import Any, NamedTuple, Optional
3
4
 
4
5
  from .spy_log import SpyLog
decoy/context_managers.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Wrappers around contextlib types and fallbacks."""
2
+
2
3
  from typing import Any, AsyncContextManager, ContextManager, Generic, TypeVar
3
4
 
4
5
  from contextlib import (
@@ -44,6 +45,6 @@ __all__ = [
44
45
  "AsyncContextManager",
45
46
  "AsyncGeneratorContextManager",
46
47
  "ContextManager",
47
- "GeneratorContextManager",
48
48
  "ContextWrapper",
49
+ "GeneratorContextManager",
49
50
  ]
decoy/core.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Decoy implementation logic."""
2
+
2
3
  import inspect
3
4
  from typing import Any, Callable, Optional
4
5
 
decoy/errors.py CHANGED
@@ -4,6 +4,7 @@ See the [errors guide][] for more details.
4
4
 
5
5
  [errors guide]: usage/errors-and-warnings.md#errors
6
6
  """
7
+
7
8
  from typing import Optional, Sequence
8
9
 
9
10
  from .spy_events import SpyEvent, VerifyRehearsal
@@ -18,8 +19,10 @@ class MockNameRequiredError(ValueError):
18
19
  [MockNameRequiredError guide]: usage/errors-and-warnings.md#mocknamerequirederror
19
20
  """
20
21
 
21
- def __init__(self) -> None:
22
- super().__init__("Mocks without `cls` or `func` require a `name`.")
22
+ @classmethod
23
+ def create(cls) -> "MockNameRequiredError":
24
+ """Create a MockNameRequiredError."""
25
+ return cls("Mocks without `cls` or `func` require a `name`.")
23
26
 
24
27
 
25
28
  class MissingRehearsalError(ValueError):
@@ -35,8 +38,10 @@ class MissingRehearsalError(ValueError):
35
38
  [MissingRehearsalError guide]: usage/errors-and-warnings.md#missingrehearsalerror
36
39
  """
37
40
 
38
- def __init__(self) -> None:
39
- super().__init__("Rehearsal not found.")
41
+ @classmethod
42
+ def create(cls) -> "MissingRehearsalError":
43
+ """Create a MissingRehearsalError."""
44
+ return cls("Rehearsal not found.")
40
45
 
41
46
 
42
47
  class MockNotAsyncError(TypeError):
@@ -67,12 +72,14 @@ class VerifyError(AssertionError):
67
72
  calls: Sequence[SpyEvent]
68
73
  times: Optional[int]
69
74
 
70
- def __init__(
71
- self,
75
+ @classmethod
76
+ def create(
77
+ cls,
72
78
  rehearsals: Sequence[VerifyRehearsal],
73
79
  calls: Sequence[SpyEvent],
74
80
  times: Optional[int],
75
- ) -> None:
81
+ ) -> "VerifyError":
82
+ """Create a VerifyError."""
76
83
  if times is not None:
77
84
  heading = f"Expected exactly {count(times, 'call')}:"
78
85
  elif len(rehearsals) == 1:
@@ -87,7 +94,9 @@ class VerifyError(AssertionError):
87
94
  include_calls=times is None or times == len(calls),
88
95
  )
89
96
 
90
- super().__init__(message)
91
- self.rehearsals = rehearsals
92
- self.calls = calls
93
- self.times = times
97
+ result = cls(message)
98
+ result.rehearsals = rehearsals
99
+ result.calls = calls
100
+ result.times = times
101
+
102
+ return result
decoy/matchers.py CHANGED
@@ -26,20 +26,42 @@ See the [matchers guide][] for more details.
26
26
  Identity comparisons (`is`) will not work with matchers. Decoy only uses
27
27
  equality comparisons (`==`) for stubbing and verification.
28
28
  """
29
+
29
30
  from re import compile as compile_re
30
31
  from typing import cast, Any, List, Mapping, Optional, Pattern, Type, TypeVar
31
32
 
32
33
 
33
34
  __all__ = [
34
35
  "Anything",
36
+ "Captor",
37
+ "ErrorMatching",
35
38
  "IsA",
36
39
  "IsNot",
37
40
  "StringMatching",
38
- "ErrorMatching",
39
- "Captor",
40
41
  ]
41
42
 
42
43
 
44
+ class _AnythingOrNone:
45
+ def __eq__(self, target: object) -> bool:
46
+ return True
47
+
48
+ def __repr__(self) -> str:
49
+ """Return a string representation of the matcher."""
50
+ return "<AnythingOrNone>"
51
+
52
+
53
+ def AnythingOrNone() -> Any:
54
+ """Match anything including None.
55
+
56
+ !!! example
57
+ ```python
58
+ assert "foobar" == AnythingOrNone()
59
+ assert None == AnythingOrNone()
60
+ ```
61
+ """
62
+ return _AnythingOrNone()
63
+
64
+
43
65
  class _Anything:
44
66
  def __eq__(self, target: object) -> bool:
45
67
  """Return true if target is not None."""
@@ -228,6 +250,41 @@ def DictMatching(values: Mapping[str, Any]) -> Any:
228
250
  return _DictMatching(values)
229
251
 
230
252
 
253
+ class _ListMatching:
254
+ _values: List[Any]
255
+
256
+ def __init__(self, values: List[Any]) -> None:
257
+ self._values = values
258
+
259
+ def __eq__(self, target: object) -> bool:
260
+ """Return true if target matches all given values."""
261
+ if not hasattr(target, "__iter__"):
262
+ return False
263
+
264
+ return all(
265
+ any(item == target_item for target_item in target) for item in self._values
266
+ )
267
+
268
+ def __repr__(self) -> str:
269
+ """Return a string representation of the matcher."""
270
+ return f"<ListMatching {self._values!r}>"
271
+
272
+
273
+ def ListMatching(values: List[Any]) -> Any:
274
+ """Match any list with the passed in values.
275
+
276
+ Arguments:
277
+ values: Values to check.
278
+
279
+ !!! example
280
+ ```python
281
+ value = [1, 2, 3]
282
+ assert value == matchers.ListMatching([1, 2])
283
+ ```
284
+ """
285
+ return _ListMatching(values)
286
+
287
+
231
288
  class _StringMatching:
232
289
  _pattern: Pattern[str]
233
290
 
@@ -270,7 +327,7 @@ class _ErrorMatching:
270
327
 
271
328
  def __eq__(self, target: object) -> bool:
272
329
  """Return true if target is not self._reject_value."""
273
- error_match = type(target) == self._error_type
330
+ error_match = type(target) is self._error_type
274
331
  message_match = (
275
332
  str(target) == self._string_matcher
276
333
  if self._string_matcher is not None
decoy/mypy/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Decoy mypy plugin entrypoint."""
2
+
2
3
  from .plugin import plugin
3
4
 
4
5
  __all__ = ["plugin"]
decoy/mypy/plugin.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Decoy mypy plugin."""
2
+
2
3
  from typing import Callable, Optional, Type as ClassType
3
4
 
4
5
  from mypy.errorcodes import FUNC_RETURNS_VALUE
decoy/pytest_plugin.py CHANGED
@@ -4,6 +4,7 @@ The plugin will be registered with pytest when you install Decoy. It adds a
4
4
  fixture without modifying any other pytest behavior. Its usage is optional
5
5
  but highly recommended.
6
6
  """
7
+
7
8
  from typing import Iterable
8
9
 
9
10
  import pytest
decoy/spy.py CHANGED
@@ -3,6 +3,7 @@
3
3
  Classes in this module are heavily inspired by the
4
4
  [unittest.mock library](https://docs.python.org/3/library/unittest.mock.html).
5
5
  """
6
+
6
7
  import inspect
7
8
  from types import TracebackType
8
9
  from typing import Any, ContextManager, Dict, Optional, Type, Union, cast, overload
@@ -186,14 +187,12 @@ class SpyCreator:
186
187
  self._decoy_spy_call_handler = call_handler
187
188
 
188
189
  @overload
189
- def create(self, *, core: SpyCore) -> AnySpy:
190
- ...
190
+ def create(self, *, core: SpyCore) -> AnySpy: ...
191
191
 
192
192
  @overload
193
193
  def create(
194
194
  self, *, spec: Optional[object], name: Optional[str], is_async: bool
195
- ) -> AnySpy:
196
- ...
195
+ ) -> AnySpy: ...
197
196
 
198
197
  def create(
199
198
  self,
decoy/spy_core.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Core spy logic."""
2
+
2
3
  import inspect
3
4
  import functools
4
5
  import warnings
@@ -15,7 +16,7 @@ from typing import (
15
16
  )
16
17
 
17
18
  from .spy_events import SpyInfo
18
- from .warnings import IncorrectCallWarning, MissingSpecAttributeWarning
19
+ from .warnings import IncorrectCallWarning
19
20
 
20
21
 
21
22
  class _FROM_SOURCE:
@@ -122,15 +123,12 @@ class SpyCore:
122
123
  source = self._source
123
124
  child_name = f"{self._name}.{name}"
124
125
  child_source = None
125
- child_found = False
126
126
 
127
127
  if inspect.isclass(source):
128
128
  # use type hints to get child spec for class attributes
129
129
  child_hint = _get_type_hints(source).get(name)
130
130
  # use inspect to get child spec for methods and properties
131
131
  child_source = inspect.getattr_static(source, name, child_hint)
132
- # record whether a child was found before we make modifications
133
- child_found = child_source is not None
134
132
 
135
133
  if isinstance(child_source, property):
136
134
  child_source = _get_type_hints(child_source.fget).get("return")
@@ -151,13 +149,6 @@ class SpyCore:
151
149
 
152
150
  child_source = _unwrap_optional(child_source)
153
151
 
154
- if source is not None and child_found is False:
155
- # stacklevel: 4 ensures warning is linked to call location
156
- warnings.warn(
157
- MissingSpecAttributeWarning(f"{self._name} has no attribute '{name}'"),
158
- stacklevel=4,
159
- )
160
-
161
152
  return SpyCore(
162
153
  source=child_source,
163
154
  name=child_name,
decoy/spy_events.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Spy interaction event value objects."""
2
+
2
3
  import enum
3
4
  from typing import Any, Dict, NamedTuple, Optional, Tuple, Union
4
5
 
decoy/spy_log.py CHANGED
@@ -1,16 +1,17 @@
1
1
  """Spy activity log."""
2
+
2
3
  from typing import List, Sequence
3
4
 
4
5
  from .errors import MissingRehearsalError
5
6
  from .spy_events import (
6
7
  AnySpyEvent,
8
+ PropAccessType,
9
+ PropRehearsal,
7
10
  SpyCall,
8
11
  SpyEvent,
9
12
  SpyPropAccess,
10
- WhenRehearsal,
11
13
  VerifyRehearsal,
12
- PropAccessType,
13
- PropRehearsal,
14
+ WhenRehearsal,
14
15
  )
15
16
 
16
17
 
@@ -32,10 +33,10 @@ class SpyLog:
32
33
  try:
33
34
  event = self._log[-1]
34
35
  except IndexError as e:
35
- raise MissingRehearsalError() from e
36
+ raise MissingRehearsalError.create() from e
36
37
 
37
38
  if not isinstance(event, SpyEvent):
38
- raise MissingRehearsalError()
39
+ raise MissingRehearsalError.create()
39
40
 
40
41
  spy, payload = _apply_ignore_extra_args(event, ignore_extra_args)
41
42
 
@@ -58,12 +59,12 @@ class SpyLog:
58
59
 
59
60
  while len(rehearsals) < count:
60
61
  if index < 0:
61
- raise MissingRehearsalError()
62
+ raise MissingRehearsalError.create()
62
63
 
63
64
  event = self._log[index]
64
65
 
65
66
  if not isinstance(event, (SpyEvent, PropRehearsal)):
66
- raise MissingRehearsalError()
67
+ raise MissingRehearsalError.create()
67
68
 
68
69
  if _is_verifiable(event):
69
70
  rehearsal = VerifyRehearsal(
@@ -81,7 +82,7 @@ class SpyLog:
81
82
  try:
82
83
  event = self._log[-1]
83
84
  except IndexError as e:
84
- raise MissingRehearsalError() from e
85
+ raise MissingRehearsalError.create() from e
85
86
 
86
87
  spy, payload = event
87
88
 
@@ -90,7 +91,7 @@ class SpyLog:
90
91
  or not isinstance(payload, SpyPropAccess)
91
92
  or payload.access_type != PropAccessType.GET
92
93
  ):
93
- raise MissingRehearsalError()
94
+ raise MissingRehearsalError.create()
94
95
 
95
96
  rehearsal = PropRehearsal(spy, payload)
96
97
  self._log[-1] = rehearsal
decoy/stringify.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Message string generation."""
2
+
2
3
  import os
3
4
  from typing import Sequence
4
5
 
decoy/stub_store.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Stub creation and storage."""
2
+
2
3
  from typing import Any, Callable, List, NamedTuple, Optional, Union
3
4
 
4
5
  from .spy_events import SpyEvent, WhenRehearsal, match_event
decoy/types.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Common type definitions."""
2
+
2
3
  from typing import Any, Callable, TypeVar
3
4
 
4
5
  FuncT = TypeVar("FuncT", bound=Callable[..., Any])
decoy/verifier.py CHANGED
@@ -1,8 +1,9 @@
1
1
  """Spy call verification."""
2
+
2
3
  from typing import Optional, Sequence
3
4
 
4
- from .spy_events import SpyEvent, VerifyRehearsal, match_event
5
5
  from .errors import VerifyError
6
+ from .spy_events import SpyEvent, VerifyRehearsal, match_event
6
7
 
7
8
  # ensure decoy.verifier does not pollute Pytest tracebacks
8
9
  __tracebackhide__ = True
@@ -41,7 +42,7 @@ class Verifier:
41
42
  calls_verified = match_count != 0 if times is None else match_count == times
42
43
 
43
44
  if not calls_verified:
44
- raise VerifyError(
45
+ raise VerifyError.create(
45
46
  rehearsals=rehearsals,
46
47
  calls=calls,
47
48
  times=times,
decoy/warning_checker.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Warning checker."""
2
+
2
3
  from collections import defaultdict
3
4
  from itertools import groupby
4
5
  from typing import Dict, List, NamedTuple, Sequence
@@ -8,9 +9,9 @@ from .spy_events import (
8
9
  AnySpyEvent,
9
10
  SpyCall,
10
11
  SpyEvent,
12
+ SpyRehearsal,
11
13
  VerifyRehearsal,
12
14
  WhenRehearsal,
13
- SpyRehearsal,
14
15
  match_event,
15
16
  )
16
17
  from .warnings import DecoyWarning, MiscalledStubWarning, RedundantVerifyWarning
@@ -77,7 +78,7 @@ def _check_no_miscalled_stubs(all_events: Sequence[AnySpyEvent]) -> None:
77
78
 
78
79
  if is_stubbed and all(len(c.matching_rehearsals) == 0 for c in calls):
79
80
  _warn(
80
- MiscalledStubWarning(
81
+ MiscalledStubWarning.create(
81
82
  calls=[c.event for c in calls],
82
83
  rehearsals=rehearsals,
83
84
  )
@@ -89,8 +90,8 @@ def _check_no_redundant_verify(all_calls: Sequence[AnySpyEvent]) -> None:
89
90
  verify_rehearsals = [c for c in all_calls if isinstance(c, VerifyRehearsal)]
90
91
 
91
92
  for vr in verify_rehearsals:
92
- if any(wr for wr in when_rehearsals if wr == vr):
93
- _warn(RedundantVerifyWarning(rehearsal=vr))
93
+ if any(wr for wr in when_rehearsals if wr == vr): # type: ignore[comparison-overlap]
94
+ _warn(RedundantVerifyWarning.create(rehearsal=vr))
94
95
 
95
96
 
96
97
  def _warn(warning: DecoyWarning) -> None:
decoy/warnings.py CHANGED
@@ -4,11 +4,12 @@ See the [warnings guide][] for more details.
4
4
 
5
5
  [warnings guide]: usage/errors-and-warnings.md#warnings
6
6
  """
7
+
7
8
  import os
8
9
  from typing import Sequence
9
10
 
10
11
  from .spy_events import SpyEvent, SpyRehearsal, VerifyRehearsal
11
- from .stringify import stringify_call, stringify_error_message, count
12
+ from .stringify import count, stringify_call, stringify_error_message
12
13
 
13
14
 
14
15
  class DecoyWarning(UserWarning):
@@ -30,18 +31,20 @@ class MiscalledStubWarning(DecoyWarning):
30
31
  [MiscalledStubWarning guide]: usage/errors-and-warnings.md#miscalledstubwarning
31
32
 
32
33
  Attributes:
33
- rehearsals: The mocks's configured rehearsals.
34
+ rehearsals: The mock's configured rehearsals.
34
35
  calls: Actual calls to the mock.
35
36
  """
36
37
 
37
38
  rehearsals: Sequence[SpyRehearsal]
38
39
  calls: Sequence[SpyEvent]
39
40
 
40
- def __init__(
41
- self,
41
+ @classmethod
42
+ def create(
43
+ cls,
42
44
  rehearsals: Sequence[SpyRehearsal],
43
45
  calls: Sequence[SpyEvent],
44
- ) -> None:
46
+ ) -> "MiscalledStubWarning":
47
+ """Create a MiscalledStubWarning."""
45
48
  heading = os.linesep.join(
46
49
  [
47
50
  "Stub was called but no matching rehearsal found.",
@@ -55,9 +58,11 @@ class MiscalledStubWarning(DecoyWarning):
55
58
  calls=calls,
56
59
  )
57
60
 
58
- super().__init__(message)
59
- self.rehearsals = rehearsals
60
- self.calls = calls
61
+ result = cls(message)
62
+ result.rehearsals = rehearsals
63
+ result.calls = calls
64
+
65
+ return result
61
66
 
62
67
 
63
68
  class RedundantVerifyWarning(DecoyWarning):
@@ -73,7 +78,11 @@ class RedundantVerifyWarning(DecoyWarning):
73
78
  [RedundantVerifyWarning guide]: usage/errors-and-warnings.md#redundantverifywarning
74
79
  """
75
80
 
76
- def __init__(self, rehearsal: VerifyRehearsal) -> None:
81
+ rehearsal: VerifyRehearsal
82
+
83
+ @classmethod
84
+ def create(cls, rehearsal: VerifyRehearsal) -> "RedundantVerifyWarning":
85
+ """Create a RedundantVerifyWarning."""
77
86
  message = os.linesep.join(
78
87
  [
79
88
  "The same rehearsal was used in both a `when` and a `verify`.",
@@ -82,8 +91,11 @@ class RedundantVerifyWarning(DecoyWarning):
82
91
  "See https://michael.cousins.io/decoy/usage/errors-and-warnings/#redundantverifywarning",
83
92
  ]
84
93
  )
85
- super().__init__(message)
86
- self.rehearsal = rehearsal
94
+
95
+ result = cls(message)
96
+ result.rehearsal = rehearsal
97
+
98
+ return result
87
99
 
88
100
 
89
101
  class IncorrectCallWarning(DecoyWarning):
@@ -95,13 +107,3 @@ class IncorrectCallWarning(DecoyWarning):
95
107
 
96
108
  [IncorrectCallWarning guide]: usage/errors-and-warnings.md#incorrectcallwarning
97
109
  """
98
-
99
-
100
- class MissingSpecAttributeWarning(DecoyWarning):
101
- """A warning raised if a Decoy mock with a spec is used with a missing attribute.
102
-
103
- This will become an error in the next major version of Decoy.
104
- See the [MissingSpecAttributeWarning guide][] for more details.
105
-
106
- [MissingSpecAttributeWarning guide]: usage/errors-and-warnings.md#missingspecattributewarning
107
- """
@@ -1,29 +1,22 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: decoy
3
- Version: 2.1.2
3
+ Version: 2.2.1
4
4
  Summary: Opinionated mocking library for Python
5
- License: MIT
5
+ License-Expression: MIT
6
+ License-File: LICENSE
6
7
  Author: Michael Cousins
7
- Author-email: michael@cousins.io
8
- Requires-Python: >=3.7,<4.0
8
+ Author-email: michael@cousins.io>
9
+ Requires-Python: >=3.7
9
10
  Classifier: Development Status :: 5 - Production/Stable
10
11
  Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.7
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Programming Language :: Python :: 3.13
21
13
  Classifier: Topic :: Software Development :: Testing
22
14
  Classifier: Topic :: Software Development :: Testing :: Mocking
23
15
  Classifier: Typing :: Typed
24
16
  Project-URL: Changelog, https://github.com/mcous/decoy/releases
25
17
  Project-URL: Documentation, https://michael.cousins.io/decoy/
26
18
  Project-URL: Homepage, https://michael.cousins.io/decoy/
19
+ Project-URL: Issues, https://github.com/mcous/decoy/issues
27
20
  Project-URL: Repository, https://github.com/mcous/decoy
28
21
  Description-Content-Type: text/markdown
29
22
 
@@ -0,0 +1,25 @@
1
+ decoy/__init__.py,sha256=v3LUyAxuxFrO2M4Iw0VRX0IM6axFMxPmtgIbBemmwj8,11232
2
+ decoy/call_handler.py,sha256=fuhWr9VAnh0FUvbfWw11fpdQ_GCUE3Hah-EXvdk3gGw,1512
3
+ decoy/context_managers.py,sha256=oYsH99ZPmkaCcqjCrOWInueO5ympHm_ICWmbpFoQzqA,1287
4
+ decoy/core.py,sha256=0YsQFnL4Kw_oDX7jRgOVLw9BGsnNdekymQCd1aq-4sQ,5830
5
+ decoy/errors.py,sha256=mmUAOoQQyXhCLjMcf_3bHMz3PjU7bingIU6sC3hk2B4,3119
6
+ decoy/matchers.py,sha256=_hIyrBZfwRqhQKpiBczN7OVIBw1n3IRfZRv2lhQqgzM,11299
7
+ decoy/mypy/__init__.py,sha256=GdJCf-gXpYB4SBr-Rm8zCLZQxAUuLjPyUieIWgLmdwA,86
8
+ decoy/mypy/plugin.py,sha256=nCfo4XpDxBRDsInDsvCe6h8t7-1OV0ZCJzIJqTsAU0I,1359
9
+ decoy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ decoy/pytest_plugin.py,sha256=muiWN6zwQPD-AaL4YH6usp-25P4EYjrROTx6wdLE9hM,853
11
+ decoy/spy.py,sha256=cxlwDFxd2dcTb6vFJJGc0B3IuFCPJs2cgVMeB0qNXW4,7581
12
+ decoy/spy_core.py,sha256=b3PTgCkYnijT08ak_iRUKnA95FnmspSRmT8cs_YtjtM,7965
13
+ decoy/spy_events.py,sha256=PFTfkgN7CVWfbVoSSUMSaCSxfRrUMd7U419iwQ1Xofg,2383
14
+ decoy/spy_log.py,sha256=5JcjcH7zHrn12jp7xyrs_UWY7ATcWJkQ4v9oCR_Cclg,3929
15
+ decoy/stringify.py,sha256=6JA2VA9eK-4AtwSSZPnMYT8ucW7EMpGdbTXokbOpgOg,2240
16
+ decoy/stub_store.py,sha256=tVqKOW65NjotVdDhDDXpLfwagKHypMS4WMob71RYk1c,1744
17
+ decoy/types.py,sha256=LXzky6T0RXvS53NoPpVgCi3KVV6KCyB6EvnBl0NaKug,404
18
+ decoy/verifier.py,sha256=3ZVqaUJPjaNM9Nue3FK6Zzj15qWskX2CEQXoe_yjUEs,1586
19
+ decoy/warning_checker.py,sha256=OOkcrr_I98Pzs3o9UUNgaihJDHwNiu1LlcEK9hi5ZFs,3577
20
+ decoy/warnings.py,sha256=DLdX1ABPk6QvhJWt5s98uR7rCQFQdJJjSPPXRSX0nXA,3203
21
+ decoy-2.2.1.dist-info/METADATA,sha256=NSyb-y2FGxtmuCo_rCHkHbxosqfmC6yzh5x9hUMWdkU,6855
22
+ decoy-2.2.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
23
+ decoy-2.2.1.dist-info/entry_points.txt,sha256=P2wF8zdthEM-3Yo32kxHDhZDjbW6AE489HPWqnvPLOU,38
24
+ decoy-2.2.1.dist-info/licenses/LICENSE,sha256=Rxi19kHgqakAsJNG1jMuORmgKx9bI8Pcu_gtzFkhflQ,1078
25
+ decoy-2.2.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.1
2
+ Generator: poetry-core 2.2.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,25 +0,0 @@
1
- decoy/__init__.py,sha256=vwNdsTHo61z4UQH3U4hiy6ECb9mKK9pUfNGEzxnAwn8,11280
2
- decoy/call_handler.py,sha256=vbqY_N6UWIRdwoOljEzl7y5iTDiFC-zxHfzl8ykuhxU,1511
3
- decoy/context_managers.py,sha256=-Zmqtd_kZESZpLrBSxgOSofH6scgsOwcqPATFb49LWM,1286
4
- decoy/core.py,sha256=4zQTVa3f8UNqwb6VQOGR1IDOSdvNOWIKuemmqPb5P1M,5829
5
- decoy/errors.py,sha256=F-8T_e_xDVcuVaGYJG1Bdc0RRVGQFyBtH-A4eMW_AYQ,2888
6
- decoy/matchers.py,sha256=zTYmT7gPuMa2UZDeMdaukmanWRDuwWDm94_5Mq9txFg,9963
7
- decoy/mypy/__init__.py,sha256=_Va3O7r0QoniZAD3IaRWo2DHt1OF0o5vgR9gmr48U3s,85
8
- decoy/mypy/plugin.py,sha256=LLJCfYQdEEayym0S6Re3BZG4gdi3drmyoIjiFXasfw0,1358
9
- decoy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- decoy/pytest_plugin.py,sha256=bPJ9v8cwEFxtPQRWeTuP97WsWsaWHaXPPVKxiFf3VyE,852
11
- decoy/spy.py,sha256=kpPXruiWcGwu8jOlIU4HWjaJUiSEzZ_AFc19eFevOhc,7596
12
- decoy/spy_core.py,sha256=QrmS7q8ABo-pXabk55IXVot3TN_fMhaG5CUGNuk2mz8,8435
13
- decoy/spy_events.py,sha256=paDLR_Pdn9KDemSOFRvHcDSidt-yzjX7sidXE8S67z8,2382
14
- decoy/spy_log.py,sha256=cgl4inDJ-bpAiwuGbkWKWRaOGlGPTN8DKv1LiDJVbd4,3886
15
- decoy/stringify.py,sha256=-xlYGfYfjHJuAATmH9dQk7kIQbiBknzZ07GOzO4p7yU,2239
16
- decoy/stub_store.py,sha256=lLJGqv1CIxZlJFHhnXuoQdbp_q524eT-e8QHEDPSAew,1743
17
- decoy/types.py,sha256=y2DYDekkGzjKTshW0VN3Yw6pifb5T9AyqdrpwohKV3A,403
18
- decoy/verifier.py,sha256=FK_HmJnNSqwkkCAlkeZVtb2sK0sUZEwhnlP42dgXN4Y,1578
19
- decoy/warning_checker.py,sha256=NVA4KwPeM7vneTku60XiDGjD9tG_ywkz6R7WvmTAlQA,3526
20
- decoy/warnings.py,sha256=kPRR0ujdNGS_W_ciNvR5dGWyQuuWbooZduPW6OB32t8,3347
21
- decoy-2.1.2.dist-info/LICENSE,sha256=Rxi19kHgqakAsJNG1jMuORmgKx9bI8Pcu_gtzFkhflQ,1078
22
- decoy-2.1.2.dist-info/METADATA,sha256=5qGNUFcf_B1Blly9uBnBQV_S3nemThdQobxh6dZudv0,7220
23
- decoy-2.1.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
24
- decoy-2.1.2.dist-info/entry_points.txt,sha256=P2wF8zdthEM-3Yo32kxHDhZDjbW6AE489HPWqnvPLOU,38
25
- decoy-2.1.2.dist-info/RECORD,,