
    <i-              	          S 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SK	J
r
  SrSr " S S\5      r " S	 S
\5      rS rS rS rS r0 SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_SS\4_S S!\4_S"S"\4_S#S#\4_S$S$\4_S%S&\4_S'S(\4_S)\4S*\4S+\4S,\4S-\4S.\4S/\4S0.ErSS10rS8S2 jrS3 rS4 rS5 r " S6 S7\5      rg! \ a	    SSKJ
r
   Nf = f)9a  
Parsing for Bandwidth Authority metrics as described in Tor's
`bandwidth-file-spec <https://gitweb.torproject.org/torspec.git/tree/bandwidth-file-spec.txt>`_.

**Module Overview:**

::

  BandwidthFile - Tor bandwidth authority measurements.

.. versionadded:: 1.8.0
    N)_mappings_for
Descriptor)OrderedDicts   =====s   ====c                       \ rS rSrSrS rSrg)RecentStats)   a1  
Statistical information collected over the last 'data_period' (by default
five days).

:var int consensus_count: number of consensuses published during this period

:var int prioritized_relays: number of relays prioritized to be measured
:var int prioritized_relay_lists: number of times a set of relays were
  prioritized to be measured

:var int measurement_attempts: number of relay measurements we attempted
:var int measurement_failures: number of measurement attempts that failed

:var RelayFailures relay_failures: number of relays we failed to measure
c                 h    S U l         S U l        S U l        S U l        S U l        [        5       U l        g N)consensus_countprioritized_relaysprioritized_relay_listsmeasurement_attemptsmeasurement_failuresRelayFailuresrelay_failuresselfs    X/home/james-whalen/.local/lib/python3.13/site-packages/stem/descriptor/bandwidth_file.py__init__RecentStats.__init__:   s4    D"D#'D  $D $D'/D    )r   r   r   r   r   r   N__name__
__module____qualname____firstlineno____doc__r   __static_attributes__ r   r   r   r   )   s     *r   r   c                       \ rS rSrSrS rSrg)r   C   a  
Summary of the number of relays we were unable to measure.

:var int no_measurement: number of relays that did not have any successful
  measurements
:var int insuffient_period: number of relays whos measurements were collected
  over a period that was too small (1 day by default)
:var int insufficient_measurements: number of relays we did not collect
  enough measurements for (2 by default)
:var int stale: number of relays whos latest measurement is too old (5 days
  by default)
c                 <    S U l         S U l        S U l        S U l        g r
   )no_measurementinsuffient_periodinsufficient_measurementsstaler   s    r   r   RelayFailures.__init__Q   s!    D!D%)D"DJr   )r%   r$   r#   r&   Nr   r   r   r   r   r   C   s    r   r   c                     U $ r
   r   vals    r   _strr+   [   s    	*r   c                 T    U (       a   U R                  5       (       a  [        U 5      $ S $ r
   )isdigitintr)   s    r   _intr/   _   s    ckkmmS6$6r   c                 v     [         R                  R                  R                  U 5      $ ! [         a     g f = fr
   )stemutil	str_tools_parse_iso_timestamp
ValueErrorr)   s    r   _dater6   c   s4    9933C88	 s   (+ 
88c                 V    U b%  [        [        S U R                  S5      5      5      $ S $ )Nc                 "    U R                  5       $ r
   )strip)vs    r   <lambda>_csv.<locals>.<lambda>k   s
    AGGIr   ,)listmapsplitr)   s    r   _csvrA   j   s'    ;>?c%syy~6	7TPTTr   versionsoftwaresoftware_versionearliest_bandwidthlatest_bandwidth
