§
    ›Çj*Q  ã                  ó\  — d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
mZmZmZmZ dZ	 ddlZn# e$ r dZY nw xY w G d„ d¦  «        Zd%d„Zddœd&d„Zd'd„Zd(d)d„Z G d„ de¦  «        Z G d„ de¦  «        Z G d„ d e¦  «        Zd!„ Z	  e¦   «         Zn# e$ r dZY nw xY wd*d+d$„ZdS ),aÙ  Make a browser tree die when the process that launched it does.

SHARED BY EVERY PRODUCT THAT LAUNCHES A BROWSER, which is why it lives here -
written when there were two: each of them launched the patched Firefox and
each of them had the same problem; the automation wrapper solved it in
2026-07-26 and the profile manager did not, so on 2026-07-27 the manager still
leaked its ENTIRE tree every single time it was killed - measured at eight
surviving processes, three runs out of three, 100%. Two copies of this would
have been two chances to fix it once. The manager was deleted 2026-08-18; the
module is unchanged by that, because it was never written FOR a fixed count of
consumers.

THE BUG IT EXISTS FOR
---------------------
Twelve runs of one test left eleven browsers alive. The first diagnosis blamed
"any path where teardown does not run cleanly", lumping three situations
together; measured separately, only one of them leaks. An exception out of the
``with`` block does NOT leak - teardown runs, the driver cleans up, zero
survivors over an interleaved A/B. The leak is the KILLED-RUNNER path, where
teardown never executes at all: launch, kill, count - eight survivors, then
twelve. For the manager the equivalent is the user closing the app, the OS
ending it, or a crash: all the same case, and none of them reaches anything the
app could have written.

Nothing written inside a teardown method can reach that, so the guarantee has to
come from the operating system. On Windows a job object with KILL_ON_JOB_CLOSE
does exactly it: the launching process holds the only handle, and when it ends by
any means the kernel terminates the job's members. Processes created inside a job
join it automatically, so content processes spawned later need no tracking.

THE DESIGN
----------
Four pieces with one responsibility each, so the Win32 layer is not tangled with
the policy and neither is tangled with any launcher:

    SessionToken   identity. Mints itself, stamps an environment, and answers
                   whether a given process is ours.
    find_processes the search.
    terminate      immediate cleanup, returning a COUNT.
    LifetimeGuard  the strategy. ``JobObjectGuard`` on Windows, ``NullGuard``
                   everywhere else and whenever the mechanism is unavailable.

``os.name`` is tested in exactly one place - ``guard_for()`` - so no caller
branches on the platform. NullGuard is a Null Object in the strict sense: it
satisfies the interface and REPORTS that it guarantees nothing (``bound == 0``,
``guaranteed is False``) rather than quietly doing nothing and looking
successful.

WHY A TOKEN AND NOT A SEARCH
----------------------------
The manager used to find its browser by matching the ``-profile`` directory,
which works because it owns that path - but the wrapper cannot, because
Playwright creates the profile and never says where. Everything else available -
"a firefox that appeared after we started", "a firefox running our binary" - is
a heuristic, and it invites the one failure a reaper must never have: on a fleet,
several sessions start on the same binary within the same second, and a wrong
guess kills a healthy browser belonging to someone else.

So each session mints a random token and puts it in the browser's environment.
Children inherit it, so every process in the tree carries it, and a process
either has this session's token or it does not. Verified on Windows, where the
launcher stub made this necessary: ``psutil.Process.environ()`` reads a
same-user process's block and children show the inherited value.

