a
    I”DgÈ5  ã                   @   sÂ   d 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
mZmZ dZdZdZe d	ej¡Ze d
¡Ze e¡Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ ZdS )a  
Parsing and reformatting of usage messages.

The :mod:`~humanfriendly.usage` module parses and reformats usage messages:

- The :func:`format_usage()` function takes a usage message and inserts ANSI
  escape sequences that highlight items of special significance like command
  line options, meta variables, etc. The resulting usage message is (intended
  to be) easier to read on a terminal.

- The :func:`render_usage()` function takes a usage message and rewrites it to
  reStructuredText_ suitable for inclusion in the documentation of a Python
  package. This provides a DRY solution to keeping a single authoritative
  definition of the usage message while making it easily available in
  documentation. As a cherry on the cake it's not just a pre-formatted dump of
  the usage message but a nicely formatted reStructuredText_ fragment.

- The remaining functions in this module support the two functions above.

Usage messages in general are free format of course, however the functions in
this module assume a certain structure from usage messages in order to
successfully parse and reformat them, refer to :func:`parse_usage()` for
details.

.. _DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
.. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
é    N)Úimport_module)ÚStringIO)ÚdedentÚsplit_paragraphsÚtrim_empty_lines)Úfind_meta_variablesÚformat_usager   Úinject_usageÚparse_usageÚrender_usageÚUSAGE_MARKERzUsage:úSupported options:aê  
    # Make sure whatever we're matching isn't preceded by a non-whitespace
    # character.
    (?<!\S)
    (
        # A short command line option or a long command line option
        # (possibly including a meta variable for a value).
        (-\w|--\w+(-\w+)*(=\S+)?)
        # Or ...
        |
        # An environment variable.
        \$[A-Za-z_][A-Za-z0-9_]*
        # Or ...
        |
        # Might be a meta variable (usage() will figure it out).
        [A-Z][A-Z0-9_]+
    )
z^(-\w|--\w+(-\w+)*(=\S+)?)$c              	      st   ddl m‰m‰  g }t| ƒ}|  ¡  d¡D ]>}| t¡rL| ˆ|ˆ d¡ q*| t	||‡ ‡fdd„ƒ¡ q*d 
|¡S )aö  
    Highlight special items in a usage message.

    :param usage_text: The usage message to process (a string).
    :returns: The usage message with special items highlighted.

    This function highlights the following special items:

    - The initial line of the form "Usage: ..."
    - Short and long command line options
    - Environment variables
    - Meta variables (see :func:`find_meta_variables()`)

    All items are highlighted in the color defined by
    :data:`.HIGHLIGHT_COLOR`.
    r   )Ú	ansi_wrapÚHIGHLIGHT_COLORT©Úcolorc                    s   ˆ| ˆ dS )Nr   © ©Útoken©r   r   r   úd/mounts/lovelace/software/anaconda3/envs/paleomix/lib/python3.9/site-packages/humanfriendly/usage.pyÚ<lambda>z   ó    zformat_usage.<locals>.<lambda>Ú )Zhumanfriendly.terminalr   r   r   ÚstripÚ
splitlinesÚ
startswithr   ÚappendÚreplace_special_tokensÚjoin)Ú
usage_textZformatted_linesÚmeta_variablesÚliner   r   r   r   \   s    
þr   c                 C   sP   t ƒ }t | ¡D ]6}| d¡}| d¡r| d¡\}}}|r| |¡ qt|ƒS )a‘  
    Find the meta variables in the given usage message.

    :param usage_text: The usage message to parse (a string).
    :returns: A list of strings with any meta variables found in the usage
              message.

    When a command line option requires an argument, the convention is to
    format such options as ``--option=ARG``. The text ``ARG`` in this example
    is the meta variable.
    r   ú-ú=)ÚsetÚUSAGE_PATTERNÚfinditerÚgroupr   Ú	partitionÚaddÚlist)r    r!   Úmatchr   ÚoptionÚ_Úvaluer   r   r   r      s    

