o
    Ni]c                     @  s`  d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
mZ ddlmZmZ ddlmZ erBdd	lmZmZ dd
lmZ edZdOddZdOddZdPddZG dd deZG dd deZG dd dZ					dQdRd)d*Zed+Z dOd,d-Z!dSdTd3d4Z"dUd6d7Z#	/							8dVdWdBdCZ$dd8ddddd/dDdXdHdIZ%dYdMdNZ&dS )Zz
babel.messages.pofile
~~~~~~~~~~~~~~~~~~~~~

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

:copyright: (c) 2013-2026 by the Babel Team.
:license: BSD, see LICENSE for more details.
    )annotationsN)Iterable)TYPE_CHECKINGLiteral)Locale)CatalogMessage)TextWrapper)IOAnyStr)SupportsWritez\\([\\trn"])stringstrreturnc                 C  s0   dd }d| vr| dd S t || 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   I/home/ubuntu/.local/lib/python3.10/site-packages/babel/messages/pofile.pyreplace_escapes+   s   
z!unescape.<locals>.replace_escapes\r   )_unescape_resub)r   r   r   r   r   unescape    s   r!   c                 C  s>   d| v r|   }| dr|dd }dtt|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joinmapr!   )r   escaped_linesr   r   r   denormalize;   s   
r)   line	list[str]c                 C  s   d| vrd| vr|    S g }d}d}| D ]5}|dkr%|r"tdd}q|dkr2|s/tdd}q|dkrG|r=||7 }q|rF|| d}q||7 }q|rY|rTtd|| |S )	zExtract locations from location comments.

    Locations are extracted while properly handling First Strong
    Isolate (U+2068) and Pop Directional Isolate (U+2069), used by
    gettext to enclose filenames with spaces and tabs in their names.
       ⁨   ⁩r#   Fzglocation comment contains more First Strong Isolate characters, than Pop Directional Isolate charactersTzglocation comment contains more Pop Directional Isolate characters, than First Strong Isolate characters )lstripsplit
ValueErrorappend)r*   	locationslocationin_filenamecr   r   r   _extract_locationsY   sF   



r7   c                      s"   e Zd ZdZd fddZ  ZS )PoFileErrorzDException thrown by PoParser when an invalid po file is encountered.messager   catalogr   r*   linenointr   Nonec                   s,   t  | d|  || _|| _|| _d S )Nz on )super__init__r:   r*   r;   )selfr9   r:   r*   r;   	__class__r   r   r?      s   
zPoFileError.__init__)
r9   r   r:   r   r*   r   r;   r<   r   r=   )__name__
__module____qualname____doc__r?   __classcell__r   r   rA   r   r8      s    r8   c                      s(   e Zd Zd	 fddZd
ddZ  ZS )_NormalizedStringargsr   r   r=   c                   s   t  ttj| d S N)r>   r?   r'   r   strip)r@   rI   rA   r   r   r?      s   z_NormalizedString.__init__c                 C  s   | sdS d tt| S )Nr#   )r&   r'   r!   r@   r   r   r   r)      s   z_NormalizedString.denormalize)rI   r   r   r=   )r   r   )rC   rD   rE   r?   r)   rG   r   r   rA   r   rH      s    rH   c                   @  s~   e Zd ZdZ		d!d"d
dZd#ddZd#ddZd#ddZd$d#ddZd$d#ddZ	d#ddZ
d#ddZd%ddZd#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.
    Fr:   r   ignore_obsoleteboolabort_invalidr   r=   c                 C  s*   || _ || _d| _d| _|| _|   d S )Nr   )r:   rN   counteroffsetrP   _reset_message_state)r@   r:   rN   rP   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translationsr3   flagsuser_commentsauto_commentscontextobsoletein_msgid	in_msgstr
in_msgctxtrL   r   r   r   rS      s   
z!PoFileParser._reset_message_statec              
   C  s"  t | jdkr@tdd | jD }dd t| jjD }t| jD ]\}}|| jjkr4| d| j	d q!|
 ||< q!t|}n| jd 
 }| jd d 
 }| jrX| j
 nd	}t||| j| j| j| j| j	d |d
}| jr| js~|| j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      | ]}|  V  qd S rJ   )r)   ).0r   r   r   r   	<genexpr>       z,PoFileParser._add_message.<locals>.<genexpr>c                 S  s   g | ]}d qS r#   r   )r_   _r   r   r   
<listcomp>   s    z-PoFileParser._add_message.<locals>.<listcomp>r#   z5msg has more translations than num_plurals of catalogr   N)r;   rY   )lenrT   tupleranger:   num_pluralssortedrU   _invalid_pofilerR   r)   rY   r   r3   rV   rX   rW   rZ   rN   _key_forrQ   rS   )r@   msgidr   idxtranslationmsgctxtr9   r   r   r   _add_message   sB   


zPoFileParser._add_messagec                 C  sP   | j r&| js | d| jd| j d   d | jdt g |   d S d S )Nr#   zmissing msgstr for msgid 'r   ')rT   rU   rj   rR   r)   r2   rH   rp   rL   r   r   r   _finish_current_message   s   z$PoFileParser._finish_current_messagec                 C  s6   |sd S |d dkr|  || d S | ||| d S )Nr   ")!_process_string_continuation_line_process_keyword_line)r@   r;   r*   rZ   r   r   r   _process_message_line   s
   z"PoFileParser._process_message_linec                 C  s   | d\}}}|dv r|   || _|dkr|| _|dv r.d| _d| _| jt| d S |dkr<d| _t|| _	d S |dksE|
d	rtd| _d| _| d
\}}}	|r]t|	d d nd}
|dkrgt|nt }| j|
|g d S | ||d d S )Nr.   )rl   ro   rl   )rl   msgid_pluralFTro   msgstrmsgstr[[r   r   r"   zUnknown or misformatted keyword)	partitionrr   rZ   rR   r]   r[   rT   r2   rH   rY   r%   r\   r<   rU   rj   )r@   r;   r*   rZ   keywordrc   argkwarghas_bracketidxargrm   sr   r   r   ru      s0   
z"PoFileParser._process_keyword_linec                 C  sZ   | 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[   rT   r\   rU   r]   rY   rj   r2   rK   )r@   r*   r;   r   r   r   r   rt     s   z.PoFileParser._process_string_continuation_linec              	   C  s  |    |d d }|dkrCt|dd  D ]*}|d\}}}|r8z| j|t|f W q ty7   Y qw | j|d f qd S |dkr]| jdd |dd  	 
dD  d S |dkrs|dd   }|rq| j| d S | j|d	d    d S )
N   z#::z#,c                 s  r^   rJ   )rK   )r_   flagr   r   r   r`   @  ra   z0PoFileParser._process_comment.<locals>.<genexpr>,z#.r   )rr   r7   
rpartitionr3   r2   r<   r1   rV   extendr/   r0   rK   rX   rW   )r@   r*   prefixr4   acolonbcommentr   r   r   _process_comment/  s,   (zPoFileParser._process_commentfileobjIO[AnyStr] | Iterable[AnyStr]c                 C  s*  d}t |D ]d\}}| }|du rt|t }|sq|r$|| jj}|d dkrd|dd dkrA| j||dd  dd qz| 	| W q t
yc } z| ||t| W Y d}~qd}~ww | || q|   | js| js{| js{| jr| jt  | jdt g |   dS dS dS )aV  
        Reads from the file-like object (or iterable of string-likes) `fileobj`
        and adds any po file units found in it to the `Catalog`
        supplied to the constructor.

        All of the items in the iterable must be the same type; either `str`
        or `bytes` (decoded with the catalog charset), but not a mixture.
        Nr   #r   z#~T)rZ   )	enumeraterK   
isinstancer   decoder:   charsetrv   r/   r   r1   rj   rr   rQ   rV   rW   rX   rT   r2   rH   rU   rp   )r@   r   needs_decoder;   r*   excr   r   r   parseM  s2   	zPoFileParser.parsec                 C  sJ   t |tsJ | jrt|| j||td| td|d  d| d S )NzWARNING:zWARNING: Problem on line r   z: )r   r   rP   r8   r:   print)r@   r*   r;   msgr   r   r   rj   v  s
   