The rule throughout: act on POSITIVE identification only. A process whose
environment cannot be read is left alone. Leaking a browser is a bug; killing
someone else's is an incident, and only one of the two is recoverable.
é    )ÚannotationsN)ÚABCÚabstractmethod)ÚAnyÚIterableÚListÚMappingÚOptionalÚINVPW_SESSION_TOKENc                  óh   — e Zd ZdZdZddd„Zedd
„¦   «         Zdd„Zdd„Z	d d„Z
d!d„Zd"d„Zd#d„ZdS )$ÚSessionTokenan  What makes a process ours, and the only thing that does.

    A value object: two tokens are equal when their values are, and an EMPTY
    token is falsy and matches nothing. That last part is load-bearing rather
    than defensive tidiness - without it, a session that failed before minting
    one would sweep every process whose variable happens to be empty.
    ©ÚvalueÚ r   ÚstrÚreturnÚNonec                ó   — || _         d S ©Nr   )Úselfr   s     úGC:\Projects\Historical\venv\Lib\site-packages\invisible_core/process.pyÚ__init__zSessionToken.__init__j   s   € ØˆŒ
ˆ
ˆ
ó    ú'SessionToken'c                ó<   —  | t          j        d¦  «        ¦  «        S )Né   )ÚsecretsÚ	token_hex)Úclss    r   ÚmintzSessionToken.mintm   s   € àˆs•7Ô$ RÑ(Ô(Ñ)Ô)Ð)r   Úboolc                ó*   — t          | j        ¦  «        S r   )r!   r   ©r   s    r   Ú__bool__zSessionToken.__bool__q   ó   € Ý�D”JÑÔÐr   ÚotherÚobjectc                óL   — t          |t          ¦  «        o|j        | j        k    S r   )Ú
isinstancer   r   )r   r&   s     r   Ú__eq__zSessionToken.__eq__t   s    € Ý˜%¥Ñ.Ô.ÐL°5´;À$Ä*Ò3LÐLr   Úintc                ó*   — t          | j        ¦  «        S r   )Úhashr   r#   s    r   Ú__hash__zSessionToken.__hash__w   r%   r   c                ó0   — | rd| j         d d…         › d�ndS )NzSessionToken(é   z...)zSessionToken(empty)r   r#   s    r   Ú__repr__zSessionToken.__repr__z   s)   € Ø7;ÐVÐ3˜tœz¨"¨1¨"œ~Ð3Ð3Ð3Ð3ÐAVÐVr   ÚenvúMapping[str, str]Údictc                óB   — t          |¦  «        }| j        |t          <   |S )z;A copy of ``env`` the browser will pass on to its children.)r4   r   Ú	TOKEN_VAR)r   r2   Úouts      r   ÚstampzSessionToken.stamp}   s   € å�3‰iŒiˆØœˆ�I‰Øˆ
r   Úprocr   c                ó˜   — | sdS 	 |                      ¦   «                              t          ¦  «        | j        k    S # t          $ r Y dS w xY w)z±True only when the environment can be READ and holds this token.

        Every failure answers False. A process we cannot inspect is a process
        we do not touch.
        F)ÚenvironÚgetr6   r   Ú	Exception)r   r9   s     r   ÚmatcheszSessionToken.matchesƒ   s[   € ð ð 	Ø�5ð	Ø—<’<‘>”>×%Ò%¥iÑ0Ô0°D´JÒ>Ð>øÝð 	ð 	ð 	Ø�5�5ð	øøøs   †4; »
A	ÁA	N)r   )r   r   r   r   )r   r   )r   r!   )r&   r'   r   r!   )r   r+   )r   r   )r2   r3   r   r4   ©r9   r   r   r!   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__r   Úclassmethodr    r$   r*   r.   r1   r8   r>   © r   r   r   r   _   sÞ   € € € € € ðð ð €Iðð ð ð ð ð ð*ð *ð *ñ „[ð*ð ð  ð  ð  ðMð Mð Mð Mð ð  ð  ð  ðWð Wð Wð Wðð ð ð ðð ð ð ð ð r   r   Útokenr   ú	List[Any]c                óº   ‡ ‡‡— t           �‰ sg S ˆ fd„t          j        dg¦  «        D ¦   «         }d„ |D ¦   «         Šdd„Š|                     ˆˆfd	„d
¬¦  «         |S )zÑEvery live process carrying ``token``, deepest child first.

    Deepest-first so that terminating in order reaches the content processes
    before the parent that would otherwise orphan or restart them.
    Nc                ó>   •— g | ]}‰                      |¦  «        ¯|‘ŒS rF   )r>   )Ú.0ÚprG   s     €r   ú
