
    Vi*7                         S r SSKJrJrJrJr  SSKJr   SSKJrJ	r	  SSKJrJrJrJrJr  SSKJr  SSKJrJrJrJrJrJrJrJrJrJrJ r J!r!  SS	K"J#r#   " S
 S\$5      r% " S S\$5      r&g! \
 a    \r\r	 NYf = f)a  glom's helpers for streaming use cases.

Specifier types which yield their results incrementally so that they
can be applied to targets which are themselves streaming (e.g. chunks
of rows from a database, lines from a file) without excessive memory
usage.

glom's streaming functionality revolves around a single :class:`Iter`
Specifier type, which has methods to transform the target stream.
    )islice	dropwhile	takewhilechain)partial)imapifilter)
split_iterchunked_iterwindowed_iterunique_iterfirst)FunctionBuilder   )glomTSTOPSKIP_MISSINGPathTargetRegistryCallSpecSbbreprformat_invocation)Checkc                       \ rS rSrSr\4S jrS rS rS r	S r
S r\4S	 jr\4S
 jrS rSS jrS r\4S jrS rS r\4S jr\4S jrS r\S4S jrSrg)Iter   a  ``Iter()`` is glom's counterpart to Python's built-in :func:`iter()`
function. Given an iterable target, ``Iter()`` yields the result
of applying the passed spec to each element of the target, similar
to the built-in ``[]`` spec, but streaming.

The following turns a list of strings into integers using Iter(),
before deduplicating and converting it to a tuple:

>>> glom(['1', '2', '1', '3'], (Iter(int), set, tuple))
(1, 2, 3)

``Iter()`` also has many useful methods which can be chained to
compose a stream processing pipeline. The above can also be
written as:

>>> glom(['1', '2', '1', '3'], (Iter().map(int).unique(), tuple))
(1, 2, 3)

``Iter()`` also respects glom's :data:`~glom.SKIP` and
:data:`~glom.STOP` singletons for filtering and breaking
iteration.

Args:

   subspec: A subspec to be applied on each element from the iterable.
   sentinel: Keyword-only argument, which, when found in the
     iterable stream, causes the iteration to stop. Same as with the
     built-in :func:`iter`.

