
    <ii              	          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rSSKrSSK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  SrSrSq\R(                  " S5      r\R(                  " S5      r\R                  " SS	S	5      rS
 rSS jrSS jrSS jrSSS\R8                  SSSSS4	S jrSS jrSS jrSS jr  " S S\!5      r" " S S\!5      r#g)a  
Descriptor archives are available from `CollecTor
<https://metrics.torproject.org/collector.html>`_. If you need Tor's topology
at a prior point in time this is the place to go!

With CollecTor you can either read descriptors directly...

.. literalinclude:: /_static/example/collector_reading.py
   :language: python

... or download the descriptors to disk and read them later.

.. literalinclude:: /_static/example/collector_caching.py
   :language: python

::

  get_instance - Provides a singleton CollecTor used for...
    |- get_server_descriptors - published server descriptors
    |- get_extrainfo_descriptors - published extrainfo descriptors
    |- get_microdescriptors - published microdescriptors
    |- get_consensus - published router status entries
    |
    |- get_key_certificates - authority key certificates
    |- get_bandwidth_files - bandwidth authority heuristics
    +- get_exit_lists - TorDNSEL exit list

  File - Individual file residing within CollecTor
    |- read - provides descriptors from this file
    +- download - download this file to disk

  CollecTor - Downloader for descriptors from CollecTor
    |- get_server_descriptors - published server descriptors
    |- get_extrainfo_descriptors - published extrainfo descriptors
    |- get_microdescriptors - published microdescriptors
    |- get_consensus - published router status entries
    |
    |- get_key_certificates - authority key certificates
    |- get_bandwidth_files - bandwidth authority heuristics
    |- get_exit_lists - TorDNSEL exit list
    |
    |- index - metadata for content available from CollecTor
    +- files - files available from CollecTor

.. versionadded:: 1.8.0
    N)CompressionDocumentHandlerz!https://collector.torproject.org/  z-(\d{4})-(\d{2})\.z%(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})i'     c                  0    [         c
  [        5       q [         $ )z
Provides the singleton :class:`~stem.descriptor.collector.CollecTor`
used for this module's shorthand functions.

:returns: singleton :class:`~stem.descriptor.collector.CollecTor` instance
)SINGLETON_COLLECTOR	CollecTor     S/home/james-whalen/.local/lib/python3.13/site-packages/stem/descriptor/collector.pyget_instancer   P   s      #+	r   F   c              #   X   #    [        5       R                  XX#XE5       H  nUv   M	     g7f)zn
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_server_descriptors`
on our singleton instance.
N)r   get_server_descriptorsstartendcache_tobridgetimeoutretriesdescs          r   r   r   `   s)      n33ERYcd
J d   (*c              #   X   #    [        5       R                  XX#XE5       H  nUv   M	     g7f)zq
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_extrainfo_descriptors`
on our singleton instance.
N)r   get_extrainfo_descriptorsr   s          r   r   r   k   s)      n66u8U\fd
J gr   c              #   X   #    [        5       R                  XX#U5       H  nUv   M	     g7f)zl
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_microdescriptors`
on our singleton instance.
N)r   get_microdescriptorsr   r   r   r   r   r   s         r   r   r   v   )      n11%hQXYd
J Zr   c	              #   \   #    [        5       R                  XX#XEXgU5	       H  n	U	v   M	     g7f)ze
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_consensus`
on our singleton instance.
N)r   get_consensus)
r   r   r   document_handlerversionmicrodescriptorr   r   r   r   s
             r   r!   r!      s6      n**5xSZms  F  Gd
J Gs   *,c              #   X   #    [        5       R                  XX#U5       H  nUv   M	     g7f)zl
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_key_certificates`
on our singleton instance.
N)r   get_key_certificatesr   s         r   r&   r&      r   r   c              #   X   #    [        5       R                  XX#U5       H  nUv   M	     g7f)zk
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_bandwidth_files`
on our singleton instance.
N)r   get_bandwidth_filesr   s         r   r(   r(      s)      n00XPWXd
J Yr   c              #   X   #    [        5       R                  XX#U5       H  nUv   M	     g7f)zf
Shorthand for
:func:`~stem.descriptor.collector.CollecTor.get_exit_lists`
on our singleton instance.
N)r   get_exit_listsr   s         r   r*   r*      s(      n++E7Sd
J Tr   c                   t    \ rS rSrSrS rSSSS\R                  SS4S jrSS jr	\
S 5       r\
S	 5       rS
rg)File   a,  
File within CollecTor.

:var str path: file path within collector
:var tuple types: descriptor types contained within this file
:var stem.descriptor.Compression compression: file compression, **None** if
  this cannot be determined
:var int size: size of the file
:var str sha256: file's sha256 checksum

:var datetime start: first publication within the file, **None** if this
  cannot be determined
:var datetime end: last publication within the file, **None** if this cannot
  be determined
:var datetime last_modified: when the file was last modified
c                    Xl         U(       a  [        U5      OSU l        [        R	                  U5      U l        X0l        X@l        [        R                  R                  US5      U l
        S U l        U(       aR  U(       aK  [        R                  R                  US5      U l        [        R                  R                  US5      U l        g [        R                  U5      u  U l        U l        g )Nr
   z%Y-%m-%d %H:%M)pathtupletypesr,   _guess_compressioncompressionsizesha256datetimestrptimelast_modified_downloaded_tor   r   _guess_time_range)selfr/   r1   r4   r5   first_publishedlast_publishedr8   s           r   __init__File.__init__   s    I!&uBDJ..t4DIK!**33MCSTDD
 >$$--o?OPdj""++N<LMdh!33D9dj$(r   Nr   c           	   #     #    Uc  [        U R                   Vs/ s H  oR                  S5      S   PM     sn5      n	U R                  (       d  [        S5      e[	        U	5      S:  a'  [        SSR                  U R                  5      -  5      eU R                  S   nUc  U R                  (       aX  [        R                  R                  U R                  5      (       a*  [        R                  R                  U R                  5      nOK[        R                  " 5       n
U R                  XX4XVU5       H  nUv   M	     [        R                  " U
5        gU R!                  USXg5      n["        R$                  R'                  XS	9 Hj  nUb0  UR)                  UR+                  5       R,                  5      (       d  M6  [/        US
S5      nU(       a  U(       a  X:  a  MX  U(       a  X:  a  Mf  Uv   Ml     gs  snf 7f)a  
Provides descriptors from this archive. Descriptors are downloaded or read
from disk as follows...

* If this file has already been downloaded through
  :func:`~stem.descriptor.collector.CollecTor.download' these descriptors
  are read from disk.

