o
    mi]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   O/home/kim/smarthome/.venv/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   Z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locationZin_filenamecr   r   r   _extract_locationsY   sF   



r5   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__r8   r)   r9   )selfr7   r8   r)   r9   	__class__r   r   r=      s   
zPoFileError.__init__)
r7   r   r8   r   r)   r   r9   r:   r   r;   )__name__
__module____qualname____doc__r=   __classcell__r   r   r?   r   r6      s    r6   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>   rG   r?   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)rG   r   r   r;   )r   r   )rA   rB   rC   r=   r(   rE   r   r   r?   r   rF      s    rF   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.
    Fr8   r   ignore_obsoleteboolabort_invalidr   r;   c                 C  s*   || _ || _d| _d| _|| _|   d S )Nr   )r8   rL   counteroffsetrN   _reset_message_state)r>   r8   rL   rN   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translationsr2   flagsuser_commentsauto_commentscontextobsoletein_msgid	in_msgstr
in_msgctxtrJ   r   r   r   rQ      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 rH   )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)r9   rW   )lenrR   tupleranger8   num_pluralssortedrS   _invalid_pofilerP   r(   rW   r   r2   rT   rV   rU   rX   rL   Z_key_forrO   rQ   )r>   msgidr   idxtranslationmsgctxtr7   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   ')rR   rS   rh   rP   r(   r1   rF   rm   rJ   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>   r9   r)   rX   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-   )ri   rl   ri   )ri   Zmsgid_pluralFTrl   Zmsgstrmsgstr[[r   r   r"   zUnknown or misformatted keyword)	partitionro   rX   rP   r[   rY   rR   r1   rF   rW   r%   rZ   r:   rS   rh   )r>   r9   r)   rX   keywordra   argkwargZhas_bracketZidxargrj   sr   r   r   rr      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)	rY   rR   rZ   rS   r[   rW   rh   r1   rI   )r>   r)   r9   rz   r   r   r   rq     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\   rH   )rI   )r]   flagr   r   r   r^   @  r_   z0PoFileParser._process_comment.<locals>.<genexpr>,z#.r   )ro   r5   
rpartitionr2   r1   r:   r0   rT   extendr.   r/   rI   rV   rU   )r>   r)   prefixr3   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)rX   )	enumeraterI   
isinstancer   decoder8   charsetrs   r.   r   r0   rh   ro   rO   rT   rU   rV   rR   r1   rF   rS   rm   )r>   r   Zneeds_decoder9   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   rN   r6   r8   print)r>   r)   r9   msgr   r   r   rh   v  s
   
zPoFileParser._invalid_pofileN)FF)r8   r   rL   rM   rN   rM   r   r;   )r   r;   )F)r   r   r   r;   )rA   rB   rC   rD   r=   rQ   rm   ro   rs   rr   rq   r   r   rh   r   r   r   r   rK      s    	


(
#

)rK   Fr   r   localeLocale | str | Nonedomain
str | NonerL   rM   r   rN   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   )rN   )r   rK   r   )r   r   r   rL   r   rN   r8   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\nrp   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   rb     s    znormalize.<locals>.<listcomp>)	rc   r$   r   WORD_SEPr/   reverser1   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]r8   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
    Nr7   r3   rL   r   r   r   r   sort_byr   backslashreplace)generate_por   r   encoder   write)r   r8   r   r   r   r   r   rL   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   )wraprI   )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#   rt   dz] zmsgstr )
r   idlistrd   rW   r   re   rf   r   
IndexError)r7   r   rj   r   )r8   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#~ r`   )r	   _sort_messagesr   Zheader_commentr$   r   r&   rU   rV   rg   r2   	TypeErrorr   ossepr   r1   rT   Zprevious_idr   rc   rX   values)r8   rL   r   r   r   r   r   r   Zcomment_widthZheader_wrapperr   r   r7   Zcomment_headerr   r)   r   locsr2   r   r9   r3   Znorm_previous_idr   )r8   r   r   r   r   o  s~   





 


r   rR   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]
    r7   r3   c                 S  s   | j S rH   )r2   )r   r   r   r   r     s    z _sort_messages.<locals>.<lambda>r   )r   sort)rR   r   r   r   r   r     s   r   )r   r   r   r   )r)   r   r   r*   )NNFNF)r   r   r   r   r   r   rL   rM   r   r   rN   rM   r   r   )r#   r   )r   r   r   r   r   r:   r   r   )r   r   r   r   )r   FFFFFFT)r   r   r8   r   r   r:   r   rM   r   rM   r   rM   r   rM   rL   rM   r   rM   r   rM   r   r;   )r8   r   rL   rM   r   rM   r   rM   r   rM   r   rM   r   r   r   r:   r   r   )rR   r   r   r   r   r   )'rD   
__future__r   r   recollections.abcr   typingr   r   Z
babel.corer   Zbabel.messages.catalogr   r   Z
babel.utilr	   r
   r   Z	_typeshedr   compiler   r!   r(   r5   	Exceptionr6   r   rF   rK   r   r   r   r   r   r   r   r   r   r   r   r   <module>   sd    



2

 b<
	
=Po