
    .PhH                        d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZ ddlmZ ddlmZ ddlmZ g dZej        ej        z   Z ej        d	ez   d
z             Z ej        d          Zd Zd ZdPdZd ZdQdZ ddddZ!dRdZ"d Z#d Z$d Z%d Z&i ddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d6d7d8d9d:d;d<d=d>i d?d?d@dAdBdCdDdEdFdGdHdIdJdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[d\d]d^i d_d_d`dadbdcdddedddfdgdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzd{d|d}d~i ddddddddddddddddddddddddddddddddddi ddddddddddddddddddddddddddddddddddi ddddddddÓddœddǓddɓdd˓dd͓ddϓddѓddӓddՓddדddٓddۓddݓddddZ'd e'(                                D             Z) ej        dej*                  Z+d Z,d Z- ej        dej.                  Z/d Z0dRdZ1d Z2 G d de3          Z4i ddddddddddddddddddddddd dddddddddddi d	dd
dddddddddddddddddddddddddddd d!d"d#d#d#d$d$d%d%d%d&d&d'd'd(d(d)Z5 e4e5          Z6d*Z7d+  e8e7          D             Z9 e: e;e9e9d,d                             Z<dSd-Z= G d. d/e          Z>d0 Z?d1Z@d2ZAd3 ZBdTd5ZC ej        d6ej*                  ZDd7 ZEd8eFfd9ZGdUd;ZHdVd=ZI ej        d>          jJ        ZKdWd?ZLdWd@ZMdXdCZNdYdDZO	 	 dZdEZPdXdFZQ G dG dH          ZRdI ZSd[dKZTdLeUdMeUdNeUfdOZVdS (\  zSo much practical programming involves string manipulation, which
Python readily accommodates. Still, there are dozens of basic and
common capabilities missing from the standard library, several of them
provided by ``strutils``.
    N)Mapping)GzipFile)
HTMLParser)entities)BytesIO) camel2underunder2camelslugifysplit_punct_wsunit_len
ordinalizecardinalize	pluralizesingularizeasciifyis_asciiis_uuid	html2text
strip_ansibytes2humanfind_hashtagsa10n
gzip_bytesgunzip_bytesiter_splitlinesindentescape_shell_argsargs2cmdargs2shparse_int_listformat_int_listcomplement_int_listint_ranges_from_int_listMultiReplacemulti_replaceunwrap_textremoveprefix[z]+z(((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))c                 \    t                               d|                                           S )zConverts a camelcased string to underscores. Useful for turning a
    class name into a function name.

    >>> camel2under('BasicParseTest')
    'basic_parse_test'
    z_\1)_camel2under_resublower)camel_strings    P/var/www/html/test/jupyter/venv/lib/python3.11/site-packages/boltons/strutils.pyr   r   D   s&     v|44::<<<    c                 f    d                     d |                     d          D                       S )zConverts an underscored string to camelcased. Useful for turning a
    function name into a class name.

    >>> under2camel('complex_tokenizer')
    'ComplexTokenizer'
     c              3   B   K   | ]}|                                 pd V  dS )_N)
capitalize.0ws     r.   	<genexpr>zunder2camel.<locals>.<genexpr>U   s/      JJQ1<<>>(SJJJJJJr/   r3   )joinsplit)under_strings    r.   r	   r	   N   s3     77JJ,2D2DS2I2IJJJJJJr/   r3   TFc                     | r$|                     t          |                     p|nd}|rt          |          }|r|                                }|S )u   
    A basic function that turns text full of scary characters
    (i.e., punctuation and whitespace), into a relatively safe
    lowercased string separated only by the delimiter specified
    by *delim*, which defaults to ``_``.

    The *ascii* convenience flag will :func:`asciify` the slug if
    you require ascii-only slugs.

    >>> slugify('First post! Hi!!!!~1    ')
    'first_post_hi_1'

    >>> slugify("Kurt Gödel's pretty cool.", ascii=True) ==         b'kurt_goedel_s_pretty_cool'
    True

    r1   )r9   r   r   r,   )textdelimr,   asciirets        r.   r
   r
   X   sW    $ 8<
C%**^D))
*
*
3eC cll iikkJr/   c                 J    d t                               |           D             S )a  While :meth:`str.split` will split on whitespace,
    :func:`split_punct_ws` will split on punctuation and
    whitespace. This used internally by :func:`slugify`, above.

    >>> split_punct_ws('First post! Hi!!!!~1    ')
    ['First', 'post', 'Hi', '1']
    c                     g | ]}||S  rC   r5   s     r.   
