a `I@sdZddlmZddlZddlZddlZddlZddlZddlZddl Z ddl m Z ddl m Z ddl mZddlmZddlmZmZdd lmZmZmZmZdd lmZdd lmZdd lmZmZm Z m!Z!dd l"m#Z#ddl$m%Z%m&Z&ddl'm(Z(ddl)m*Z+ddl,m-Z.ddl/m0Z0m1Z1zddl2m3Z3Wn e4yPddl5m3Z3Yn0er\dndZ6d-ddZ7Gddde.Z-Gddde-Z8Gddde-Z9ddZ:Gd d!d!e-Z;Gd"d#d#e-Zd&d'Z?d.d(d)Z@gfd*d+ZAeBd,kre?dS)/z babel.messages.frontend ~~~~~~~~~~~~~~~~~~~~~~~ Frontends for the message extraction functionality. :copyright: (c) 2013-2021 by the Babel Team. :license: BSD, see LICENSE for more details. )print_functionN) OrderedDict)datetime)getpreferredencoding) __version__)Locale localedata)StringIO string_types text_typePY2)UnknownLocaleError)Catalog)DEFAULT_KEYWORDSDEFAULT_MAPPINGcheck_and_call_extract_fileextract_from_dir)write_mo)read_powrite_po)LOCALTZ)log)Command)DistutilsOptionErrorDistutilsSetupError)RawConfigParserZrUrcCsg}t|ttfs|g}|D]N}|dur*qt|ttfrL|t||dq|ddt||Dqtdd|DsJ|S)a Make a list out of an argument. Values from `distutils` argument parsing are always single strings; values from `optparse` parsing may be lists of strings that may need to be further split. No matter the input, this function returns a flat list of whitespace-trimmed strings, with `None` values filtered out. >>> listify_value("foo bar") ['foo', 'bar'] >>> listify_value(["foo bar"]) ['foo', 'bar'] >>> listify_value([["foo"], "bar"]) ['foo', 'bar'] >>> listify_value([["foo"], ["bar", None, "foo"]]) ['foo', 'bar', 'foo'] >>> listify_value("foo, bar, quux", ",") ['foo', 'bar', 'quux'] :param arg: A string or a list of strings :param split: The argument to pass to `str.split()`. :return: Nsplitcss|]}|VqdSNstrip).0sr$;/usr/lib/python3.9/site-packages/babel/messages/frontend.py Sz listify_value..css|]}t|tVqdSr) isinstancer )r"valr$r$r%r&Tr')r(listtupleextend listify_valuer rall)argroutr)r$r$r%r-.s r-c@s.eZdZdZdZdZiZiZeZ dddZ dS)rNr$cCs0||_|d|_d|_d|_d|_d|_dS)NFr) distributioninitialize_optionsZ_dry_runverboseforcehelpZ finalized)selfdistr$r$r%__init__vszCommand.__init__)N) __name__ __module__ __qualname__as_argsmultiple_value_optionsboolean_optionsoption_aliasesoption_choices distutils_logrr8r$r$r$r%rXsrc@sDeZdZdZdZgdZddgZddZdd Zd d Z d d Z dS)compile_catalogaCatalog compilation command for use in ``setup.py`` scripts. If correctly installed, this command is available to Setuptools-using setup scripts automatically. For projects using plain old ``distutils``, the command needs to be registered explicitly in ``setup.py``:: from babel.messages.frontend import compile_catalog setup( ... cmdclass = {'compile_catalog': compile_catalog} ) .. versionadded:: 0.9 z+compile message catalogs to binary MO files))domain=Dz>domains of PO files (space separated list, default 'messages'))z directory=d.path to base directory containing the catalogsz input-file=izname of the input file) output-file=ozQname of the output file (default '//LC_MESSAGES/.mo')locale=lz locale of the catalog to compile) use-fuzzyfzalso include fuzzy translations) statisticsNz#print statistics about translationsrNrPcCs.d|_d|_d|_d|_d|_d|_d|_dSNmessagesF)domain directory input_file output_filelocale use_fuzzyrPr6r$r$r%r2sz"compile_catalog.initialize_optionscCs8t|j|_|js |js td|js4|js4tddS)Nzr2rZrbr\r$r$r$r%rBs   rBc@sbeZdZdZdZgdZgdZdZdZddd d d Z d d iZ ddZ ddZ ddZ ddZdS)extract_messagesaMessage extraction command for use in ``setup.py`` scripts. If correctly installed, this command is available to Setuptools-using setup scripts automatically. For projects using plain old ``distutils``, the command needs to be registered explicitly in ``setup.py``:: from babel.messages.frontend import extract_messages setup( ... cmdclass = {'extract_messages': extract_messages} ) z1extract localizable strings from the project code))zcharset=Nz3charset to use in the output file (default "utf-8"))z keywords=kzispace-separated list of keywords to look for in addition to the defaults (may be repeated multiple times))no-default-keywordsNz#do not include the default keywords)z mapping-file=Fz&path to the mapping configuration file) no-locationNz>do not include location comments with filename and line number)z add-location=Nzlocation lines format. If it is not given or "full", it generates the lines with both file name and line number. If it is "file", the line number part is omitted. If it is "never", it completely suppresses the lines (same as --no-location).) omit-headerNz'do not include msgid "" entry in header)rIrJzname of the output filezwidth=wz"set output line width (default 76)no-wrapNzVdo not break long message lines, longer than the output line width, into several lines) sort-outputNz&generate sorted output (default False)) sort-by-fileNz,sort output by file location (default False))zmsgid-bugs-address=Nzset report address for msgid)zcopyright-holder=Nzset copyright holder in output)zproject=Nzset project name in output)zversion=Nzset project version in output)z add-comments=czuplace comment block with TAG (or those preceding keyword lines) in output file. Separate multiple TAGs with commas(,))strip-commentsr#z)strip the comment TAGs from the comments.)z input-paths=Nzofiles or directories that should be scanned for messages. Separate multiple files or directories with commas(,))z input-dirs=Nz@alias for input-paths (does allow files as well as directories).)rrrrrrrz input-paths)z add-commentskeywords)z --keyword)z --mapping)z--output)z--strip-comment-tags)rz mapping-filez output-filerz add-location)ZfullfilenevercCsd|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ d|_d|_d|_d|_d|_d|_d|_dS)Nzutf-8FT)charsetrno_default_keywords mapping_file no_location add_location omit_headerrV input_dirs input_pathswidthno_wrap sort_output sort_by_filemsgid_bugs_addresscopyright_holderprojectversion add_commentsstrip_commentsinclude_linenorYr$r$r%r2[s*z#extract_messages.initialize_optionscCs|jr|js|j|_ntd|jr*i}nt}|tt|j ||_ |j sZtd|j shtd|j r||j r|td|j s|j sd|_ n|j durt |j |_ |jr|jrtd|jrt|jtrtd|j|_n6|jdurtdd |jjpd D|_ng|_|js$td |jD] }tj|s*td |q*t|jpXd d |_|jr|js||j|_|js|j |_|j!dkrd|_"n|j!dkrd|_#dS)Nz1input-dirs and input-paths are mutually exclusivez=you must specify new keywords if you disable the default oneszno output file specified0'--no-wrap' and '--width' are mutually exclusiveLz;'--sort-output' and '--sort-by-file' are mutually exclusivez,\s*cSsg|]}|dddqS).r[rr)r"rr$r$r% sz5extract_messages.finalize_options..r$z'no input files or directories specifiedzInput path: %s does not exist,rTrF)$rrrrrcopyupdateparse_keywordsr-rrVrrintrrr(r rerr1dictfromkeysZpackageskeysrjrkrnrrZget_namerZ get_versionrrr)r6rrkr$r$r%rZrs\               z!extract_messages.finalize_optionsc sN}tjd"}tjjjjjd}|D]\}}fdd}t j rt }t |||jjj|}nt||jj|jd}|D]P\} } } } } t j r| }nt j t j | }|j| d|| fg| | dqq8jdjt||jjjjjjdWdn1s@0YdS) Nrh)rrrrrcsl|dkr dStjr}ntjtj|}d}|rXdddd|D}jd||dS)Nignorez (%s)z, cSsg|]\}}d||fqS)z%s="%s"r$)r"rvr$r$r%rsz:extract_messages.run..callback..zextracting messages from %s%s)rjrkisfilenormpathrlr]rrr)filenamemethodoptionsfilepathZoptstrrkr6r$r%callbacks   z&extract_messages.run..callback)rZ comment_tagsrZstrip_comment_tags)Z auto_commentscontextzwriting PO template file to %s)rrrrrr) _get_mappingsrprVrrrrrrrjrkrgetcwdrrrrrrrladdrrrrrrrrrr)r6mappingsr{r` method_map options_maprZ current_dirZ extractedrrurzZcommentsrrr$rr%rbsN     zextract_messages.runc Cs g}|jr`t|jt}t|\}}Wdn1s80Y|jD]}||||fqHnt|jddr|jj}| D]h\}}t |t rtt |\}}n4gi}}|D]$\}} } ||| f| pi||<q||||fq~n|jD]}||t ifq|S)Nmessage_extractors)rrppo_file_read_mode parse_mappingrrigetattrr1rr]r(r r r) r6rfileobjrrrkrmappingpatternrrr$r$r%rs&*    zextract_messages._get_mappingsN)r9r:r;r|r}r~r>r<r=r?r@r2rZrbrr$r$r$r%r s"1C=rcCs"|dks Jt|tstddS)agValidate the ``message_extractors`` keyword argument to ``setup()``. :param dist: the distutils/setuptools ``Distribution`` object :param name: the name of the keyword argument (should always be "message_extractors") :param value: the value of the keyword argument :raise `DistutilsSetupError`: if the value is not valid rzDthe value of the "message_extractors" parameter must be a dictionaryN)r(rr)r7namevaluer$r$r%check_message_extractorss  rc@s:eZdZdZdZgdZdgZddZddZd d Z d S) init_catalogaNew catalog initialization command for use in ``setup.py`` scripts. If correctly installed, this command is available to Setuptools-using setup scripts automatically. For projects using plain old ``distutils``, the command needs to be registered explicitly in ``setup.py``:: from babel.messages.frontend import init_catalog setup( ... cmdclass = {'init_catalog': init_catalog} ) z(create a new catalog based on a POT file)rCrDz&domain of PO file (default 'messages')rG) output-dir=rEzpath to output directoryrIrJzQname of the output file (default '//LC_MESSAGES/.po'))rLrMz$locale for the new localized catalogrrrcCs.d|_d|_d|_d|_d|_d|_d|_dSrQ) output_dirrVrUrWrSrrrYr$r$r%r2Aszinit_catalog.initialize_optionsc Cs|jstd|jstdzt|j|_Wn,tyZ}zt|WYd}~n d}~00|jsp|jsptd|jst j |j|jd|j d|_t j t j |jst t j |j|jr|jrtd|js|jsd|_n|jdurt|j|_dS)Nyou must specify the input filez-you must provide a locale for the new catalogz%you must specify the output directoryrcrdrr)rUrrWrparse_localer rVrrjrkrlrSrndirnamemakedirsrrr)r6er$r$r%rZJs,     zinit_catalog.finalize_optionscCs|jd|j|jt|jd}t||jd}Wdn1sD0Y|j|_t t |_ d|_ t|jd }t |||jdWdn1s0YdS)Nzcreating catalog %s based on %srf)rWFrh)r)rrrrVrUrprrWrrZnowrZ revision_datersrr)r6ryr`r{r$r$r%rbfs , zinit_catalog.runN r9r:r;r|r}r~r>r2rZrbr$r$r$r%rs rc@s<eZdZdZdZgdZgdZddZddZd d Z d S) update_catalogaCatalog merging command for use in ``setup.py`` scripts. If correctly installed, this command is available to Setuptools-using setup scripts automatically. For projects using plain old ``distutils``, the command needs to be registered explicitly in ``setup.py``:: from babel.messages.frontend import update_catalog setup( ... cmdclass = {'update_catalog': update_catalog} ) .. versionadded:: 0.9 z'update message catalogs from a POT file) rrG)rrErFr)rNz%do not include msgid entry in headerrKrr)zignore-obsolete=Nz1whether to omit obsolete messages from the output)no-fuzzy-matchingNzdo not use fuzzy matching)update-header-commentNzupdate target header comment)previousNz+keep previous msgids of translated messages)rrzignore-obsoleterrrcCsLd|_d|_d|_d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ dSrQ) rSrUrrVrrWrrignore_obsoleteno_fuzzy_matchingupdate_header_commentrrYr$r$r%r2sz!update_catalog.initialize_optionscCs|jstd|js"|js"td|jr6|js6td|jrJ|jrJtd|js^|js^d|_n|jdurtt|j|_|jr|j rd|_ dS)Nrz-you must specify the output file or directoryzyou must specify the localerrF) rUrrVrrWrrrrrrYr$r$r%rZs       zupdate_catalog.finalize_optionsc Cs>g}|js|jr:||jtj|j|jd|jdfqt|jD]8}tj|j|d|jd}tj |rF|||fqFn||j|jf|st d|j}|stj tj |j d}t|j d}t|}Wdn1s0Y|D]:\}}|jd||j t|d}t|||d}Wdn1sH0Y|j||j|jdtjtj|ttj |} zLt| d ,} t| ||j|j|j|jd Wdn1s0YWnt| Yn0zt| |Wqty6t|t !| |t| Yq0qdS) Nrcrdrerrfzupdating catalog %s based on %s)rWrS)rrh)rrZinclude_previousr)"rVrWrirjrkrlrrSrmrnrsplitextbasenamerUrprrrrrrrrtempfileZ gettempprefixrrrrrremoverenameOSErrorshutilr) r6rvrWrwrSrytemplaterr`ZtmpnameZtmpfiler$r$r%rbsj  & .  *   zupdate_catalog.runNrr$r$r$r%rxsrc@s^eZdZdZdZdeZdddddZee e e dZ d Z dd d Zd d ZddZddZd S)CommandLineInterfacezCommand-line interface. This class provides a simple command-line interface to the message extraction and PO file generation functionality. z%%prog %s [options] %sz %%prog %sz$compile message catalogs to MO filesz:extract messages from source files and generate a POT filez+create new message catalogs from a POT filez0update existing message catalogs from a POT file)compileextractinitrNc Cs|durtj}tj|jd|jd|_|j|j|j_ |jj ddddd|jj d d d d t j d d|jj ddd d t j dd|jjdt jd|j|dd\}}||j|jr6t}tdd|D}|d|d}|D]>}t|}|||jf} t| tjjp(tp(ddqdS|sH|j d|d} | |j!vrl|j d| |"| |dd} | #S)z{Main entry point of the command-line interface. :param argv: list of arguments passed on the command-line N)commandz[args])usagerz--list-locales list_locales store_truez print all known locales and exit)destactionr5z-vz --verbose store_constloglevelzprint as much as possible)rrconstr5z-qz--quietzprint as little as possibleF)rrr[cSsg|] }t|qSr$r^)r" identifierr$r$r%rAr'z,CommandLineInterface.run..z %%-%ds %%sasciireplacerzQno valid command or option passed. Try the -h/--help option for more information.zunknown command "%s")$sysargvoptparse OptionParserrrparserZdisable_interspersed_args_help print_help add_optionloggingDEBUGZERROR set_defaultsINFO parse_args_configure_loggingrrrZlocale_identifiersmaxsortrrZ english_nameprintencodestdoutencodingrr_commands_configure_commandrb) r6rrargsZ identifierslongestformatrrWoutputcmdnamecmdinstr$r$r%rb$sV           zCommandLineInterface.runcCsdtd|_|j||jjr.|jjd}nt}|j|||td}||dS)Nbabelrz %(message)s) rZ getLoggerrZsetLevelhandlersZ StreamHandlerZ addHandlerZ FormatterZ setFormatter)r6rZhandler formatterr$r$r%rWs     z'CommandLineInterface._configure_loggingcCslt|jtdtdd|jD}dtd|d}t|j}|D]\}}t|||fqNdS)Nz commands:cSsg|] }t|qSr$r)r"rr$r$r%rir'z.CommandLineInterface._help..z %%-%ds %%sr[)r r format_helprr sortedr])r6rrr rr}r$r$r%rfs zCommandLineInterface._helpc Cs|j|}|}|jr|j|_t|ts,J|tj|j|df|j|d}t |dd}|j D]\}}} | d} t || dd} d| g} |r| d || |j| d|j| d } | |kr|jd | 7_qd| |jvr|j| d | d qd| |jvr&|j| d| | dqd|j| | | | dqd||\}}|rdt|| dd|t|D]\}}t|||qpz |Wn4ty}z|t|WYd }~n d }~00|S)zB :type cmdname: str :type argv: list[str] r)rr}r<r$=-_z--%sz-%sNz<%s>r)rr5ri)rr5choices)r5defaultr)command_classesrr(rr2rrrr rr~r!rrir,r?getr@r>rr=rsetattrvarsr]Zensure_finalizedrr_str)r6rrZcmdclassrrr<ZlongZshortr5rrZstrsrrrkeyrerrr$r$r%rosH        $z'CommandLineInterface._configure_command)N)r9r:r;r|rVERSIONrr rBrrrr rrbrrrr$r$r$r%r s$ 3 rcCsttjSr)rrbrrr$r$r$r%mainsr(c Csi}g}i}t}t|j|_tr0|||n ||||D]V}|dkr`t||}qDdd| ddD\}}| ||ft||||<qD|rt |D](\} \}}||vr||}||f|| <q||fS)aParse an extraction method mapping from a file-like object. >>> buf = StringIO(''' ... [extractors] ... custom = mypackage.module:myfunc ... ... # Python source files ... [python: **.py] ... ... # Genshi templates ... [genshi: **/templates/**.html] ... include_attrs = ... [genshi: **/templates/**.txt] ... template_class = genshi.template:TextTemplate ... encoding = latin-1 ... ... # Some custom extractor ... [custom: **/custom/*.*] ... ''') >>> method_map, options_map = parse_mapping(buf) >>> len(method_map) 4 >>> method_map[0] ('**.py', 'python') >>> options_map['**.py'] {} >>> method_map[1] ('**/templates/**.html', 'genshi') >>> options_map['**/templates/**.html']['include_attrs'] '' >>> method_map[2] ('**/templates/**.txt', 'genshi') >>> options_map['**/templates/**.txt']['template_class'] 'genshi.template:TextTemplate' >>> options_map['**/templates/**.txt']['encoding'] 'latin-1' >>> method_map[3] ('**/custom/*.*', 'mypackage.module:myfunc') >>> options_map['**/custom/*.*'] {} :param fileobj: a readable file-like object containing the configuration text to parse :see: `extract_from_directory` extractorscSsg|] }|qSr$r )r"partr$r$r%rr'z!parse_mapping..:r[) rrZ _sectionsr ZreadfpZ read_fileZsectionsrr]rriro) rrr)rrrsectionrrrxr$r$r%rs(1   rcCsi}|D]}d|vr$|d\}}n |d}}||vr|rg}|dD]:}|ddkrt|t|dddfqH|t|qHt|}|||<q|S)aQParse keywords specifications from the given list of strings. >>> kw = sorted(parse_keywords(['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2']).items()) >>> for keyword, indices in kw: ... print((keyword, indices)) ('_', None) ('dgettext', (2,)) ('dngettext', (2, 3)) ('pgettext', ((1, 'c'), 2)) r+Nrr)rrirr+)stringsrrqfuncnameindicesZindsxr$r$r%rs    r__main__)N)N)Cr|Z __future__rrrrjrrrr collectionsrrrWrrrr'rrZ babel._compatr r r r Z babel.corer Zbabel.messages.catalogrZbabel.messages.extractrrrrZbabel.messages.mofilerZbabel.messages.pofilerrZ babel.utilrZ distutilsrrAZ distutils.cmdrZ_CommandZdistutils.errorsrrZ ConfigParserr ImportErrorZ configparserrr-rBrrrrobjectrr(rrr9r$r$r$r%s\            *) [ N