o
    ۭhce                     @  sX  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mZ erDdd	lmZmZ dd
lmZ dNddZdNddZdOddZG dd deZG dd dZG dd dZ					dPdQd(d)Zed*ZdNd+d,Z dRdSd2d3Z!dTd5d6Z"	.							7dUdVdAdBZ#dd7ddddd.dCdWdGdHZ$dXdLdMZ%dS )Yz
    babel.messages.pofile
    ~~~~~~~~~~~~~~~~~~~~~

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

    :copyright: (c) 2013-2025 by the Babel Team.
    :license: BSD, see LICENSE for more details.
    )annotationsN)Iterable)TYPE_CHECKINGLiteral)Locale)CatalogMessage)TextWrapper_cmp)IOAnyStr)SupportsWritestringstrreturnc                 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   Z/var/www/html/stock_analysis/be/venv/lib/python3.10/site-packages/babel/messages/pofile.pyreplace_escapes&   s   
z!unescape.<locals>.replace_escapesz\\([\\trn"])r   )recompilesub)r   r   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   escaped_lineslinesr   r   r   denormalize3   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_locationsR   s:   



r9   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=   )selfr;   r<   r,   r=   	__class__r   r   rA      s   
zPoFileError.__init__)
r;   r   r<   r   r,   r   r=   r>   r   r?   )__name__
__module____qualname____doc__rA   __classcell__r   r   rC   r   r:   ~   s    r:   c                   @  s   e Zd Zd#ddZd$dd	Zd%d
dZd&ddZd%ddZd'ddZd(ddZ	d(ddZ
d(ddZd(ddZd(ddZd(d d!Zd"S ))_NormalizedStringargsr   r   r?   c                 G  s   g | _ |D ]}| | qd S N)_strsr4   )rB   rK   argr   r   r   rA      s   z_NormalizedString.__init__sc                 C  s   | j |  d S rL   )rM   r4   strip)rB   rO   r   r   r   r4      s   z_NormalizedString.appendc                 C  s   d tt| jS )Nr$   )r(   r'   r"   rM   rB   r   r   r   r+      s   z_NormalizedString.denormalizeboolc                 C  s
   t | jS rL   )rR   rM   rQ   r   r   r   __bool__   s   
z_NormalizedString.__bool__c                 C  s   t j| jS rL   )oslinesepr(   rM   rQ   r   r   r   __repr__      z_NormalizedString.__repr__otherobjectr>   c                 C  s   |sdS t t| t|S )Nr   )r
   r   rB   rX   r   r   r   __cmp__   s   z_NormalizedString.__cmp__c                 C  s   |  |dkS Nr   r[   rZ   r   r   r   __gt__   rW   z_NormalizedString.__gt__c                 C  s   |  |dk S r\   r]   rZ   r   r   r   __lt__   rW   z_NormalizedString.__lt__c                 C  s   |  |dkS r\   r]   rZ   r   r   r   __ge__   rW   z_NormalizedString.__ge__c                 C  s   |  |dkS r\   r]   rZ   r   r   r   __le__   rW   z_NormalizedString.__le__c                 C  s   |  |dkS r\   r]   rZ   r   r   r   __eq__   rW   z_NormalizedString.__eq__c                 C  s   |  |dkS r\   r]   rZ   r   r   r   __ne__   rW   z_NormalizedString.__ne__N)rK   r   r   r?   )rO   r   r   r?   )r   r   )r   rR   )rX   rY   r   r>   )rX   rY   r   rR   )rE   rF   rG   rA   r4   r+   rS   rV   r[   r^   r_   r`   ra   rb   rc   r   r   r   r   rJ      s    










rJ   c                   @  s   e Zd ZdZg 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.
    )msgidmsgstrmsgctxtmsgid_pluralFr<   r   ignore_obsoleterR   abort_invalidr   r?   c                 C  s*   || _ || _d| _d| _|| _|   d S r\   )r<   ri   counteroffsetrj   _reset_message_state)rB   r<   ri   rj   r   r   r   rA      s   zPoFileParser.__init__c                 C  sF   g | _ g | _g | _g | _g | _g | _d | _d| _d| _d| _	d| _
d S )NF)messagestranslationsr5   flagsuser_commentsauto_commentscontextobsoletein_msgid	in_msgstr
in_msgctxtrQ   r   r   r   rm      s   
z!PoFileParser._reset_message_statec              
   C  s@  | 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rc| j nd	}t||t| jt| 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  s    | ]}|  V  qd S rL   )r+   ).0r   r   r   r   	<genexpr>   s    z,PoFileParser._add_message.<locals>.<genexpr>r   c                 S  s   g | ]}d qS r$   r   )rx   _r   r   r   
<listcomp>   s    z-PoFileParser._add_message.<locals>.<listcomp>r$   z5msg has more translations than num_plurals of catalogN)r=   rs   )ro   sortlenrn   tupler+   
isinstancelistranger<   num_plurals_invalid_pofilerl   rs   r   r5   setrp   rr   rq   rt   ri   _key_forrk   rm   )rB   re   r   idxtranslationrg   r;   r   r   r   _add_message   s2   


zPoFileParser._add_messagec                 C  sR   | j r'| js!| d| jd| j d   d | jdtdg |   d S d S )Nr$   zmissing msgstr for msgid 'r   ')rn   ro   r   rl   r+   r4   rJ   r   rQ   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)rB   r=   r,   rt   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)r0   [z$Keyword must be followed by a stringz0Start of line didn't match any expected keyword.)re   rg   re   )re   rh   FTrf   r   r   ]r   rg   )	_keywordsr&   r~   
IndexErrorr   r   rt   rl   rw   ru   rn   r4   rJ   rv   r2   ro   r>   rs   )rB   r=   r,   rt   keywordrN   r   msgr   r   r   r     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)ru   rn   rv   ro   rw   rs   r   r4   )rB   r,   r=   rO   r   r   r   r   -  s   z.PoFileParser._process_string_continuation_linec              	   C  s4  |    |dd  drNt|dd  D ]6}|d}|dkrCzt||d d  }W n	 ty5   Y qw | j|d | |f q| j|d f qd S |dd  dro|dd   	dD ]
}| j
|  qbd S |dd  dr|dd   }|r| j| d S d S | j|dd    d S )Nr   :   r   ,.)r   r&   r9   rfindr>   r3   r5   r4   r1   r2   rp   rP   rr   rq   )rB   r,   r6   posr=   flagcommentr   r   r   _process_comment9  s.   

zPoFileParser._process_commentfileobjIO[AnyStr] | Iterable[AnyStr]c                 C  s  t |D ]]\}}| }t|ts|| jj}|sq|dr[|dd dr8| j||dd 	 dd qz| 
| W q tyZ } z| ||t| W Y d}~qd}~ww | || q|   | js| jsr| jsr| jr| 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)rt   r#   r   )	enumeraterP   r   r   decoder<   charsetr&   r   r1   r   r3   r   r   rk   rp   rq   rr   rn   r4   rJ   ro   r   )rB   r   r=   r,   excr   r   r   parseT  s,   

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   rj   r:   r<   print)rB   r,   r=   r   r   r   r   r   t  s
   
zPoFileParser._invalid_pofileN)FF)r<   r   ri   rR   rj   rR   r   r?   )r   r?   )F)r   r   r   r?   )rE   rF   rG   rH   r   rA   rm   r   r   r   r   r   r   r   r   r   r   r   r   rd      s    


 
)

 rd   Fr   r   localeLocale | str | Nonedomain
str | Noneri   rR   r   rj   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))
    (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 (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   )rj   )r   rd   r   )r   r   r   ri   r   rj   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"\z\\r   z\tr   z\rr   z\nr   z\")replace)r   r   r   r   escape  s   r   r$   L   prefix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   )rx   r,   r   r   r   r|     s    znormalize.<locals>.<listcomp>)	r~   r%   r   WORD_SEPr2   reverser4   popr(   )
r   r   r   	prefixlenr*   r,   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).
    r0   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(u'foo %(name)s', locations=[('main.py', 1)],
    ...             flags=('fuzzy',))
    <Message...>
    >>> catalog.add((u'bar', u'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
    Nr;   r6   ri   r   r   r   r   sort_byr   backslashreplace)generate_por   r   encoder   write)r   r<   r   r   r   r   r   ri   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   r0   r   )wraprP   )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$   zmsgstr[dz] zmsgstr )
r   idr   r   rs   r   r   r   r   r   )r;   r   r   r   )r<   r   r   r   _format_message  s$   ""&&z$generate_po.<locals>._format_message)r   r   Nr   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   r0   r   z, r   )r   |r   r   z#~ rz   )r	   _sort_messagesr   header_commentr%   r   r(   rq   rr   sortedr5   	TypeErrorr   rT   sepr   r4   rp   previous_idr   r~   rt   values)r<   ri   r   r   r   r   r   r   comment_widthheader_wrapperr   r   r;   comment_headerr*   r,   r   locsr5   r   r=   r6   norm_previous_idr   )r<   r   r   r   r   l  s|   





 


r   rn   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]
    r;   r6   c                 S  s   | j S rL   )r5   )r   r   r   r   r     s    z _sort_messages.<locals>.<lambda>r   )r   r}   )rn   r   r   r   r   r     s   
r   )r   r   r   r   )r,   r   r   r-   )NNFNF)r   r   r   r   r   r   ri   rR   r   r   rj   rR   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   rR   r   rR   r   rR   r   rR   ri   rR   r   rR   r   rR   r   r?   )r<   r   ri   rR   r   rR   r   rR   r   rR   r   rR   r   r   r   r>   r   r   )rn   r   r   r   r   r   )&rH   
__future__r   rT   r   collections.abcr   typingr   r   
babel.corer   babel.messages.catalogr   r   
babel.utilr	   r
   r   r   	_typeshedr   r"   r+   r9   	Exceptionr:   rJ   rd   r   r    r   r   r   r   r   r   r   r   r   r   r   <module>   s^    



,
, K
<

=Pm