<listcomp>z"split_punct_ws.<locals>.<listcomp>z   s    222!2A222r/   )	_punct_rer:   r=   s    r.   r   r   r   s$     32yt,,2222r/   itemc                 \    t          |           }t          ||          }|r| d| S d| S )a;  Returns a plain-English description of an iterable's
    :func:`len()`, conditionally pluralized with :func:`cardinalize`,
    detailed below.

    >>> print(unit_len(range(10), 'number'))
    10 numbers
    >>> print(unit_len('aeiou', 'vowel'))
    5 vowels
    >>> print(unit_len([], 'worry'))
    No worries
     zNo )lenr   )sized_iterable	unit_nouncountunitss       r.   r   r   }   sH     E	5))E "!!%!!!==r/   stndrd)123c                 (   t          |           d}}|rw|d         t          j        v rc	 |d         dk    rd}n!t                              |d         d          }n1# t
          $ r$ t                              |d         d          }Y nw xY w|r|S ||z   S )a  Turns *number* into its cardinal form, i.e., 1st, 2nd,
    3rd, 4th, etc. If the last character isn't a digit, it returns the
    string value unchanged.

    Args:
        number (int or str): Number to be cardinalized.
        ext_only (bool): Whether to return only the suffix. Default ``False``.

    >>> print(ordinalize(1))
    1st
    >>> print(ordinalize(3694839230))
    3694839230th
    >>> print(ordinalize('hi'))
    hi
    >>> print(ordinalize(1515))
    1515th
    r1   rR   th)strstringdigits_ORDINAL_MAPget
IndexError)numberext_onlynumstrexts       r.   r   r      s    $ f++rCF 
5&*--		5bzS   #&&vbz488 	5 	5 	5""6":t44CCC	5  
|s   0A +BBc                 0    |dk    r| S t          |           S )a  Conditionally pluralizes a singular word *unit_noun* if
    *count* is not one, preserving case when possible.

    >>> vowels = 'aeiou'
    >>> print(len(vowels), cardinalize('vowel', len(vowels)))
    5 vowels
    >>> print(3, cardinalize('Wish', 3))
    3 Wishes
       )r   )rL   rM   s     r.   r   r      s!     zzYr/   c                    | |                                                                  } }| r	| t          v r|S t                              |           }|r|}n|                     d          s|S t          |           dk    r| dd         }ne|                     d          r| dd         dvr| dd         d	z   }n6|                     d
          r| d         dk    r| dd         }n
| dd         }t          ||          S )a  Semi-intelligently converts an English plural *word* to its
    singular form, preserving case pattern.

    >>> singularize('chances')
    'chance'
    >>> singularize('Activities')
    'Activity'
    >>> singularize('Glasses')
    'Glass'
    >>> singularize('FEET')
    'FOOT'

    s   NrV   iesaeiouyesrW   )stripr,   _IRR_S2P_IRR_P2Sr]   endswithrJ   _match_case)word	orig_wordirr_singularsingulars       r.   r   r      s    DJJLL..00tI 48##<<%%L ]]3 		Ta9	u		 $r"u+W"<"<9s?	t		 bS99y(+++r/   c                    | |                                                                  } }| r	| t          v r|S t                              |           }|r|}n|                     d          r| dd         dvr| dd         dz   }nX| d         dk    s*|                     d          s|                     d	          r|                     d
          r| n| d
z   }n| dz   }t          ||          S )zSemi-intelligently converts an English *word* from singular form to
    plural, preserving case pattern.

    >>> pluralize('friend')
    'friends'
    >>> pluralize('enemy')
    'enemies'
    >>> pluralize('Sheep')
    'Sheep'
    rl   rW   rV   rk   Nrh   rf   chshrm   )rn   r,   rp   ro   r]   rq   rr   )rs   rt   
irr_pluralplurals       r.   r   r      s     DJJLL..00tI 48##d##J 	s		 RU7 : :crcU"	bSDMM$//4==3F3Ft,,=$+y&)))r/   c                 :   |                                  s|S |                                 | k    r|                                S |                                 | k    r|                                S |                                 | k    r|                                S |S N)rn   r,   uppertitle)masterdisciples     r.   rr   rr     s    <<>> ||~~~~	6	!	!~~	6	!	!~~Or/   addendumaddendaalgaalgaealumnaalumnaealumnusalumnianalysisanalysesantennaantennaeappendix
appendicesaxisaxesbacillusbacilli	bacteriumbacteriabasisbasesbeaubeauxbisonbureaubureauscactuscacticalfcalveschildchildrencorpscorpuscorporacrisiscrises	criterioncriteria
curriculum	curriculadatumdatadeer	diagnosis	diagnosesdiedicedwarfdwarvesechoechoeselfelvesellipsisellipsesembargo	embargoesemphasisemphaseserratumerratafiremanfiremenfishfocusfocifootfeetformulaformulaeformulasfungusfungigenusgeneragoosegeesehalfhalvesheroheroeshippopotamushippopotamihoofhooves
hypothesis
hypothesesindexindicesknifeknivesleafleaveslifelivesloafloaveslouselicemanmenmatrixmatricesmeansmediummedia
memorandum	memoranda
millenniummilenniamoosemosquito
mosquitoesmousemicenebulanebulaeneurosisneurosesnucleusnucleioasisoasesoctopusoctopi	offspringovumovaoxoxen	paralysis	paralysesparenthesisparenthesespersonpeople
phenomenon	phenomenapotatopotatoesradiusradiiscarfscarvesscissorsselfselvessensesensesseriessheepshelfshelvesspeciesstimulusstimulistratumstratasyllabussyllabi	symposiumsymposiasynopsissynopses	synthesis	synthesestableautableauxthatthosethesisthesesthiefthievesthisthesetomatotomatoestoothteethtorpedo	torpedoesvertebra	vertebraevetovetoesvitavitaewatchwatcheswiveswolveswomen)wifewolfwomanc                     i | ]\  }}||	S rC   rC   )r6   kvs      r.   
<dictcomp>rK  7  s    ...TQAq...r/   u   (?:^|\s)[＃#]{1}(\w+)c                 6    t                               |           S )a.  Finds and returns all hashtags in a string, with the hashmark
    removed. Supports full-width hashmarks for Asian languages and
    does not false-positive on URL anchors.

    >>> find_hashtags('#atag http://asite/#ananchor')
    ['atag']

    ``find_hashtags`` also works with unicode hashtags.
    )
