decoy 2.2.2__py3-none-any.whl → 2.4.0__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.
@@ -0,0 +1,78 @@
1
+ import warnings
2
+ from typing import Any, Callable, Generic, ParamSpec, TypeVar
3
+
4
+ from .errors import createVerifyError
5
+ from .inspect import bind_args
6
+ from .state import DecoyState
7
+ from .values import (
8
+ AttributeEvent,
9
+ CallEvent,
10
+ Event,
11
+ EventMatcher,
12
+ MatchOptions,
13
+ MockInfo,
14
+ )
15
+ from .warnings import createRedundantVerifyWarning
16
+
17
+ SpecT = TypeVar("SpecT")
18
+ ParamsT = ParamSpec("ParamsT")
19
+
20
+
21
+ class Verify(Generic[SpecT]):
22
+ """[Verify](./verify.md) how a mock was called."""
23
+
24
+ def __init__(
25
+ self,
26
+ state: DecoyState,
27
+ mock: MockInfo,
28
+ match_options: MatchOptions,
29
+ ) -> None:
30
+ self._mock = mock
31
+ self._state = state
32
+ self._match_options = match_options
33
+
34
+ def _verify(self, expected: Event) -> None:
35
+ matcher = EventMatcher(expected, self._match_options)
36
+ result = self._state.use_verification(self._mock, matcher)
37
+
38
+ if not result.is_success:
39
+ raise createVerifyError(
40
+ self._mock.name,
41
+ self._match_options,
42
+ expected,
43
+ result.mock_events,
44
+ )
45
+
46
+ if result.is_redundant:
47
+ warnings.warn(
48
+ createRedundantVerifyWarning(self._mock.name, expected),
49
+ stacklevel=3,
50
+ )
51
+
52
+ def called_with(
53
+ self: "Verify[Callable[ParamsT, Any]]",
54
+ *args: ParamsT.args,
55
+ **kwargs: ParamsT.kwargs,
56
+ ) -> None:
57
+ """Verify that a mock was called."""
58
+ bound_args = bind_args(
59
+ signature=self._mock.signature,
60
+ args=args,
61
+ kwargs=kwargs,
62
+ ignore_extra_args=self._match_options.ignore_extra_args,
63
+ )
64
+ expected = CallEvent(args=bound_args.args, kwargs=bound_args.kwargs)
65
+
66
+ self._verify(expected)
67
+
68
+ def set(self, value: SpecT) -> None:
69
+ """Verify that an [attribute was set](./attributes.md#verify-a-setter)."""
70
+ expected = AttributeEvent.set(value)
71
+
72
+ self._verify(expected)
73
+
74
+ def delete(self) -> None:
75
+ """Verify that an [attribute was deleted](./attributes.md#verify-a-deleter)."""
76
+ expected = AttributeEvent.delete()
77
+
78
+ self._verify(expected)
@@ -0,0 +1,35 @@
1
+ from ... import warnings
2
+ from .stringify import count, join_lines, stringify_event, stringify_event_list
3
+ from .values import CallEvent, Event
4
+
5
+
6
+ def createMiscalledStubWarning(
7
+ name: str,
8
+ expected_events: list[Event],
9
+ actual_event: CallEvent,
10
+ ) -> warnings.MiscalledStubWarning:
11
+ """Create a MiscalledStubWarning."""
12
+ message = join_lines(
13
+ "Stub was called but no matching rehearsal found.",
14
+ f"Found {count(len(expected_events), 'rehearsal')}:",
15
+ stringify_event_list(name, expected_events),
16
+ "Found 1 call:",
17
+ f"1.\t{stringify_event(name, actual_event)}",
18
+ )
19
+
20
+ return warnings.MiscalledStubWarning(message)
21
+
22
+
23
+ def createRedundantVerifyWarning(
24
+ name: str,
25
+ event: Event,
26
+ ) -> warnings.RedundantVerifyWarning:
27
+ """Create a RedundantVerifyWarning."""
28
+ message = join_lines(
29
+ "The same rehearsal was used in both a `when` and a `verify`.",
30
+ "This is redundant and probably a misuse of the mock.",
31
+ f"\t{stringify_event(name, event)}",
32
+ "See https://michael.cousins.io/decoy/usage/errors-and-warnings/#redundantverifywarning",
33
+ )
34
+
35
+ return warnings.RedundantVerifyWarning(message)
@@ -0,0 +1,136 @@
1
+ from contextlib import AbstractAsyncContextManager, AbstractContextManager
2
+ from typing import (
3
+ Any,
4
+ Awaitable,
5
+ Callable,
6
+ Generic,
7
+ NoReturn,
8
+ ParamSpec,
9
+ TypeVar,
10
+ overload,
11
+ )
12
+
13
+ from .inspect import bind_args, ensure_callable
14
+ from .state import DecoyState
15
+ from .values import (
16
+ AttributeEvent,
17
+ Behavior,
18
+ CallEvent,
19
+ Event,
20
+ EventMatcher,
21
+ MatchOptions,
22
+ MockInfo,
23
+ )
24
+
25
+ CallableSpecT = TypeVar("CallableSpecT", covariant=True)
26
+ AttributeSpecT = TypeVar("AttributeSpecT")
27
+ ParamsT = ParamSpec("ParamsT")
28
+ ReturnT = TypeVar("ReturnT")
29
+ ContextValueT = TypeVar("ContextValueT")
30
+
31
+
32
+ class Stub(Generic[ParamsT, ReturnT, ContextValueT]):
33
+ """Configure how a mock behaves [when called](./when.md)."""
34
+
35
+ def __init__(
36
+ self,
37
+ state: DecoyState,
38
+ mock: MockInfo,
39
+ matcher: EventMatcher,
40
+ ) -> None:
41
+ self._state = state
42
+ self._mock = mock
43
+ self._matcher = matcher
44
+
45
+ def then_return(self, *values: ReturnT) -> None:
46
+ """Mock a return value."""
47
+ behaviors = [Behavior(return_value=value) for value in values]
48
+ self._push_behaviors(behaviors)
49
+
50
+ def then_enter_with(self, *values: ContextValueT) -> None:
51
+ """Mock a context manager value for a generator context manager."""
52
+ behaviors = [Behavior(context=value) for value in values]
53
+ self._push_behaviors(behaviors)
54
+
55
+ def then_raise(self, *errors: Exception) -> None:
56
+ """Mock a raised exception."""
57
+ behaviors = [Behavior(error=error) for error in errors]
58
+ self._push_behaviors(behaviors)
59
+
60
+ def then_do(self, *actions: Callable[ParamsT, ReturnT]) -> None:
61
+ """Trigger a callback function."""
62
+ behaviors = [
63
+ Behavior(action=ensure_callable(action, is_async=self._mock.is_async))
64
+ for action in actions
65
+ ]
66
+ self._push_behaviors(behaviors)
67
+
68
+ def _push_behaviors(self, behaviors: list[Behavior]) -> None:
69
+ self._state.push_behaviors(self._mock, self._matcher, behaviors)
70
+
71
+
72
+ class When(Generic[CallableSpecT, AttributeSpecT]):
73
+ """Configure [when a mock is triggered](./when.md)."""
74
+
75
+ def __init__(
76
+ self,
77
+ state: DecoyState,
78
+ mock: MockInfo,
79
+ match_options: MatchOptions,
80
+ ) -> None:
81
+ self._state = state
82
+ self._mock = mock
83
+ self._match_options = match_options
84
+
85
+ @overload
86
+ def called_with(
87
+ self: "When[Callable[ParamsT, AbstractAsyncContextManager[ContextValueT] | AbstractContextManager[ContextValueT]], Callable[..., ReturnT]]",
88
+ *args: ParamsT.args,
89
+ **kwargs: ParamsT.kwargs,
90
+ ) -> Stub[ParamsT, ReturnT, ContextValueT]: ...
91
+
92
+ @overload
93
+ def called_with(
94
+ self: "When[Callable[ParamsT, Awaitable[ReturnT]], Any]",
95
+ *args: ParamsT.args,
96
+ **kwargs: ParamsT.kwargs,
97
+ ) -> Stub[ParamsT, ReturnT | Awaitable[ReturnT], NoReturn]: ...
98
+
99
+ @overload
100
+ def called_with(
101
+ self: "When[Callable[ParamsT, ReturnT], Any]",
102
+ *args: ParamsT.args,
103
+ **kwargs: ParamsT.kwargs,
104
+ ) -> Stub[ParamsT, ReturnT, NoReturn]: ...
105
+
106
+ def called_with(
107
+ self,
108
+ *args: Any,
109
+ **kwargs: Any,
110
+ ) -> Stub[Any, Any, Any]:
111
+ """Configure a stub to react to certain passed-in arguments."""
112
+ bound_args = bind_args(
113
+ signature=self._mock.signature,
114
+ args=args,
115
+ kwargs=kwargs,
116
+ ignore_extra_args=self._match_options.ignore_extra_args,
117
+ )
118
+ event = CallEvent(args=bound_args.args, kwargs=bound_args.kwargs)
119
+
120
+ return self._create_stub(event)
121
+
122
+ def get(self) -> Stub[[], AttributeSpecT, NoReturn]:
123
+ """Configure a stub to react to an attribute get."""
124
+ return self._create_stub(AttributeEvent.get())
125
+
126
+ def set(self, value: AttributeSpecT) -> Stub[[AttributeSpecT], None, NoReturn]:
127
+ """Configure a stub to react to an attribute set."""
128
+ return self._create_stub(AttributeEvent.set(value))
129
+
130
+ def delete(self) -> Stub[[], None, NoReturn]:
131
+ """Configure a stub to react to an attribute delete."""
132
+ return self._create_stub(AttributeEvent.delete())
133
+
134
+ def _create_stub(self, event: Event) -> Stub[Any, Any, NoReturn]:
135
+ matcher = EventMatcher(event=event, options=self._match_options)
136
+ return Stub(self._state, self._mock, matcher)
decoy/spy.py CHANGED
@@ -10,7 +10,7 @@ from typing import Any, ContextManager, Dict, Optional, Type, Union, cast, overl
10
10
 