created_atfile_createdgenerated_atgenerator_startedconsensus_sizenumber_consensus_relayseligible_countnumber_eligible_relayseligible_percentpercent_eligible_relays	min_countminimum_number_eligible_relaysmin_percentminimum_percent_eligible_relaysscanner_countrydestinations_countriestime_to_report_half_networkzrecent_stats.consensus_countrecent_consensus_countz$recent_stats.prioritized_relay_listsrecent_priority_list_countrecent_priority_relay_count recent_measurement_attempt_count recent_measurement_failure_count(recent_measurements_excluded_error_count'recent_measurements_excluded_near_count&recent_measurements_excluded_few_count&recent_measurements_excluded_old_count)zrecent_stats.prioritized_relaysz!recent_stats.measurement_attemptsz!recent_stats.measurement_failuresz*recent_stats.relay_failures.no_measurementz-recent_stats.relay_failures.insuffient_periodz5recent_stats.relay_failures.insufficient_measurementsz!recent_stats.relay_failures.stale1.0.0c              +   F   #    [        U R                  5       U40 UD6v   g7f)a  
Iterates over the bandwidth authority metrics in a file.

:param file descriptor_file: file with descriptor content
:param bool validate: checks the validity of the descriptor's content if
  **True**, skips these checks otherwise
:param dict kwargs: additional arguments for the descriptor constructor

:returns: :class:`stem.descriptor.bandwidth_file.BandwidthFile` object

:raises:
  * **ValueError** if the contents is malformed and validate is **True**
  * **IOError** if the file can't be read
N)BandwidthFileread)descriptor_filevalidatekwargss      r   _parse_filerh      s"       	o**,hA&AAs   !c                 L   [        5       n[        R                  " U R                  5       5      nUR	                  5         SnS n UR	                  5       R                  5       nU(       d  OU[        [        4;   a  OqU(       d  SU;   a  OcSU;   aH  [        R                  R                  R                  U5      R                  SS5      u  pxXU'   US:X  a  UnO[        SU-  5      eUS-  nM  X l        [        5       U l        ["        R%                  5        Hw  u  n	u  pU nU	R                  S5      S S  H  n['        X5      nM     [)        XR                  S5      S   U" UR+                  U
[,        R+                  U	5      5      5      5        My     Ub  US:w  a  [        S	5      eg g )
N   s   node_id=   ==rB   z3Header expected to be key=value pairs, but had '%s'.z3The 'version' header must be in the second position)r   ioBytesIO	get_bytesreadliner9   
HEADER_DIVHEADER_DIV_ALTr1   r2   r3   _to_unicoder@   r5   headerr   recent_statsHEADER_ATTRitemsgetattrsetattrgetHEADER_DEFAULT)
descriptorentriesrv   contentindexversion_indexlinekeyvalue	full_attrkeywordclsobjattrs                 r   _parse_headerr      s   =&JJz++-.'	
%-##%D	*n-	-t+t|99&&2248>>sAFjcSk			LtSTT	QJE' 	* 'M*#.#4#4#6i'
C$Sb)Cc * C%b)3vzz'>CUCUV_C`/a+bc $7 =A#5
J
KK $6r   c                 $   [         R                  " U R                  5       5      R                  5       R	                  5       nUR                  5       (       a.  [        R                  R                  [        U5      5      U l	        g [        SU-  5      e)Nz3First line should be a unix timestamp, but was '%s')ro   rp   rq   rr   r9   r-   datetimeutcfromtimestampr.   	timestampr5   )r~   r   
first_lines      r   _parse_timestampr      sh    zz*..01::<BBD*#,,==c*oNJ
JZW
XXr   c                    [         R                  " U R                  5       5      nU R                  S:X  a  UR	                  5         O]UR	                  5       R                  5       S[        [        4;  a0   UR	                  5       R                  5       S[        [        4;  a  M0  0 nUR                  5        H  n[        R                  R                  R                  UR                  5       5      n[        [        SU5      5      nUR                  SS5      R!                  S5      nU(       d  [#        SU-  5      eXc;   a  [#        SU-  5      eXSU'   M     X0l        g )Nra    measurementnode_id$z+Every meaurement must include 'node_id': %szBRelay %s is listed multiple times. It should only be present once.)ro   rp   rq   rB   rr   r9   rs   rt   	readlinesr1   r2   r3   ru   dictr   r|   lstripr5   measurements)r~   r   r   r   r   r   fingerprints          r   _parse_bodyr      s    JJz++-.'7"




"
"
$R^,L
L
 



"
"
$R^,L
L ,!d99**4::<8DmT23D((9b)005KDtKLL		$[^iijj $ " )r   c                      ^ \ rS rSrSrSrS\40 \40 \4S.r	\	R                  \" \R                  5        V VVs/ s H  oS[        44PM     snnn 5      5        \S	S j5       rS
U4S jjrSrU=r$ s  snnn f )rc   i  a$  
Tor bandwidth authority measurements.

:var dict measurements: **\*** mapping of relay fingerprints to their
  bandwidth measurement metadata

:var dict header: **\*** header metadata
:var datetime timestamp: **\*** time when these metrics were published
:var str version: **\*** document format version

:var str software: application that generated these metrics
:var str software_version: version of the application that generated these metrics

:var datetime earliest_bandwidth: time of the first sampling
:var datetime latest_bandwidth: time of the last sampling
:var datetime created_at: time when this file was created
:var datetime generated_at: time when collection of these metrics started

:var int consensus_size: number of relays in the consensus
:var int eligible_count: relays with enough measurements to be included
:var int eligible_percent: percentage of consensus with enough measurements
:var int min_count: minimum eligible relays for results to be provided
:var int min_percent: minimum measured percentage of the consensus

:var str scanner_country: country code where this scan took place
:var list destinations_countries: all country codes that were scanned

:var int time_to_report_half_network: estimated number of seconds required to
  measure half the network, given recent measurements

:var RecentStats recent_stats: statistical information collected over the
  last 'data_period' (by default five days)

**\*** attribute is either required when we're parsed with validation or has
a default value, others are left as **None** if undefined
zbandwidth-fileN)r   rv   r   r   c           	      &   U(       a  [        SU R                  -  5      eUb  [        U5      O	[        5       nUR                  S[	        [        [        R                  " 5       5      5      5      nUR                  S/ 5      nUR                  S[        R                  S5      5      n/ nSU;  a8  UR                  [        R                  R                  R                  U5      5        US:X  a  U(       a  [        S5      eUS:w  a  SU;  aJ  UR                  [        R                  R                  R                  SUR                  S5      -  5      5        UR                  5        HD  u  pUR                  [        R                  R                  R                  U	< SU
< 35      5        MF     UR                  [         5        U H;  nUR                  [        R                  R                  R                  U5      5        M=     S	R#                  U5      $ )
a  
Creates descriptor content with the given attributes. This descriptor type
differs somewhat from others and treats our attr/exclude attributes as
follows...

  * 'timestamp' is a reserved key for our mandatory header unix timestamp.

  * 'content' is a reserved key for our bandwidth measurement lines.

  * All other keys are treated as header fields.

For example...

::

  BandwidthFile.content({
    'timestamp': '12345',
    'version': '1.2.0',
    'content': [],
  })
zSigning of %s not implementedr   r   rB   ra   z2Headers require BandwidthFile version 1.1 or laterz
version=%srl      
)NotImplementedErrorr   r   popstrr.   timer|   r}   appendr1   r2   r3   	_to_bytesr5   ry   rs   join)r   r   excludesignrv   r   r   rB   lineskr:   r   s               r   r   BandwidthFile.content4  s   0  ?#,, NOO"&"2[F

;C		,<(=>IjjB'GjjN$6$6y$ABGE'!ll499&&00;<'fKLL	G	 
'	!TYY((22<&**YBW3WXY,,.$!TYY((22a3CDE ! ll:ll499&&00=>  ::er   c                    > [         [        U ]  X(       + S9  U(       a%  [        U S 5        [	        U S 5        [        U S 5        g g )N)	lazy_load)superrc   r   r   r   r   )r   raw_contentrf   	__class__s      r   r   BandwidthFile.__init__k  s=    	-''NtT"D$$ r   )Nr   FF)r   r   r   r   r   TYPE_ANNOTATION_NAMEr   r   r   
ATTRIBUTESupdater   rx   keysclassmethodr   r   r   __classcell__).0r   r   r   s   000@r   rc   rc     s    #J * ()=!%* Dk>N>N>PQ>Pt]34>PQRS4 4l s Rs   A1rc   r   )r   r   ro   r   stem.util.str_toolsr1   stem.descriptorr   r   collectionsr   ImportErrorstem.util.ordereddictrs   rt   objectr   r   r+   r/   r6   rA   rx   r}   rh   r   r   r   rc   r   r   r   <module>r      s$    	  
0% 
*& *4F 07U' i'
 z4 ' )40' -u5' )51' '' &.' .5'  -t4!'" 0$7#'$ 0$7%'& 3T:''. './'0 5t<1'8  "?!F9'< !#;T"B='> )+G*N?'@ 'DT%J(JD'Q(JD'Q1[]a0b4]_c3d<dfj;k(PRV'WM'T W
B&*LZY):mJ mK  0/0s   C C$#C$