HASHTAG_REfindallrZ   s    r.   r   r   <  s     f%%%r/   c                 ~    t          |           dk     r| S | d          t          | dd                    | d          S )aO  That thing where "internationalization" becomes "i18n", what's it
    called? Abbreviation? Oh wait, no: ``a10n``. (It's actually a form
    of `numeronym`_.)

    >>> a10n('abbreviation')
    'a10n'
    >>> a10n('internationalization')
    'i18n'
    >>> a10n('')
    ''

    .. _numeronym: http://en.wikipedia.org/wiki/Numeronym
       r   rd   rV   )rJ   rO  s    r.   r   r   M  sF     6{{QQi8VAbD\**8F2J888r/   uP  
    \x1B            # Sequence starts with ESC, i.e. hex 0x1B
    (?:
        [@-Z\\-_]   # Second byte:
                    #   all 0x40–0x5F range but CSI char, i.e ASCII @A–Z\]^_
    |               # Or
        \[          # CSI sequences, starting with [
        [0-?]*      # Parameter bytes:
                    #   range 0x30–0x3F, ASCII 0–9:;<=>?
        [ -/]*      # Intermediate bytes:
                    #   range 0x20–0x2F, ASCII space and !"#$%&'()*+,-./
        [@-~]       # Final byte
                    #   range 0x40–0x7E, ASCII @A–Z[\]^_`a–z{|}~
    )
c                    d}t          | t          t          f          r$t          |           }|                     d          } t
                              d|           }|r|t          |          k    r ||d          }|S )a  Strips ANSI escape codes from *text*. Useful for the occasional
    time when a log or redirected output accidentally captures console
    color codes and the like.

    >>> strip_ansi('[0m[1;36mart[46;34m')
    'art'

    Supports str, bytes and bytearray content as input. Returns the
    same type as the input.

    There's a lot of ANSI art available for testing on `sixteencolors.net`_.
    This function does not interpret or render ANSI art, but you can do so with
    `ansi2img`_ or `escapes.js`_.

    .. _sixteencolors.net: http://sixteencolors.net
    .. _ansi2img: http://www.bedroomlan.org/projects/ansi2img
    .. _escapes.js: https://github.com/atdt/escapes.js
    Nutf-8r1   )
isinstancebytes	bytearraytypedecodeANSI_SEQUENCESr+   )r=   target_typecleaneds      r.   r   r   r  s    . K$	*++ $4jj{{7##  T**G  0{d7mm33+gw//Nr/   c                 Z   	 	 |                      d          S # t          $ r- |                     d          } |                      d          cY S w xY w# t          $ rN d}|rd}t	          j        d|                     t                              }|                     d|          }|cY S w xY w)uA  Converts a unicode or bytestring, *text*, into a bytestring with
    just ascii characters. Performs basic deaccenting for all you
    Europhiles out there.

    Also, a gentle reminder that this is a **utility**, primarily meant
    for slugification. Whenever possible, make your application work
    **with** unicode, not against it.

    Args:
        text (str): The string to be asciified.
        ignore (bool): Configures final encoding to ignore remaining
            unasciified string instead of replacing it.

    >>> asciify('Beyoncé') == b'Beyonce'
    True
    r?   rS  replaceignoreNFKD)encodeUnicodeDecodeErrorrX  UnicodeEncodeErrorunicodedata	normalize	translateDEACCENT_MAP)r=   r^  modetransdr@   s        r.   r   r     s    "	(;;w'''! 	( 	( 	(;;w''D;;w'''''	(     	D&vt~~l/K/KLLmmGT**