11
11
  from .call_handler import CallHandler
12
12
  from .spy_core import SpyCore
13
- from .spy_events import SpyCall, SpyEvent, SpyPropAccess, PropAccessType
13
+ from .spy_events import PropAccessType, SpyCall, SpyEvent, SpyPropAccess
14
14
 
15
15
 
16
16
  class BaseSpy(ContextManager[Any]):
@@ -1,23 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: decoy
3
- Version: 2.2.2
3
+ Version: 2.4.0
4
4
  Summary: Opinionated mocking library for Python
5
- License-Expression: MIT
6
- License-File: LICENSE
7
5
  Author: Michael Cousins
8
- Author-email: michael@cousins.io>
9
- Requires-Python: >=3.7
6
+ Author-email: Michael Cousins <michael@cousins.io>>
7
+ License-Expression: MIT
10
8
  Classifier: Development Status :: 5 - Production/Stable
11
9
  Classifier: Intended Audience :: Developers
12
10
  Classifier: Operating System :: OS Independent
13
11
  Classifier: Topic :: Software Development :: Testing
14
12
  Classifier: Topic :: Software Development :: Testing :: Mocking
15
13
  Classifier: Typing :: Typed
16
- Project-URL: Changelog, https://github.com/mcous/decoy/releases
17
- Project-URL: Documentation, https://michael.cousins.io/decoy/
14
+ Requires-Dist: typing-extensions>=4.10.0 ; python_full_version >= '3.10' and python_full_version < '3.13'
15
+ Requires-Python: >=3.7
18
16
  Project-URL: Homepage, https://michael.cousins.io/decoy/
