23
23
TypeVar ,
24
24
cast ,
25
25
final ,
26
+ get_args ,
26
27
overload ,
27
28
)
28
29
from typing_extensions import override
62
63
"xor" ,
63
64
]
64
65
_UnOpName : TypeAlias = Literal ["abs" , "neg" , "pos" , "invert" ]
65
- _OpName : TypeAlias = Literal [_BinOpName , _UnOpName ]
66
+ _OpName : TypeAlias = Literal [_UnOpName , _BinOpName ]
66
67
67
68
###
68
69
69
- ROOT_DIR : Final = Path (__file__ ).parent .parent
70
- TARGET_DIR : Final = ROOT_DIR / "src" / "numpy-stubs" / "@test" / "generated"
70
+ DIR_ROOT : Final = Path (__file__ ).parent .parent
71
+ DIR_SRC : Final = DIR_ROOT / "src"
72
+ DIRS_TARGET : Final = {
73
+ dir_package .stem : dir_package / "@test" / "generated"
74
+ for dir_package in DIR_SRC .iterdir ()
75
+ if dir_package .is_dir ()
76
+ }
71
77
72
78
TAB : Final = " " * 4
73
79
BR : Final = "\n "
@@ -366,6 +372,7 @@ def _strip_preamble(source: str) -> tuple[str | None, str]:
366
372
367
373
368
374
class TestGen (abc .ABC ):
375
+ package : ClassVar [str ]
369
376
stdlib_imports : ClassVar [tuple [str , ...]] = ("from typing import assert_type" ,)
370
377
numpy_imports : ClassVar [tuple [str , ...]] = (f"import numpy as { NP } " ,)
371
378
@@ -383,7 +390,7 @@ def __init__(self) -> None:
383
390
@property
384
391
def path (self ) -> Path :
385
392
assert self .testname
386
- return TARGET_DIR / f"{ self .testname } .pyi"
393
+ return DIRS_TARGET [ self . package ] / f"{ self .testname } .pyi"
387
394
388
395
def get_names (self ) -> Iterable [tuple [str , str ]]:
389
396
return ()
@@ -411,7 +418,7 @@ def _generate_section(self, /, *lines: str) -> Generator[str]:
411
418
412
419
def _generate_preamble (self ) -> Generator [str ]:
413
420
timestamp = f"{ np .datetime64 ('now' )} Z"
414
- here = Path (__file__ ).relative_to (ROOT_DIR )
421
+ here = Path (__file__ ).relative_to (DIR_ROOT )
415
422
416
423
yield f"# { PREAMBLE_PREFIX } { timestamp } with { here } "
417
424
@@ -492,7 +499,7 @@ def regenerate(self, /, *, always: bool = False) -> Iterator[str]:
492
499
head_new , body_new = _strip_preamble (src_new )
493
500
assert head_new , src_new
494
501
495
- path_new = str (self .path .relative_to (ROOT_DIR ))
502
+ path_new = str (self .path .relative_to (DIR_ROOT ))
496
503
date_new = head_new .split (" " , 1 )[0 ]
497
504
498
505
if src_old := self ._read ():
@@ -516,13 +523,14 @@ def regenerate(self, /, *, always: bool = False) -> Iterator[str]:
516
523
tofile = path_new ,
517
524
fromfiledate = date_old ,
518
525
tofiledate = date_new if write else date_old ,
519
- n = 0 ,
526
+ n = 1 ,
520
527
lineterm = BR ,
521
528
)
522
529
523
530
524
531
@final
525
532
class EMath (TestGen ):
533
+ package = "numpy-stubs"
526
534
testname = "emath"
527
535
528
536
VALUES : Final [dict [str , list [Any ]]] = {
@@ -735,6 +743,7 @@ def get_testcases(self) -> Iterable[str | None]:
735
743
736
744
@final
737
745
class LiteralBoolOps (TestGen ):
746
+ package = "numpy-stubs"
738
747
testname = "literal_bool_ops"
739
748
740
749
UNOPS : ClassVar = {
@@ -887,6 +896,7 @@ def get_testcases(self) -> Iterable[str | None]:
887
896
888
897
@final
889
898
class ScalarOps (TestGen ):
899
+ package = "numpy-stubs"
890
900
testname = "scalar_ops_{}"
891
901
892
902
OPS_ARITHMETIC : ClassVar [dict [str , _BinOp ]] = {
@@ -1144,6 +1154,7 @@ def get_testcases(self) -> Iterable[str | None]:
1144
1154
1145
1155
1146
1156
class NDArrayOps (TestGen ):
1157
+ package = "numpy-stubs"
1147
1158
testname = "ndarray_{}"
1148
1159
numpy_imports_extra : tuple [str , ...] = ("import _numtype as _nt" ,)
1149
1160
@@ -1527,36 +1538,52 @@ def get_testcases(self) -> Iterable[str | None]:
1527
1538
TESTGENS : Final [Sequence [TestGen ]] = [
1528
1539
EMath (binary = False ),
1529
1540
LiteralBoolOps (),
1530
- ScalarOps ("arithmetic" ),
1531
- ScalarOps ("modular" ),
1532
- ScalarOps ("bitwise" ),
1533
- ScalarOps ("comparison" ),
1534
- NDArrayOps ("pos" ),
1535
- NDArrayOps ("neg" ),
1536
- NDArrayOps ("abs" ),
1537
- NDArrayOps ("invert" ),
1538
- NDArrayOps ("add" ),
1539
- NDArrayOps ("sub" ),
1540
- NDArrayOps ("mul" ),
1541
- NDArrayOps ("matmul" ),
1542
- NDArrayOps ("pow" ),
1543
- NDArrayOps ("truediv" ),
1544
- NDArrayOps ("floordiv" ),
1545
- NDArrayOps ("mod" ),
1546
- NDArrayOps ("divmod" ),
1547
- NDArrayOps ("lshift" ),
1548
- NDArrayOps ("rshift" ),
1549
- NDArrayOps ("and" ),
1550
- NDArrayOps ("xor" ),
1551
- NDArrayOps ("or" ),
1541
+ * (ScalarOps (op_kind ) for op_kind in get_args (_BinOpKind )),
1542
+ * (NDArrayOps (op_name ) for op_name in get_args (_OpName )),
1552
1543
]
1553
1544
1554
1545
1555
1546
@np .errstate (all = "ignore" )
1556
1547
def main () -> None :
1557
- """(Re)generate the `src/numpy-stubs/@test/generated/{}.pyi` type-tests."""
1548
+ """(Re)generate the `src/*/@test/generated/{}.pyi` type-tests."""
1549
+ cwd = Path .cwd ()
1550
+ paths : dict [str , dict [Path , bool ]] = {}
1551
+
1558
1552
for testgen in TESTGENS :
1559
- sys .stdout .writelines (testgen .regenerate ())
1553
+ path = testgen .path
1554
+ diff = testgen .regenerate ()
1555
+ diff_out , diff_check = itertools .tee (diff , 2 )
1556
+ sys .stderr .writelines (diff_out )
1557
+ sys .stderr .write ("\n " )
1558
+ sys .stderr .flush ()
1559
+
1560
+ diff_count = sum (1 for _ in diff_check )
1561
+ if not diff_count :
1562
+ sys .stdout .write (f"skipped ./{ path .relative_to (cwd )} \n " )
1563
+ sys .stdout .flush ()
1564
+
1565
+ package_paths = paths .setdefault (testgen .package , {})
1566
+ assert path not in package_paths , path
1567
+ package_paths [path ] = bool (diff_count )
1568
+
1569
+ orphans : list [Path ] = []
1570
+ for package , testdir in DIRS_TARGET .items ():
1571
+ if not testdir .exists ():
1572
+ continue
1573
+ assert testdir .is_dir ()
1574
+
1575
+ known = paths .get (package , {})
1576
+ for path in testdir .rglob ("*.pyi" ):
1577
+ assert path .is_file ()
1578
+ if path not in known :
1579
+ orphans .append (path )
1580
+
1581
+ for orphan in orphans :
1582
+ assert orphan .is_file ()
1583
+ orphan .unlink ()
1584
+
1585
+ sys .stderr .write (f"removed ./{ orphan .relative_to (cwd )} \n " )
1586
+ sys .stderr .flush ()
1560
1587
1561
1588
1562
1589
if __name__ == "__main__" :
0 commit comments