o
    
^hU                     @   s   d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZmZ dd Zd	d
 ZG dd deZG dd deZG dd deZd ddZedZdd Zd!ddZ			d"ddZdd ZdS )#z
    babel.messages.pofile
    ~~~~~~~~~~~~~~~~~~~~~

    Reading and writing of files in the ``gettext`` PO (portable object)
    format.

    :copyright: (c) 2013-2019 by the Babel Team.
    :license: BSD, see LICENSE for more details.
    )print_functionN)CatalogMessage)wraptext)	text_typecmpc                 C   s"   dd }t d|| dd S )zReverse `escape` the given string.

    >>> print(unescape('"Say:\\n  \\"hello, world!\\"\\n"'))
    Say:
      "hello, world!"
    <BLANKLINE>

    :param string: the string to unescape
    c                 S   s2   |  d}|dkrdS |dkrdS |dkrdS |S )N   n
t	r)group)matchm r   7/usr/lib/python3/dist-packages/babel/messages/pofile.pyreplace_escapes    s   
z!unescape.<locals>.replace_escapesz\\([\\trn"])r   )recompilesub)stringr   r   r   r   unescape   s   

r   c                 C   sB   d| v r|   }| dr|dd }tt|}d|S t| S )a  Reverse the normalization done by the `normalize` function.

    >>> print(denormalize(r'''""
    ... "Say:\n"
    ... "  \"hello, world!\"\n"'''))
    Say:
      "hello, world!"
    <BLANKLINE>

    >>> print(denormalize(r'''""
    ... "Say:\n"
    ... "  \"Lorem ipsum dolor sit "
    ... "amet, consectetur adipisicing"
    ... " elit, \"\n"'''))
    Say:
      "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
    <BLANKLINE>

    :param string: the string to denormalize
    r
   ""r   N )
splitlines
startswithmapr   join)r   Zescaped_lineslinesr   r   r   denormalize-   s   


r"   c                       s    e Zd ZdZ fddZ  ZS )PoFileErrorzDException thrown by PoParser when an invalid po file is encountered.c                    s0   t t| dj||d || _|| _|| _d S )Nz{message} on {lineno})messagelineno)superr#   __init__formatcatalogliner%   )selfr$   r)   r*   r%   	__class__r   r   r'   N   s   
zPoFileError.__init__)__name__
__module____qualname____doc__r'   __classcell__r   r   r,   r   r#   L   s    r#   c                   @   sp   e Zd Zdd Zdd Zdd Zdd Z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 )_NormalizedStringc                 G   s   g | _ |D ]}| | qd S N)_strsappend)r+   argsargr   r   r   r'   W   s   z_NormalizedString.__init__c                 C   s   | j |  d S r4   )r5   r6   strip)r+   sr   r   r   r6   \   s   z_NormalizedString.appendc                 C   s   d tt| jS )Nr   )r    r   r   r5   r+   r   r   r   r"   _   s   z_NormalizedString.denormalizec                 C   s
   t | jS r4   )boolr5   r;   r   r   r   __nonzero__b   s   
z_NormalizedString.__nonzero__c                 C   s   t j| jS r4   )oslinesepr    r5   r;   r   r   r   __repr__g      z_NormalizedString.__repr__c                 C   s   |sdS t t| t|S )Nr   )r   r   r+   otherr   r   r   __cmp__j   s   z_NormalizedString.__cmp__c                 C   s   |  |dkS Nr   rD   rB   r   r   r   __gt__p   rA   z_NormalizedString.__gt__c                 C   s   |  |dk S rE   rF   rB   r   r   r   __lt__s   rA   z_NormalizedString.__lt__c                 C   s   |  |dkS rE   rF   rB   r   r   r   __ge__v   rA   z_NormalizedString.__ge__c                 C   s   |  |dkS rE   rF   rB   r   r   r   __le__y   rA   z_NormalizedString.__le__c                 C   s   |  |dkS rE   rF   rB   r   r   r   __eq__|   rA   z_NormalizedString.__eq__c                 C   s   |  |dkS rE   rF   rB   r   r   r   __ne__   rA   z_NormalizedString.__ne__N)r.   r/   r0   r'   r6   r"   r=   __bool__r@   rD   rG   rH   rI   rJ   rK   rL   r   r   r   r   r3   U   s    r3   c                   @   sn   e Zd ZdZg dZdddZdd Zdd	 Zd
d ZdddZ	dddZ
dd Zdd Zdd Zdd ZdS )PoFileParserzSupport class to  read messages from a ``gettext`` PO (portable object) file
    and add them to a `Catalog`

    See `read_po` for simple cases.
    )msgidmsgstrmsgctxtmsgid_pluralFc                 C   s*   || _ || _d| _d| _|| _|   d S rE   )r)   ignore_obsoletecounteroffsetabort_invalid_reset_message_state)r+   r)   rS   rV   r   r   r   r'      s   zPoFileParser.__init__c                 C   sF   g | _ g | _g | _g | _g | _g | _d | _d| _d| _d| _	d| _
