
    -il2                       % S r SSKJr  SSKrSSKrSSKrSSKJrJrJ	r	  SSK
Jr  SSKJr  SSKJrJrJrJrJr  SS	KJrJr  SS
KJrJrJr  SS/r\(       a  SSKrSSKJrJrJ r   SSK!J"r"  O\#r S r"\" S5      r$\" S5      r%0 r&S\'S'    " S S\RP                  5      r)\)RT                  r*SS\*\*S.           SS jjr+        SS jr, " S S\ 5      r-      S S jr.g)!z]
Public testing utilities.

See also _lib._testing for additional private testing utilities.
    )annotationsN)CallableIteratorSequence)wraps)
ModuleType)TYPE_CHECKINGAny	ParamSpecTypeVarcast   )is_dask_namespaceis_jax_namespace)jax_autojitpickle_flattenpickle_unflattenlazy_xp_functionpatch_lazy_xp_functions)GraphKeySchedulerGetCallable)overridec                    U $ N )funcs    U/var/www/html/venv/lib/python3.13/site-packages/scipy/_lib/array_api_extra/testing.pyr   r       s        PTzdict[object, dict[str, Any]]_ufuncs_tagsc                      \ rS rSrSrSrSrg)
Deprecated*   z&Unique type for deprecated parameters.r   r   N)__name__
__module____qualname____firstlineno____doc__
DEPRECATED__static_attributes__r   r   r   r$   r$   *   s
    0Jr   r$   FT)allow_dask_computejax_jitstatic_argnumsstatic_argnamesc                   U[         Ld	  U[         La  [        R                  " S[        SS9  UUS.n XPl        g! [
         a    U[        U '    gf = f)a  
Tag a function to be tested on lazy backends.

Tag a function so that when any tests are executed with ``xp=jax.numpy`` the
function is replaced with a jitted version of itself, and when it is executed with
``xp=dask.array`` the function will raise if it attempts to materialize the graph.
This will be later expanded to provide test coverage for other lazy backends.

In order for the tag to be effective, the test or a fixture must call
:func:`patch_lazy_xp_functions`.

Parameters
----------
func : callable
    Function to be tested.
allow_dask_compute : bool | int, optional
    Whether `func` is allowed to internally materialize the Dask graph, or maximum
    number of times it is allowed to do so. This is typically triggered by
    ``bool()``, ``float()``, or ``np.asarray()``.

    Set to 1 if you are aware that `func` converts the input parameters to NumPy and
    want to let it do so at least for the time being, knowing that it is going to be
    extremely detrimental for performance.

    If a test needs values higher than 1 to pass, it is a canary that the conversion
    to NumPy/bool/float is happening multiple times, which translates to multiple
    computations of the whole graph. Short of making the function fully lazy, you
    should at least add explicit calls to ``np.asarray()`` early in the function.
    *Note:* the counter of `allow_dask_compute` resets after each call to `func`, so
    a test function that invokes `func` multiple times should still work with this
    parameter set to 1.

    Set to True to allow `func` to materialize the graph an unlimited number
    of times.

    Default: False, meaning that `func` must be fully lazy and never materialize the
    graph.
jax_jit : bool, optional
    Set to True to replace `func` with a smart variant of ``jax.jit(func)`` after
    calling the :func:`patch_lazy_xp_functions` test helper with ``xp=jax.numpy``.
    Set to False if `func` is only compatible with eager (non-jitted) JAX.

    Unlike with vanilla ``jax.jit``, all arguments and return types that are not JAX
    arrays are treated as static; the function can accept and return arbitrary
    wrappers around JAX arrays. This difference is because, in real life, most users
    won't wrap the function directly with ``jax.jit`` but rather they will use it
    within their own code, which is itself then wrapped by ``jax.jit``, and
    internally consume the function's outputs.

    In other words, the pattern that is being tested is::

        >>> @jax.jit
        ... def user_func(x):
        ...     y = user_prepares_inputs(x)
        ...     z = func(y, some_static_arg=True)
        ...     return user_consumes(z)

    Default: True.
static_argnums :
    Deprecated; ignored
static_argnames :
    Deprecated; ignored

See Also
--------
patch_lazy_xp_functions : Companion function to call from the test or fixture.
jax.jit : JAX function to compile a function for performance.

Examples
--------
In ``test_mymodule.py``::

  from array_api_extra.testing import lazy_xp_function from mymodule import myfunc

  lazy_xp_function(myfunc)

  def test_myfunc(xp):
      a = xp.asarray([1, 2])
      # When xp=jax.numpy, this is similar to `b = jax.jit(myfunc)(a)`
      # When xp=dask.array, crash on compute() or persist()
      b = myfunc(a)

Notes
-----
In order for this tag to be effective, the test function must be imported into the
test module globals without its namespace; alternatively its namespace must be
declared in a ``lazy_xp_modules`` list in the test module globals.

Example 1::

  from mymodule import myfunc

  lazy_xp_function(myfunc)

  def test_myfunc(xp):
      x = myfunc(xp.asarray([1, 2]))

Example 2::

  import mymodule

  lazy_xp_modules = [mymodule]
  lazy_xp_function(mymodule.myfunc)

  def test_myfunc(xp):
      x = mymodule.myfunc(xp.asarray([1, 2]))

A test function can circumvent this monkey-patching system by using a namespace
outside of the two above patterns. You need to sanitize your code to make sure this
only happens intentionally.

Example 1::

  import mymodule
  from mymodule import myfunc

  lazy_xp_function(myfunc)

  def test_myfunc(xp):
      a = xp.asarray([1, 2])
      b = myfunc(a)  # This is wrapped when xp=jax.numpy or xp=dask.array
      c = mymodule.myfunc(a)  # This is not

Example 2::

  import mymodule

  class naked:
      myfunc = mymodule.myfunc

  lazy_xp_modules = [mymodule]
  lazy_xp_function(mymodule.myfunc)

  def test_myfunc(xp):
      a = xp.asarray([1, 2])
      b = mymodule.myfunc(a)  # This is wrapped when xp=jax.numpy or xp=dask.array
      c = naked.myfunc(a)  # This is not
z{The `static_argnums` and `static_argnames` parameters are deprecated and ignored. They will be removed in a future version.   )
stacklevel)r-   r.   N)r+   warningswarnDeprecationWarning_lazy_xp_functionAttributeErrorr"   )r   r-   r.   r/   r0   tagss         r   r   r   3   sa    d Z'?*+LI 	
 1D
"!% "!T"s   : AAc          	       ^
 [        [        U R                  5      nU/[        [        [           [	        US/ 5      5      Qm
  S
U
4S jjn[        U5      (       aC  U" 5        H7  u  p5pgUS   nUSL a  SnOUSL a  Sn[        Xh5      n	UR                  X5U	5        M9     g	[        U5      (       a;  U" 5        H/  u  p5pgUS   (       d  M  [        U5      n	UR                  X5U	5        M1     g	g	)a`  
Test lazy execution of functions tagged with :func:`lazy_xp_function`.

If ``xp==jax.numpy``, search for all functions which have been tagged with
:func:`lazy_xp_function` in the globals of the module that defines the current test,
as well as in the ``lazy_xp_modules`` list in the globals of the same module,
and wrap them with :func:`jax.jit`. Unwrap them at the end of the test.

If ``xp==dask.array``, wrap the functions with a decorator that disables
``compute()`` and ``persist()`` and ensures that exceptions and warnings are raised
eagerly.

This function should be typically called by your library's `xp` fixture that runs
tests on multiple backends::

    @pytest.fixture(params=[numpy, array_api_strict, jax.numpy, dask.array])
    def xp(request, monkeypatch):
        patch_lazy_xp_functions(request, monkeypatch, xp=request.param)
        return request.param

but it can be otherwise be called by the test itself too.

Parameters
----------
request : pytest.FixtureRequest
    Pytest fixture, as acquired by the test itself or by one of its fixtures.
monkeypatch : pytest.MonkeyPatch
    Pytest fixture, as acquired by the test itself or by one of its fixtures.
xp : array_namespace
    Array namespace to be tested.

See Also
--------
lazy_xp_function : Tag a function to be tested on lazy backends.
pytest.FixtureRequest : `request` test function parameter.
lazy_xp_modulesc               3    >#    T H  n U R                   R                  5        Hu  u  pS n[        R                  " [        5         UR
                  nS S S 5        Uc1  [        R                  " [        [        5         [        U   nS S S 5        Uc  Mo  XX#4v   Mw     M     g ! , (       d  f       NV= f! , (       d  f       N3= f7fr   )	__dict__items
contextlibsuppressr8   r7   KeyError	TypeErrorr"   )modnamer   r9   modss       r   iter_tagged,patch_lazy_xp_functions.<locals>.iter_tagged  s      C!ll002
.2((811D 9<#,,XyA+D1 B#T// 3  98 BAs<   ACB!*C;
B2	CC!
B/+C2
C <Cr-   Ti@B Fr   r.   N)returnzDIterator[tuple[ModuleType, str, Callable[..., Any], dict[str, Any]]])
r   r   modulelistgetattrr   
_dask_wrapsetattrr   r   )requestmonkeypatchxprC   rF   rD   r   r9   nwrappedrE   s             @r   r   r      s    N z7>>
*CN$tJ'6G)LMND0L0 %0]!Ct)*ADye )G73 &3 
"		%0]!CtI%d+##Cw7 &3 
r   c                  V    \ rS rSr% SrS\S'   S\S'   S\S'   SS jr\SS	 j5       rS
r	g)CountingDaskScheduleri"  af  
Dask scheduler that counts how many times `dask.compute` is called.

If the number of times exceeds 'max_count', it raises an error.
This is a wrapper around Dask's own 'synchronous' scheduler.

Parameters
----------
max_count : int
    Maximum number of allowed calls to `dask.compute`.
msg : str
    Assertion to raise when the count exceeds `max_count`.
intcount	max_countstrmsgc                *    SU l         Xl        X l        g )Nr   rV   rW   rY   )selfrW   rY   s      r   __init__CountingDaskScheduler.__init__5  s    
"r   c                    SS K nU =R                  S-  sl        U R                  U R                  ::  d   U R                  5       eUR                  " X40 UD6$ )Nr   r   )daskrV   rW   rY   get)r\   dskkeyskwargsr`   s        r   __call__CountingDaskScheduler.__call__:  sG    

a
 zzT^^+5TXX5+xx,V,,r   r[   N)rW   rU   rY   rX   )rb   r   rc   zSequence[Key] | Keyrd   r
   rH   r
   )
r&   r'   r(   r)   r*   __annotations__r]   r   re   r,   r   r   r   rT   rT   "  s1     JN	H
 - -r   rT   c           	        ^ ^^^^ SSK mSSKJm  [        T S[	        T 5      5      nT(       a  ST 3OSnSTS-    SU S	U S
TS-    S3	m[        T 5      SUUU UU4S jj5       nU$ )z
Wrap `func` to raise if it attempts to call `dask.compute` more than `n` times.

After the function returns, materialize the graph in order to re-raise exceptions.
r   Nr&   zonly up to noz,Called `dask.compute()` or `dask.persist()` r   z times, but z* calls are allowed. Set `lazy_xp_function(z, allow_dask_compute=zA)` to allow for more (but note that this will harm performance). c                   > [        T
T	5      nTR                  R                  SU05         T" U 0 UD6nS S S 5        [        WTR                  5      u  pETR                  USS9S   n[        XE5      $ ! , (       d  f       ND= f)N	schedulerthreads)rk   r   )rT   configsetr   Arraypersistr   )argsrd   rk   outarraysrestdar`   r   rY   rQ   s         r   wrapper_dask_wrap.<locals>.wrapperZ  sw    )!S1	[[__k956''C 7 &c2884f	:1=-- 76s   	A22
B )rq   zP.argsrd   zP.kwargsrH   r!   )r`   
dask.arrayarrayrK   rX   r   )r   rQ   	func_namen_strrv   ru   r`   rY   s   ``   @@@r   rL   rL   F  s     j#d)4I!"k!E
6q1ug >g &K'<QUG DI	I  4[
. 
. 
. Nr   )r   zCallable[..., Any]r-   z
bool | intr.   boolr/   r$   r0   r$   rH   None)rN   zpytest.FixtureRequestrO   zpytest.MonkeyPatchrP   r   rH   r}   )r   Callable[P, T]rQ   rU   rH   r~   )/r*   
__future__r   r?   enumr4   collections.abcr   r   r   	functoolsr   typesr   typingr	   r
   r   r   r   _lib._utils._compatr   r   _lib._utils._helpersr   r   r   __all__pytestdask.typingr   r   r   typing_extensionsr   objectr    r!   r"   rg   Enumr$   r+   r   r   rT   rL   r   r   r   <module>r      sA   #    8 8   ? ? D O O8
9<<* " cNCL-/* /  ""
 &+!+",c"
c" #c" 	c"
 c"  c" 
c"LF8"F81CF8LVF8	F8R!-0 !-H!
! !!r   