<listcomp>z"find_processes.<locals>.<listcomp>¦   s*   ø€ ÐIÐIÐI�1¸¿ºÀaÑ8HÔ8HÐIˆQÐIÐIÐIr   Úpidc                ó   — h | ]	}|j         ’Œ
S rF   )rN   )rK   rL   s     r   ú	<setcomp>z!find_processes.<locals>.<setcomp>§   s   € Ð!Ð!Ð!�aˆAŒEÐ!Ð!Ð!r   r9   r   r   c                óN   — 	 |                       ¦   «         S # t          $ r Y d S w xY wr   )Úppidr=   ©r9   s    r   Ú_parentzfind_processes.<locals>._parent©   s7   € ð	Ø—9’9‘;”;ÐøÝð 	ð 	ð 	Ø�4�4ð	øøøs   ‚ –
$£$c                ó   •—  ‰| ¦  «        ‰v S r   rF   )rL   rT   Úpidss    €€r   ú<lambda>z find_processes.<locals>.<lambda>±   s   ø€ ˜W˜W Q™ZœZ¨4Ð/€ r   T)ÚkeyÚreverse)r9   r   r   r   )ÚpsutilÚprocess_iterÚsort)rG   ÚfoundrT   rV   s   ` @@r   Úfind_processesr^   “   s�   øøø€ õ €~˜U€~Øˆ	ð JÐIÐIÐI�Ô+¨U¨GÑ4Ô4ÐIÑIÔI€EØ!Ð!˜5Ð!Ñ!Ô!€Dðð ð ð ð 
‡J‚JÐ/Ð/Ð/Ð/Ð/¸€JÑ>Ô>Ð>Ø€Lr   ç      @©ÚtimeoutÚprocsúIterable[Any]ra   Úfloatr+   c               óœ  — t          | ¦  «        } | sdS | D ]'}	 |                     ¦   «          Œ# t          $ r Y Œ$w xY w| }	 t          j        | |¬¦  «        \  }}n# t          $ r Y nw xY w|D ]'}	 |                     ¦   «          Œ# t          $ r Y Œ$w xY w|r(	 t          j        |d¬¦  «         n# t          $ r Y nw xY wt          | ¦  «        S )zìGraceful terminate, then kill the survivors. Returns how many were sent.

    A count rather than nothing, so a caller can tell "nothing was leaked" from
    "the reaper did not run" - two outcomes that look identical from outside.
    r   r`   ç      ð?)ÚlistÚ	terminater=   rZ   Ú
wait_procsÚkillÚlen)rb   ra   r9   ÚaliveÚ_s        r   rh   rh   µ   s=  € õ �‰KŒK€EØð ØˆqØð ð ˆð	Ø�NŠNÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøà€EðÝÔ$ U°GÐ<Ñ<Ô<‰ˆˆ5ˆ5øÝð ð ð Øˆðøøøàð ð ˆð	Ø�IŠI‰KŒKˆKˆKøÝð 	ð 	ð 	ØˆDð	øøøàð ð	ÝÔ˜e¨SÐ1Ñ1Ô1Ð1Ð1øÝð 	ð 	ð 	ØˆDð	øøøåˆu‰:Œ:ÐsB   ™.®
;º;ÁA Á
A(Á'A(Á0BÂ
BÂBÂB/ Â/
B<Â;B<r9   r   r!   c                ó¨   — | �t           €dS 	 |                      ¦   «         o!|                      ¦   «         t           j        k    S # t          $ r Y dS w xY w)a  Cross-OS liveness for one process. False when gone OR a zombie.

    A zombie answers ``is_running()`` True on POSIX, and reading that as alive
    is how a UI shows a profile RUNNING forever after its browser has died.
    Came from the manager, which is where that was learned.
    NF)rZ   Ú