r   c                 C   sÐ   g }g }t | ƒ}|r8|d tk}| | d¡¡ |rq8qt d|¡ |r¼| t| d¡ƒ¡ g }|r¦dd„ t d|d ¡D ƒ}t	dd„ |D ƒƒr”q¦q`| | d¡¡ q`| td 
|¡ƒ¡ qDt d	|¡ ||fS )
aÊ  
    Parse a usage message by inferring its structure (and making some assumptions :-).

    :param text: The usage message to parse (a string).
    :returns: A tuple of two lists:

              1. A list of strings with the paragraphs of the usage message's
                 "introduction" (the paragraphs before the documentation of the
                 supported command line options).

              2. A list of strings with pairs of command line options and their
                 descriptions: Item zero is a line listing a supported command
                 line option, item one is the description of that command line
                 option, item two is a line listing another supported command
                 line option, etc.

    Usage messages in general are free format of course, however
    :func:`parse_usage()` assume a certain structure from usage messages in
    order to successfully parse them:

    - The usage message starts with a line ``Usage: ...`` that shows a symbolic
      representation of the way the program is to be invoked.

    - After some free form text a line ``Supported options:`` (surrounded by
      empty lines) precedes the documentation of the supported command line
      options.

    - The command line options are documented as follows::

        -v, --verbose

          Make more noise.

      So all of the variants of the command line option are shown together on a
      separate line, followed by one or more paragraphs describing the option.

    - There are several other minor assumptions, but to be honest I'm not sure if
      anyone other than me is ever going to use this functionality, so for now I
      won't list every intricate detail :-).

      If you're curious anyway, refer to the usage message of the `humanfriendly`
      package (defined in the :mod:`humanfriendly.cli` module) and compare it with
      the usage message you see when you run ``humanfriendly --help`` and the
      generated usage message embedded in the readme.

      Feel free to request more detailed documentation if you're interested in
      using the :mod:`humanfriendly.usage` module outside of the little ecosystem
      of Python packages that I have been building over the past years.
    r   zParsed introduction: %sc                 S   s    g | ]}|r|  ¡ s| ¡ ‘qS r   )Úisspacer   ©Ú.0Útr   r   r   Ú
<listcomp>Þ   r   zparse_usage.<locals>.<listcomp>z,\sc                 s   s   | ]}t  |¡V  qd S ©N)ÚOPTION_PATTERNr,   r1   r   r   r   Ú	<genexpr>ß   r   zparse_usage.<locals>.<genexpr>ú

zParsed options: %s)r   ÚSTART_OF_OPTIONS_MARKERr   ÚpopÚloggerÚdebugr   ÚreÚsplitÚallr   )ÚtextÚintroductionZdocumented_optionsZ
paragraphsZend_of_introÚdescriptionÚtokensr   r   r   r
   •   s(    2r
   c           	         sÞ   t | ƒ‰ t| ƒ\}}‡ fdd„|D ƒ}|r¾| d g d¢¡¡ tƒ }t |¡}|r˜| d¡}| d¡}| t	|ˆ ƒd ‡ fdd„t
|ƒD ƒ¡ ¡ g¡ qN| ¡  ¡ }| d d	d„ |D ƒ¡¡ t d
|¡ d dd„ |D ƒ¡S )zÔ
    Reformat a command line program's usage message to reStructuredText_.

    :param text: The plain text usage message (a string).
    :returns: The usage message rendered to reStructuredText_ (a string).
    c                    s   g | ]}t |ˆ ƒ‘qS r   ©Úrender_paragraph©r2   Úp©r!   r   r   r4   ó   r   z render_usage.<locals>.<listcomp>Ú
)z.. csv-table::z   :header: Option, Descriptionz   :widths: 30, 70r   r   r8   c                 3   s   | ]}t |ˆ ƒV  qd S r5   rD   rF   rH   r   r   r7     r   zrender_usage.<locals>.<genexpr>c                 s   s   | ]}d | V  qdS )z   %sNr   ©r2   r"   r   r   r   r7     r   zRendered output: %sc                 s   s   | ]}t |ƒV  qd S r5   )r   )r2   Úor   r   r   r7     r   )r   r
   r   r   r   ÚcsvÚwriterr:   ÚwriterowrE   r   ÚrstripÚgetvaluer   r;   r<   )	r@   rA   ÚoptionsÚoutputZ
