
    <i.a                     .   S r SSKrSSKrSSKrSSKrSSK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  \" SS5      rSS jrS rS rS	 rS
 rS rS rS rS rS rSS jr " S S\	5      r " S S\5      r  " S S\ 5      r! " S S\5      r" " S S\5      r#g)a  
Parsing for router status entries, the information for individual routers
within a network status document. This information is provided from a few
sources...

* control port via 'GETINFO ns/\*' and 'GETINFO md/\*' queries
* router entries in a network status document, like the cached-consensus

**Module Overview:**

::

  RouterStatusEntry - Common parent for router status entries
    |- RouterStatusEntryV2 - Entry for a network status v2 document
    |   +- RouterStatusEntryBridgeV2 - Entry for a bridge flavored v2 document
    |
    |- RouterStatusEntryV3 - Entry for a network status v3 document
    +- RouterStatusEntryMicroV3 - Entry for a microdescriptor flavored v3 document
    N)KEYWORD_LINE
Descriptor_descriptor_content_value_values_descriptor_components_parse_protocol_line_read_until_keywords_random_nickname_random_ipv4_address_random_datepr	protocolsc              #   T  #    U(       a  U R                  U5        OU R                  5       nU(       a~  Sn[        R                  " [        R
                  R                  R                  U R                  5       5      5      n	U	(       a  U	R                  5       S   nU R                  U5        X;   a  gUb  U R                  5       U:  a_  [        U4U-   U SUSS9u  p[        R                  SU
5      nU(       a  U" X/UQ76 v   X;   a  gOgUc  MH  U R                  5       U:  a  M^  gg7f)a_  
Reads a range of the document_file containing some number of entry_class
instances. We deliminate the entry_class entries by the keyword on their
first line (entry_keyword). When finished the document is left at the
end_position.

Either an end_position or section_end_keywords must be provided.

:param file document_file: file with network status document content
:param bool validate: checks the validity of the document's contents if
  **True**, skips these checks otherwise
:param class entry_class: class to construct instance for
:param str entry_keyword: first keyword for the entry instances
:param int start_position: start of the section, default is the current position
:param int end_position: end of the section
:param tuple section_end_keywords: keyword(s) that deliminate the end of the
  section if no end_position was provided
:param tuple extra_args: extra arguments for the entry_class (after the
  content and validate flag)

:returns: iterator over entry_class instances

:raises:
  * **ValueError** if the contents is malformed and validate is **True**
  * **IOError** if the file can't be read
Nr   T)ignore_firstend_positioninclude_ending_keyword    )seektellr   matchstemutil	str_tools_to_unicodereadlinegroupsr
   bytesjoin)document_filevalidateentry_classentry_keywordstart_positionr   section_end_keywords
extra_argsfirst_keyword
line_match
desc_linesending_keyworddesc_contents                ]/home/james-whalen/.local/lib/python3.13/site-packages/stem/descriptor/router_status_entry.py_parse_filer-   0   s    8 ~&"'')N M##DII$7$7$C$CMDZDZD\$]^J '')!,m~&, 2 2 4| C!5--!#"J ::c:.L<<< 
	/ 