is_runningÚstatusÚSTATUS_ZOMBIEr=   rS   s    r   rl   rl   Õ   s^   € ð €|•v�~ØˆuðØ�ŠÑ Ô ÐJ T§[¢[¡]¤]µfÔ6JÒ%JÐJøÝð ð ð Øˆuˆuðøøøs   �5A Á
AÁAç      $@c                óô   — t          j        ¦   «         |z   }t          j        ¦   «         |k     r<t          | ¦  «        sdS t          j        d¦  «         t          j        ¦   «         |k     °<t          | ¦  «         S )z?True once nothing carries ``token`` any more, False on timeout.Tgš™™™™™É?)ÚtimeÚ	monotonicr^   Úsleep)rG   ra   Údeadlines      r   Úwait_until_gonerx   ä   sq   € åŒ~ÑÔ 'Ñ)€HÝ
Œ.Ñ
Ô
˜XÒ
%Ð
%Ý˜eÑ$Ô$ð 	Ø�4ÝŒ
�3‰Œˆõ Œ.Ñ
Ô
˜XÒ
%Ð
%õ ˜eÑ$Ô$Ð$Ð$r   c                  óN   — e Zd ZU dZdZded<   eddœdd„¦   «         Zddœdd„ZdS )ÚLifetimeGuardz8Tie a session's process tree to this process's lifetime.Fr!   Ú
guaranteedrr   )ÚwaitrG   r   r|   rd   r   r+   c               ó   — dS )zAAdopt the tree carrying ``token``. Returns how many were adopted.NrF   )r   rG   r|   s      r   ÚbindzLifetimeGuard.bindö   s   € € € r   r_   r`   ra   c               ó>   — t          t          |¦  «        |¬¦  «        S )zÖTerminate the tree now, for teardowns that DO get to run.

        Kept even where ``bind`` guarantees the kill, because a browser that
        refuses ``close()`` should not wait for this process to exit.
        r`   )rh   r^   )r   rG   ra   s      r   ÚreapzLifetimeGuard.reapú   s   € õ �¨Ñ.Ô.¸Ð@Ñ@Ô@Ð@r   N)rG   r   r|   rd   r   r+   )rG   r   ra   rd   r   r+   )	r@   rA   rB   rC   r{   Ú__annotations__r   r~   r€   rF   r   r   rz   rz   ð   sŒ   € € € € € € ØBÐBð €JÐÐÐÑàØ9=ð Pð Pð Pð Pð Pñ „^ðPð =@ð Að Að Að Að Að Að Að Ar   rz   c                  ó2   — e Zd ZdZdZdddœdd„Zddœd„ZdS )Ú	NullGuarda  No OS-level guarantee here, and it says so instead of implying one.

    The honest answer on platforms without the mechanism and on any failure to
    obtain it. ``reap`` still works: it is inherited, because immediate cleanup
    is not the part that needs a kernel.
    Frr   rf   ©r|   ÚsettlerG   r   r|   rd   r…   r   r+   c               ó   — dS ©Nr   rF   )r   rG   r|   r…   s       r   r~   zNullGuard.bind  s   € àˆqr   N©r2   c               ó*   — ddl } |j        |fd|i|¤ŽS )zÌAn ordinary spawn, and it says so by being a NullGuard.

        Same signature as the real one so no caller branches on the platform -
        that is the whole point of the Null implementation.
        r   Nr2   )Ú