* If a **directory** argument is provided and the file is already present
  these descriptors are read from disk.

* If a **directory** argument is provided and the file is not present the
  file is downloaded this location then read.

* If the file has neither been downloaded and no **directory** argument
  is provided then the file is downloaded to a temporary directory that's
  deleted after it is read.

:param str directory: destination to download into
:param str descriptor_type: `descriptor type
  <https://metrics.torproject.org/collector.html#data-formats>`_, this is
  guessed if not provided
:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param stem.descriptor.__init__.DocumentHandler document_handler: method in
  which to parse a :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
:param int timeout: timeout when connection becomes idle, no timeout
  applied if **None**
:param int retries: maximum attempts to impose

:returns: iterator for :class:`~stem.descriptor.__init__.Descriptor`
  instances in the file

:raises:
  * **ValueError** if unable to determine the descirptor type
  * **TypeError** if we cannot parse this descriptor type
  * :class:`~stem.DownloadFailed` if the download fails
N r   z/Unable to determine this file's descriptor typer   z;Unable to disambiguate file's descriptor type from among %sz, T)r"   	published)setr1   split
ValueErrorlenjoinr9   osr/   existsdirnametempfilemkdtempreadshutilrmtreedownloadstem
descriptor
parse_file
startswithtype_annotationnamegetattr)r;   	directorydescriptor_typer   r   r"   r   r   t
base_typestmp_directoryr   r/   rB   s                 r   rM   	File.read   s    P 
 <AQ<=jZZJKKz?QVY]YbYbcgcmcmYnnoo**Q-			0C0C!D!DGGOOD$7$78	
 !((*IImeJZelmD* n 	m$==D';D
 **4*U		 O$>$>t?S?S?U?Z?Z$[$[ D+t4	y(y
 V= =s   G9G4FG9;>G9c           	         U R                   R                  S5      S   nU R                  [        R                  :w  a  U(       a  UR                  SS5      S   n[        R                   R                  U5      n[        R                   R                  X5      n[        R                   R                  U5      (       d  [        R                  " U5        [        R                   R                  U5      (       a  [        U5       n[        R                  " [        R                  " U R                   5      5      n	["        R                   " UR%                  5       5      R'                  5       n
X:X  a  UsSSS5        $ U(       d  [)        U< SU	< SU
< S	35      e SSS5        [*        R,                  R.                  R1                  [2        U R                   -   X45      nU(       a  U R                  R5                  U5      n[        US
5       nUR7                  U5        SSS5        Xpl        U$ ! , (       d  f       N= f! , (       d  f       N'= f)a  
Downloads this file to the given location. If a file already exists this is
a no-op.

:param str directory: destination to download into
:param bool decompress: decompress written file
:param int timeout: timeout when connection becomes idle, no timeout
  applied if **None**
:param int retries: maximum attempts to impose
:param bool overwrite: if this file exists but mismatches CollecTor's
  checksum then overwrites if **True**, otherwise rases an exception

:returns: **str** with the path we downloaded to

:raises:
  * :class:`~stem.DownloadFailed` if the download fails
  * **IOError** if a mismatching file exists and **overwrite** is **False**
/.r   r   Nz? already exists but mismatches CollecTor's checksum (expected: z
, actual: )wb)r/   rD   r3   r   	PLAINTEXTrsplitrH   
expanduserrG   rI   makedirsopenbinasciihexlifybase64	b64decoder5   hashlibrM   	hexdigestIOErrorrQ   util
connectionrP   COLLECTOR_URL
decompresswriter9   )r;   rX   rs   r   r   	overwritefilenamer/   
prior_fileexpected_hashactual_hashresponseoutput_files                r   rP   File.download+  s   ( yys#B'H;000Za(+h""9-I77<<	,D77>>)$$kk) 
ww~~d: (()9)9$++)FGnnZ__%67AAC' : nr  uB  DO  P  Q  Q   yy##,,]TYY-FYH!!,,X6h	dD	[! 
 K% : 
	s    A-H)7H)H:)
H7:
Ic                     [         R                  [         R                  [         R                  4 H'  nU R	                  UR
                  5      (       d  M%  Us  $    [         R                  $ )z6
Determine file comprssion from CollecTor's filename.
)r   LZMABZ2GZIPendswith	extensionrd   )r/   r3   s     r   r2   File._guess_compressionb  sO     $((+//;;K;KL	{,,	-	- M    r   c                    [         R                  U 5      nU(       av  [        [        UR	                  5       5      u  p#[
        R
                  " X#S5      nUS:  a  U[
        R
                  " X#S-   S5      4$ U[
        R
                  " US-   SS5      4$ [        R                  U 5      nU(       aG  [
        R
                  R                  UR                  S5      S5      nXD[
        R                  " SS9-   4$ g)z
Attemt to determine the (start, end) time range from CollecTor's filename.
This provides (None, None) if this cannot be determined.
r      z%Y-%m-%d-%H-%M-%Sr   )seconds)NN)
	YEAR_DATEsearchmapintgroupsr6   SEC_DATEr7   group	timedelta)r/   
year_matchyearmonthr   	sec_matchs         r   r:   File._guess_time_rangen  s     !!$'JZ..01kdQ/e	x((qy!<==x((1a899%I
 (();=PQeX//$??@@r   )	r9   r3   r   r8   r/   r5   r4   r   r1   )TNr   F)__name__
__module____qualname____firstlineno____doc__r>   r   ENTRIESrM   rP   staticmethodr2   r:   __static_attributes__r
   r   r   r,   r,      sc    ":$ "T4thwhh  LP  \] Xt5n 	! 	!  r   r,   c            	           \ rS rSrSrSS jrSS jrSS jrSS	 jrSSS\	R                  SSSSS4	S
 jrSS jrSS jrSS jrSS jrSS jr\S 5       rSrg)r	   i  aE  
Downloader for descriptors from CollecTor. The contents of CollecTor are
provided in `an index <https://collector.torproject.org/index/index.json>`_
that's fetched as required.

:var int retries: number of times to attempt the request if downloading it
  fails
:var float timeout: duration before we'll time out our request
Nc                 F    Xl         X l        S U l        S U l        SU l        g )Nr   )r   r   _cached_index_cached_files_cached_index_at)r;   r   r   s      r   r>   CollecTor.__init__  s$    LLDDDr   Fr   c           
   #      #    U(       d  SOSnU R                  XqU5       H  nUR                  X7XXVS9 H  n	U	v   M	     M!     g7f)a  
Provides server descriptors published during the given time range, sorted
oldest to newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param bool bridge: standard descriptors if **False**, bridge if **True**
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.server_descriptor.ServerDescriptor` for the
  given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
zserver-descriptorzbridge-server-descriptorr   r   NfilesrM   
r;   r   r   r   r   r   r   	desc_typefr   s
             r   r    CollecTor.get_server_descriptors  sF     * ,2#7QIZZ	#.&&eG&_$
 ` /   AAc           
   #      #    U(       d  SOSnU R                  XqU5       H  nUR                  X7XXVS9 H  n	U	v   M	     M!     g7f)a  
Provides extrainfo descriptors published during the given time range,
sorted oldest to newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param bool bridge: standard descriptors if **False**, bridge if **True**
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor`
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
z
extra-infozbridge-extra-infor   Nr   r   s
             r   r   #CollecTor.get_extrainfo_descriptors  sE     * %+0CIZZ	#.&&eG&_$
 ` /r   c           
   #   x   #    U R                  SX5       H   nUR                  USXXES9 H  nUv   M	     M"     g7f)ab  
Provides microdescriptors estimated to be published during the given time
range, sorted oldest to newest. Unlike server/extrainfo descriptors,
microdescriptors change very infrequently...

::

  "Microdescriptors are expected to be relatively static and only change
  about once per week." -dir-spec section 3.3

CollecTor archives only contain microdescriptors that *change*, so hourly
tarballs often contain very few. Microdescriptors also do not contain
their publication timestamp, so this is estimated.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.microdescriptor.Microdescriptor
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
r$   r   Nr   r;   r   r   r   r   r   r   r   s           r   r   CollecTor.get_microdescriptors  s?     < ZZ)56&&#4eG&g$
 h 7   8:c
              #   h  #    US:X  a  U(       d
  U(       d  Sn
OaUS:X  a  U(       a
  U(       d  Sn
OJUS:X  a  U(       d
  U(       d  Sn
O3U(       a  Sn
O)U(       a  US:w  a  [        SU-  5      e[        SU-  5      eU R                  XU5       H   nUR                  X:XXHU	S	9 H  nUv   M	     M"     g
7f)aq  
Provides consensus router status entries published during the given time
range, sorted oldest to newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param stem.descriptor.__init__.DocumentHandler document_handler: method in
  which to parse a :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
:param int version: consensus variant to retrieve (versions 2 or 3)
:param bool microdescriptor: provides the microdescriptor consensus if
  **True**, standard consensus otherwise
:param bool bridge: standard descriptors if **False**, bridge if **True**
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.router_status_entry.RouterStatusEntry`
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
r   znetwork-status-consensus-3z$network-status-microdesc-consensus-3   znetwork-status-2zbridge-network-statusz7Only v3 microdescriptors are available (not version %s)zCOnly v2 and v3 router status entries are available (not version %s)r   N)rE   r   rM   )r;   r   r   r   r"   r#   r$   r   r   r   r   r   r   s                r   r!   CollecTor.get_consensus  s     4 !|OF.i	A/&8i	Aof$i	)i	W\RU\\]]^ahhiiZZ	#.&&e:Jip&q$
 r /s   B0B2c           
   #   x   #    U R                  SX5       H   nUR                  USXXES9 H  nUv   M	     M"     g7f)a  
Directory authority key certificates for the given time range,
sorted oldest to newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.networkstatus.KeyCertificate
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
zdir-key-certificate-3r   Nr   r   s           r   r&   CollecTor.get_key_certificates$  s@     ( ZZ/<&&#:ERY&m$
 n =r   c           
   #   x   #    U R                  SX5       H   nUR                  USXXES9 H  nUv   M	     M"     g7f)a  
Bandwidth authority heuristics for the given time range, sorted oldest to
newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.bandwidth_file.BandwidthFile
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
zbandwidth-filer   Nr   r   s           r   r(   CollecTor.get_bandwidth_files<  s?     ( ZZ(%5&&#3U7&f$
 g 6r   c           
   #   x   #    U R                  SX5       H   nUR                  USXXES9 H  nUv   M	     M"     g7f)a  
`TorDNSEL exit lists <https://www.torproject.org/projects/tordnsel.html.en>`_
for the given time range, sorted oldest to newest.

:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with
:param str cache_to: directory to cache archives into, if an archive is
  available here it is not downloaded
:param int timeout: timeout for downloading each individual archive when
  the connection becomes idle, no timeout applied if **None**
:param int retries: maximum attempts to impose on a per-archive basis

:returns: **iterator** of
  :class:`~stem.descriptor.tordnsel.TorDNSEL
  for the given time range

:raises: :class:`~stem.DownloadFailed` if the download fails
tordnselr   Nr   r   s           r   r*   CollecTor.get_exit_listsT  s=     ( ZZ
E/&&:uW&`$
 a 0r   c                 (   U R                   (       a+  [        R                  " 5       U R                  -
  [        :  GaK  US:X  a[  [        R
                  [        R                  [        R                  [        R                  4 H  nUR                  (       d  M  Un  O   OUc  [        R                  nU[        R                  :w  a  UR                  OSn[        S-   U-   nUR                  [        R                  R                  R!                  X@R"                  U R$                  5      5      n[&        R(                  " [        R                  R*                  R-                  U5      5      U l         [        R                  " 5       U l        U R                   $ )a  
Provides the archives available in CollecTor.

:param descriptor.Compression compression: compression type to
  download from, if undefiled we'll use the best decompression available

:returns: **dict** with the archive contents

:raises:
  If unable to retrieve the index this provide...

    * **ValueError** if json is malformed
    * **IOError** if unable to decompress
    * :class:`~stem.DownloadFailed` if the download fails
best zindex/index.json)r   timer   REFRESH_INDEX_RATEr   r~   r   r   rd   	availabler   rr   rs   rQ   rp   rq   rP   r   r   jsonloads	str_tools_to_unicode)r;   r3   optionr   urlrz   s         r   indexCollecTor.indexl  s   " t/D/D!DHZ!Z		"''+:J:JKLaLabF K c !+++6+:O:O+O+''UWi..:c''		(<(<(E(Ec<<Y]YeYe(fgh::dii&9&9&E&Eh&OPd"iikdr   c           	      J   U R                   (       a*  [        R                  " 5       U R                  -
  [        :  a2  [	        [
        R                  U R                  5       / 5      S S9U l         / nU R                    H  nU(       a  UR                  b  UR                  U:  a  M)  U(       a  UR                  b  UR                  U:  a  MO  Ub;  [        UR                   Vs/ s H  ofR                  U5      PM     sn5      (       d  M  UR                  U5        M     U$ s  snf )a
  
Provides files CollecTor presently has, sorted oldest to newest.

:param str descriptor_type: descriptor type or prefix to retrieve
:param datetime.datetime start: publication time to begin with
:param datetime.datetime end: publication time to end with

:returns: **list** of :class:`~stem.descriptor.collector.File`

:raises:
  If unable to retrieve the index this provide...

    * **ValueError** if json is malformed
    * **IOError** if unable to decompress
    * :class:`~stem.DownloadFailed` if the download fails
c                 H    U R                   (       a  U R                   $ [        $ )N)r   FUTURE)xs    r   <lambda>!CollecTor.files.<locals>.<lambda>  s    abahahVWV]V]VtntVtr   )key)r   r   r   r   sortedr	   _filesr   r   r   anyr1   rT   append)r;   rY   r   r   matchesr   r   s          r   r   CollecTor.files  s    $ t/D/D!DHZ!Z!)"2"24::<"DLtudG	AEEMQUUU]AGGOqww}		 C`a`g`g(h`gS\)=)=o)N`g(h$i$iq   N )is   !D 
c                 T   [        U [        5      (       d  / $ / nU R                  5        H  u  p4US:X  a  U H  nSR                  XR	                  S5      /-   5      nUR                  [        XeR	                  S5      UR	                  S5      UR	                  S5      UR	                  S5      UR	                  S5      UR	                  S	5      5      5        M     M  US
:X  d  M  U H:  nUR                  [        R                  XQUR	                  S5      /-   5      5        M<     M     U$ )z
Recursively provies files within the index.

:param dict val: index hash
:param list path: path we've transversed into

:returns: **list** of :class:`~stem.descriptor.collector.File`
r   r_   r/   r1   r4   r5   r<   r=   r8   directories)

isinstancedictitemsrG   getr   r,   extendr	   r   )valr/   r   kvattr	file_paths          r   r   CollecTor._files  s*    c4  iE			
gDhhtxx'7&889)
,,tIxx'8$((6:JDHHU]L^`d`h`hiz`{  ~B  ~F  ~F  GW  ~X  Z^  Zb  Zb  cr  Zs  t  u  D
,,y''dhhv6F5G.GH
I   Lr   )r   r   r   r   r   )r   NNNNFNr   NNNNr   )r   )NNN)r   r   r   r   r   r>   r   r   r   r   r   r!   r&   r(   r*   r   r   r   r   r   r
   r   r   r	   r	     s    66 D #'dtXgXoXo{|  QV  af  rv  BC *X000!F D  r   r	   r   r   )$r   rk   ri   r6   rm   r   rH   rerN   rK   r   stem.descriptorrQ   stem.util.connectionstem.util.str_toolsr   r   rr   r   r   compiler   r   r   r   r   r   r   r   r!   r&   r(   r*   objectr,   r	   r
   r   r   <module>r      s   -^      	 	       83  JJ./	::CD 
		4A	&  dtP_PgPgst  IN  Y^  jn  z{ ]6 ]@} }r   