c                     Xl         UR                  S/ 5      U l        UR                  S[        5      U l        U(       a  [        S[        U5      -  5      eg )N_iter_stacksentinelz unexpected keyword arguments: %r)subspecpopr"   r   r#   	TypeErrorsorted)selfr$   kwargss      a/home/james-whalen/.local/share/pipx/venvs/semgrep/lib/python3.13/site-packages/glom/streaming.py__init__Iter.__init__:   sG    !::mR8

:t4>OPP    c                    SnU R                   [        :w  a  U R                   4n[        U R                  R                  U[
        S9nU/n[        U R                  5       H  u  pEn[        X5      n[        R                  " U5      nUR                  SS  Ul        UR                  5       n	/ n
[        U5      S:  a  U	(       a  S[        X5      pUR                  S[        XEU
[
        S9-   5        M     SR!                  U5      $ )N )reprr   . )r$   r   r   	__class____name__r   reversedr"   getattrr   	from_funcargsget_arg_nameslenzipappendjoin)r(   	base_argsbasechunksfnamer8   _methfb	arg_namesr)   s              r*   __repr__Iter.__repr__C   s    	<<1I !8!8)&Q&t'7'78NE4'D **40BggabkBG((*IF4y1}!3y#7fMM# 1%vF SST 9 wwvr-   c                     U R                  X5      n[        U R                  5       H  u    pEU" X25      nM     [        U5      $ N)_iterater5   r"   iter)r(   targetscopeiteratorrB   callbacks         r*   glomitIter.glomitU   s?    ==/&t'7'78NAq0H 9 H~r-   c           
   #     #    U[            R                  SX[           S9n U" U5      nU[           n[        U5       Hh  u  pxXg/-   U[        '   U R                  [        L a  UOU[           " XR                  U5      n	U	[        L a  MK  XR                  L d	  U	[        L a    g U	v   Mj     g ! [         a>  n[	        SUR
                  R                  < S[        U[           6 < SU< S35      eS nAff = f7f)Niterate)pathz&failed to iterate on instance of type z at z (got ))r   get_handlerr   	Exceptionr&   r3   r4   	enumerater$   r   r   r   r#   r   )
r(   rL   rM   rS   rN   e	base_pathitylds
             r*   rJ   Iter._iterate]   s     '33IvRVK3X	RvH
 $K	h'DA#c/E$K)1uT{1llE/RCd{% I ( 	!  	R%//88$d:LaQ R R	Rs(   C6B+ BC6+
C359C..C33C6c                 V    [        U 5      " U R                  XU4/U R                  -   S9$ )N)r$   r"   )typer$   r"   )r(   opnamer8   rO   s       r*   _add_opIter._add_ops   s+    Dz$,,fH=U<VY]YiYi<ijjr-   c                 4   ^ U R                  ST4U4S j5      $ )a  Return a new :class:`Iter()` spec which will apply the provided
*subspec* to each element of the iterable.

>>> glom(range(5), Iter().map(lambda x: x * 2).all())
[0, 2, 4, 6, 8]

Because a spec can be a callable, :meth:`Iter.map()` does
everything the built-in :func:`map` does, but with the full
power of glom specs.

>>> glom(['a', 'B', 'C'], Iter().map(T.islower()).all())
[True, False, False]
mapc                 (   >^ [        UU4S jU 5      $ )Nc                 &   > T[            " U TT5      $ rI   r   )r\   rM   r$   s    r*   <lambda>,Iter.map.<locals>.<lambda>.<locals>.<lambda>   s    %+a%8r-   )r   )iterablerM   r$   s    `r*   ri   Iter.map.<locals>.<lambda>   s    D8(%Dr-   rb   )r(   r$   s    `r*   re   Iter.mapv   s&      ||JDE 	Er-   c                 ~   ^ [        U[        5      (       a  UO[        U[        S9mU R                  SU4U4S j5      $ )aH  Return a new :class:`Iter()` spec which will include only elements matching the
given *key*.

>>> glom(range(6), Iter().filter(lambda x: x % 2).all())
[1, 3, 5]

Because a spec can be a callable, :meth:`Iter.filter()` does
everything the built-in :func:`filter` does, but with the full
power of glom specs. For even more power, combine,
:meth:`Iter.filter()` with :class:`Check()`.

>>> # PROTIP: Python's ints know how many binary digits they require, using the bit_length method
>>> glom(range(9), Iter().filter(Check(T.bit_length(), one_of=(2, 4), default=SKIP)).all())
[2, 3, 8]

)defaultfilterc                 (   >^ [        UU4S jU 5      $ )Nc                 2   > T[            " U TT5      [        L$ rI   )r   r   )r\   
check_specrM   s    r*   ri   /Iter.filter.<locals>.<lambda>.<locals>.<lambda>   s    %+aU;4Gr-   )r	   )rk   rM   rt   s    `r*   ri   Iter.filter.<locals>.<lambda>   s    GG%Sr-   )
isinstancer   r   rb   )r(   keyrt   s     @r*   rq   Iter.filter   s@    ( 'sE22Sc48P
||FST 	Tr-   c                 f   ^ SU0mU4nU[         La
  UTS'   X24-  nU R                  SUU4S j5      $ )a  Return a new :class:`Iter()` spec which groups elements in the iterable
into lists of length *size*.

If the optional *fill* argument is provided, iterables not
evenly divisible by *size* will be padded out by the *fill*
constant. Otherwise, the final chunk will be shorter than *size*.

>>> list(glom(range(10), Iter().chunked(3)))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
>>> list(glom(range(10), Iter().chunked(3, fill=None)))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]]
sizefillchunkedc                    > [        U 40 TD6$ rI   )r   )itrM   kws     r*   ri   Iter.chunked.<locals>.<lambda>   s    |B/E"/Er-   )r   rb   )r(   r{   r|   r8   r   s       @r*   r}   Iter.chunked   sJ     d^uxBvJGOD||tEG 	Gr-   c                 4   ^ U R                  ST4U4S j5      $ )a  Return a new :class:`Iter()` spec which will yield a sliding window of
adjacent elements in the iterable. Each tuple yielded will be
of length *size*.

Useful for getting adjacent pairs and triples.

>>> list(glom(range(4), Iter().windowed(2)))
[(0, 1), (1, 2), (2, 3)]
windowedc                    > [        U T5      $ rI   )r   )r   rM   r{   s     r*   ri   Iter.windowed.<locals>.<lambda>   s    =T3Jr-   rm   )r(   r{   s    `r*   r   Iter.windowed   s"     ||!JL 	Lr-   Nc                 :   ^^ U R                  STT4UU4S j5      $ )a  Return a new :class:`Iter()` spec which will lazily split an iterable based
on a separator (or list of separators), *sep*. Like
:meth:`str.split()`, but for all iterables.

``split_iter()`` yields lists of non-separator values. A separator will
never appear in the output.

>>> target = [1, 2, None, None, 3, None, 4, None]
>>> list(glom(target, Iter().split()))
[[1, 2], [3], [4]]

Note that ``split_iter`` is based on :func:`str.split`, so if
*sep* is ``None``, ``split()`` **groups** separators. If empty lists
are desired between two contiguous ``None`` values, simply use
``sep=[None]``:

>>> list(glom(target, Iter().split(sep=[None])))
[[1, 2], [], [3], [4], []]

A max number of splits may also be set:

>>> list(glom(target, Iter().split(maxsplit=2)))
[[1, 2], [3], [4, None]]

splitc                    > [        U TTS9$ )N)sepmaxsplit)r
   )r   rM   r   r   s     r*   ri   Iter.split.<locals>.<lambda>   s    jxHr-   rm   )r(   r   r   s    ``r*   r   
Iter.split   s%    4 ||(OHJ 	Jr-   c                 *    U R                  SSS 5      $ )zReturns a new :class:`Iter()` instance which combines iterables into a
single iterable.

>>> target = [[1, 2], [3, 4], [5]]
>>> list(glom(target, Iter().flatten()))
[1, 2, 3, 4, 5]
flattenr/   c                 .    [         R                  " U 5      $ rI   )r   from_iterable)r   rM   s     r*   ri   Iter.flatten.<locals>.<lambda>   s    e11"5r-   rm   r(   s    r*   r   Iter.flatten   s     ||57 	7r-   c                 4   ^ U R                  ST4U4S j5      $ )a  Return a new :class:`Iter()` spec which lazily filters out duplicate
values, i.e., only the first appearance of a value in a stream will
be yielded.

>>> target = list('gloMolIcious')
>>> out = list(glom(target, Iter().unique(T.lower())))
>>> print(''.join(out))
gloMIcus
uniquec                 $   >^ [        U UU4S jS9$ )Nc                 &   > T[            " U TT5      $ rI   rh   r\   rx   rM   s    r*   ri   /Iter.unique.<locals>.<lambda>.<locals>.<lambda>  s    E$K3PU<Vr-   )rx   )r   r   rM   rx   s    `r*   ri   Iter.unique.<locals>.<lambda>  s    k"2VWr-   rm   r(   rx   s    `r*   r   Iter.unique   s#     ||FWY 	Yr-   c                    ^  [        / /TQ76   U R                  STU4S j5      $ ! [         a    [        ST< 35      ef = f)a  Returns a new :class:`Iter()` spec which trims iterables in the
same manner as :func:`itertools.islice`.

>>> target = [0, 1, 2, 3, 4, 5]
>>> glom(target, Iter().slice(3).all())
[0, 1, 2]
>>> glom(target, Iter().slice(2, 4).all())
[2, 3]

This method accepts only positional arguments.
zinvalid slice arguments: slicec                    > [        U /TQ76 $ rI   r   )r   rM   r8   s     r*   ri   Iter.slice.<locals>.<lambda>  s    VB=N=Nr-   )r   r&   rb   )r(   r8   s    `r*   r   
Iter.slice  sM    	E2 ||GT+NOO  	ETCDD	Es	   & A c                 4   ^ U R                  ST4U4S j5      $ )zvA convenient alias for :meth:`~Iter.slice`, which takes a single
argument, *count*, the max number of items to yield.
limitc                    > [        U T5      $ rI   r   )r   rM   counts     r*   ri   Iter.limit.<locals>.<lambda>  s    EARr-   rm   )r(   r   s    `r*   r   
Iter.limit  s     ||GeX/RSSr-   c                 4   ^ U R                  ST4U4S j5      $ )zReturns a new :class:`Iter()` spec which stops the stream once
*key* becomes falsy.

>>> glom([3, 2, 0, 1], Iter().takewhile().all())
[3, 2]

:func:`itertools.takewhile` for more details.
r   c                 (   >^ [        UU4S jU 5      $ )Nc                 &   > T[            " U TT5      $ rI   rh   r   s    r*   ri   2Iter.takewhile.<locals>.<lambda>.<locals>.<lambda>,      %+ae4r-   )r   r   s    `r*   ri    Iter.takewhile.<locals>.<lambda>+      i4b:r-   rm   r   s    `r*   r   Iter.takewhile  s#     ||F:; 	;r-   c                 4   ^ U R                  ST4U4S j5      $ )a  Returns a new :class:`Iter()` spec which drops stream items until
*key* becomes falsy.

>>> glom([0, 0, 3, 2, 0], Iter().dropwhile(lambda t: t < 1).all())
[3, 2, 0]

Note that while similar to :meth:`Iter.filter()`, the filter
only applies to the beginning of the stream. In a way,
:meth:`Iter.dropwhile` can be thought of as
:meth:`~str.lstrip()` for streams. See
:func:`itertools.dropwhile` for more details.

r   c                 (   >^ [        UU4S jU 5      $ )Nc                 &   > T[            " U TT5      $ rI   rh   r   s    r*   ri   2Iter.dropwhile.<locals>.<lambda>.<locals>.<lambda>A  r   r-   )r   r   s    `r*   ri    Iter.dropwhile.<locals>.<lambda>@  r   r-   rm   r   s    `r*   r   Iter.dropwhile.  s#     ||F:; 	;r-   c                     U [         4$ )a  A convenience method which returns a new spec which turns an
iterable into a list.

>>> glom(range(5), Iter(lambda t: t * 2).all())
[0, 2, 4, 6, 8]

Note that this spec will always consume the whole iterable, and as
such, the spec returned is *not* an :class:`Iter()` instance.
)listr   s    r*   allIter.allE  s     d|r-   c                     U [        XS94$ )a  A convenience method for lazily yielding a single truthy item from
an iterable.

>>> target = [False, 1, 2, 3]
>>> glom(target, Iter().first())
1

This method takes a condition, *key*, which can also be a
glomspec, as well as a *default*, in case nothing matches the
condition.

As this spec yields at most one item, and not an iterable, the
spec returned from this method is not an :class:`Iter()` instance.
)rx   rp   )First)r(   rx   rp   s      r*   r   
Iter.firstQ  s     e566r-   )r"   r#   r$   )NN)r4   
__module____qualname____firstlineno____doc__r   r+   rF   rP   rJ   rb   re   rq   r   r}   r   r   r   r   r   r   r   r   r   r   __static_attributes__r/   r-   r*   r   r      s    <  ! $,kE,  T6 "* G*LJ>7  Y P(T  ;  ;.
 4 7r-   r   c                   6    \ rS rSrSrSr\S4S jrS rS r	Sr
g)	r   ic  zGet the first element of an iterable which matches *key*, if there
is one, otherwise return *default* (``None`` if unset).

>>> is_odd = lambda x: x % 2
>>> glom([0, 1, 2, 3], First(is_odd))
1
>>> glom([0, 2, 4], First(is_odd, default=False))
False
)_spec_default_firstNc                     Xl         X l        [        [        [        [        U R                   5      R
                  4S[        0S95      n[        [        [        4X#S.S9U l	        g )NrM   )r8   r)   )rp   rx   )
r   r   r   r   r   r   r   r   r   r   )r(   rx   rp   	spec_gloms       r*   r+   First.__init__s  sK    
gT$**-=-B-B,DgWX\Z[	5t4Z[r-   c                 8    U R                   R                  X5      $ rI   )r   rP   )r(   rL   rM   s      r*   rP   First.glomitz  s    {{!!&00r-   c                     U R                   R                  nU R                  c  U< S[        U R                  5      < S3$ U< S[        U R                  5      < S[        U R                  5      < S3$ )N(rU   z
, default=)r3   r4   r   r   r   )r(   cns     r*   rF   First.__repr__}  sN    ^^$$== !6$**#566')6$**+=vdmm?TUUr-   )r   r   r   )r4   r   r   r   r   	__slots__r   r+   rP   rF   r   r/   r-   r*   r   r   c  s$     0Id \1Vr-   r   N)'r   	itertoolsr   r   r   r   	functoolsr   r   r	   ImportErrorre   rq   boltons.iterutilsr
   r   r   r   r   boltons.funcutilsr   corer   r   r   r   r   r   r   r   r   r   r   r   matchingr   objectr   r   r/   r-   r*   <module>r      sq   	 : 9 ' Z Y - o o o o E76 E7P
VF Vg
  DGs   A+ +
A87A8