oprattr 0.2.0__tar.gz → 0.4.0__tar.gz
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.
Potentially problematic release.
This version of oprattr might be problematic. Click here for more details.
- oprattr-0.4.0/CHANGELOG.md +19 -0
- oprattr-0.4.0/LICENSE +32 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/PKG-INFO +3 -1
- {oprattr-0.2.0 → oprattr-0.4.0}/pyproject.toml +5 -1
- {oprattr-0.2.0 → oprattr-0.4.0}/src/oprattr/__init__.py +16 -6
- {oprattr-0.2.0 → oprattr-0.4.0}/src/oprattr/_operations.py +38 -29
- oprattr-0.4.0/src/oprattr/abstract.py +56 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/src/oprattr/mixins.py +12 -11
- oprattr-0.4.0/src/oprattr/typeface.py +42 -0
- oprattr-0.4.0/src/oprattr/typeface.pyi +5 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/tests/test_object.py +14 -4
- oprattr-0.4.0/uv.lock +522 -0
- oprattr-0.2.0/CHANGELOG.md +0 -9
- oprattr-0.2.0/src/oprattr/abstract.py +0 -144
- oprattr-0.2.0/src/oprattr/operators.py +0 -41
- oprattr-0.2.0/uv.lock +0 -416
- {oprattr-0.2.0 → oprattr-0.4.0}/.gitignore +0 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/.python-version +0 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/DEVELOPERS.md +0 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/README.md +0 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/src/oprattr/py.typed +0 -0
- {oprattr-0.2.0 → oprattr-0.4.0}/tests/print-exceptions.py +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## NEXT
|
|
2
|
+
|
|
3
|
+
## v0.4.0
|
|
4
|
+
|
|
5
|
+
- Replace local `operators` module with equivalent module from `numerical` package
|
|
6
|
+
- Redefine equality to always return a single boolean value
|
|
7
|
+
|
|
8
|
+
## v0.3.0
|
|
9
|
+
|
|
10
|
+
- Incorporate `numerical` package
|
|
11
|
+
- Add `typeface` module
|
|
12
|
+
|
|
13
|
+
## v0.2.0
|
|
14
|
+
|
|
15
|
+
- Rename `_types` submodule to `abstract`
|
|
16
|
+
|
|
17
|
+
## v0.1.0
|
|
18
|
+
|
|
19
|
+
- Hello world!
|
oprattr-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
BSD 3-Clause License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2025, Matt Young
|
|
6
|
+
All rights reserved.
|
|
7
|
+
|
|
8
|
+
Redistribution and use in source and binary forms, with or without
|
|
9
|
+
modification, are permitted provided that the following conditions are met:
|
|
10
|
+
|
|
11
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
12
|
+
list of conditions and the following disclaimer.
|
|
13
|
+
|
|
14
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
15
|
+
this list of conditions and the following disclaimer in the documentation
|
|
16
|
+
and/or other materials provided with the distribution.
|
|
17
|
+
|
|
18
|
+
* Neither the name of the copyright holder nor the names of its
|
|
19
|
+
contributors may be used to endorse or promote products derived from
|
|
20
|
+
this software without specific prior written permission.
|
|
21
|
+
|
|
22
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
23
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
24
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
25
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
26
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
27
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
28
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
29
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
30
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
31
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
+
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oprattr
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Author-email: Matthew Young <myoung.space.science@gmail.com>
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: numerical
|
|
7
9
|
Requires-Dist: numpy>=2.2.1
|
|
8
10
|
Requires-Dist: scipy>=1.15.0
|
|
9
11
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "oprattr"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Add your description here"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -8,6 +8,7 @@ authors = [
|
|
|
8
8
|
]
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
dependencies = [
|
|
11
|
+
"numerical",
|
|
11
12
|
"numpy>=2.2.1",
|
|
12
13
|
"scipy>=1.15.0",
|
|
13
14
|
]
|
|
@@ -21,3 +22,6 @@ dev = [
|
|
|
21
22
|
"ipython>=8.31.0",
|
|
22
23
|
"pytest>=8.3.4",
|
|
23
24
|
]
|
|
25
|
+
|
|
26
|
+
[tool.uv.sources]
|
|
27
|
+
numerical = { git = "https://github.com/myoung-space-science/numerical", rev = "v0.2.0" }
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import collections.abc
|
|
1
2
|
import functools
|
|
2
3
|
import numbers
|
|
3
|
-
import typing
|
|
4
4
|
|
|
5
|
+
from numerical import operators
|
|
5
6
|
import numpy
|
|
6
7
|
|
|
7
|
-
from . import mixins
|
|
8
|
-
from . import operators
|
|
9
8
|
from . import abstract
|
|
9
|
+
from . import mixins
|
|
10
|
+
from . import typeface
|
|
10
11
|
from ._operations import (
|
|
11
12
|
unary,
|
|
12
13
|
equality,
|
|
@@ -16,7 +17,7 @@ from ._operations import (
|
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
T =
|
|
20
|
+
T = typeface.TypeVar('T')
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class Operand(abstract.Object[T], mixins.Numpy):
|
|
@@ -114,12 +115,21 @@ class Operand(abstract.Object[T], mixins.Numpy):
|
|
|
114
115
|
|
|
115
116
|
def __rpow__(self, other):
|
|
116
117
|
"""Called for other ** self."""
|
|
117
|
-
return
|
|
118
|
+
return NotImplemented
|
|
118
119
|
|
|
119
120
|
def __array__(self, *args, **kwargs):
|
|
120
121
|
"""Called for numpy.array(self)."""
|
|
121
122
|
return numpy.array(self._data, *args, **kwargs)
|
|
122
123
|
|
|
124
|
+
def _apply_ufunc(self, ufunc, method, *args, **kwargs):
|
|
125
|
+
if ufunc in (numpy.equal, numpy.not_equal):
|
|
126
|
+
# NOTE: We are probably here because the left operand is a
|
|
127
|
+
# `numpy.ndarray`, which would otherwise take control and return the
|
|
128
|
+
# pure `numpy` result.
|
|
129
|
+
f = getattr(ufunc, method)
|
|
130
|
+
return equality(f, *args)
|
|
131
|
+
return super()._apply_ufunc(ufunc, method, *args, **kwargs)
|
|
132
|
+
|
|
123
133
|
|
|
124
134
|
@Operand.implementation(numpy.array_equal)
|
|
125
135
|
def array_equal(
|
|
@@ -155,7 +165,7 @@ def gradient(x: Operand[T], *args, **kwargs):
|
|
|
155
165
|
return type(x)(data, **meta)
|
|
156
166
|
|
|
157
167
|
|
|
158
|
-
def wrapnumpy(f:
|
|
168
|
+
def wrapnumpy(f: collections.abc.Callable):
|
|
159
169
|
"""Implement a numpy function for objects with metadata."""
|
|
160
170
|
@functools.wraps(f)
|
|
161
171
|
def method(x: Operand[T], **kwargs):
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
from numerical import operators
|
|
2
2
|
|
|
3
|
-
from . import
|
|
4
|
-
from .abstract import Object
|
|
3
|
+
from .abstract import Quantity
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
class MetadataError(TypeError):
|
|
@@ -11,8 +10,8 @@ class MetadataError(TypeError):
|
|
|
11
10
|
self,
|
|
12
11
|
f: operators.Operator,
|
|
13
12
|
*args,
|
|
14
|
-
error:
|
|
15
|
-
key:
|
|
13
|
+
error: str | None = None,
|
|
14
|
+
key: str | None = None,
|
|
16
15
|
) -> None:
|
|
17
16
|
super().__init__(*args)
|
|
18
17
|
self._f = f
|
|
@@ -33,8 +32,8 @@ class MetadataError(TypeError):
|
|
|
33
32
|
def _build_error_message(
|
|
34
33
|
f: operators.Operator,
|
|
35
34
|
*types: type,
|
|
36
|
-
error:
|
|
37
|
-
key:
|
|
35
|
+
error: str | None = None,
|
|
36
|
+
key: str | None = None,
|
|
38
37
|
) -> str:
|
|
39
38
|
"""Helper for `_raise_metadata_exception`.
|
|
40
39
|
|
|
@@ -50,9 +49,9 @@ def _build_error_message(
|
|
|
50
49
|
if len(types) == 2:
|
|
51
50
|
a, b = types
|
|
52
51
|
endstr = "because {} has metadata"
|
|
53
|
-
if issubclass(a,
|
|
52
|
+
if issubclass(a, Quantity):
|
|
54
53
|
return f"{errmsg} between {a} and {b} {endstr.format(str(a))}"
|
|
55
|
-
if issubclass(b,
|
|
54
|
+
if issubclass(b, Quantity):
|
|
56
55
|
return f"{errmsg} between {a} and {b} {endstr.format(str(b))}"
|
|
57
56
|
if errstr == 'type':
|
|
58
57
|
if key is None:
|
|
@@ -71,7 +70,7 @@ def _build_error_message(
|
|
|
71
70
|
|
|
72
71
|
def unary(f: operators.Operator, a):
|
|
73
72
|
"""Compute the unary operation f(a)."""
|
|
74
|
-
if isinstance(a,
|
|
73
|
+
if isinstance(a, Quantity):
|
|
75
74
|
meta = {}
|
|
76
75
|
for key, value in a._meta.items():
|
|
77
76
|
try:
|
|
@@ -86,32 +85,42 @@ def unary(f: operators.Operator, a):
|
|
|
86
85
|
|
|
87
86
|
def equality(f: operators.Operator, a, b):
|
|
88
87
|
"""Compute the equality operation f(a, b)."""
|
|
89
|
-
if isinstance(a,
|
|
88
|
+
x = a._data if isinstance(a, Quantity) else a
|
|
89
|
+
y = b._data if isinstance(b, Quantity) else b
|
|
90
|
+
fxy = f(x, y)
|
|
91
|
+
try:
|
|
92
|
+
iter(fxy)
|
|
93
|
+
except TypeError:
|
|
94
|
+
r = bool(fxy)
|
|
95
|
+
else:
|
|
96
|
+
r = all(fxy)
|
|
97
|
+
isne = f(1, 2)
|
|
98
|
+
if isinstance(a, Quantity) and isinstance(b, Quantity):
|
|
90
99
|
if a._meta != b._meta:
|
|
91
|
-
return
|
|
92
|
-
return
|
|
93
|
-
if isinstance(a,
|
|
100
|
+
return isne
|
|
101
|
+
return r
|
|
102
|
+
if isinstance(a, Quantity):
|
|
94
103
|
if not a._meta:
|
|
95
|
-
return
|
|
96
|
-
return
|
|
97
|
-
if isinstance(b,
|
|
104
|
+
return r
|
|
105
|
+
return isne
|
|
106
|
+
if isinstance(b, Quantity):
|
|
98
107
|
if not b._meta:
|
|
99
|
-
return
|
|
100
|
-
return
|
|
101
|
-
return
|
|
108
|
+
return r
|
|
109
|
+
return isne
|
|
110
|
+
return r
|
|
102
111
|
|
|
103
112
|
|
|
104
113
|
def ordering(f: operators.Operator, a, b):
|
|
105
114
|
"""Compute the ordering operation f(a, b)."""
|
|
106
|
-
if isinstance(a,
|
|
115
|
+
if isinstance(a, Quantity) and isinstance(b, Quantity):
|
|
107
116
|
if a._meta == b._meta:
|
|
108
117
|
return f(a._data, b._data)
|
|
109
118
|
raise MetadataError(f, a, b, error='unequal') from None
|
|
110
|
-
if isinstance(a,
|
|
119
|
+
if isinstance(a, Quantity):
|
|
111
120
|
if not a._meta:
|
|
112
121
|
return f(a._data, b)
|
|
113
122
|
raise MetadataError(f, a, b, error='non-empty') from None
|
|
114
|
-
if isinstance(b,
|
|
123
|
+
if isinstance(b, Quantity):
|
|
115
124
|
if not b._meta:
|
|
116
125
|
return f(a, b._data)
|
|
117
126
|
raise MetadataError(f, a, b, error='non-empty') from None
|
|
@@ -120,15 +129,15 @@ def ordering(f: operators.Operator, a, b):
|
|
|
120
129
|
|
|
121
130
|
def additive(f: operators.Operator, a, b):
|
|
122
131
|
"""Compute the additive operation f(a, b)."""
|
|
123
|
-
if isinstance(a,
|
|
132
|
+
if isinstance(a, Quantity) and isinstance(b, Quantity):
|
|
124
133
|
if a._meta == b._meta:
|
|
125
134
|
return type(a)(f(a._data, b._data), **a._meta)
|
|
126
135
|
raise MetadataError(f, a, b, error='unequal') from None
|
|
127
|
-
if isinstance(a,
|
|
136
|
+
if isinstance(a, Quantity):
|
|
128
137
|
if not a._meta:
|
|
129
138
|
return type(a)(f(a._data, b))
|
|
130
139
|
raise MetadataError(f, a, b, error='non-empty') from None
|
|
131
|
-
if isinstance(b,
|
|
140
|
+
if isinstance(b, Quantity):
|
|
132
141
|
if not b._meta:
|
|
133
142
|
return type(b)(f(a, b._data))
|
|
134
143
|
raise MetadataError(f, a, b, error='non-empty') from None
|
|
@@ -137,7 +146,7 @@ def additive(f: operators.Operator, a, b):
|
|
|
137
146
|
|
|
138
147
|
def multiplicative(f: operators.Operator, a, b):
|
|
139
148
|
"""Compute the multiplicative operation f(a, b)."""
|
|
140
|
-
if isinstance(a,
|
|
149
|
+
if isinstance(a, Quantity) and isinstance(b, Quantity):
|
|
141
150
|
keys = set(a._meta) & set(b._meta)
|
|
142
151
|
meta = {}
|
|
143
152
|
for key in keys:
|
|
@@ -154,7 +163,7 @@ def multiplicative(f: operators.Operator, a, b):
|
|
|
154
163
|
if key not in keys:
|
|
155
164
|
meta[key] = value
|
|
156
165
|
return type(a)(f(a._data, b._data), **meta)
|
|
157
|
-
if isinstance(a,
|
|
166
|
+
if isinstance(a, Quantity):
|
|
158
167
|
meta = {}
|
|
159
168
|
for key, value in a._meta.items():
|
|
160
169
|
try:
|
|
@@ -164,7 +173,7 @@ def multiplicative(f: operators.Operator, a, b):
|
|
|
164
173
|
else:
|
|
165
174
|
meta[key] = v
|
|
166
175
|
return type(a)(f(a._data, b), **meta)
|
|
167
|
-
if isinstance(b,
|
|
176
|
+
if isinstance(b, Quantity):
|
|
168
177
|
meta = {}
|
|
169
178
|
for key, value in b._meta.items():
|
|
170
179
|
try:
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import collections.abc
|
|
2
|
+
import numbers
|
|
3
|
+
|
|
4
|
+
import numerical
|
|
5
|
+
import numpy.typing
|
|
6
|
+
|
|
7
|
+
from . import typeface
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DataType = typeface.TypeVar(
|
|
11
|
+
'DataType',
|
|
12
|
+
int,
|
|
13
|
+
float,
|
|
14
|
+
numbers.Number,
|
|
15
|
+
numpy.number,
|
|
16
|
+
numpy.typing.ArrayLike,
|
|
17
|
+
numpy.typing.NDArray,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@typeface.runtime_checkable
|
|
22
|
+
class Quantity(numerical.Quantity[DataType], typeface.Protocol):
|
|
23
|
+
"""Protocol for numerical objects with metadata."""
|
|
24
|
+
|
|
25
|
+
_meta: collections.abc.Mapping[str, typeface.Any]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Object(numerical.Real, typeface.Generic[DataType]):
|
|
29
|
+
"""A real-valued object with metadata attributes."""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
__data: DataType,
|
|
34
|
+
**metadata,
|
|
35
|
+
) -> None:
|
|
36
|
+
if not isinstance(__data, numerical.Real):
|
|
37
|
+
raise TypeError("Data input to Object must be real-valued")
|
|
38
|
+
self._data = __data
|
|
39
|
+
self._meta = metadata
|
|
40
|
+
|
|
41
|
+
def __repr__(self):
|
|
42
|
+
"""Called for repr(self)."""
|
|
43
|
+
try:
|
|
44
|
+
datastr = numpy.array2string(
|
|
45
|
+
self._data,
|
|
46
|
+
separator=", ",
|
|
47
|
+
threshold=6,
|
|
48
|
+
edgeitems=2,
|
|
49
|
+
prefix=f"{self.__class__.__qualname__}(",
|
|
50
|
+
suffix=")"
|
|
51
|
+
)
|
|
52
|
+
except Exception:
|
|
53
|
+
datastr = str(self._data)
|
|
54
|
+
metastr = "metadata={" + ", ".join(f"{k!r}" for k in self._meta) + "}"
|
|
55
|
+
return f"{self.__class__.__qualname__}({datastr}, {metastr})"
|
|
56
|
+
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import collections.abc
|
|
1
2
|
import numbers
|
|
2
|
-
import typing
|
|
3
3
|
|
|
4
4
|
import numpy
|
|
5
5
|
|
|
6
6
|
from . import abstract
|
|
7
|
+
from . import typeface
|
|
7
8
|
|
|
8
9
|
|
|
9
|
-
T =
|
|
10
|
+
T = typeface.TypeVar('T')
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Real:
|
|
@@ -82,7 +83,7 @@ class Real:
|
|
|
82
83
|
return self
|
|
83
84
|
|
|
84
85
|
|
|
85
|
-
UserFunction =
|
|
86
|
+
UserFunction = collections.abc.Callable[..., T]
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
class Numpy:
|
|
@@ -127,7 +128,7 @@ class Numpy:
|
|
|
127
128
|
numpy.ndarray,
|
|
128
129
|
numbers.Number,
|
|
129
130
|
list,
|
|
130
|
-
abstract.
|
|
131
|
+
abstract.Quantity,
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
def __array_ufunc__(self, ufunc, method, *args, **kwargs):
|
|
@@ -162,7 +163,7 @@ class Numpy:
|
|
|
162
163
|
return NotImplemented
|
|
163
164
|
if out:
|
|
164
165
|
kwargs['out'] = tuple(
|
|
165
|
-
x._data if isinstance(x, abstract.
|
|
166
|
+
x._data if isinstance(x, abstract.Quantity)
|
|
166
167
|
else x for x in out
|
|
167
168
|
)
|
|
168
169
|
if self._implements(ufunc):
|
|
@@ -285,7 +286,7 @@ class Numpy:
|
|
|
285
286
|
types = self._get_numpy_types(types)
|
|
286
287
|
return array.__array_function__(func, types, args, kwargs)
|
|
287
288
|
|
|
288
|
-
def _get_numpy_array(self) ->
|
|
289
|
+
def _get_numpy_array(self) -> numpy.typing.NDArray | None:
|
|
289
290
|
"""Convert the data interface to an array for `numpy` mixin methods.
|
|
290
291
|
|
|
291
292
|
Notes
|
|
@@ -324,7 +325,7 @@ class Numpy:
|
|
|
324
325
|
`arg` if `arg` is an instance of the base object class; otherwise, it
|
|
325
326
|
will return the unmodified argument.
|
|
326
327
|
"""
|
|
327
|
-
if isinstance(arg, abstract.
|
|
328
|
+
if isinstance(arg, abstract.Quantity):
|
|
328
329
|
return arg._data
|
|
329
330
|
return arg
|
|
330
331
|
|
|
@@ -344,7 +345,7 @@ class Numpy:
|
|
|
344
345
|
)
|
|
345
346
|
|
|
346
347
|
@classmethod
|
|
347
|
-
def _implements(cls, operation:
|
|
348
|
+
def _implements(cls, operation: collections.abc.Callable):
|
|
348
349
|
"""True if this class defines a custom implementation for `operation`.
|
|
349
350
|
|
|
350
351
|
This is a helper methods that gracefully handles the case in which a
|
|
@@ -356,11 +357,11 @@ class Numpy:
|
|
|
356
357
|
return False
|
|
357
358
|
return result
|
|
358
359
|
|
|
359
|
-
_FUNCTIONS:
|
|
360
|
+
_FUNCTIONS: dict[str, collections.abc.Callable]=None
|
|
360
361
|
"""Internal collection of custom `numpy` function implementations."""
|
|
361
362
|
|
|
362
363
|
@classmethod
|
|
363
|
-
def implementation(cls, numpy_function:
|
|
364
|
+
def implementation(cls, numpy_function: collections.abc.Callable, /):
|
|
364
365
|
"""Register a custom implementation of this `numpy` function.
|
|
365
366
|
|
|
366
367
|
Parameters
|
|
@@ -424,7 +425,7 @@ class Numpy:
|
|
|
424
425
|
@classmethod
|
|
425
426
|
def implement(
|
|
426
427
|
cls,
|
|
427
|
-
numpy_function:
|
|
428
|
+
numpy_function: collections.abc.Callable,
|
|
428
429
|
user_function: UserFunction,
|
|
429
430
|
/,
|
|
430
431
|
) -> None:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Support for type annotations.
|
|
3
|
+
|
|
4
|
+
This module provides a single interface to type annotations, including those
|
|
5
|
+
that are not defined by the operative Python version and those that this package
|
|
6
|
+
prefers to use from future versions.
|
|
7
|
+
|
|
8
|
+
Examples
|
|
9
|
+
--------
|
|
10
|
+
* Suppose `BestType` is available in the `typing` module starting with Python
|
|
11
|
+
version 3.X and is available in the `typing_extensions` module for earlier
|
|
12
|
+
versions. If the user is running with Python version <3.X, this module will
|
|
13
|
+
import `BestType` from `typing_extensions`. Otherwise, it will import
|
|
14
|
+
`BestType` from `typing`.
|
|
15
|
+
* Support `UpdatedType` is available in the `typing` module for the user's
|
|
16
|
+
version of Python, but this package wishes to take advantage of updates since
|
|
17
|
+
that version. This module will automatically import the version from
|
|
18
|
+
`typing_extensions`.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import typing
|
|
22
|
+
import typing_extensions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ()
|
|
26
|
+
|
|
27
|
+
EXTENDED = [
|
|
28
|
+
'Protocol',
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def __getattr__(name: str) -> type:
|
|
32
|
+
"""Get a built-in type annotation."""
|
|
33
|
+
if name in EXTENDED:
|
|
34
|
+
return getattr(typing_extensions, name)
|
|
35
|
+
try:
|
|
36
|
+
attr = getattr(typing, name)
|
|
37
|
+
except AttributeError:
|
|
38
|
+
attr = getattr(typing_extensions, name)
|
|
39
|
+
return attr
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
@@ -196,10 +196,20 @@ def test_equality():
|
|
|
196
196
|
assert x(1) != x(-1)
|
|
197
197
|
assert x(1) == 1
|
|
198
198
|
assert x(1) != -1
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
assert x(1, name=
|
|
202
|
-
assert 1 != x(1, name=
|
|
199
|
+
sA = Symbol('A')
|
|
200
|
+
sB = Symbol('B')
|
|
201
|
+
assert x(1, name=sA) == x(1, name=sA)
|
|
202
|
+
assert x(1, name=sA) != x(1, name=sB)
|
|
203
|
+
assert x(1, name=sA) != 1
|
|
204
|
+
assert 1 != x(1, name=sA)
|
|
205
|
+
array = numpy.array([-1, +1])
|
|
206
|
+
assert x(array) == x(array)
|
|
207
|
+
assert x(array, name=sA) == x(array, name=sA)
|
|
208
|
+
assert x(array, name=sA) != x(array, name=sB)
|
|
209
|
+
assert x(array, name=sA) != array
|
|
210
|
+
assert x(array) == array
|
|
211
|
+
assert array == x(array)
|
|
212
|
+
assert array != x(array, name=sA)
|
|
203
213
|
|
|
204
214
|
|
|
205
215
|
def test_ordering():
|