19
- Project-URL: Issues, https://github.com/mcous/decoy/issues
17
+ Project-URL: Documentation, https://michael.cousins.io/decoy/
20
18
  Project-URL: Repository, https://github.com/mcous/decoy
19
+ Project-URL: Issues, https://github.com/mcous/decoy/issues
20
+ Project-URL: Changelog, https://github.com/mcous/decoy/releases
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  <div align="center">
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
  <a title="Code Coverage" href="https://app.codecov.io/gh/mcous/decoy/"><img src="https://img.shields.io/codecov/c/github/mcous/decoy?style=flat-square"></a>
31
31
  <a title="License" href="https://github.com/mcous/decoy/blob/main/LICENSE"><img src="https://img.shields.io/github/license/mcous/decoy?style=flat-square"></a>
32
32
  <a title="PyPI Version"href="https://pypi.org/project/decoy/"><img src="https://img.shields.io/pypi/v/decoy?style=flat-square"></a>
33
- <a title="Supported Python Versions" href="https://pypi.org/project/decoy/"><img src="https://img.shields.io/pypi/pyversions/decoy?style=flat-square"></a>
33
+ <a title="Supported Python Versions" href="https://pypi.org/project/decoy/"><img src="https://img.shields.io/python/required-version-toml?style=flat-square&tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fmcous%2Fdecoy%2Fmain%2Fpyproject.toml"></a>
34
34
  </p>