subprocessÚPopen)r   Úargvr2   Úpopen_kwargsrŠ   s        r   Ú
spawn_intozNullGuard.spawn_into  s0   € ð 	ÐÐÐàˆzÔ Ð>Ð>¨#Ð>°Ð>Ð>Ð>r   ©rG   r   r|   rd   r…   rd   r   r+   )r@   rA   rB   rC   r{   r~   rŽ   rF   r   r   rƒ   rƒ     sg   € € € € € ðð ð €Jà9=Ø ðð ð ð ð ð ð '+ð ?ð ?ð ?ð ?ð ?ð ?ð ?r   rƒ   c                  ó‚   — e Zd ZdZdZdZdZdZdZd d„Z	e
d!d„¦   «         Zdddœd"d„Zddœd„Zed#d„¦   «         Zd$d„ZdS )%ÚJobObjectGuarda'  Windows. The kernel empties the job when our last handle closes.

    The handle is held for the life of the guard and never closed explicitly:
    closing it is precisely the event that kills the job, so the process
    exiting - by any route, including being killed - is what triggers it.
    Ti    é	   é   é   Úhandler   Úkernel32r   r   c                óH   — || _         || _        t          ¦   «         | _        d S r   )Ú_handleÚ	_kernel32ÚsetÚ_adopted)r   r•   r–   s      r   r   zJobObjectGuard.__init__+  s   € ØˆŒØ!ˆŒÝ ™UœUˆŒˆˆr   ú'LifetimeGuard'c                óè  — 	 ddl }|                     dd¬¦  «        }|                     dd¦  «        }|st          ¦   «         S t	          ¦   «         }| j        |j        _        |                     || j	        | 
                    |¦  «        |                     |¦  «        ¦  «        }|s#|                     |¦  «         t          ¦   «         S  | ||¦  «        S # t          $ r t          ¦   «         cY S w xY w)z:A guard, or a NullGuard if the mechanism is not available.r   Nr–   T)Úuse_last_error)ÚctypesÚWinDLLÚCreateJobObjectWrƒ   Ú_ExtendedLimitInformationÚ_KILL_ON_JOB_CLOSEÚBasicLimitInformationÚ
LimitFlagsÚSetInformationJobObjectÚ_EXTENDED_LIMIT_INFORMATIONÚbyrefÚsizeofÚCloseHandler=   )r   rŸ   r–   r•   ÚinfoÚoks         r   ÚcreatezJobObjectGuard.create0  s  € ð	ØˆMˆMˆMà—}’} ZÀ�}ÑEÔEˆHØ×.Ò.¨t°TÑ:Ô:ˆFØð #Ý ‘{”{Ð"Ý,Ñ.Ô.ˆDØ47Ô4JˆDÔ&Ô1Ø×1Ò1Ø˜Ô7Ø—’˜TÑ"Ô" F§M¢M°$Ñ$7Ô$7ñô ˆBð ð #Ø×$Ò$ VÑ,Ô,Ð,Ý ‘{”{Ð"Ø�3�v˜xÑ(Ô(Ð(øÝð 	ð 	ð 	Ý‘;”;ÐÐÐð	øøøs   ‚A C ÁBC Ã
C ÃC1Ã0C1rr   rf   r„   rG   r   r|   rd   r…   r+   c               óä  — |sdS t          j        ¦   «         |z   }d}d}t          j        ¦   «         |k     rºd}t          |¦  «        D ]O}|j        | j        v rŒ|                      |j        ¦  «        r$| j                             |j        ¦  «         |dz  }ŒP||z  }t          j        ¦   «         }	|rd}n|r|€|	}n
|	|z
  |k    rn+t          j        d¦  «         t          j        ¦   «         |k     °º|S )a  Wait for the tree to appear, then adopt ALL of it.

        The wait exists because the driver spawns the browser asynchronously.
        The token makes it exact rather than a guess about how long that takes.

        WHY IT KEEPS SCANNING AFTER THE FIRST SUCCESS. Until 2026-07-27 this
        loop did `if bound: break`, so it adopted whatever existed at the first
        instant anything existed, and returned. Processes appearing a moment
        later escaped unless Windows added them automatically - which it only
        does for CHILDREN of a job member, and on Windows `firefox.exe` is a
        launcher stub that exits, re-parenting the real browser away from
        anything that was adopted.

        Measured by killing the runner mid-session and counting survivors:
        four runs gave sync 0/0/0/0 and async 2/0/0/2. Intermittent, ~50% on
        the async path, 0% on the sync one - not because the two APIs differ
        here, but because their timing does, which is exactly the shape that
        makes a race look like a working feature.

        So it now scans until a full pass adds nothing for `settle` seconds,
        bounded by `wait`. The cost is about a second on launch; the thing it
        buys is that "the kernel is holding this tree" stops being a coin flip.
        r   Nr”   g      Ð?)rt   ru   r^   rN   r›   Ú_adoptÚaddrv   )