0 % 	 2 2 4| Cs   DD(D(&D(c                 L   [        SU5      n[        U [        5      (       + nUR                  S5      nU(       d  UR	                  SS 5        [        U5      S:  a/  U(       a  SOSn[        U R                  5       < SU< SU< 35      e[        R                  R                  R                  US	   5      (       d#  [        U R                  5       < S
US	   < 35      e[        R                  R                  R                  US   5      (       d#  [        U R                  5       < SUS   < 35      e[        R                  R                  R                  US   5      (       d#  [        U R                  5       < SUS   < 35      e[        R                  R                  R                  US   SS9(       d#  [        U R                  5       < SUS   < 35      eUS	   U l        [!        US   5      U l        U(       a  [!        US   5      U l        US   U l        [)        US   5      U l        US   S:X  a  S O[)        US   5      U l         US   < SUS   < 3n[        R                  R.                  R1                  U5      U l        g ! [         a    [        SU-  5      ef = f)Nr       eightsevenz 'r' line must have z values: r r   z nickname isn't valid:    z% address isn't a valid IPv4 address:    z ORPort is invalid:    T)
allow_zeroz DirPort is invalid:    0      z+Publication time time wasn't parsable: r %s)r   
isinstanceRouterStatusEntryMicroV3splitinsertlen
ValueError_namer   r   	tor_toolsis_valid_nickname
connectionis_valid_ipv4_addressis_valid_portnickname_base64_to_hexfingerprintdigestaddressintor_portdir_portr   _parse_timestamp	published)
descriptorentriesvalueinclude_digestr_compexpected_field_countrR   s          r,   _parse_r_linerY   s   s*    g
%!*.FGG.;;s& 

MM!T[1_&47'

@P@P@RThjop
qq				.	.vay	9	9
j6F6F6H&QR)T
UU9955fQi@@
JDTDTDVX^_`Xab
cc99--fQi88
:3C3C3EvayQ
RR99--fQid-K
J4D4D4Fq	R
SSq	*)&)4*&vay1Jay*6!9~* &q	S 0c&)n*L!!9fQi0I99..??	JJ	 L
BUJ
KKLs   =J
 
J#c           
         / n[        SU5       GHl  nSU;  a   [        U R                  5       < SU< 35      eUR                  SS5      u  pE[        R
                  R                  R                  U5      (       dM  [        R
                  R                  R                  USS9(       d   [        U R                  5       < SU< 35      e[        R
                  R                  R                  U5      (       ad  UR                  UR                  S5      R                  S	5      [        U5      [        R
                  R                  R                  USS945        GMK  [        U R                  5       < S
U< SU< 35      e   X l        g )Na:z5 'a' line must be of the form '[address]:[ports]': a r9   T)allow_bracketsz- 'a' line must start with an IPv6 address: a []z 'a' line had an invalid port (z): a )r   rB   rC   rsplitr   r   rF   rG   is_valid_ipv6_addressrH   appendlstriprstriprN   or_addresses)rS   rT   re   rU   rM   ports         r,   _parse_a_linerg      sA    ,sG$e
%V`VfVfVhjopqqLLa(MG9955g>>tyyG[G[GqGqry  MQGq  HRjN^N^N`bghiiyy))$//7>>#.55c:CItyyG[G[GqGqry  MQGq  HR  S  TzGWGWGY[_afghh % )r   c                 "   [        SU5      nUS:X  a  / OUR                  S5      nX0l        U H^  nUR                  U5      S:  a   [	        U R                  5       < SU< 35      eUS:X  d  M@  [	        U R                  5       < SU< 35      e   g )Ns r0   r9   z had duplicate flags: s z) had extra whitespace on its 's' line: s )r   r?   flagscountrB   rC   )rS   rT   rU   rk   flags        r,   _parse_s_linern      s     g
%"S!1%d{{419I9I9KUSTT	*JZJZJ\^cdee	 r   c                    [        SU5      nX l        UR                  S5      (       a)   [        R                  R                  USS  5      U l        g g ! [         a)  n[        U R                  5       < SU< SU< 35      eS nAff = f)NvzTor r<   z has a malformed tor version (z): v )r   version_line
startswithr   version_get_versionrB   rC   rS   rT   rU   excs       r,   _parse_v_linerw      s     g
%!
fg<<44U12Y?j   gjFVFVFXZ]_deffgs   'A 
B$BBc                 ^   [        SU5      nUR                  S5      n[        U5      S:  a   [        U R	                  5       < SU< 35      eUS   R                  S5      (       d   [        U R	                  5       < SU< 35      eS nS nSn/ nU GH  nS	U;   a  UR                  S	S5      u  pOUS pU	S
:X  aI  U
(       a  U
R                  5       (       d   [        U R	                  5       < SU< 35      e[        U
5      nMq  U	S:X  aI  U
(       a  U
R                  5       (       d   [        U R	                  5       < SU< 35      e[        U
5      nM  U	S:X  a*  U
S:w  a   [        U R	                  5       < SU< 35      eSnM  UR                  U5        GM     X@l	        XPl
        X`l        Xpl        g )Nwr0   r9   z 'w' line is blank: w r   z
Bandwidth=z6 'w' line needs to start with a 'Bandwidth=' entry: w F=	Bandwidthz5 'Bandwidth=' entry needs to have a numeric value: w Measuredz4 'Measured=' entry needs to have a numeric value: w 
Unmeasured1z4 'Unmeasured=' should only have the value of '1': w T)r   r?   rA   rB   rC   rr   isdigitrN   rb   	bandwidthmeasuredis_unmeasuredunrecognized_bandwidth_entries)rS   rT   rU   w_compr   r   r   r   w_entryw_keyw_values              r,   _parse_w_liner      s    g
%;;s&[1_
Z5E5E5GO
PP!9--
U_UeUeUgino
pp)(-#% g
g~}}S!,neWW'//++XbXhXhXjlqrssg,i	*	'//++WaWgWgWikpqrrWh	,		CWaWgWgWikpqrrm$++G4- 0 # *.L+r   c                     [        SU5      n [        R                  R                  U5      U l        g ! [         a)  n[	        U R                  5       < SU< SU< 35      eS nAff = f)Npz exit policy is malformed (z): p )r   r   exit_policyMicroExitPolicyrB   rC   ru   s       r,   _parse_p_liner     s^     g
%b!--==eDJ	 b
AQAQASUXZ_`
aabs   $3 
A&$A!!A&c                 F   [        SU5      nU(       a  U R                  (       a;  U R                  R                  (       d   [        U R	                  5       < SU< 35      eUR                  5       n[        U5      S:  a  US   U l        US   U l        g [        SU-  5      eg )Nidz+ 'id' line should only appear in votes: id r1   r   r9   z='id' lines should contain both the key type and digest: id %s)	r   documentis_voterB   rC   r?   rA   identifier_type
identifier)rS   rT   rU   
value_comps       r,   _parse_id_liner     s     w
%
:#6#6#>#>JL\L\L^`efggJ
:!#-a=j (mjVY^^__ r   c                    / n[        SU5       GH5  nUR                  S5      nU R                  (       a  U R                  R                  (       d9  U R                  (       a  SOSn[	        U R                  5       < SU< SU< 35      e[        U5      S:  a   [	        U R                  5       < SU< 35      e US	   R                  S
5       Vs/ s H  n[        U5      PM     nn0 nUSS   HA  nSU;  a   [	        U R                  5       < SU< 35      eUR                  SS5      u  pXU	'   MC     UR                  Xx45        GM8     X l	        g s  snf ! [         a!    [	        U R                  5       < SU< 35      ef = f)Nmr0   votez<undefined document>z5 'm' line should only appear in votes (appeared in a z): m r9   z5 'm' line needs to start with a series of methods: m r   ,zK microdescriptor methods should be a series of comma separated integers: m rz   zL can only have a series of 'algorithm=digest' mappings after the methods: m )
r   r?   r   r   rB   rC   rA   rN   rb   microdescriptor_hashes)rS   rT   
all_hashesrU   m_compvote_statusentrymethodshashes	hash_namerL   s              r,   _parse_m_liner   5  s    *sG$e[[FJ$7$7$?$?(11F7Mk]g]m]m]oq|  D  E  F  F	VqV`VfVfVhjopqqH)/)=>)=U)=g> F	E	oyoo  pB  DI  J  K  	K++c1-i Y  w'(/ %2 '1# ? Hlvl|l|l~  AF  G  H  HHs   9EE$EE+F c                 X    [        SU5      U l        [        [        SU5      SS9U l        g )Nr   F)check_if_fingerprint)r   microdescriptor_digestrJ   rL   )rS   rT   s     r,   _parse_microdescriptor_m_liner   W  s,     '-S'&:*# %VC%9RWX*r   c                 r    [         R                  R                  R                  [         R                  R                  R	                  U 5      5      n[        R                  " U5      R                  5       n[         R                  R                  5       (       a)  [         R                  R                  R                  U5      nU(       aB  [         R                  R                  R                  U5      (       d  [        SU < SU< S35      eU$ ! [
        [        R                  4 a    [        SU -  5      ef = f)a  
Decodes a base64 value to hex. For example...

::

  >>> _base64_to_hex('p1aag7VwarGxqctS7/fS0y5FU+s')
  'A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB'

:param str identity: encoded fingerprint from the consensus
:param bool check_if_fingerprint: asserts that the result is a fingerprint if **True**

:returns: **str** with the uppercase hex encoding of the relay's fingerprint

:raises: **ValueError** if the result isn't a valid fingerprint
z%Unable to decode identity string '%s'z	Decoded 'z	' to be 'z"', which isn't a valid fingerprint)r   r   r   _decode_b64	_to_bytes	TypeErrorbinasciiErrorrB   hexlifyupperprereqis_python_3r   rD   is_valid_fingerprint)identityr   identity_decodedrK   s       r,   rJ   rJ   b  s    "Iyy**66tyy7J7J7T7TU]7^_   !1288:+	[[))%%11+>K9933K@@U]_jkll	 X^^	$ I
<xG
HHIs   AD )D6c            
          ^  \ rS rSrSrS\4S\4S\4S\4S\4S\4S\4S\4S\4S.	r\\\S.r	\
S 5       rSU 4S jjrSS jrS	 rS
 rSrU =r$ )RouterStatusEntryi  a!  
Information about an individual router stored within a network status
document. This is the common parent for concrete status entry types.

:var stem.descriptor.networkstatus.NetworkStatusDocument document: **\*** document that this descriptor came from

:var str nickname: **\*** router's nickname
:var str fingerprint: **\*** router's fingerprint
:var datetime published: **\*** router's publication
:var str address: **\*** router's IP address
:var int or_port: **\*** router's ORPort
:var int dir_port: **\*** router's DirPort

:var list flags: **\*** list of :data:`~stem.Flag` associated with the relay

:var stem.version.Version version: parsed version of tor, this is **None** if
  the relay's using a new versioning scheme
:var str version_line: versioning information reported by the relay
N)	rI   rK   rR   rM   rO   rP   rk   rq   rs   r/   ri   rp   c           	         U [         :X  a  [        S5      eSU;   a  [        S5      eUR                  SS5      nUR                  SS5      n[	        [        [        R                  " [        R                  R                  R                  U5      5      X@40 UD65      nU(       a  U$ [        U5      S:X  a  US   $ [        S	[        U5      -  5      e)
Nz`Please use the from_str() method from RouterStatusEntry subclasses, not RouterStatusEntry itselfdescriptor_typezRouter status entries don't have their own @type annotation. As such providing a 'descriptor_type' argument with RouterStatusEntry.from_str() does not work. Please drop the 'descriptor_type' argument when using this these subclasses' from_str() method.multipleFr!   r9   r   zDescriptor.from_str() expected a single descriptor, but had %i instead. Please include 'multiple = True' if you want a list of results instead.)r   NotImplementedErrorrB   poplistr-   ioBytesIOr   r   r   r   rA   )clscontentkwargsis_multipler!   resultss         r,   from_strRouterStatusEntry.from_str  s    
   !C  D  D	f	$  V  W  W**Z/Kzz*e,H;rzz$))*=*=*G*G*PQS[kdjklGn	W	QZ  i  lo  pw  lx  x  y  yr   c           
        > [         [        U ]  X(       + S9  X0l        [	        X5      nU(       Ga
  U R                  5        H6  nXT;  d  M
  [        U R                  S5      < SU< S[        U 5      < 35      e   U R                  5        HP  nXT;   d  M
  [        XE   5      S:  d  M  [        SU R                  S5      U[        XE   5      [        U 5      4-  5      e   S[        UR                  5       5      S   :w  a*  [        U R                  S5      < S	[        U 5      < 35      eU R                  XB5        g
X@l        g
)a_  
Parse a router descriptor in a network status document.

:param str content: router descriptor content to be parsed
:param NetworkStatusDocument document: document this descriptor came from
:param bool validate: checks the validity of the content if **True**, skips
  these checks otherwise

:raises: **ValueError** if the descriptor data is invalid
)	lazy_loadTz must have a 'z' line:
r9   z/%s can only have a single '%s' line, got %i:
%sr/   r   z( are expected to start with a 'r' line:
N)superr   __init__r   r   _required_fieldsrB   rC   str_single_fieldsrA   r   keys_parse_entries)selfr   r!   r   rT   keyword	__class__s         r,   r   RouterStatusEntry.__init__  s8    

T+G+NM$W7G**,'!djj>NPWY\]aYbcd
d - ((*'#g&6"7!";MQUQ[Q[\`Qacjlopw  qA  mB  DG  HL  DM  QN  N  O  O + 
W\\^$Q'	'DJJW[L\^abf^ghii
kk'$mr   c                     U(       a  S$ S$ )z 
Name for this descriptor type.
zRouter status entrieszRouter status entry r   	is_plurals     r,   rC   RouterStatusEntry._name  s    
 '0"J5JJr   c                     g)z4
Provides lines that must appear in the descriptor.
r   r   r   s    r,   r   "RouterStatusEntry._required_fields      
 r   c                     g)z=
Provides lines that can only appear in the descriptor once.
r   r   r   s    r,   r    RouterStatusEntry._single_fields  r   r   )r   r   )FNF)__name__
__module____qualname____firstlineno____doc__rY   rn   rw   
ATTRIBUTESPARSER_FOR_LINEclassmethodr   r   rC   r   r   __static_attributes____classcell__)r   s   @r,   r   r     s    * }%-(&m$m$}%M"=)m$* 
		/ y y(@K r   r   c                   t    \ rS rSrSrSr\" \R                  40 SS\	40D6r\
SS j5       rSS jrS	 rS
 rSrg)RouterStatusEntryV2i  a  
Information about an individual router stored within a version 2 network
status document.

:var str digest: **\*** router's upper-case hex digest

**\*** attribute is either required when we're parsed with validation or has
a default value, others are left as **None** if undefined
znetwork-status-consensus-2rL   Nr   c                     U(       a  [        SU R                  -  5      e[        XS[        5       < S[	        5       < S[        5       < S3445      $ )NSigning of %s not implementedr/   9 p1aag7VwarGxqctS7/fS0y5FU+s oQZFLYe9e4A7bOkWKR7TaNxb0JE r0    9001 0r   r   r   r   r   r   r   attrexcludesigns       r,   r   RouterStatusEntryV2.content	  sW     ?#,, NOOt
YiYkmym{  ~R  ~T  U  V/  r   c                     U(       a  S$ S$ )NzRouter status entries (v2)zRouter status entry (v2)r   r   s     r,   rC   RouterStatusEntryV2._name      +4'T:TTr   c                     g)Nr/   r   r   s    r,   r   $RouterStatusEntryV2._required_fields  s    r   c                     g)Nr   r   r   s    r,   r   "RouterStatusEntryV2._single_fields      r   Nr   Fr   )r   r   r   r   r   TYPE_ANNOTATION_NAMEdictr   r   rY   r   r   rC   r   r   r   r   r   r,   r   r     sZ     6%00 t]#5 *  Ur   r   c                       \ rS rSrSrSrSrg)RouterStatusEntryBridgeV2i  z
Information about an individual router stored within a bridge flavored
version 2 network status document.

.. versionadded:: 1.8.0
zbridge-network-statusr   N)r   r   r   r   r   r   r   r   r   r,   r  r    s     1r   r  c                       \ rS rSrSrSr\" \R                  40 S\	4/ \
4S\4S\4S\4S\4S\4/ \4S\40 \4/ \4S.D6r\" \R                   40 \
\\\\\S.D6r\SS	 j5       rSS
 jrS rS rSrg)RouterStatusEntryV3i'  a  
Information about an individual router stored within a version 3 network
status document.

:var list or_addresses: **\*** relay's OR addresses, this is a tuple listing
  of the form (address (**str**), port (**int**), is_ipv6 (**bool**))
:var str identifier_type: identity digest key type
:var str identifier: base64 encoded identity digest
:var str digest: **\*** router's upper-case hex digest

:var int bandwidth: bandwidth measured to be available by the relay, this is
  an arbitrary units (currently kilobytes per second) heuristic generated by
  the Bandwidth authoritites to weight relay selection
:var int measured: *bandwidth* vote provided by a bandwidth authority
:var bool is_unmeasured: *bandwidth* measurement isn't based on three or more
  measurements
:var list unrecognized_bandwidth_entries: **\*** bandwidth weighting
  information that isn't yet recognized

:var stem.exit_policy.MicroExitPolicy exit_policy: router's exit policy
:var dict protocols: mapping of protocols to their supported versions

:var list microdescriptor_hashes: **\*** tuples of two values, the list of
  consensus methods for generating a set of digests and the 'algorithm =>
  digest' mappings

**\*** attribute is either required when we're parsed with validation or has
a default value, others are left as **None** if undefined

.. versionchanged:: 1.5.0
   Added the identifier and identifier_type attributes.

.. versionchanged:: 1.6.0
   Added the protocols attribute.
znetwork-status-consensus-3NF)rL   re   r   r   r   r   r   r   r   r   r   )r[   ry   r   r   r   r   r   c                     U(       a  [        SU R                  -  5      e[        XS[        5       < S[	        5       < S[        5       < S34S45      $ )Nr   r/   r   r0   r   )ri   zFast Named Running Stable Validr   r   s       r,   r   RouterStatusEntryV3.contentg  sZ     ?#,, NOOt
YiYkmym{  ~R  ~T  U  V./  r   c                     U(       a  S$ S$ )NzRouter status entries (v3)zRouter status entry (v3)r   r   s     r,   rC   RouterStatusEntryV3._nameq  r   r   c                     g)N)r/   ri   r   r   s    r,   r   $RouterStatusEntryV3._required_fieldst  s    r   c                     g)N)r/   ri   rp   ry   r   r   r   r   s    r,   r   "RouterStatusEntryV3._single_fieldsw      *r   r   r   )r   r   r   r   r   r   r   r   r   rY   rg   r   r   r   _parse_pr_liner   r   r   r   rC   r   r   r   r   r   r,   r  r  '  s    "H 6%00 ]#'n-(&}%]+')=&9-(n%!=15 *  *:: 			

	? /  U+r   r  c                       \ rS rSrSrSr\" \R                  40 / \	4S\
4S\
4S\
4/ \
40 \4S\4S\4S.D6r\" \R                  40 \	\
\\S.D6r\SS	 j5       rSS
 jrS rS rSrg)r>   i{  a  
Information about an individual router stored within a microdescriptor
flavored network status document.

:var list or_addresses: **\*** relay's OR addresses, this is a tuple listing
  of the form (address (**str**), port (**int**), is_ipv6 (**bool**))
:var int bandwidth: bandwidth claimed by the relay (in kb/s)
:var int measured: bandwidth measured to be available by the relay
:var bool is_unmeasured: bandwidth measurement isn't based on three or more
  measurements
:var list unrecognized_bandwidth_entries: **\*** bandwidth weighting
  information that isn't yet recognized
:var dict protocols: mapping of protocols to their supported versions

:var str digest: **\*** router's hex encoded digest of our corresponding
  microdescriptor (**deprecated**, use microdescriptor_digest instead)
:var str microdescriptor_digest: **\*** router's base64 encoded digest of our corresponding microdescriptor

.. versionchanged:: 1.6.0
   Added the protocols attribute.

.. versionchanged:: 1.7.0
   Added the or_addresses attribute.

.. versionchanged:: 1.7.0
   Added the microdescriptor_digest attribute to replace our now deprecated digest attribute.

**\*** attribute is either required when we're parsed with validation or has
a default value, others are left as **None** if undefined
z$network-status-microdesc-consensus-3NF)re   r   r   r   r   r   r   rL   )r[   ry   r   r   r   c                     U(       a  [        SU R                  -  5      e[        XS[        5       < S[	        5       < S[        5       < S34SS45      $ )Nr   r/   z ARIJF2zbqirB9IwsW0mQznccWww r0   z
 9001 9030)r   z+aiUklwBrua82obG5AsTX+iEpkjQA2+AQHxZ7GwMfY70)ri   z1Fast Guard HSDir Named Running Stable V2Dir Validr   r   s       r,   r    RouterStatusEntryMicroV3.content  sO     ?#,, NOOt
@P@RT`Tbdxdz{|:@/  r   c                     U(       a  S$ S$ )Nz Router status entries (micro v3)zRouter status entry (micro v3)r   r   s     r,   rC   RouterStatusEntryMicroV3._name  s    1:-`@``r   c                     g)N)r/   ri   r   r   r   s    r,   r   )RouterStatusEntryMicroV3._required_fields  r   r   c                     g)N)r/   ri   rp   ry   r   r   r   r   s    r,   r   'RouterStatusEntryMicroV3._single_fields  r  r   r   r   )r   r   r   r   r   r   r   r   r   rg   r   r  r   r   r   r   rC   r   r   r   r   r   r,   r>   r>   {  s    > @%00 
'&}%]+')=&9n%#%BC23
5 
* *:: 			&
	? /  a+r   r>   )r/   NNr   r   )T)$r   r   r   stem.exit_policyr   stem.prereqstem.util.str_toolsstem.descriptorr   r   r   r   r   r   r	   r
   r   r   r   r  r-   rY   rg   rn   rw   r   r   r   r   r   rJ   r   r   r  r  r>   r   r   r,   <module>r     s   (  	       &dK8@F1Lh).fg$,M^b `.1DYDq
 qh!+ !H1 3 1Q++ Q+hG+0 G+r   