Skip to content

Commit

Permalink
Merge pull request #28 from jorenham/feature/missing-reversed
Browse files Browse the repository at this point in the history
add ops for `__reversed__` and `__missing__`
  • Loading branch information
jorenham authored Mar 21, 2024
2 parents ab2a2f8 + 00b1a22 commit 4d3e376
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 9 deletions.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -987,8 +987,8 @@ Additionally, there is `optype.CanAIterSelf[V]`, with both the
<code>_.__missing__()</code>
(<a href="https://docs.python.org/3/reference/datamodel.html#object.__missing__">docs</a>)
</td>
<td></td>
<td></td>
<td><code>do_missing</code></td>
<td><code>DoesMissing</code></td>
<td><code>__missing__</code></td>
<td><code>CanMissing[K, V]</code></td>
</tr>
Expand All @@ -1015,17 +1015,21 @@ Additionally, there is `optype.CanAIterSelf[V]`, with both the
</tr>
<tr>
<td><code>reversed(_)</code></td>
<td></td>
<td></td>
<td><code>do_reversed</code></td></td>
<td><code>DoesReversed</code></td>
<td><code>__reversed__</code></td>
<td><code>CanReversed[Y]</code></td>
<td><code>CanReversed[Vs] | CanSequence[V]</code></td>
</tr>
</table>

Because `CanMissing[K, M]` generally doesn't show itself without
`CanGetitem[K, V]` there to hold its hand, `optype` conveniently stitched them
together as `optype.CanGetMissing[K, V, M]`.

Similarly, there is `optype.CanSequence[I: CanIndex, V]`, which is the
combination of both `CanLen` and `CanItem[I, V]`, and serves as a more
specific and flexible `collections.abc.Sequence[V]`.

Additionally, `optype` provides protocols for types with (custom) *hash* or
*index* methods:

Expand Down
10 changes: 10 additions & 0 deletions optype/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
'CanRound1',
'CanRound2',
'CanRshift',
'CanSequence',
'CanSet',
'CanSetName',
'CanSetattr',
Expand Down Expand Up @@ -150,6 +151,7 @@
'DoesLshift',
'DoesLt',
'DoesMatmul',
'DoesMissing',
'DoesMod',
'DoesMul',
'DoesNe',
Expand All @@ -173,6 +175,7 @@
'DoesRTruediv',
'DoesRXor',
'DoesRepr',
'DoesReversed',
'DoesRound',
'DoesRshift',
'DoesSetattr',
Expand Down Expand Up @@ -246,6 +249,7 @@
'do_lshift',
'do_lt',
'do_matmul',
'do_missing',
'do_mod',
'do_mul',
'do_ne',
Expand All @@ -258,6 +262,7 @@
'do_rand',
'do_rdivmod',
'do_repr',
'do_reversed',
'do_rfloordiv',
'do_rlshift',
'do_rmatmul',
Expand Down Expand Up @@ -376,6 +381,7 @@
CanRound1,
CanRound2,
CanRshift,
CanSequence,
CanSet,
CanSetName,
CanSetattr,
Expand Down Expand Up @@ -436,6 +442,7 @@
do_lshift,
do_lt,
do_matmul,
do_missing,
do_mod,
do_mul,
do_ne,
Expand All @@ -448,6 +455,7 @@
do_rand,
do_rdivmod,
do_repr,
do_reversed,
do_rfloordiv,
do_rlshift,
do_rmatmul,
Expand Down Expand Up @@ -518,6 +526,7 @@
DoesLshift,
DoesLt,
DoesMatmul,
DoesMissing,
DoesMod,
DoesMul,
DoesNe,
Expand All @@ -541,6 +550,7 @@
DoesRTruediv,
DoesRXor,
DoesRepr,
DoesReversed,
DoesRound,
DoesRshift,
DoesSetattr,
Expand Down
17 changes: 14 additions & 3 deletions optype/_can.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,17 @@ def __missing__(self, __k: K) -> V: ...
class CanGetMissing[K, V, M](CanGetitem[K, V], CanMissing[K, M], Protocol): ...


@runtime_checkable
class CanSequence[I: 'CanIndex', V](CanLen, CanGetitem[I, V], Protocol):
"""
A sequence is an object with a __len__ method and a
__getitem__ method that takes int(-like) argument as key (the index).
Additionally, it is expected to be 0-indexed (the first element is at
index 0) and "dense" (i.e. the indices are consecutive integers, and are
obtainable with e.g. `range(len(_))`).
"""


