44"""
55import os
66import sys
7+ import pathlib
78import platform
89import re
910import gc
1920import sysconfig
2021import concurrent .futures
2122import threading
23+ import importlib .metadata
2224
2325import numpy as np
2426from numpy ._core import (
2527 intp , float32 , empty , arange , array_repr , ndarray , isnat , array )
2628from numpy import isfinite , isnan , isinf
2729import numpy .linalg ._umath_linalg
2830from numpy ._utils import _rename_parameter
31+ from numpy ._core .tests ._natype import pd_NA
2932
3033from io import StringIO
3134
35+
3236__all__ = [
3337 'assert_equal' , 'assert_almost_equal' , 'assert_approx_equal' ,
3438 'assert_array_equal' , 'assert_array_less' , 'assert_string_equal' ,
4246 'HAS_REFCOUNT' , "IS_WASM" , 'suppress_warnings' , 'assert_array_compare' ,
4347 'assert_no_gc_cycles' , 'break_cycles' , 'HAS_LAPACK64' , 'IS_PYSTON' ,
4448 'IS_MUSL' , 'check_support_sve' , 'NOGIL_BUILD' ,
45- 'IS_EDITABLE' , 'run_threaded' ,
49+ 'IS_EDITABLE' , 'IS_INSTALLED' , 'NUMPY_ROOT' , ' run_threaded' ,
4650 ]
4751
4852
@@ -54,10 +58,40 @@ class KnownFailureException(Exception):
5458KnownFailureTest = KnownFailureException # backwards compat
5559verbose = 0
5660
61+ NUMPY_ROOT = pathlib .Path (np .__file__ ).parent
62+
63+ try :
64+ np_dist = importlib .metadata .distribution ('numpy' )
65+ except importlib .metadata .PackageNotFoundError :
66+ IS_INSTALLED = IS_EDITABLE = False
67+ else :
68+ IS_INSTALLED = True
69+ try :
70+ if sys .version_info >= (3 , 13 ):
71+ IS_EDITABLE = np_dist .origin .dir_info .editable
72+ else :
73+ # Backport importlib.metadata.Distribution.origin
74+ import json , types # noqa: E401
75+ origin = json .loads (
76+ np_dist .read_text ('direct_url.json' ) or '{}' ,
77+ object_hook = lambda data : types .SimpleNamespace (** data ),
78+ )
79+ IS_EDITABLE = origin .dir_info .editable
80+ except AttributeError :
81+ IS_EDITABLE = False
82+
83+ # spin installs numpy directly via meson, instead of using meson-python, and
84+ # runs the module by setting PYTHONPATH. This is problematic because the
85+ # resulting installation lacks the Python metadata (.dist-info), and numpy
86+ # might already be installed on the environment, causing us to find its
87+ # metadata, even though we are not actually loading that package.
88+ # Work around this issue by checking if the numpy root matches.
89+ if not IS_EDITABLE and np_dist .locate_file ('numpy' ) != NUMPY_ROOT :
90+ IS_INSTALLED = False
91+
5792IS_WASM = platform .machine () in ["wasm32" , "wasm64" ]
5893IS_PYPY = sys .implementation .name == 'pypy'
5994IS_PYSTON = hasattr (sys , "pyston_version_info" )
60- IS_EDITABLE = not bool (np .__path__ ) or 'editable' in np .__path__ [0 ]
6195HAS_REFCOUNT = getattr (sys , 'getrefcount' , None ) is not None and not IS_PYSTON
6296HAS_LAPACK64 = numpy .linalg ._umath_linalg ._ilp64
6397
@@ -101,14 +135,15 @@ def GetPerformanceAttributes(object, counter, instance=None,
101135 # thread's CPU usage is either 0 or 100). To read counters like this,
102136 # you should copy this function, but keep the counter open, and call
103137 # CollectQueryData() each time you need to know.
104- # See http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp (dead link)
138+ # See http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp
139+ #(dead link)
105140 # My older explanation for this was that the "AddCounter" process
106141 # forced the CPU to 100%, but the above makes more sense :)
107142 import win32pdh
108143 if format is None :
109144 format = win32pdh .PDH_FMT_LONG
110- path = win32pdh .MakeCounterPath ( (machine , object , instance , None ,
111- inum , counter ))
145+ path = win32pdh .MakeCounterPath ((machine , object , instance , None ,
146+ inum , counter ))
112147 hq = win32pdh .OpenQuery ()
113148 try :
114149 hc = win32pdh .AddCounter (hq , path )
@@ -166,7 +201,7 @@ def jiffies(_proc_pid_stat=f'/proc/{os.getpid()}/stat', _load_time=[]):
166201 l = f .readline ().split (' ' )
167202 return int (l [13 ])
168203 except Exception :
169- return int (100 * (time .time ()- _load_time [0 ]))
204+ return int (100 * (time .time () - _load_time [0 ]))
170205else :
171206 # os.getpid is not in all platforms available.
172207 # Using time is safe but inaccurate, especially when process
@@ -182,15 +217,15 @@ def jiffies(_load_time=[]):
182217 import time
183218 if not _load_time :
184219 _load_time .append (time .time ())
185- return int (100 * (time .time ()- _load_time [0 ]))
220+ return int (100 * (time .time () - _load_time [0 ]))
186221
187222
188223def build_err_msg (arrays , err_msg , header = 'Items are not equal:' ,
189224 verbose = True , names = ('ACTUAL' , 'DESIRED' ), precision = 8 ):
190225 msg = ['\n ' + header ]
191226 err_msg = str (err_msg )
192227 if err_msg :
193- if err_msg .find ('\n ' ) == - 1 and len (err_msg ) < 79 - len (header ):
228+ if err_msg .find ('\n ' ) == - 1 and len (err_msg ) < 79 - len (header ):
194229 msg = [msg [0 ] + ' ' + err_msg ]
195230 else :
196231 msg .append (err_msg )
@@ -659,14 +694,14 @@ def assert_approx_equal(actual, desired, significant=7, err_msg='',
659694 # Normalized the numbers to be in range (-10.0,10.0)
660695 # scale = float(pow(10,math.floor(math.log10(0.5*(abs(desired)+abs(actual))))))
661696 with np .errstate (invalid = 'ignore' ):
662- scale = 0.5 * (np .abs (desired ) + np .abs (actual ))
697+ scale = 0.5 * (np .abs (desired ) + np .abs (actual ))
663698 scale = np .power (10 , np .floor (np .log10 (scale )))
664699 try :
665- sc_desired = desired / scale
700+ sc_desired = desired / scale
666701 except ZeroDivisionError :
667702 sc_desired = 0.0
668703 try :
669- sc_actual = actual / scale
704+ sc_actual = actual / scale
670705 except ZeroDivisionError :
671706 sc_actual = 0.0
672707 msg = build_err_msg (
@@ -687,7 +722,7 @@ def assert_approx_equal(actual, desired, significant=7, err_msg='',
687722 return
688723 except (TypeError , NotImplementedError ):
689724 pass
690- if np .abs (sc_desired - sc_actual ) >= np .power (10. , - (significant - 1 )):
725+ if np .abs (sc_desired - sc_actual ) >= np .power (10. , - (significant - 1 )):
691726 raise AssertionError (msg )
692727
693728
@@ -1379,10 +1414,10 @@ def check_support_sve(__cache=[]):
13791414 """
13801415 gh-22982
13811416 """
1382-
1417+
13831418 if __cache :
13841419 return __cache [0 ]
1385-
1420+
13861421 import subprocess
13871422 cmd = 'lscpu'
13881423 try :
@@ -1543,7 +1578,7 @@ def measure(code_str, times=1, label=None):
15431578 i += 1
15441579 exec (code , globs , locs )
15451580 elapsed = jiffies () - elapsed
1546- return 0.01 * elapsed
1581+ return 0.01 * elapsed
15471582
15481583
15491584def _assert_valid_refcount (op ):
@@ -1557,7 +1592,7 @@ def _assert_valid_refcount(op):
15571592 import gc
15581593 import numpy as np
15591594
1560- b = np .arange (100 * 100 ).reshape (100 , 100 )
1595+ b = np .arange (100 * 100 ).reshape (100 , 100 )
15611596 c = b
15621597 i = 1
15631598
@@ -1735,7 +1770,7 @@ def assert_array_almost_equal_nulp(x, y, nulp=1):
17351770 ax = np .abs (x )
17361771 ay = np .abs (y )
17371772 ref = nulp * np .spacing (np .where (ax > ay , ax , ay ))
1738- if not np .all (np .abs (x - y ) <= ref ):
1773+ if not np .all (np .abs (x - y ) <= ref ):
17391774 if np .iscomplexobj (x ) or np .iscomplexobj (y ):
17401775 msg = f"Arrays are not equal to { nulp } ULP"
17411776 else :
@@ -1851,7 +1886,7 @@ def nulp_diff(x, y, dtype=None):
18511886 (x .shape , y .shape ))
18521887
18531888 def _diff (rx , ry , vdt ):
1854- diff = np .asarray (rx - ry , dtype = vdt )
1889+ diff = np .asarray (rx - ry , dtype = vdt )
18551890 return np .abs (diff )
18561891
18571892 rx = integer_repr (x )
@@ -2596,7 +2631,7 @@ def check_free_memory(free_bytes):
25962631 except ValueError as exc :
25972632 raise ValueError (f'Invalid environment variable { env_var } : { exc } ' )
25982633
2599- msg = (f'{ free_bytes / 1e9 } GB memory required, but environment variable '
2634+ msg = (f'{ free_bytes / 1e9 } GB memory required, but environment variable '
26002635 f'NPY_AVAILABLE_MEM={ env_value } set' )
26012636 else :
26022637 mem_free = _get_mem_available ()
@@ -2607,7 +2642,9 @@ def check_free_memory(free_bytes):
26072642 "the test." )
26082643 mem_free = - 1
26092644 else :
2610- msg = f'{ free_bytes / 1e9 } GB memory required, but { mem_free / 1e9 } GB available'
2645+ free_bytes_gb = free_bytes / 1e9
2646+ mem_free_gb = mem_free / 1e9
2647+ msg = f'{ free_bytes_gb } GB memory required, but { mem_free_gb } GB available'
26112648
26122649 return msg if mem_free < free_bytes else None
26132650
0 commit comments