d S )NF)messagestranslations	locationsflagsuser_commentsauto_commentscontextobsoletein_msgid	in_msgstr
in_msgctxtr;   r   r   r   rW      s   
z!PoFileParser._reset_message_statec              
   C   s8  | j   t| jdkrtdd | jD }n| jd  }t|ttfrRdd t| j	j
D }| j D ]\}}|| j	j
krF| d| jd q3| ||< q3t|}n	| j d d  }| jrd| j }nd}t||t| jt| j| j| j| jd |d	}| jr| js|| j	j|< n|| j	|< |  jd7  _|   dS )
z
        Add a message to the catalog based on the current parser state and
        clear the state ready to process the next message.
        r   c                 S   s   g | ]}|  qS r   )r"   ).0r   r   r   r   
<listcomp>   s    z-PoFileParser._add_message.<locals>.<listcomp>r   c                 S   s   g | ]}d qS r   r   )rc   _r   r   r   rd      s    r   z5msg has more translations than num_plurals of catalogN)r%   r^   )rY   sortlenrX   tupler"   
isinstancelistranger)   num_plurals_invalid_pofilerU   r^   r   rZ   setr[   r]   r\   r_   rS   rT   rW   )r+   rO   r   idxtranslationrQ   r$   r   r   r   _add_message   s6   


zPoFileParser._add_messagec                 C   s   | j r	|   d S d S r4   )rX   rr   r;   r   r   r   _finish_current_message   s   z$PoFileParser._finish_current_messagec                 C   s,   | dr| || d S | ||| d S )N")r   !_process_string_continuation_line_process_keyword_line)r+   r%   r*   r_   r   r   r   _process_message_line   s   
z"PoFileParser._process_message_linec              	   C   sH  | j D ],}z||r|t| dv r|t|d  }W  nW q ty/   | ||d Y qw | ||d d S |dv rA|   || _|dkrK|| _|dv r_d| _d| _	| j
t| d S |d	krd| _	d| _|d
r|dd  dd\}}| jt|t|g d S | jdt|g d S |dkrd| _t|| _d S d S )N) [z$Keyword must be followed by a stringz0Start of line didn't match any expected keyword.)rO   rQ   rO   )rO   rR   FTrP   ry   r   ]r   rQ   )	_keywordsr   rh   
IndexErrorrn   rs   r_   rU   rb   r`   rX   r6   r3   ra   splitrY   intr^   )r+   r%   r*   r_   keywordr8   rp   msgr   r   r   rv      s>   

z"PoFileParser._process_keyword_linec                 C   sV   | j r	| jd }n| jr| jd d }n| jr| j}n	| ||d d S || d S )Nr   r   z<Got line starting with " but not in msgid, msgstr or msgctxt)r`   rX   ra   rY   rb   r^   rn   r6   )r+   r*   r%   r:   r   r   r   ru      s   z.PoFileParser._process_string_continuation_linec              	   C   s8  |    |dd  drP|dd    D ]6}|d}|dkrEzt||d d  }W n	 ty7   Y qw | j|d | |f q| j|d f qd S |dd  drq|dd   dD ]
}| j	|
  qdd S |dd  dr|dd  
 }|r| j| d S d S | j|dd  
  d S )Nr   :   r   ,.)rs   r   lstripr}   rfindr~   
ValueErrorrZ   r6   r[   r9   r]   r\   )r+   r*   locationposr%   flagcommentr   r   r   _process_comment	  s.   

zPoFileParser._process_commentc                 C   s   t |D ]@\}}| }t|ts|| jj}|sq|dr>|dd dr8| j||dd 	 dd q| 
| q| || q|   | jsm| jsU| jsU| jro| jtd | jd	tdg |   dS dS dS )
z
        Reads from the file-like object `fileobj` and adds any po file
        units found in it to the `Catalog` supplied to the constructor.
        #r   N~r   T)r_   r   r   )	enumerater9   rj   r   decoder)   charsetr   rw   r   r   rs   rT   r[   r\   r]   rX   r6   r3   rY   rr   )r+   fileobjr%   r*   r   r   r   parse$  s"   