s(    4AA AA AB*)B*c                 8   t          | t                    r(	 |                     d           nm# t          $ r Y dS w xY wt          | t                    r(	 |                     d           n0# t          $ r Y dS w xY wt          dt          |           z            dS )u  Check if a string or bytestring, *text*, is composed of ascii
    characters only. Raises :exc:`ValueError` if argument is not text.

    Args:
        text (str): The string to be checked.

    >>> is_ascii('Beyoncé')
    False
    >>> is_ascii('Beyonce')
    True
    r?   Fzexpected text or bytes, not %rT)	rT  rY   r`  rb  rU  rX  ra  
ValueErrorrW  rF   s    r.   r   r     s     $ H	KK    ! 	 	 	55		D%	 	  H	KK    ! 	 	 	55	 9DJJFGGG4s   - 
;;A* *
A87A8c                       e Zd ZdZd ZdS )DeaccenterDictz+A small caching dictionary for deaccenting.c                    |                      |          }||S 	 t          j        t          |                    }|                    d          \  }}}t          |d          dk    r|                      |          }nt          |d          }n-# t          t          f$ r |                      ||          }Y nw xY w|| |<   |S )NrI      i  )r]   rc  decompositionchr
rpartitionintr^   rj  )r  keyrx   dep1r3   p2s          r.   __missing__zDeaccenterDict.__missing__  s    XXc]]>I	$*3s8844Bc**IB22r{{e##XXc]]R[[J' 	$ 	$ 	$#s##BBB	$S		s   A4B 'B:9B:N)__name__
__module____qualname____doc__rw  rC   r/   r.   rl  rl    s)        11    r/   rl     AE   D   OE   Th   Ae   Oe   Ue   A         C   E         I      O         U      ss   ae   d   oe   rX            ue   a         c   eiou'")                           i   i   i   i   )	BKMGTPr  ZYc                 "    g | ]\  }}d |z  |fS )i   rC   )r6   r  syms      r.   rD   rD   "  s%    HHHVQC HHHr/   rd   c                     t          |           }t          D ]\  \  }}\  }}||k    r nt          |           |z  }d                    |||          S )a&  Turns an integer value of *nbytes* into a human readable format. Set
    *ndigits* to control how many digits after the decimal point
    should be shown (default ``0``).

    >>> bytes2human(128991)
    '126K'
    >>> bytes2human(100001221)
    '95M'
    >>> bytes2human(0, 2)
    '0.00B'
    z{hnbytes:.{ndigits}f}{symbol})hnbytesndigitssymbol)abs_SIZE_RANGESfloatformat)nbytesr  	abs_bytessizer  	next_sizenext_symbolr  s           r.   r   r   &  sy     FI4@  0v0K	!!E "Fmmd"G*11':A9? 2 A A Ar/   c                   ,    e Zd Zd Zd Zd Zd Zd ZdS )HTMLTextExtractorc                 X    |                                   d| _        d| _        g | _        d S )NFT)resetstrictconvert_charrefsresultr  s    r.   __init__zHTMLTextExtractor.__init__=  s)    

 $r/   c                 :    | j                             |           d S r}   )r  append)r  r  s     r.   handle_datazHTMLTextExtractor.handle_dataC  s    1r/   c                     |d         dk    s|d         dk    rt          |dd          d          }nt          |          }| j                            t          |                     d S )Nr   xXrd   rn  )rr  r  r  rp  )r  r_   	codepoints      r.   handle_charrefz HTMLTextExtractor.handle_charrefF  se    !9vayC//F122J++IIFI3y>>*****r/   c                     	 t           j        |         }| j                            t	          |                     d S # t
          $ r$ | j                            d|z   dz              Y d S w xY w)N&;)htmlentitydefsname2codepointr  r  rp  KeyError)r  namer  s      r.   handle_entityrefz"HTMLTextExtractor.handle_entityrefM  s}    	/&5d;I Ks9~~.....  	1 	1 	1KsTzC/000000	1s   = *A+*A+c                 6    d                     | j                  S )Nr1   )r9   r  r  s    r.   get_textzHTMLTextExtractor.get_textU  s    wwt{###r/   N)rx  ry  rz  r  r  r  r  r  rC   r/   r.   r  r  <  s_            + + +/ / /$ $ $ $ $r/   r  c                 p    t                      }|                    |            |                                S )u  Strips tags from HTML text, returning markup-free text. Also, does
    a best effort replacement of entities like "&nbsp;"

    >>> r = html2text(u'<a href="#">Test &amp;<em>(Δ&#x03b7;&#956;&#x03CE;)</em></a>')
    >>> r == u'Test &(Δημώ)'
    True
    )r  feedr  )htmlrf   s     r.   r   r   Y  s,     	AFF4LLL::<<r/   s   9U empty          s)   U not_empty K,I-NȯT nb   c                 F    t          j        | dt           j        z             S )a  The :mod:`gzip` module is great if you have a file or file-like
    object, but what if you just have bytes. StringIO is one
    possibility, but it's often faster, easier, and simpler to just
    use this one-liner. Use this tried-and-true utility function to
    decompress gzip from bytes.

    >>> gunzip_bytes(_EMPTY_GZIP_BYTES) == b''
    True
    >>> gunzip_bytes(_NON_EMPTY_GZIP_BYTES).rstrip() == b'bytesahoy!'
    True
    rn  )zlib
decompress	MAX_WBITS)
bytestrings    r.   r   r   k  s     ?:rDN':;;;r/      c                     t                      }t          |d|          }|                    |            |                                 |                                S )a  Turn some bytes into some compressed bytes.

    >>> len(gzip_bytes(b'a' * 10000))
    46

    Args:
        bytestring (bytes): Bytes to be compressed
        level (int): An integer, 1-9, controlling the
          speed/compression. 1 is fastest, least compressed, 9 is
          slowest, but most compressed.

    Note that all levels of gzip are pretty fast these days, though
    it's not really a competitor in compression, at any level.
    wb)fileobjrg  compresslevel)StringIOr   writeclosegetvalue)r  leveloutfs       r.   r   r   z  sN     **C4u===AGGJGGIII<<>>r/   z'(\r\n|\n|\x0b|\f|\r|\x85|\x2028|\x2029)c              #     K   dt          |           }}t                              |           D ]J}|                    d          |                    d          }}||k    r| ||         V  ||k    rdV  |}K| |d         }|r|V  dS )a  Like :meth:`str.splitlines`, but returns an iterator of lines
    instead of a list. Also similar to :meth:`file.next`, as that also
    lazily reads and yields lines from a file.

    This function works with a variety of line endings, but as always,
    be careful when mixing line endings within a file.

    >>> list(iter_splitlines('\nhi\nbye\n'))
    ['', 'hi', 'bye', '']
    >>> list(iter_splitlines('\r\nhi\rbye\r\n'))
    ['', 'hi', 'bye', '']
    >>> list(iter_splitlines(''))
    []
    r   rd   r1   N)rJ   _line_ending_refinditerstartend)r=   prev_endlen_textmatchr
  r  tails          r.   r   r     s       CIIhH !))$//  [[^^UYYq\\sux~&&&&(??HHH		?D 



Fr/   
c                 h    fdt          |           D             }|                    |          S )a  The missing counterpart to the built-in :func:`textwrap.dedent`.

    Args:
        text (str): The text to indent.
        margin (str): The string to prepend to each line.
        newline (str): The newline used to rejoin the lines (default: ``\n``)
        key (callable): Called on each line to determine whether to
          indent it. Default: :class:`bool`, to ensure that empty lines do
          not get whitespace added.
    c                 4    g | ]} |          r|z   n|S rC   rC   )r6   liners  margins     r.   rD   zindent.<locals>.<listcomp>  s>     9 9 9 ),D		;v}}t 9 9 9r/   )r   r9   )r=   r  newliners  indented_liness    ` ` r.   r   r     sK    9 9 9 9 9"1$"7"79 9 9N<<'''r/      c                     t          | t          j                  s4	 t          j        |           } n# t          t          t
          f$ r Y dS w xY w|r| j        t          |          k    rdS dS )a  Check the argument is either a valid UUID object or string.

    Args:
        obj (object): The test target. Strings and UUID objects supported.
        version (int): The target UUID version, set to 0 to skip version check.

    >>> is_uuid('e682ccca-5a4c-4ef2-9711-73f9ad1e15ea')
    True
    >>> is_uuid('0221f0d9-d4b9-11e5-a478-10ddb1c2feb9')
    False
    >>> is_uuid('0221f0d9-d4b9-11e5-a478-10ddb1c2feb9', version=1)
    True
    FT)rT  uuidUUID	TypeErrorrj  AttributeErrorversionrr  )objr  s     r.   r   r     sy     c49%% 	)C..CC:~6 	 	 	55	 3;#g,,..u4s   1 AArI   c                     |st           j        dk    rdnd}|dk    rt          | |          S |dk    rt          | |          S t	          d|z            )a?  Returns an escaped version of each string in *args*, according to
    *style*.

    Args:
        args (list): A list of arguments to escape and join together
        sep (str): The separator used to join the escaped arguments.
        style (str): The style of escaping to use. Can be one of
          ``cmd`` or ``sh``, geared toward Windows and Linux/BSD/etc.,
          respectively. If *style* is ``None``, then it is picked
          according to the system platform.

    See :func:`args2cmd` and :func:`args2sh` for details and example
    output for each style.
    win32cmdry   )sepz+style expected one of 'cmd' or 'sh', not %r)sysplatformr   r   rj  )argsr"  styles      r.   r   r     sm      ;00d}}t%%%%	%#&&&&
BUJ
K
KKr/   z[^a-zA-Z0-9_@%+=:,./-]c                    g }| D ]n}|s|                     d           t          |          |                     |           ?|                     d|                    dd          z   dz              od                    |          S )a  Return a shell-escaped string version of *args*, separated by
    *sep*, based on the rules of sh, bash, and other shells in the
    Linux/BSD/MacOS ecosystem.

    >>> print(args2sh(['aa', '[bb]', "cc'cc", 'dd"dd']))
    aa '[bb]' 'cc'"'"'cc' 'dd"dd'

    As you can see, arguments with no special characters are not
    escaped, arguments with special characters are quoted with single
    quotes, and single quotes themselves are quoted with double
    quotes. Double quotes are handled like any other special
    character.

    Based on code from the :mod:`pipes`/:mod:`shlex` modules. Also
    note that :mod:`shlex` and :mod:`argparse` have functions to split
    and parse strings escaped in this manner.
    z''Nr  z'"'"'rI   )r  _find_sh_unsafer]  r9   )r%  r"  ret_listargs       r.   r   r     s    $ H 	A 	A 	OOD!!!3'OOC    	ckk#y999C?@@@@88Hr/   c                 j   	 g }d}| D ]}g }|r|                     d           d|v pd|v p| }|r|                     d           |D ]}|dk    r|                     |           |dk    r@|                     dt          |          z  dz             g }|                     d           d|r|                    |           g }|                     |           |r|                    |           |r*|                    |           |                     d           d                    |          S )	a  Return a shell-escaped string version of *args*, separated by
    *sep*, using the same rules as the Microsoft C runtime.

    >>> print(args2cmd(['aa', '[bb]', "cc'cc", 'dd"dd']))
    aa [bb] cc'cc dd\"dd

    As you can see, escaping is through backslashing and not quoting,
    and double quotes are the only special character. See the comment
    in the code for more details. Based on internal code from the
    :mod:`subprocess` module.

    FrI   	r  \rg   z\"r1   )r  rJ   extendr9   )r%  r"  r  	needquoter*  bs_bufr  s          r.   r   r     sw   2 FI ! !  	MM#CZ<TS[<W	 	MM# 	! 	!ADyya    cdS[[02333e$$$$   MM&)))Fa      	"MM&!!! 	MM&!!!MM#776??r/   ,-c           
         g }|                                                      |          D ]}||v rst          t          t          |                    |                              }|t          t          t          |          t          |          dz                       z  }y|s||                    t	          |                     t          |          S )aT  Returns a sorted list of positive integers based on
    *range_string*. Reverse of :func:`format_int_list`.

    Args:
        range_string (str): String of comma separated positive
            integers or ranges (e.g. '1,2,4-6,8'). Typical of a custom
            page range string used in printer dialogs.
        delim (char): Defaults to ','. Separates integers and
            contiguous ranges of integers.
        range_delim (char): Defaults to '-'. Indicates a contiguous
            range of integers.

    >>> parse_int_list('1,3,5-8,10-11,15')
    [1, 3, 5, 6, 7, 8, 10, 11, 15]

    rd   )
rn   r:   listmaprr  rangeminmaxr  sorted)range_stringr>   range_delimoutputr  range_limitss         r.   r    r    k  s    " F!!''.. " " !C)=)= > >??Ld5\!2!2C4E4Ea4GHHIIIFF  	" MM#a&&!!!!&>>r/   c                    g }t          j                    }t          |           D ]K}t          |          dk     r|                    |           ,t          |          dk    r||d         z
  }|dk    r|                    |           f|dk    rpd                    t          |          |t          |                    }|                    |           |                                 |                    |           ܌||d         z
  }|dk    r|                    |           |dk    r@|                    |	                                d           |                    |           KMt          |          dk    r>|                    |	                                d           |                                 nmt          |          dk    rZd                    t          |          |t          |                    }|                    |           |                                 |r|dz   
                    |          }	n|
                    |          }	|	S )a  Returns a sorted range string from a list of positive integers
    (*int_list*). Contiguous ranges of integers are collapsed to min
    and max values. Reverse of :func:`parse_int_list`.

    Args:
        int_list (list): List of positive integers to be converted
           into a range string (e.g. [1,2,4,5,6,8]).
        delim (char): Defaults to ','. Separates integers and
           contiguous ranges of integers.
        range_delim (char): Defaults to '-'. Indicates a contiguous
           range of integers.
        delim_space (bool): Defaults to ``False``. If ``True``, adds a
           space after all *delim* characters.

    >>> format_int_list([1,3,5,6,7,8,10,11,15])
    '1,3,5-8,10-11,15'

    rd   rV   z
{:d}{}{:d}r   r  rI   )collectionsdequer9  rJ   r  r  r7  r8  clearpopleftr9   )
int_listr>   r;  delim_spacer<  contig_ranger  deltarange_substr
output_strs
             r.   r!   r!     s   & F$&&LH :! :! |q  """" ""R((E zz##A&&&& +223|3D3D6A69,6G6G I  I l+++""$$$##A&&&&  Q'E zz##A&&&& !5!5!7!7;;<<<##A&&&&  |!!MM\113377888     ""'..s</@/@2=25l2C2CE EL MM,'''    (Ci%%f--

ZZ''
r/   c                 
   t          t          | ||                    }||rt          |          dz   }n|}t          t          |                    |z
  t          t          |                    z
  }t	          |||          S )a 	   Returns range string that is the complement of the one provided as
    *range_string* parameter.

    These range strings are of the kind produce by :func:`format_int_list`, and
    parseable by :func:`parse_int_list`.

    Args:
        range_string (str): String of comma separated positive integers or
           ranges (e.g. '1,2,4-6,8'). Typical of a custom page range string
           used in printer dialogs.
        range_start (int): A positive integer from which to start the resulting
           range. Value is inclusive. Defaults to ``0``.
        range_end (int): A positive integer from which the produced range is
           stopped. Value is exclusive. Defaults to the maximum value found in
           the provided ``range_string``.
        delim (char): Defaults to ','. Separates integers and contiguous ranges
           of integers.
        range_delim (char): Defaults to '-'. Indicates a contiguous range of
           integers.

    >>> complement_int_list('1,3,5-8,10-11,15')
    '0,2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_start=0)
    '0,2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_start=1)
    '2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_start=2)
    '2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_start=3)
    '4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=15)
    '0,2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=14)
    '0,2,4,9,12-13'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=13)
    '0,2,4,9,12'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=20)
    '0,2,4,9,12-14,16-19'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=0)
    ''

    >>> complement_int_list('1,3,5-8,10-11,15', range_start=-1)
    '0,2,4,9,12-14'

    >>> complement_int_list('1,3,5-8,10-11,15', range_end=-1)
    ''

    >>> complement_int_list('1,3,5-8', range_start=1, range_end=1)
    ''

    >>> complement_int_list('1,3,5-8', range_start=2, range_end=2)
    ''

    >>> complement_int_list('1,3,5-8', range_start=2, range_end=3)
    '2'

    >>> complement_int_list('1,3,5-8', range_start=-10, range_end=-5)
    ''

    >>> complement_int_list('1,3,5-8', range_start=20, range_end=10)
    ''

    >>> complement_int_list('')
    ''
    Nrd   )setr    r8  r6  r!   )r:  range_start	range_endr>   r;  rC  complement_valuess          r.   r"   r"     s    Z >,{CCDDH 	$H)II#Ii $%'*5+=+='>'>?,e[AAAr/   c                 8   g }t          t          | ||                    } | rj|                     d          D ]T}d|v r|                    d          \  }}n||}}|                    t	          |          t	          |          f           Ut          |          S )a   Transform a string of ranges (*range_string*) into a tuple of tuples.

    Args:
        range_string (str): String of comma separated positive integers or
           ranges (e.g. '1,2,4-6,8'). Typical of a custom page range string
           used in printer dialogs.
        delim (char): Defaults to ','. Separates integers and contiguous ranges
           of integers.
        range_delim (char): Defaults to '-'. Indicates a contiguous range of
           integers.

    >>> int_ranges_from_int_list('1,3,5-8,10-11,15')
    ((1, 1), (3, 3), (5, 8), (10, 11), (15, 15))

    >>> int_ranges_from_int_list('1')
    ((1, 1),)

    >>> int_ranges_from_int_list('')
    ()
    r1  r2  )r!   r    r:   r  rr  tuple)r:  r>   r;  
int_tuplesboundsr
  r  s          r.   r#   r#   B  s    * J"|UK88: :L 6"((-- 	6 	6Ff}}#\\#..
ss#Vss5zz3s8845555r/   c                   $    e Zd ZdZd Zd Zd ZdS )r$   a  
    MultiReplace is a tool for doing multiple find/replace actions in one pass.

    Given a mapping of values to be replaced it allows for all of the matching
    values to be replaced in a single pass which can save a lot of performance
    on very large strings. In addition to simple replace, it also allows for
    replacing based on regular expressions.

    Keyword Arguments:

    :type regex: bool
    :param regex: Treat search keys as regular expressions [Default: False]
    :type flags: int
    :param flags: flags to pass to the regex engine during compile

    Dictionary Usage::

        from boltons import strutils
        s = strutils.MultiReplace({
            'foo': 'zoo',
            'cat': 'hat',
            'bat': 'kraken'
        })
        new = s.sub('The foo bar cat ate a bat')
        new == 'The zoo bar hat ate a kraken'

    Iterable Usage::

        from boltons import strutils
        s = strutils.MultiReplace([
            ('foo', 'zoo'),
            ('cat', 'hat'),
            ('bat', 'kraken)'
        ])
        new = s.sub('The foo bar cat ate a bat')
        new == 'The zoo bar hat ate a kraken'


    The constructor can be passed a dictionary or other mapping as well as
    an iterable of tuples. If given an iterable, the substitution will be run
    in the order the replacement values are specified in the iterable. This is
    also true if it is given an OrderedDict. If given a dictionary then the
    order will be non-deterministic::

        >>> 'foo bar baz'.replace('foo', 'baz').replace('baz', 'bar')
        'bar bar bar'
        >>> m = MultiReplace({'foo': 'baz', 'baz': 'bar'})
        >>> m.sub('foo bar baz')
        'baz bar bar'

    This is because the order of replacement can matter if you're inserting
    something that might be replaced by a later substitution. Pay attention and
    if you need to rely on order then consider using a list of tuples instead
    of a dictionary.
    c                 :   ddd}|                     |           i | _        g }t          |t                    r|                                }t          |          D ]\  }}d| }t          |d         t                    r,|d         st          j        |d                   }n|d         }n|d         j	        }|
                    d| d| d           |d	         | j        |<   t          j        d
                    |          |d                   | _        dS )z6Compile any regular expressions that have been passed.Fr   )regexflagsgrouprT  z(?P<>)rd   |rU  )rU  N)update	group_maprT  r   items	enumeraterY   reescapepatternr  compiler9   combined_pattern)	r  sub_mapkwargsoptionsregex_valuesidxvals
group_nameexps	            r.   r  zMultiReplace.__init__  s:    
 
 	vgw'' 	&mmooG"7++ 	1 	1IC&J$q'3'' &w' ")DG,,CCq'CC1go :z : :C : : :;;;)-aDN:&& "
HH\""'"!
 !
 !
r/   c                 n    |                                 fdD             d         }| j        |         S )z,Given a match object find replacement value.c                 $    g | ]}|         
|S rC   rC   )r6   r  
group_dicts     r.   rD   z+MultiReplace._get_value.<locals>.<listcomp>  s"    666Q
16q666r/   r   )	groupdictr[  )r  r  rs  rm  s      @r.   
_get_valuezMultiReplace._get_value  s=    __&&
6666*666q9~c""r/   c                 B    | j                             | j        |          S )z
        Run substitutions on the input text.

        Given an input string, run all substitutions given in the
        constructor.
        )rb  r+   ro  )r  r=   s     r.   r+   zMultiReplace.sub  s     $(($???r/   N)rx  ry  rz  r{  r  ro  r+   rC   r/   r.   r$   r$   e  sR        6 6p
 
 
@# # #@ @ @ @ @r/   r$   c                 F    t          |fi |}|                    |           S )aE  
    Shortcut function to invoke MultiReplace in a single call.

    Example Usage::

        from boltons.strutils import multi_replace
        new = multi_replace(
            'The foo bar cat ate a bat',
            {'foo': 'zoo', 'cat': 'hat', 'bat': 'kraken'}
        )
        new == 'The zoo bar hat ate a kraken'
    )r$   r+   )r=   rc  rd  ms       r.   r%   r%     s)     	W''''A55;;r/   

c                 j   g }g }|                                  D ]X}|                                }|r|                    |           .|                    d                    |                     g }Y|r(|                    d                    |                     ||S |                    |          S )a  
    Unwrap text, the natural complement to :func:`textwrap.wrap`.

    >>> text = "Short \n lines  \nwrapped\nsmall.\n\nAnother\nparagraph."
    >>> unwrap_text(text)
    'Short lines wrapped small.\n\nAnother paragraph.'

    Args:
       text: A string to unwrap.
       ending (str): The string to join all unwrapped paragraphs
          by. Pass ``None`` to get the list. Defaults to '\n\n' for
          compatibility with Markdown and RST.

    rI   )
splitlinesrn   r  r9   )r=   ending	all_grafscur_grafr  s        r.   r&   r&     s     IH!!  zz|| 	OOD!!!!SXXh//000HH -(++,,,~;;y!!!r/   r=   prefixreturnc                 ^    |                      |          r| t          |          d         S | S )a  
    Remove `prefix` from start of `text` if present.

    Backport of `str.removeprefix` for Python versions less than 3.9.

    Args:
        text: A string to remove the prefix from.
        prefix: The string to remove from the beginning of `text`.
    N)
startswithrJ   )r=   ry  s     r.   r'   r'     s2     v "CKKLL!!Kr/   )r3   TF)rG   )F)r   )r  )r  )rI   N)rI   )r1  r2  )r1  r2  F)r   Nr1  r2  )rs  )Wr{  builtinsr^  r#  r  r  rZ   rc  r?  collections.abcr   gzipr   html.parserr   r  r   r  ior   r   __all__punctuation
whitespace_punct_ws_strra  rE   r*   r   r	   r
   r   r   r\   r   r   r   r   rr   ro   r\  rp   UNICODErM  r   r   VERBOSErY  r   r   r   dictrl  _BASE_DEACCENT_MAPrf  _SIZE_SYMBOLSr]  _SIZE_BOUNDSr4  zipr  r   r  r   _EMPTY_GZIP_BYTES_NON_EMPTY_GZIP_BYTESr   r   r  r   boolr   r   r   searchr(  r   r   r    r!   r"   r#   r$   r%   r&   rY   r'   rC   r/   r.   <module>r     sA  >   				 



            # # # # # #       " " " " " " + + + + + + " " " " " "; ; ; "V%66BJs]*T122	"*GHH= = =K K K   43 3 3   &  
! ! ! !H     , , ,D* * *4	 	 	"0J	 "067 "0Hi "0x"0!+Z"09BJ"0"0&,f"06@)"0 "0 &-g"0 8>w"0 W	"0 '		"0 4<W	"0
 H"0
 &z"0 "0
 4;G"0 i"0 "*8"0 6A*"0 +"0 (/"0 9?"0 "0 ',V"0 6=i"0 H"0 $W"0 /9*"0 {"0 %/
"0 =Fx"0 y"0 "0 "0 #)&"0 3:6"0 F"0 &z"0 4=j"0 g"0  '"0 3:7"0 H"0 %h"0 1?"0 H"0 +L"0 ;B9"0  X!"0   &x!"0  28!"0 "0 "0" H#"0" &v#"0" 05e#"0$ j%"0$ #*7%"0$ 5=g%"0& +'"0& (4Z'"0& BI''"0( )"0( '.v)"0( 8@)"0* 
+"0* %.x+"0* :A'+"0, x-"0, "-k-"0 "0 "0, <B5-"0. &/"0. &{/"0. 5B=/"00 h1"00 !-k1"00 <DZ1"02 g3"02  '	3"02 4>z3"04 H5"04 &x5"04 2:85"04 FM7"06 i7"06 *3I7"06 @J9"0 "0 "08 !(9"08 -7	9"08 DO;"0: #J;"0: 1<[;"0< z="0< $*7="0< 5=h="0> Y?"0> !'?"0> 2::?"0> HOA"0@ A"0@ .8A"0@ GMC"0B gC"0B (/	C"0 "0B DKwE"0 "0 "0L /.X^^--...RZ12:>>
& & &"9 9 9(   Z "# # #L   B  6    T   .0$0#0 	$0 	$	0
 	$0 	$0 	$0 	#0 	#0 	#0 	#0 	#0 	#0 	#0 	#0  	#!0" 	##0 0$ 	#%0& 	#'0( 	#)0* 	#+0, 	$-0. 	$/00 	#102 	$304 	$506 	$708 	$90: 	$;0< 	#=0> 	#?0@ 	#A0B 	#C0D 	#E0 0F 







_0 0 0 f ~011 >HHyy/G/GHHHtCCl122&67788A A A A,$ $ $ $ $
 $ $ $:   k  J < < <   . "*GZ) )  @ "&4 ( ( ( (    0L L L L4 "*677>   DL L L L^" " " "JW W W Wv 04"UB UB UB UBp       Ff@ f@ f@ f@ f@ f@ f@ f@R  "" " " "<s C C      r/   