zPoFileParser._invalid_pofileN)FF)r:   r   rN   rO   rP   rO   r   r=   )r   r=   )F)r   r   r   r=   )rC   rD   rE   rF   r?   rS   rp   rr   rv   ru   rt   r   r   rj   r   r   r   r   rM      s    	


(
#

)rM   Fr   r   localeLocale | str | Nonedomain
str | NonerN   rO   r   rP   r   c                 C  s*   t |||d}t|||d}||  |S )a  Read messages from a ``gettext`` PO (portable object) file from the given
    file-like object (or an iterable of lines) and return a `Catalog`.

    >>> from datetime import datetime
    >>> from io 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))
    ('foo %(name)s', 'quux %(name)s')
      ([('main.py', 1)], ['fuzzy', 'python-format'])
      ([], [])
    (('bar', 'baz'), ('bar', 'baaz'))
      ([('main.py', 3)], [])
      (['A user comment'], ['An auto comment'])

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

    :param fileobj: the file-like object (or iterable of lines) 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
    )r   r   r   )rP   )r   rM   r   )r   r   r   rN   r   rP   r:   parserr   r   r   read_po~  s   6
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"r   z\\r   z\tr   z\rr   z\nrs   z\")replace)r   r   r   r   escape  s   r   r#   L   r   widthr<   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   )r_   r*   r   r   r   rd     s    znormalize.<locals>.<listcomp>)	re   r$   r   WORD_SEPr0   reverser2   popr&   )
r   r   r   	prefixlenlinesr*   chunksbufsizelengthr   r   r   	normalize  s>   


