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

Commit 39984a4

Browse files
Bas van Beekperson142
Bas van Beek
authored andcommitted
MAINT: Fix issue 77: Don't allow initializing generic #80
This pull request addresses the issues raised in #77: constructors were previously available for ``np.generic`` and a number of its subclasses, many of which could not actually be instantiated.
1 parent 1eaccd4 commit 39984a4

File tree

4 files changed

+92
-30
lines changed

4 files changed

+92
-30
lines changed

Diff for: numpy-stubs/__init__.pyi

+61-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import builtins
22
import sys
33
import datetime as dt
4+
from abc import abstractmethod, ABCMeta
45

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

363+
# NOTE: while `np.generic` is not technically an instance of `ABCMeta`,
364+
# the `@abstractmethod` decorator is herein used to (forcefully) deny
365+
# the creation of `np.generic` instances.
366+
# The `# type: ignore` comments are necessary to silence mypy errors regarding
367+
# the missing `ABCMeta` metaclass.
368+
369+
# See https://github.com/numpy/numpy-stubs/pull/80 for more details.
370+
362371
class generic(_ArrayOrScalarCommon):
363-
def __init__(self, value: Any = ...) -> None: ...
372+
@abstractmethod
373+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
364374
@property
365375
def base(self) -> None: ...
366376

367-
class _real_generic(generic):
377+
class _real_generic(generic): # type: ignore
368378
@property
369379
def real(self: _ArraySelf) -> _ArraySelf: ...
370380
@property
371381
def imag(self: _ArraySelf) -> _ArraySelf: ...
372382

373-
class number(generic):
374-
def __init__(self, value: Union[SupportsInt, SupportsFloat] = ...) -> None: ...
383+
class number(generic): ... # type: ignore
384+
385+
class bool_(_real_generic):
386+
def __init__(self, value: object = ...) -> None: ...
375387

376-
class bool_(_real_generic): ...
377-
class object_(generic): ...
388+
class object_(generic):
389+
def __init__(self, value: object = ...) -> None: ...
378390

379391
class datetime64:
380392
@overload
@@ -386,8 +398,8 @@ class datetime64:
386398
def __add__(self, other: Union[timedelta64, int]) -> datetime64: ...
387399
def __sub__(self, other: Union[timedelta64, datetime64, int]) -> timedelta64: ...
388400

389-
class integer(number, _real_generic): ...
390-
class signedinteger(integer): ...
401+
class integer(number, _real_generic): ... # type: ignore
402+
class signedinteger(integer): ... # type: ignore
391403

392404
class int8(signedinteger):
393405
def __init__(self, value: SupportsInt = ...) -> None: ...
@@ -419,7 +431,7 @@ class timedelta64(signedinteger):
419431
def __truediv__(self, other: float) -> timedelta64: ...
420432
def __mod__(self, other: timedelta64) -> timedelta64: ...
421433

422-
class unsignedinteger(integer): ...
434+
class unsignedinteger(integer): ... # type: ignore
423435

424436
class uint8(unsignedinteger):
425437
def __init__(self, value: SupportsInt = ...) -> None: ...
@@ -433,34 +445,60 @@ class uint32(unsignedinteger):
433445
class uint64(unsignedinteger):
434446
def __init__(self, value: SupportsInt = ...) -> None: ...
435447

436-
class inexact(number): ...
437-
class floating(inexact, _real_generic): ...
438-
class float16(floating): ...
439-
class float32(floating): ...
440-
class float64(floating): ...
448+
class inexact(number): ... # type: ignore
449+
class floating(inexact, _real_generic): ... # type: ignore
441450

442-
class complexfloating(inexact):
443-
def __init__(
444-
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex, complex] = ...
445-
) -> None: ...
451+
class float16(floating):
452+
def __init__(self, value: SupportsFloat = ...) -> None: ...
453+
454+
class float32(floating):
455+
def __init__(self, value: SupportsFloat = ...) -> None: ...
456+
457+
class float64(floating):
458+
def __init__(self, value: SupportsFloat = ...) -> None: ...
459+
460+
class complexfloating(inexact): ... # type: ignore
446461