r   rG   r|   r…   rw   ÚboundÚquiet_sinceÚaddedr9   Únows
             r   r~   zJobObjectGuard.bindG  s!  € ð2 ð 	Ø�1Ý”>Ñ#Ô# dÑ*ˆØˆØ'+ˆÝŒnÑÔ Ò)Ð)ØˆEÝ& uÑ-Ô-ð ð �Ø”8˜tœ}Ð,Ð,Øð —;’;˜tœxÑ(Ô(ð Ø”M×%Ò% d¤hÑ/Ô/Ð/Ø˜Q‘J�EøØ�U‰NˆEÝ”.Ñ"Ô"ˆCØð Ø"��Øð ØÐ&Ø"%�K�KØ˜;Ñ&¨&Ò0Ð0ØÝŒJ�tÑÔÐõ/ ŒnÑÔ Ò)Ð)ð0 ˆr   Nrˆ   c               óÊ  — ddl }d}|                     dd¦  «        |z  }	  |j        |f||dœ|¤Ž}n # t          $ r  |j        |fd|i|¤ŽcY S w xY w	 |                      |j        ¦  «        st          d¦  «        ‚| j                             |j        ¦  «         n,# t          $ r |  	                    |j        ¦  «         |cY S w xY w|  	                    |j        ¦  «         |S )a«  Start a process ALREADY INSIDE the job, so its whole tree is born in it.

        This exists because adopting afterwards does not work for this browser,
        and the measurement is unambiguous. On the profile manager's launch path,
        `AssignProcessToJobObject` returns **ERROR_ACCESS_DENIED (5)** for six of
        the eight processes and ERROR_NOT_ENOUGH_QUOTA (1816) for a seventh:
        Firefox puts its own content processes into its own sandbox jobs, and a
        process already in a job that does not permit nesting cannot be added to
        another one. Only the top process could be adopted, and the tree survived
        the kill 8 out of 8, three runs of three.

        Creating the process suspended, assigning it, and only then resuming it
        inverts that: everything it spawns is created inside our job from the
        start, which is the case Windows does support. `psutil` does the resume
        because `subprocess.Popen` does not expose the thread handle needed for
        `ResumeThread`.

        Falls back to an ordinary spawn if anything here fails - a launch that
        works without a guarantee beats a guarantee that refuses to launch.
        r   Né   Úcreationflags)r2   r·   r2   z,the process could not be assigned to the job)
rŠ   Úpopr‹   r=   r¯   rN   ÚOSErrorr›   r°   Ú_resume)r   rŒ   r2   r�   rŠ   Ú_CREATE_SUSPENDEDÚflagsr9   s           r   rŽ   zJobObjectGuard.spawn_into  sE  € ð* 	ÐÐÐà&ÐØ× Ò  °!Ñ4Ô4Ð7HÑHˆð	CØ#�:Ô# Dð 4¨cÀð 4ð 4Ø&2ð4ð 4ˆDˆDøåð 	Cð 	Cð 	CØ#�:Ô# DÐBÐB¨cÐB°\ÐBÐBÐBÐBÐBð	Cøøøð	Ø—;’;˜tœxÑ(Ô(ð NÝÐLÑMÔMÐMØŒM×Ò˜dœhÑ'Ô'Ð'Ð'øÝð 	ð 	ð 	ð �LŠL˜œÑ"Ô"Ð"ØˆKˆKˆKð		øøøð
 	�Š�T”XÑÔÐØˆs"   ¡3 ³AÁAÁAB Â&CÃCrN   c                ó€   — 	 dd l }|                     | ¦  «                             ¦   «          d S # t          $ r Y d S w xY wr‡   )rZ   ÚProcessÚresumer=   )rN   Ú_pss     r   rº   zJobObjectGuard._resume©  sY   € ð	Ø Ð Ð Ð à�KŠK˜ÑÔ×#Ò#Ñ%Ô%Ð%Ð%Ð%øÝð 	ð 	ð 	ØˆDˆDð	øøøs   ‚+/ ¯