35
35
  <p>
36
36
  <a href="https://michael.cousins.io/decoy/" class="decoy-hidden">Usage guide and documentation</a>
@@ -44,14 +44,7 @@ Decoy mocks are **async/await** and **type-checking** friendly. Decoy is heavily
44
44
  ## Install
45
45
 
46
46
  ```bash
47
- # pip
48
47
  pip install decoy
49
-
50
- # poetry
51
- poetry add --dev decoy
52
-
53
- # pipenv
54
- pipenv install --dev decoy
55
48
  ```
56
49
 
57
50
  ## Setup
@@ -171,4 +164,3 @@ See [spying with verify][] for more details.
171
164
  [creating mocks]: https://michael.cousins.io/decoy/usage/create/
172
165
  [stubbing with when]: https://michael.cousins.io/decoy/usage/when/
173
166
  [spying with verify]: https://michael.cousins.io/decoy/usage/verify/
174
-
@@ -0,0 +1,38 @@
1
+ decoy/__init__.py,sha256=v3LUyAxuxFrO2M4Iw0VRX0IM6axFMxPmtgIbBemmwj8,11232
2
+ decoy/call_handler.py,sha256=3MixA4pQs4k_2osq1ug19rXXMafzLECGSZLhltSMoo0,1767
3
+ decoy/context_managers.py,sha256=oYsH99ZPmkaCcqjCrOWInueO5ympHm_ICWmbpFoQzqA,1287
4
+ decoy/core.py,sha256=0YsQFnL4Kw_oDX7jRgOVLw9BGsnNdekymQCd1aq-4sQ,5830
5
+ decoy/errors.py,sha256=3vadsAYM-xpoT_lpgiSJbemtjvomWslC9qLfn6FKQpo,2993
6
+ decoy/matchers.py,sha256=Y55oOJww3mhuCsGn9wzUsVMVw0IzxBAMuaSYwtWgOm4,11630
7
+ decoy/mypy/__init__.py,sha256=GdJCf-gXpYB4SBr-Rm8zCLZQxAUuLjPyUieIWgLmdwA,86
8
+ decoy/mypy/plugin.py,sha256=nCfo4XpDxBRDsInDsvCe6h8t7-1OV0ZCJzIJqTsAU0I,1359
9
+ decoy/next/__init__.py,sha256=RqW1Ry5FIw1nptiEqW9W15u1qCySn_ioTRRdu38GFEk,405
10
+ decoy/next/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ decoy/next/_internal/compare.py,sha256=ab9YY3Y4vJhwfw-Kfg-SHGvsaCkZZbmadSI5cJ4j7x8,4173
12
+ decoy/next/_internal/decoy.py,sha256=Mt8cgRVOofwmT0qP4eReUaTvSPFv765MeOCWP9VIR3M,6995
13
+ decoy/next/_internal/errors.py,sha256=E3gUBWY6O-fWt77YJG67bUpOPkV8ktql8VrnZv7UE6Q,3026
14
+ decoy/next/_internal/inspect.py,sha256=LPbm_3ub621KEr6KO1ODhPhbAyxgYnI8qVM2_jmayz8,6698
15
+ decoy/next/_internal/matcher.py,sha256=UDgPgfBfBM7x67AxBFRrm545U634s6YGcc3QsDTCh2M,9141
16
+ decoy/next/_internal/mock.py,sha256=CQrx3nvGFEJSs-r0VNFyHvojtrajFNIT4nQL3_jz9aM,6558
17
+ decoy/next/_internal/state.py,sha256=kFZweF02aG9yGByPa0gtjrheHQRVdFMJwgi19vj5g7o,8247
18
+ decoy/next/_internal/stringify.py,sha256=jQIi9ma7lO2m-Qc-eIth793KnBg7OTRuwr2fg-Q1h-A,2533
19
+ decoy/next/_internal/values.py,sha256=W5yUnDgVNsTfM5qdvc2JZaZKMZ0yC8WES3WIDNZM_WY,1871
20
+ decoy/next/_internal/verify.py,sha256=CEgb7Di-uJskObO2hIycvCgTLixVd4BYCZvHkTvWXRc,2203
21
+ decoy/next/_internal/warnings.py,sha256=59T3hs3x2AcGp6jAUEEbq2pLgFMKO2QatE-1ep0Tlxg,1182
22
+ decoy/next/_internal/when.py,sha256=WtV2U_LIvGbkYgo8CfocUxo3ZDjdlnbV8gtUp6lmBSU,4376
23
+ decoy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ decoy/pytest_plugin.py,sha256=muiWN6zwQPD-AaL4YH6usp-25P4EYjrROTx6wdLE9hM,853
25
+ decoy/spy.py,sha256=G3rEHAx2yWQaucOs8fQoB-Hap8vejPrzYcxBca6XbeQ,7581
26
+ decoy/spy_core.py,sha256=b3PTgCkYnijT08ak_iRUKnA95FnmspSRmT8cs_YtjtM,7965
27
+ decoy/spy_events.py,sha256=PFTfkgN7CVWfbVoSSUMSaCSxfRrUMd7U419iwQ1Xofg,2383
28
+ decoy/spy_log.py,sha256=5JcjcH7zHrn12jp7xyrs_UWY7ATcWJkQ4v9oCR_Cclg,3929
29
+ decoy/stringify.py,sha256=6JA2VA9eK-4AtwSSZPnMYT8ucW7EMpGdbTXokbOpgOg,2240
30
+ decoy/stub_store.py,sha256=tVqKOW65NjotVdDhDDXpLfwagKHypMS4WMob71RYk1c,1744
31
+ decoy/types.py,sha256=LXzky6T0RXvS53NoPpVgCi3KVV6KCyB6EvnBl0NaKug,404
32
+ decoy/verifier.py,sha256=3ZVqaUJPjaNM9Nue3FK6Zzj15qWskX2CEQXoe_yjUEs,1586
33
+ decoy/warning_checker.py,sha256=OOkcrr_I98Pzs3o9UUNgaihJDHwNiu1LlcEK9hi5ZFs,3577
34
+ decoy/warnings.py,sha256=DLdX1ABPk6QvhJWt5s98uR7rCQFQdJJjSPPXRSX0nXA,3203
35
+ decoy-2.4.0.dist-info/WHEEL,sha256=5DEXXimM34_d4Gx1AuF9ysMr1_maoEtGKjaILM3s4w4,80
36
+ decoy-2.4.0.dist-info/entry_points.txt,sha256=En3V0KZt-83nTeWFpVB5KTotgxuUETePl7HesB81W64,40
37
+ decoy-2.4.0.dist-info/METADATA,sha256=LRHd8eo9hYOfpBqBQL8xCcJe4V3p5toYhgyBBtbJBf4,6980
38
+ decoy-2.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: uv 0.9.29
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [pytest11]
2
+ decoy = decoy.pytest_plugin
3
+
@@ -1,25 +0,0 @@
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=TFa3ZLewXPxhuUy_YDktzJWMfdOgfz4jq2TQ9oa6hwk,11298
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.2.dist-info/METADATA,sha256=hKPZiHbc6RDbYOJ9iwIZ0wvRhrOn3UYxPSCAqABg9Tc,6855
22
- decoy-2.2.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
23
- decoy-2.2.2.dist-info/entry_points.txt,sha256=P2wF8zdthEM-3Yo32kxHDhZDjbW6AE489HPWqnvPLOU,38
24
- decoy-2.2.2.dist-info/licenses/LICENSE,sha256=Rxi19kHgqakAsJNG1jMuORmgKx9bI8Pcu_gtzFkhflQ,1078
25
- decoy-2.2.2.dist-info/RECORD,,
@@ -1,3 +0,0 @@
1
- [pytest11]
2
- decoy=decoy.pytest_plugin
3
-
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020-2023, Michael Cousins
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.