r   filenamec                 C  s<   d| vr
d| vr
| S |  dsd|  } | ds| d7 } | S )zEnclose filenames which include white spaces or tabs.

    Do the same as gettext and enclose filenames which contain white
    spaces or tabs with First Strong Isolate (U+2068) and Pop
    Directional Isolate (U+2069).
    r.   r   r,   r-   )r%   endswith)r   r   r   r   _enclose_filename_if_necessary  s   

r   TSupportsWrite[bytes]r:   no_locationomit_headersort_outputsort_by_fileinclude_previousinclude_linenor=   c
              
   C  sZ   d}
|rd}
n|rd}
t |||	||||
|dD ]}t|tr%||jd}| | q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('foo %(name)s', locations=[('main.py', 1)],
    ...             flags=('fuzzy',))
    <Message...>
    >>> catalog.add(('bar', 'baz'), locations=[('main.py', 3)])
    <Message...>
    >>> from io 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
    Nr9   r4   rN   r   r   r   r   sort_byr   backslashreplace)generate_por   r   encoder   write)r   r:   r   r   r   r   r   rN   r   r   r   r*   r   r   r   write_po"  s&   8


r   r   r   %Literal['message', 'location'] | NoneIterable[str]c             	   #  s   r	dkr	nd}t |ddt ddd}	d fdd		}
d  fd
d	}t |dD ]}|jsY|r5q- j}rSdkrSg }| D ]	}||	|7 }qDd|}| dV  |jD ]	}|
|E dH  q\|jD ]}|
|ddE dH  qi|sg }zt	|j
dd d}W n ty   |j
}Y nw |D ]$\}}|tjd}t|}|r|r| d|d}||vr|| q|
d|ddE dH  |jrdddgt	|j dV  |jr|r|
dt|jd d ddE dH  t|jdkrt|jd d}|
d| ddE dH  ||E dH  dV  q-|sCt j |dD ]}|jD ]
}|
|E dH  q*||ddE dH  dV  q%dS dS )!zYield text strings representing a ``gettext`` PO (portable object) file.

    See `write_po()` for a more detailed description.
    r   r   F)r   break_long_wordsz# )r   subsequent_indentr   r#   c                 3  s.      | D ]}d| d|  dV  qd S )Nr   r.   r   )wraprK   )r   r   r*   )comment_wrapperr   r   _format_comment  s   z$generate_po.<locals>._format_commentc              
   3  s@   t | jttfrl| jr| dt| j|d dV  | dt| jd |d dV  | dt| jd |d dV  t jD ]'}z| j| }W n t	yV   d}Y nw | d	|d
dt||d dV  qBd S | jr~| dt| j|d dV  | dt| j|d dV  | dt| jpd|d dV  d S )Nzmsgctxt )r   r   r   msgid r   msgid_plural r   r#   ry   dz] zmsgstr )
r   idlistrf   rY   r   rg   rh   r   
IndexError)r9   r   rm   r   )r:   r   r   r   _format_message  s$   ""&&z$generate_po.<locals>._format_message)r   r   N.r   c                 S  s"   | d t | d tr| d pdfS )Nr   r   r   )r   r<   )xr   r   r   <lambda>  s   " zgenerate_po.<locals>.<lambda>key/r   r   r.   r   z, r   )r   |r   r   z#~ rb   )r	   _sort_messagesr   header_commentr$   r   r&   rW   rX   ri   r3   	TypeErrorr   ossepr   r2   rV   previous_idr   re   rZ   values)r:   rN   r   r   r   r   r   r   comment_widthheader_wrapperr   r   r9   comment_headerr   r*   r   locsr3   r   r;   r4   norm_previous_idr   )r:   r   r   r   r   o  s~   





 


r   rT   Iterable[Message]list[Message]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]
    r9   r4   c                 S  s   | j S rJ   )r3   )r   r   r   r   r     s    z _sort_messages.<locals>.<lambda>r   )r   sort)rT   r   r   r   r   r     s   r   )r   r   r   r   )r*   r   r   r+   )NNFNF)r   r   r   r   r   r   rN   rO   r   r   rP   rO   r   r   )r#   r   )r   r   r   r   r   r<   r   r   )r   r   r   r   )r   FFFFFFT)r   r   r:   r   r   r<   r   rO   r   rO   r   rO   r   rO   rN   rO   r   rO   r   rO   r   r=   )r:   r   rN   rO   r   rO   r   rO   r   rO   r   rO   r   r   r   r<   r   r   )rT   r   r   r   r   r   )'rF   
__future__r   r   recollections.abcr   typingr   r   
babel.corer   babel.messages.catalogr   r   
babel.utilr	   r
   r   	_typeshedr   compiler   r!   r)   r7   	Exceptionr8   r   rH   rM   r   r   r   r   r   r   r   r   r   r   r   r   <module>   sd    



2

 b<
	
=Po