csv_bufferZ
csv_writerÚvariantsrB   Z	csv_linesr   rH   r   r   ê   s$    


þr   c                 C   s,   ddl }t| ƒj}| dt|ƒ d ¡ dS )a‹  
    Use cog_ to inject a usage message into a reStructuredText_ file.

    :param module_name: The name of the module whose ``__doc__`` attribute is
                        the source of the usage message (a string).

    This simple wrapper around :func:`render_usage()` makes it very easy to
    inject a reformatted usage message into your documentation using cog_. To
    use it you add a fragment like the following to your ``*.rst`` file::

       .. [[[cog
       .. from humanfriendly.usage import inject_usage
       .. inject_usage('humanfriendly.cli')
       .. ]]]
       .. [[[end]]]

    The lines in the fragment above are single line reStructuredText_ comments
    that are not copied to the output. Their purpose is to instruct cog_ where
    to inject the reformatted usage message. Once you've added these lines to
    your ``*.rst`` file, updating the rendered usage message becomes really
    simple thanks to cog_:

    .. code-block:: sh

       $ cog.py -r README.rst

    This will inject or replace the rendered usage message in your
    ``README.rst`` file with an up to date copy.

    .. _cog: http://nedbatchelder.com/code/cog/
    r   NrI   r8   )Úcogr   Ú__doc__Úoutr   )Úmodule_namerT   r    r   r   r   r	   
  s     
r	   c                 C   sÊ   |   t¡r0|  ¡ }d|d d |dd … ¡f S | dkr@d|  S t d| ¡r|  ¡ }| d  ¡ sndd	„ |D ƒ}| dd
¡ | dd¡ d |¡S | d  ¡ sÆt 	dd| ¡} |  
dd¡} t| |dd„ ƒ} | S )Nz**%s** `%s`r   ú é   r   z**%s**z^\s*\$\s+\Sc                 S   s   g | ]}d | ‘qS )z  %sr   rJ   r   r   r   r4   ?  r   z$render_paragraph.<locals>.<listcomp>z.. code-block:: shr   rI   z`(.+?)'z"\1"Ú*z\*c                 S   s   d|  S )Nz``%s``r   r   r   r   r   r   N  r   z"render_paragraph.<locals>.<lambda>)r   r   r>   r   r=   r,   r   r0   ÚinsertÚsubÚreplacer   )Z	paragraphr!   rC   Úlinesr   r   r   rE   /  s(    

þrE   c                 C   s   t  tjt||d| ¡S )N)r!   Ú
replace_fn)r&   r\   Ú	functoolsÚpartialÚreplace_tokens_callback)r@   r!   r_   r   r   r   r   S  s    ýür   c                 C   s*   |   d¡}t d|¡r||vs&||ƒ}|S )Nr   z^[A-Z][A-Z0-9_]+$)r(   r=   r,   )r,   r!   r_   r   r   r   r   rb   [  s    
rb   )rU   rL   r`   Úloggingr=   Ú	importlibr   Zhumanfriendly.compatr   Zhumanfriendly.textr   r   r   Ú__all__r   r9   ÚcompileÚVERBOSEr&   r6   Ú	getLoggerÚ__name__r;   r   r   r
   r   r	   rE   r   rb   r   r   r   r   Ú<module>   s.   
ï

#U %$