=¼=r!   c                ó*  — | j                              | j        | j        z  d|¦  «        }|sdS 	 t	          | j                              | j        |¦  «        ¦  «        | j                              |¦  «         S # | j                              |¦  «         w xY w)NF)r™   ÚOpenProcessÚ_PROCESS_SET_QUOTAÚ_PROCESS_TERMINATEr!   ÚAssignProcessToJobObjectr˜   rª   )r   rN   r•   s      r   r¯   zJobObjectGuard._adopt²  sš   € Ø”×+Ò+ØÔ# dÔ&=Ñ=¸uÀcñ
ô 
ˆð ð 	Ø�5ð	/ÝØ”×7Ò7¸¼ÀfÑMÔMñô ð ŒN×&Ò& vÑ.Ô.Ð.Ð.øˆDŒN×&Ò& vÑ.Ô.Ð.Ð.øøøs   ¯,A6 Á6B)r•   r   r–   r   r   r   )r   rœ   r�   )rN   r+   r   r   )rN   r+   r   r!   )r@   rA   rB   rC   r{   r£   r§   rÃ   rÄ   r   rE   r­   r~   rŽ   Ústaticmethodrº   r¯   rF   r   r   r‘   r‘     së   € € € € € ðð ð €JàÐØ"#ÐØÐØÐð#ð #ð #ð #ð
 ðð ð ñ „[ðð, :>Ø ð6ð 6ð 6ð 6ð 6ð 6ðp '+ð (ð (ð (ð (ð (ðT ðð ð ñ „\ðð/ð /ð /ð /ð /ð /r   r‘   c                 ó¦   ‡‡‡‡— ddl Šddl mŠ  G ˆfd„d‰j        ¦  «        Š G ˆˆfd„d‰j        ¦  «        Š G ˆˆˆfd„d	‰j        ¦  «        } | S )
z@The Win32 layout, built once and only when ctypes is importable.r   N)Úwintypesc                  ó,   •— e Zd Zˆ fd„dD ¦   «         ZdS )ú*_define_job_structures.<locals>.IoCountersc                ó"   •— g | ]}|‰j         f‘ŒS rF   )Úc_ulonglong)rK   ÚnrŸ   s     €r   rM   z5_define_job_structures.<locals>.IoCounters.<listcomp>Æ  s3   ø€ ð Bð Bð B°�Q˜Ô*Ð+ð Bð Bð Br   )ÚReadOperationCountÚWriteOperationCountÚOtherOperationCountÚReadTransferCountÚWriteTransferCountÚOtherTransferCountN)r@   rA   rB   Ú_fields_)rŸ   s   €r   Ú
IoCountersrÊ   Å  sA   ø€ € € € € ðBð Bð Bð BðAðBñ Bô Bˆˆˆr   rÕ   c            
      óÊ   •— e Zd Zd” j        fd” j        fd”j        fd” j        fd” j        fd”j        fd”                      ” j        ¦  «        fd”j        fd	”j        fg	Zd