447462
class complex64(complexfloating):
463+
def __init__(
464+
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex] = ...
465+
) -> None: ...
448466
@property
449467
def real(self) -> float32: ...
450468
@property
451469
def imag(self) -> float32: ...
452470

453471
class complex128(complexfloating):
472+
def __init__(
473+
self, value: Union[SupportsInt, SupportsFloat, SupportsComplex] = ...
474+
) -> None: ...
454475
@property
455476
def real(self) -> float64: ...
456477
@property
457478
def imag(self) -> float64: ...
458479

459-
class flexible(_real_generic): ...
460-
class void(flexible): ...
461-
class character(_real_generic): ...
462-
class bytes_(character): ...
463-
class str_(character): ...
480+
class flexible(_real_generic): ... # type: ignore
481+
482+
class void(flexible):
483+
def __init__(self, value: Union[int, integer, bool_, bytes, bytes_]): ...
484+
485+
class character(_real_generic): ... # type: ignore
486+
487+
class bytes_(character):
488+
@overload
489+
def __init__(self, value: object = ...) -> None: ...
490+
@overload
491+
def __init__(
492+
self, value: object, encoding: str = ..., errors: str = ...
493+
) -> None: ...
494+
495+
class str_(character):
496+
@overload
497+
def __init__(self, value: object = ...) -> None: ...
498+
@overload
499+
def __init__(
500+
self, value: object, encoding: str = ..., errors: str = ...
501+
) -> None: ...
464502

465503
# TODO(alan): Platform dependent types
466504
# longcomplex, longdouble, longfloat

Diff for: tests/fail/scalars.py

+13
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,16 @@ def __float__(self):
5252
np.uint16(A()) # E: incompatible type
5353
np.uint32(A()) # E: incompatible type
5454
np.uint64(A()) # E: incompatible type
55+
56+
np.void("test") # E: incompatible type
57+
58+
np.generic(1) # E: Cannot instantiate abstract class
59+
np.number(1) # E: Cannot instantiate abstract class
60+
np.integer(1) # E: Cannot instantiate abstract class
61+
np.signedinteger(1) # E: Cannot instantiate abstract class
62+
np.unsignedinteger(1) # E: Cannot instantiate abstract class
63+
np.inexact(1) # E: Cannot instantiate abstract class
64+
np.floating(1) # E: Cannot instantiate abstract class
65+
np.complexfloating(1) # E: Cannot instantiate abstract class
66+
np.character("test") # E: Cannot instantiate abstract class
67+
np.flexible(b"test") # E: Cannot instantiate abstract class

Diff for: tests/pass/ndarray_conversion.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import tempfile
23

34
import numpy as np
@@ -28,15 +29,16 @@
2829
nd.tobytes(None)
2930

3031
# tofile
31-
with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
32-
nd.tofile(tmp.name)
33-
nd.tofile(tmp.name, "")
34-
nd.tofile(tmp.name, sep="")
32+
if os.name != "nt":
33+
with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
34+
nd.tofile(tmp.name)
35+
nd.tofile(tmp.name, "")
36+
nd.tofile(tmp.name, sep="")
3537

36-
nd.tofile(tmp.name, "", "%s")
37-
nd.tofile(tmp.name, format="%s")
38+
nd.tofile(tmp.name, "", "%s")
39+
nd.tofile(tmp.name, format="%s")
3840

39-
nd.tofile(tmp)
41+
nd.tofile(tmp)
4042

4143
# dump is pretty simple
4244
# dumps is pretty simple

Diff for: tests/pass/scalars.py

+9
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ def __float__(self):
1919

2020
np.complex64(3j)
2121
np.complex64(C())
22+
np.complex128(3j)
23+
np.complex128(C())
2224

2325
np.int8(4)
2426
np.int16(3.4)
@@ -77,3 +79,10 @@ def __float__(self):
7779
td_64 / 1.0
7880
td_64 / td_64
7981
td_64 % td_64
82+
83+
np.void(1)
84+
np.void(np.int64(1))
85+
np.void(True)
86+
np.void(np.bool_(True))
87+
np.void(b"test")
88+
np.void(np.bytes_("test"))

0 commit comments

Comments
 (0)