zPoFileParser.parsec                 C   s8   | j rt|| j||td| td|d | d S )NzWARNING:z!WARNING: Problem on line {0}: {1}r   )rV   r#   r)   printr(   )r+   r*   r%   r   r   r   r   rn   A  s   
zPoFileParser._invalid_pofileN)FF)F)r.   r/   r0   r1   r{   r'   rW   rr   rs   rw   rv   ru   r   r   rn   r   r   r   r   rN      s    
#

)rN   Fc                 C   s*   t |||d}t|||d}||  |S )a  Read messages from a ``gettext`` PO (portable object) file from the given
    file-like object and return a `Catalog`.

    >>> from datetime import datetime
    >>> from babel._compat import StringIO
    >>> buf = StringIO('''
    ... #: main.py:1
    ... #, fuzzy, python-format
    ... msgid "foo %(name)s"
    ... msgstr "quux %(name)s"
    ...
    ... # A user comment
    ... #. An auto comment
    ... #: main.py:3
    ... msgid "bar"
    ... msgid_plural "baz"
    ... msgstr[0] "bar"
    ... msgstr[1] "baaz"
    ... ''')
    >>> catalog = read_po(buf)
    >>> catalog.revision_date = datetime(2007, 4, 1)

    >>> for message in catalog:
    ...     if message.id:
    ...         print((message.id, message.string))
    ...         print(' ', (message.locations, sorted(list(message.flags))))
    ...         print(' ', (message.user_comments, message.auto_comments))
    (u'foo %(name)s', u'quux %(name)s')
      ([(u'main.py', 1)], [u'fuzzy', u'python-format'])
      ([], [])
    ((u'bar', u'baz'), (u'bar', u'baaz'))
      ([(u'main.py', 3)], [])
      ([u'A user comment'], [u'An auto comment'])

    .. versionadded:: 1.0
       Added support for explicit charset argument.

    :param fileobj: the file-like object to read the PO file from
    :param locale: the locale identifier or `Locale` object, or `None`
                   if the catalog is not bound to a locale (which basically
                   means it's a template)
    :param domain: the message domain
    :param ignore_obsolete: whether to ignore obsolete messages in the input
    :param charset: the character set of the catalog.
    :param abort_invalid: abort read if po file is invalid
    )localedomainr   )rV   )r   rN   r   )r   r   r   rS   r   rV   r)   parserr   r   r   read_poH  s   /
r   zL(\s+|[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))c                 C   s0   d|  dd dd dd dd	 d
d S )zEscape the given string so that it can be included in double-quoted
    strings in ``PO`` files.

    >>> escape('''Say:
    ...   "hello, world!"
    ... ''')
    '"Say:\\n  \\"hello, world!\\"\\n"'

    :param string: the string to escape
    z"%s"\z\\r   z\tr   z\rr
   z\nrt   z\")replace)r   r   r   r   escape  s   r   r   L   c           
         s0  |rj|dkrjt  }g }| dD ]W}t t|| |krct|}|  |rbg }d}|rXt t|d d | }	||	 |k rL||  ||	7 }n
|sU||  n|s.|d| |s(q|| qn| d}t |dkryt| S |r|d s|d= |d  d7  < dd fd	d
|D  S )a  Convert a string into a format that is appropriate for .po files.

    >>> print(normalize('''Say:
    ...   "hello, world!"
    ... ''', width=None))
    ""
    "Say:\n"
    "  \"hello, world!\"\n"

    >>> print(normalize('''Say:
    ...   "Lorem ipsum dolor sit amet, consectetur adipisicing elit, "
    ... ''', width=32))
    ""
    "Say:\n"
    "  \"Lorem ipsum dolor sit "
    "amet, consectetur adipisicing"
    " elit, \"\n"

    :param string: the string to normalize
    :param prefix: a string that should be prepended to every line
    :param width: the maximum line width; use `None`, 0, or a negative number
                  to completely disable line wrapping
    r   Tr   r   r   r   r
   z""
c                    s   g | ]} t | qS r   )r   )rc   r*   prefixr   r   rd     s    znormalize.<locals>.<listcomp>)	rh   r   r   WORD_SEPr}   reverser6   popr    )
r   r   widthZ	prefixlenr!   r*   chunksbufsizelr   r   r   	normalize  s>   


r   Tc
              	      sR  d#fdd	 fddd#fdd	}
