Skip to content
This repository was archived by the owner on Jun 10, 2020. It is now read-only.

MAINT: Fix issue 77: Don't allow initializing generic #80

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 61 additions & 23 deletions numpy-stubs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import builtins
import sys
import datetime as dt
from abc import abstractmethod, ABCMeta
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to remove the ABCMeta import I think?


from numpy.core._internal import _ctypes
from numpy.typing import ArrayLike, DtypeLike, _Shape, _ShapeLike
Expand Down Expand Up @@ -359,22 +360,33 @@ class ndarray(_ArrayOrScalarCommon, Iterable, Sized, Container):
def __contains__(self, key) -> bool: ...
def __index__(self) -> int: ...

# NOTE: while `np.generic` is not technically an instance of `ABCMeta`,
# the `@abstractmethod` decorator is herein used to (forcefully) deny
# the creation of `np.generic` instances.
# The `# type: ignore` comments are necessary to silence mypy errors regarding
# the missing `ABCMeta` metaclass.

# See https://github.com/numpy/numpy-stubs/pull/80 for more details.

class generic(_ArrayOrScalarCommon):
def __init__(self, value: Any = ...) -> None: ...
@abstractmethod
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
@property
def base(self) -> None: ...

class _real_generic(generic):
class _real_generic(generic): # type: ignore
@property
def real(self: _ArraySelf) -> _ArraySelf: ...
@property
def imag(self: _ArraySelf) -> _ArraySelf: ...

class number(generic):
def __init__(self, value: Union[SupportsInt, SupportsFloat] = ...) -> None: ...
class number(generic): ... # type: ignore

class bool_(_real_generic):
def __init__(self, value: object = ...) -> None: ...

class bool_(_real_generic): ...
class object_(generic): ...
class object_(generic):
def __init__(self, value: object = ...) -> None: ...

class datetime64:
@overload
Expand All @@ -386,8 +398,8 @@ class datetime64:
def __add__(self, other: Union[timedelta64, int]) -> datetime64: ...
def __sub__(self, other: Union[timedelta64, datetime64, int]) -> timedelta64: ...

class integer(number, _real_generic): ...
class signedinteger(integer): ...
class integer(number, _real_generic): ... # type: ignore
class signedinteger(integer): ... # type: ignore

class int8(signedinteger):
def __init__(self, value: SupportsInt = ...) -> None: ...
Expand Down Expand Up @@ -419,7 +431,7 @@ class timedelta64(signedinteger):
def __truediv__(self, other: float) -> timedelta64: ...
def __mod__(self, other: timedelta64) -> timedelta64: ...

class unsignedinteger(integer): ...
class unsignedinteger(integer): ... # type: ignore

class uint8(unsignedinteger):
def __init__(self, value: SupportsInt = ...) -> None: ...
Expand All @@ -433,34 +445,60 @@ class uint32(unsignedinteger):
class uint64(unsignedinteger):
def __init__(self, value: SupportsInt = ...) -> None: ...

class inexact(number): ...
class floating(inexact, _real_generic): ...
class float16(floating): ...
class float32(floating): ...
class float64(floating): ...
class inexact(number): ... # type: ignore
class floating(inexact, _real_generic): ... # type: ignore

class complexfloating(inexact):
def __init__(
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex, complex] = ...
) -> None: ...
class float16(floating):
def __init__(self, value: SupportsFloat = ...) -> None: ...

class float32(floating):
def __init__(self, value: SupportsFloat = ...) -> None: ...

class float64(floating):
def __init__(self, value: SupportsFloat = ...) -> None: ...

class complexfloating(inexact): ... # type: ignore

class complex64(complexfloating):
def __init__(
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex] = ...
) -> None: ...
@property
def real(self) -> float32: ...
@property
def imag(self) -> float32: ...

class complex128(complexfloating):
def __init__(
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex] = ...
) -> None: ...
@property
def real(self) -> float64: ...
@property
def imag(self) -> float64: ...

class flexible(_real_generic): ...
class void(flexible): ...
class character(_real_generic): ...
class bytes_(character): ...
class str_(character): ...
class flexible(_real_generic): ... # type: ignore

class void(flexible):
def __init__(self, value: Union[int, integer, bool_, bytes, bytes_]): ...

class character(_real_generic): ... # type: ignore

class bytes_(character):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So contrary to its bytes.__init__() counterpart in typeshed (ref), it seems that np.bytes_ can truly take any object as argument.

>>> import numpy as np

>>> np.bytes_(0)
b''

>>> np.bytes_(None)
b'None'  # Equivalent to np.bytes_(str(None))

>>> class Test: 
...     pass

>>> np.bytes_(Test)
b"<class '__main__.Test'>"
>>> np.bytes_(Test())
b'<__main__.Test object at ...>'

@overload
def __init__(self, value: object = ...) -> None: ...
@overload
def __init__(
self, value: object, encoding: str = ..., errors: str = ...
) -> None: ...

class str_(character):
@overload
def __init__(self, value: object = ...) -> None: ...
@overload
def __init__(
self, value: object, encoding: str = ..., errors: str = ...
) -> None: ...

# TODO(alan): Platform dependent types
# longcomplex, longdouble, longfloat
Expand Down
13 changes: 13 additions & 0 deletions tests/fail/scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,16 @@ def __float__(self):
np.uint16(A()) # E: incompatible type
np.uint32(A()) # E: incompatible type
np.uint64(A()) # E: incompatible type

np.void("test") # E: incompatible type

np.generic(1) # E: Cannot instantiate abstract class
np.number(1) # E: Cannot instantiate abstract class
np.integer(1) # E: Cannot instantiate abstract class
np.signedinteger(1) # E: Cannot instantiate abstract class
np.unsignedinteger(1) # E: Cannot instantiate abstract class
np.inexact(1) # E: Cannot instantiate abstract class
np.floating(1) # E: Cannot instantiate abstract class
np.complexfloating(1) # E: Cannot instantiate abstract class
np.character("test") # E: Cannot instantiate abstract class
np.flexible(b"test") # E: Cannot instantiate abstract class
16 changes: 9 additions & 7 deletions tests/pass/ndarray_conversion.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import tempfile

import numpy as np
Expand Down Expand Up @@ -28,15 +29,16 @@
nd.tobytes(None)

# tofile
with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
nd.tofile(tmp.name)
nd.tofile(tmp.name, "")
nd.tofile(tmp.name, sep="")
if os.name != "nt":
with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
nd.tofile(tmp.name)
nd.tofile(tmp.name, "")
nd.tofile(tmp.name, sep="")

nd.tofile(tmp.name, "", "%s")
nd.tofile(tmp.name, format="%s")
nd.tofile(tmp.name, "", "%s")
nd.tofile(tmp.name, format="%s")

nd.tofile(tmp)
nd.tofile(tmp)

# dump is pretty simple
# dumps is pretty simple
Expand Down
9 changes: 9 additions & 0 deletions tests/pass/scalars.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ def __float__(self):

np.complex64(3j)
np.complex64(C())
np.complex128(3j)
np.complex128(C())

np.int8(4)
np.int16(3.4)
Expand Down Expand Up @@ -77,3 +79,10 @@ def __float__(self):
td_64 / 1.0
td_64 / td_64
td_64 % td_64

np.void(1)
np.void(np.int64(1))
np.void(True)
np.void(np.bool_(True))
np.void(b"test")
np.void(np.bytes_("test"))