# 3.3.8. Emulating numeric types
# https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types

Expand Down Expand Up @@ -425,8 +436,8 @@ def __rxor__(self, __x: X, /) -> Y: ...
class CanROr[X, Y](Protocol):
def __ror__(self, __x: X, /) -> Y: ...

# augmented / in-place

# augmented / in-place

@runtime_checkable
class CanIAdd[X, Y](Protocol):
Expand Down Expand Up @@ -493,8 +504,8 @@ def __ixor__(self, __x: X, /) -> Y: ...
class CanIOr[X, Y](Protocol):
def __ior__(self, __x: X, /) -> Y: ...

# unary arithmetic

# unary arithmetic

@runtime_checkable
class CanNeg[Y](Protocol):
Expand All @@ -515,8 +526,8 @@ def __abs__(self) -> Y: ...
class CanInvert[Y](Protocol):
def __invert__(self) -> Y: ...

# numeric conversion

# numeric conversion

@runtime_checkable
class CanComplex(Protocol):
Expand Down
19 changes: 18 additions & 1 deletion optype/_do.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@
do_length_hint: _d.DoesLengthHint = _o.length_hint


# `operator.getitem` isn't used, because it has an (unreasonably loose, and
# redundant) overload for `(Sequence[T], slice) -> Sequence[T]`
# https://github.com/python/typeshed/blob/main/stdlib/_operator.pyi#L84-L86
def do_getitem[K, V, M](
obj: _c.CanGetitem[K, V] | _c.CanGetMissing[K, V, M], key: K, /,
obj: _c.CanGetitem[K, V] | _c.CanGetMissing[K, V, M],
key: K,
/,
) -> V | M:
"""Same as `value = obj[key]`."""
return obj[key]
Expand All @@ -82,11 +87,22 @@ def do_delitem[K](obj: _c.CanDelitem[K], key: K, /) -> None:
del obj[key]


def do_missing[K, V](obj: _c.CanMissing[K, V], key: K, /) -> V:
return obj.__missing__(key)


# `operator.contains` cannot be used, as it incorrectly requires `key`
# to be an **invariant** `object` instance...
def do_contains[K](obj: _c.CanContains[K], key: K, /) -> bool:
"""Same as `key in obj`."""
return key in obj


# `builtins.reversed` is annotated incorrectly within typeshed:
# https://github.com/python/typeshed/issues/11645
do_reversed: _d.DoesReversed = reversed # pyright: ignore[reportAssignmentType]


# infix ops
do_add: _d.DoesAdd = _o.add
do_sub: _d.DoesSub = _o.sub
Expand Down Expand Up @@ -221,6 +237,7 @@ def do_ror[X, Y](a: _c.CanROr[X, Y], b: X) -> Y:
_do_getitem: _d.DoesGetitem = do_getitem
_do_setitem: _d.DoesSetitem = do_setitem
_do_delitem: _d.DoesDelitem = do_delitem
_do_missing: _d.DoesMissing = do_missing
_do_contains: _d.DoesContains = do_contains

_do_radd: _d.DoesRAdd = do_radd
Expand Down
19 changes: 19 additions & 0 deletions optype/_does.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,30 @@ class DoesDelitem(Protocol):
def __call__[K](self, __o: _c.CanDelitem[K], __k: K, /) -> None: ...


@final
class DoesMissing(Protocol):
def __call__[K, V](self, __o: _c.CanMissing[K, V], __k: K, /) -> V: ...


@final
class DoesContains(Protocol):
def __call__[K](self, __o: _c.CanContains[K], __k: K, /) -> bool: ...


@final
class DoesReversed(Protocol):
"""
This is correct type of `builtins.reversed`.
Note that typeshed's annotations for `reversed` are completely wrong:
https://github.com/python/typeshed/issues/11645
"""
@overload
def __call__[Vs](self, __o: _c.CanReversed[Vs], /) -> Vs: ...
@overload
def __call__[V](self, __o: _c.CanSequence[Any, V], /) -> 'reversed[V]': ...


# binary infix operators

@final
Expand Down

0 comments on commit 4d3e376

Please sign in to comment.