S )ú*_define_job_structures.<locals>.BasicLimitÚPerProcessUserTimeLimitÚPerJobUserTimeLimitr¥   ÚMinimumWorkingSetSizeÚMaximumWorkingSetSizeÚActiveProcessLimitÚAffinityÚPriorityClassÚSchedulingClassN)	r@   rA   rB   Ú
c_longlongÚDWORDÚc_size_tÚPOINTERÚc_ulongrÔ   )rŸ   rÈ   s   €€r   Ú
BasicLimitr×   Ë  s~   ø€ € € € € à&¨Ô(9Ð:Ø" FÔ$5Ð6Ø˜8œ>Ð*Ø$ f¤oÐ6Ø$ f¤oÐ6Ø! 8¤>Ð2Ø˜Ÿš¨¬Ñ7Ô7Ð8Ø˜hœnÐ-Ø ¤Ð/ð

ˆˆˆr   rå   c                  ó`   •— e Zd Zd” fd”fd”j        fd”j        fd”j        fd”j        fgZdS )ú-_define_job_structures.<locals>.ExtendedLimitr¤   ÚIoInfoÚProcessMemoryLimitÚJobMemoryLimitÚPeakProcessMemoryUsedÚPeakJobMemoryUsedN)r@   rA   rB   râ   rÔ   )rå   rÕ   rŸ   s   €€€r   ÚExtendedLimitrç   Ø  sN   ø€ € € € € à$ jÐ1Ø�zÐ"Ø! 6¤?Ð3Ø˜vœÐ/Ø$ f¤oÐ6Ø  &¤/Ð2ð
ˆˆˆr   rí   )rŸ   rÈ   Ú	Structure)rí   rå   rÕ   rŸ   rÈ   s    @@@@r   Ú_define_job_structuresrï   À  sç   øøøø€ à€M€M€MØÐÐÐÐÐðBð Bð Bð Bð Bð Bð B�VÔ%ñ Bô Bð Bð
ð 
ð 
ð 
ð 
ð 
ð 
ð 
�VÔ%ñ 
ô 
ð 
ð
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
˜Ô(ñ 
ô 
ð 
ð Ðr   Úplatformú
str | Nonec                ó˜   — | €t           j        n| }|dk    st          �t          €t	          ¦   «         S t
                               ¦   «         S )zÙThe only place the platform is tested.

    Every caller gets a LifetimeGuard and never asks which one it is: the Null
    implementation satisfies the same interface, so no launcher code branches
    on Windows.
    NÚnt)ÚosÚnamerZ   r¢   rƒ   r‘   r­   )rð   rõ   s     r   Ú	guard_forrö   ë  sC   € ð Ð&�2Œ7ˆ7¨H€DØˆt‚|€|•v�~Õ)BÐ)JÝ‰{Œ{ÐÝ× Ò Ñ"Ô"Ð"r   )rG   r   r   rH   )rb   rc   ra   rd   r   r+   r?   )rr   )rG   r   ra   rd   r   r!   r   )rð   rñ   r   rz   )rC   Ú
__future__r   rô   r   rt   Úabcr   r   Útypingr   r   r   r	   r
   r6   rZ   r=   r   r^   rh   rl   rx   rz   rƒ   r‘   rï   r¢   rö   rF   r   r   ú<module>rú      sM  ððCð CðH #Ð "Ð "Ð "Ð "Ð "à 	€	€	€	Ø €€€Ø €€€Ø #Ð #Ð #Ð #Ð #Ð #Ð #Ð #Ø 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9Ð 9ð "€	ðà€M€M€M€MøØð ð ð Ø€F€F€Fðøøøð/ð /ð /ð /ð /ñ /ô /ð /ðhð ð ð ðD 9<ð ð ð ð ð ð ð@ð ð ð ð%ð %ð %ð %ð %ðAð Að Að Að A�Cñ Aô Að Að&?ð ?ð ?ð ?ð ?�ñ ?ô ?ð ?ð2a/ð a/ð a/ð a/ð a/�]ñ a/ô a/ð a/ðH"ð "ð "ðJ%Ø 6Ð 6Ñ 8Ô 8ÐÐøØð %ð %ð %Ø $ÐÐÐð%øøøð
#ð 
#ð 
#ð 
#ð 
#ð 
#ð 
#s   ®3 ³=¼=Â
B ÂB$Â#B$