d# fdd		}d
}|r&d}n|r*d}t |dD ]}|js^|r8q0j}rXdkrXg }| D ]}|t|dd7 }qGd|}|d  |jD ]}|
| qa|jD ]}|
|dd qk|sg }zt|j	dd d}W n t
y   |j	}Y nw |D ]#\}}|r|	r|d|tjd|f  q|d|tjd  q|
d|dd |jrшdddgt|j   |jr|r|
d |jd  dd t|jd kr|
d! |jd   dd || d q0|s%t j |dD ]}|jD ]}|
| q||d"d d qd
S d
S )$a  Write a ``gettext`` PO (portable object) template file for a given
    message catalog to the provided file-like object.

    >>> catalog = Catalog()
    >>> catalog.add(u'foo %(name)s', locations=[('main.py', 1)],
    ...             flags=('fuzzy',))
    <Message...>
    >>> catalog.add((u'bar', u'baz'), locations=[('main.py', 3)])
    <Message...>
    >>> from babel._compat import BytesIO
    >>> buf = BytesIO()
    >>> write_po(buf, catalog, omit_header=True)
    >>> print(buf.getvalue().decode("utf8"))
    #: main.py:1
    #, fuzzy, python-format
    msgid "foo %(name)s"
    msgstr ""
    <BLANKLINE>
    #: main.py:3
    msgid "bar"
    msgid_plural "baz"
    msgstr[0] ""
    msgstr[1] ""
    <BLANKLINE>
    <BLANKLINE>

    :param fileobj: the file-like object to write to
    :param catalog: the `Catalog` instance
    :param width: the maximum line width for the generated output; use `None`,
                  0, or a negative number to completely disable line wrapping
    :param no_location: do not emit a location comment for every message
    :param omit_header: do not include the ``msgid ""`` entry at the top of the
                        output
    :param sort_output: whether to sort the messages in the output by msgid
    :param sort_by_file: whether to sort the messages in the output by their
                         locations
    :param ignore_obsolete: whether to ignore obsolete messages and not include
                            them in the output; by default they are included as
                            comments
    :param include_previous: include the old msgid as a comment when
                             updating the catalog
    :param include_lineno: include line number in the location comment
    r   c                    s   t | | dS )N)r   r   )r   )keyr   )r   r   r   
_normalize  rA   zwrite_po.<locals>._normalizec                    s&   t | tr|  jd} |  d S )Nbackslashreplace)rj   r   encoder   write)text)r)   r   r   r   _write  s   
zwrite_po.<locals>._writec                    s>   r	dkr	}nd}t | |D ]} d|| f  qd S )Nr   r   z#%s %s
)r   r9   )r   r   _widthr*   )r   r   r   r   _write_comment	  s   z write_po.<locals>._write_commentc              	      s  t | jttfr\| jrd| | j|f  d| | jd |f  d| | jd |f  tjD ]!}z| j| }W n tyL   d}Y nw d|| ||f  q8d S | jrkd| | j|f  d| | j|f  d| | jpd|f  d S )	Nz%smsgctxt %s
z%smsgid %s
r   z%smsgid_plural %s
r   r   z%smsgstr[%d] %s
z%smsgstr %s
)	rj   idrk   ri   r^   rl   rm   r   r|   )r$   r   rp   r   )r   r   r)   r   r   _write_message  s6   

	
z write_po.<locals>._write_messageNr$   r   )sort_byr   z# )r   subsequent_indentr
   r   r   c                 S   s"   | d t | d tr| d pdfS )Nr   r   r   )rj   r~   )xr   r   r   <lambda>O  s   " zwrite_po.<locals>.<lambda>r   z%s:%d/z%srx   r   z#%s
z, zmsgid %s|r   zmsgid_plural %sz#~ re   )_sort_messagesr   Zheader_commentr   r   r    r\   r]   sortedrZ   	TypeErrorr6   r   r>   sepr[   Zprevious_idrh   r_   values)r   r)   r   Zno_locationZomit_headerZsort_outputZsort_by_filerS   Zinclude_previousZinclude_linenor   r   r   r$   Zcomment_headerr!   r*   r   ZlocsrZ   filenamer%   r   )r   r   r)   r   r   r   write_po  s   .











r   c                 C   s8   t | } |dkr|   | S |dkr| jdd d | S )z
    Sort the given message iterable by the given criteria.

    Always returns a list.

    :param messages: An iterable of Messages.
    :param sort_by: Sort by which criteria? Options are `message` and `location`.
    :return: list[Message]
    r$   r   c                 S   s   | j S r4   )rZ   )r   r   r   r   r     s    z _sort_messages.<locals>.<lambda>r   )rk   rg   )rX   r   r   r   r   r   r  s   
r   )NNFNF)r   r   )r   FFFFFFT)r1   Z
__future__r   r>   r   Zbabel.messages.catalogr   r   Z
babel.utilr   Zbabel._compatr   r   r   r"   	Exceptionr#   objectr3   rN   r   r   r   r   r   r   r   r   r   r   r   <module>   s.   	/ 
E
5
=
  