U ä€C^¨ã@s~dZddlZddlZddlZddlZddlZddlmZm Z ddl m Z m Z ddl mZmZmZmZe e¡ZeZGdd„deƒZdd „Zd d „ZGd d „d eƒZGdd„deƒZGdd„deƒZGdd„deƒZGdd„deƒZGdd„deƒZ Gdd„deƒZ!Gdd„de!e ƒZ"Gdd„de!eƒZ#Gdd„de ƒZ$Gd d!„d!eƒZ%Gd"d#„d#e%e ƒZ&Gd$d%„d%e%eƒZ'eee$e&e'd&œZ(dS)'a­Response parsers for the various protocol types. The module contains classes that can take an HTTP response, and given an output shape, parse the response into a dict according to the rules in the output shape. There are many similarities amongst the different protocols with regard to response parsing, and the code is structured in a way to avoid code duplication when possible. The diagram below is a diagram showing the inheritance hierarchy of the response classes. :: +--------------+ |ResponseParser| +--------------+ ^ ^ ^ +--------------------+ | +-------------------+ | | | +----------+----------+ +------+-------+ +-------+------+ |BaseXMLResponseParser| |BaseRestParser| |BaseJSONParser| +---------------------+ +--------------+ +--------------+ ^ ^ ^ ^ ^ ^ | | | | | | | | | | | | | ++----------+-+ +-+-----------++ | | |RestXMLParser| |RestJSONParser| | +-----+-----+ +-------------+ +--------------+ +----+-----+ |QueryParser| |JSONParser| +-----------+ +----------+ The diagram above shows that there is a base class, ``ResponseParser`` that contains logic that is similar amongst all the different protocols (``query``, ``json``, ``rest-json``, ``rest-xml``). Amongst the various services there is shared logic that can be grouped several ways: * The ``query`` and ``rest-xml`` both have XML bodies that are parsed in the same way. * The ``json`` and ``rest-json`` protocols both have JSON bodies that are parsed in the same way. * The ``rest-json`` and ``rest-xml`` protocols have additional attributes besides body parameters that are parsed the same (headers, query string, status code). This is reflected in the class diagram above. The ``BaseXMLResponseParser`` and the BaseJSONParser contain logic for parsing the XML/JSON body, and the BaseRestParser contains logic for parsing out attributes that come from other parts of the HTTP response. Classes like the ``RestXMLParser`` inherit from the ``BaseXMLResponseParser`` to get the XML body parsing logic and the ``BaseRestParser`` to get the HTTP header/status code/query string parsing. Additionally, there are event stream parsers that are used by the other parsers to wrap streaming bodies that represent a stream of events. The BaseEventStreamParser extends from ResponseParser and defines the logic for parsing values from the headers and payload of a message from the underlying binary encoding protocol. Currently, event streams support parsing bodies encoded as JSON and XML through the following hierarchy. +--------------+ |ResponseParser| +--------------+ ^ ^ ^ +--------------------+ | +------------------+ | | | +----------+----------+ +----------+----------+ +-------+------+ |BaseXMLResponseParser| |BaseEventStreamParser| |BaseJSONParser| +---------------------+ +---------------------+ +--------------+ ^ ^ ^ ^ | | | | | | | | +-+----------------+-+ +-+-----------------+-+ |EventStreamXMLParser| |EventStreamJSONParser| +--------------------+ +---------------------+ Return Values ============= Each call to ``parse()`` returns a dict has this form:: Standard Response { "ResponseMetadata": {"RequestId": } } Error response { "ResponseMetadata": {"RequestId": } "Error": { "Code": , "Message": , "Type": , } } éN)ÚsixÚ XMLParseError)Ú EventStreamÚNoInitialResponseError)Úparse_timestampÚ merge_dictsÚis_json_value_headerÚlowercase_dictc@s$eZdZdd„Zdd„Zdd„ZdS)ÚResponseParserFactorycCs i|_dS©N)Ú _defaults)Úself©rú6/tmp/pip-install-6_kvzl1k/botocore/botocore/parsers.pyÚ__init__‡szResponseParserFactory.__init__cKs|j |¡dS)aOSet default arguments when a parser instance is created. You can specify any kwargs that are allowed by a ResponseParser class. There are currently two arguments: * timestamp_parser - A callable that can parse a timetsamp string * blob_parser - A callable that can parse a blob type N)r Úupdate)r ÚkwargsrrrÚset_parser_defaultsŠs z)ResponseParserFactory.set_parser_defaultscCst|}|f|jŽSr )ÚPROTOCOL_PARSERSr )r Z protocol_nameZ parser_clsrrrÚ create_parser–sz#ResponseParserFactory.create_parserN)Ú__name__Ú __module__Ú __qualname__rrrrrrrr †s r cCs tƒ |¡Sr )r r)Úprotocolrrrr›srcs‡fdd„}|S)Ncs.t|dƒr|j}|dkr"d}n|}ˆ|||ƒS)NÚtextÚ)Úhasattrr)r ÚshapeZnode_or_stringr©ÚfuncrrÚ_get_text_content¥s  z(_text_content.._get_text_contentr)rr rrrÚ _text_contentŸs r!c@s eZdZdS)ÚResponseParserErrorN)rrrrrrrr"³sr"c@sreZdZdZdZdZddd„Zdd„Zdd „Zd d „Z d d „Z dd„Z dd„Z dd„Z dd„Zdd„Zdd„ZdS)ÚResponseParseraoBase class for response parsing. This class represents the interface that all ResponseParsers for the various protocols must implement. This class will take an HTTP response and a model shape and parse the HTTP response into a dictionary. There is a single public method exposed: ``parse``. See the ``parse`` docstring for more info. zutf-8NcCsH|dkr t}||_|dkr |j}||_d|_|jdk rD| ||¡|_dSr )ÚDEFAULT_TIMESTAMP_PARSERÚ_timestamp_parserÚ_default_blob_parserÚ _blob_parserÚ_event_stream_parserÚEVENT_STREAM_PARSER_CLS©r Ztimestamp_parserZ blob_parserrrrrÇs ÿzResponseParser.__init__cCs t |¡Sr )Úbase64Ú b64decode)r Úvaluerrrr&Ósz#ResponseParser._default_blob_parsercCs²t d|d¡t d|d¡|ddkrP| |¡rB| |¡}q\| ||¡}n | ||¡}|rp|j d¡rp|St|t ƒr®| di¡}|d|d <|d}t |ƒ|d <||d<|S) a>Parse the HTTP response given a shape. :param response: The HTTP response dictionary. This is a dictionary that represents the HTTP request. The dictionary must have the following keys, ``body``, ``headers``, and ``status_code``. :param shape: The model shape describing the expected output. :return: Returns a dictionary representing the parsed response described by the model. In addition to the shape described from the model, each response will also have a ``ResponseMetadata`` which contains metadata about the response, which contains at least two keys containing ``RequestId`` and ``HTTPStatusCode``. Some responses may populate additional keys, but ``RequestId`` will always be present. zResponse headers: %sÚheaderszResponse body: %sÚbodyÚ status_codei-Ú eventstreamÚResponseMetadataZHTTPStatusCodeZ HTTPHeaders) ÚLOGÚdebugÚ_is_generic_error_responseÚ_do_generic_error_parseÚ_do_error_parseÚ _do_parseÚ serializationÚgetÚ isinstanceÚdictr )r ÚresponserÚparsedZresponse_metadatar.rrrÚparseÙs         zResponseParser.parsecCsD|ddkr@d|ks |ddkr$dS|d ¡}| d¡p>| SdS)Nr0iôr/Ts)ÚstripÚ startswith)r r=r/rrrr5s  z)ResponseParser._is_generic_error_responsecCs4t d¡t|dƒtjjj |dd¡dœidœS)NzlReceived a non protocol specific error response from the service, unable to populate error code and message.r0r©ÚCodeÚMessage©ÚErrorr2)r3r4ÚstrrÚmovesÚ http_clientÚ responsesr:©r r=rrrr6s   ÿÿüz&ResponseParser._do_generic_error_parsecCstd|jjƒ‚dS)Nz %s._do_parse©ÚNotImplementedErrorÚ __class__r©r r=rrrrr8$szResponseParser._do_parsecCstd|jjƒ‚dS)Nz%s._do_error_parserLrOrrrr7's ÿzResponseParser._do_error_parsecCst|d|j|jƒ}|||ƒS)Nz _handle_%s)ÚgetattrÚ type_nameÚ_default_handle)r rÚnodeÚhandlerrrrÚ _parse_shape+s ÿzResponseParser._parse_shapecCs*g}|j}|D]}| | ||¡¡q|Sr )ÚmemberÚappendrU)r rrSr>Ú member_shapeÚitemrrrÚ _handle_list0s zResponseParser._handle_listcCs|Sr r©r rr-rrrrR9szResponseParser._default_handlecCs&|j}|d d¡}t|d|||ƒS)NÚcontextZoperation_namer/)r(r:r)r r=rÚparserÚnamerrrÚ_create_event_stream<sz#ResponseParser._create_event_stream)NN)rrrÚ__doc__ÚDEFAULT_ENCODINGr)rr&r?r5r6r8r7rUrZrRr_rrrrr#·s  ,  r#cs¶eZdZd ‡fdd„ Zdd„Zdd„Z‡fdd „Zd d „Zd d „Zdd„Z dd„Z dd„Z e dd„ƒZ e dd„ƒZe dd„ƒZe dd„ƒZe dd„ƒZe dd„ƒZeZeZeZ‡ZS)!ÚBaseXMLResponseParserNcs"tt|ƒ ||¡t d¡|_dS)Nz{.*})ÚsuperrbrÚreÚcompileÚ _namespace_rer*©rNrrrCs ÿzBaseXMLResponseParser.__init__c Cs®i}|j}|j}|j d¡pd}|j d¡p.d}|j d¡rLt|tƒsL|g}|D]X}|D]F} | | ¡} | |kr|| || ¡} qX| |kr’| || ¡} qXtd| ƒ‚qX| || <qP|S)Nr^Úkeyr-Ú flattenedzUnknown tag: %s) rhr-r9r:r;ÚlistÚ _node_tagrUr") r rrSr>Ú key_shapeÚ value_shapeZkey_location_nameZvalue_location_nameZ keyval_nodeZ single_pairZtag_nameZkey_nameZval_namerrrÚ _handle_mapHs"  z!BaseXMLResponseParser._handle_mapcCs|j d|j¡S©Nr)rfÚsubÚtag)r rSrrrrk]szBaseXMLResponseParser._node_tagcs.|j d¡rt|tƒs|g}tt|ƒ ||¡S)Nri)r9r:r;rjrcrbrZ)r rrSrgrrrZ`sz"BaseXMLResponseParser._handle_listcCsØi}|j}| |¡}|D]º}||}d|jks|j d¡rruÚxml_dictÚ member_namerXZxml_nameZ member_nodeZattribsZ location_namerhr-Znew_keyrrrÚ_handle_structurejs8   ÿ  ÿ   ÿ z'BaseXMLResponseParser._handle_structurecCsL|jdkr0|j d¡r0|jj d¡}|dk r0|S|j d¡}|dk rH|S|S)Nrjrir^)rQr9r:rV)r rr|Zlist_member_serialized_nameZserialized_namerrrrw…sÿ z&BaseXMLResponseParser._member_key_namecCstt|tƒr| |d¡Si}|D]N}| |¡}||krft||tƒrT|| |¡qn|||g||<q |||<q |S)Nr)r;rjrvrkrW)r Z parent_noder{rYrhrrrrv”s   z-BaseXMLResponseParser._build_name_to_xml_nodec Csjz2tjjjtjj ¡|jd}| |¡| ¡}Wn2tk rd}zt d||fƒ‚W5d}~XYnX|S)N)ÚtargetÚencodingz7Unable to parse response (%s), invalid XML received: %s) ÚxmlÚetreeÚ cElementTreeÚ XMLParserÚ TreeBuilderraÚfeedÚcloserr")r Ú xml_stringr]ÚrootÚerrrÚ_parse_xml_string_to_dom«s þ  ÿÿz.BaseXMLResponseParser._parse_xml_string_to_domcCsB| ¡D]4\}}t|ƒr2| |¡}| |¡||<q|j||<q|Sr )ryrjrvÚ_replace_nodesr)r r>rhr-Zsub_dictrrrr‹¸s   z$BaseXMLResponseParser._replace_nodescCs|dkr dSdSdS)NÚtrueTFr©r rrrrrÚ_handle_booleanÁsz%BaseXMLResponseParser._handle_booleancCst|ƒSr )ÚfloatrrrrÚ _handle_floatÈsz#BaseXMLResponseParser._handle_floatcCs | |¡Sr ©r%rrrrÚ_handle_timestampÌsz'BaseXMLResponseParser._handle_timestampcCst|ƒSr )ÚintrrrrÚ_handle_integerÐsz%BaseXMLResponseParser._handle_integercCs|Sr rrrrrÚ_handle_stringÔsz$BaseXMLResponseParser._handle_stringcCs | |¡Sr ©r'rrrrÚ _handle_blobØsz"BaseXMLResponseParser._handle_blob)NN)rrrrrnrkrZr}rwrvrŠr‹r!rŽrr’r”r•r—Z_handle_characterZ_handle_doubleZ _handle_longÚ __classcell__rrrgrrbBs0         rbc@s,eZdZdd„Zdd„Zdd„Zdd„Zd S) Ú QueryParsercCs\|d}| |¡}| |¡}| |¡d|kr>| | d¡¡d|krXd| d¡i|d<|S)Nr/ZErrorsÚ RequestIdr2)rŠrvr‹rÚpop)r r=rÚ xml_contentsrˆr>rrrr7ãs   zQueryParser._do_error_parsecCsZ|d}| |¡}i}|dk rJ|}d|jkr>| |jd|¡}| ||¡}| ||¡|S)Nr/Z resultWrapper)rŠr9Ú_find_result_wrapped_shaperUÚ_inject_response_metadata)r r=rrœrˆr>Ústartrrrr8ós  þ  zQueryParser._do_parsecCs| |¡}||Sr )rv)r Z element_nameZ xml_root_nodeÚmappingrrrrs z&QueryParser._find_result_wrapped_shapecCsN| |¡}| d¡}|dk rJ| |¡}| ¡D]\}}|j||<q.||d<dS©Nr2)rvr:ryr)r rSÚ inject_intor Ú child_nodeZ sub_mappingrhr-rrrržs    z%QueryParser._inject_response_metadataN)rrrr7r8rržrrrrr™ásr™cs$eZdZdd„Z‡fdd„Z‡ZS)ÚEC2QueryParsercCs.| |¡}| d¡}|dk r*d|ji|d<dS)NZ requestIdršr2)rvr:r)r rSr¢r r£rrrržs  z(EC2QueryParser._inject_response_metadatacs(tt|ƒ ||¡}d| d¡i|d<|S)NršZ RequestIDr2)rcr¤r7r›)r r=rÚoriginalrgrrr7s ÿzEC2QueryParser._do_error_parse)rrrržr7r˜rrrgrr¤sr¤c@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)ÚBaseJSONParserc Cs`|j}|dkrdSi}|D]@}||}|j d|¡}| |¡}|dk r| |||¡||<q|S)Nr^)rur9r:rU) r rr-Ú member_shapesÚ final_parsedr|rXZ json_nameZ raw_valuerrrr}-s þ z BaseJSONParser._handle_structurec CsFi}|j}|j}| ¡D](\}}| ||¡}| ||¡}|||<q|Sr )rhr-ryrU) r rr-r>rlrmrhZ actual_keyZ actual_valuerrrrn?s   zBaseJSONParser._handle_mapcCs | |¡Sr r–r[rrrr—IszBaseJSONParser._handle_blobcCs | |¡Sr r‘r[rrrr’Lsz BaseJSONParser._handle_timestampcCs˜| |d¡}dddœidœ}| d| dd¡¡|dd<| d¡}| d |oTt|ƒ¡}|dk r„d |krx| d d ¡d }||dd <| ||d ¡|S)Nr/r©rDrCrEÚmessagerDrFr0Z__typeú#érCr.)Ú_parse_body_as_jsonr:rGÚrsplitrž)r r=rr/ÚerrorZ response_codeÚcoderrrr7Os  ÿ   zBaseJSONParser._do_error_parsecCs d|kr|d| di¡d<dS)Núx-amzn-requestidr2rš)Ú setdefault)r r>r.rrrržjsÿz(BaseJSONParser._inject_response_metadatacCsF|siS| |j¡}zt |¡}|WStk r@d|iYSXdS)Nrª)ÚdecoderaÚjsonÚloadsÚ ValueError)r Ú body_contentsr/Úoriginal_parsedrrrr­os  z"BaseJSONParser._parse_body_as_jsonN) rrrr}rnr—r’r7ržr­rrrrr¦+s r¦c@s4eZdZdd„Zdd„Zdd„Zdd„Zd d „Zd S) ÚBaseEventStreamParsercCshi}|j d¡r@|d d¡}|j |¡}|rd| ||¡||<n$| |||j|¡| |||j|¡|S)Nr1r.z :event-type)r9r:rur8Ú_parse_non_payload_attrsÚ_parse_payload)r r=rr¨Z event_typeZ event_shaperrrr8~s  ÿzBaseEventStreamParser._do_parsec Cs†|d d¡}|j |¡}|dk r\| |d¡}| ||¡}d|| d| dd¡¡dœi}n&d|d d d¡|d d d¡dœi}|S) Nr.z:exception-typer/rFrDrªrrBz :error-codez:error-message)r:ruÚ_initial_body_parserU)r r=rZexception_typeZexception_shaper¸r/r¯rrrr7‹s   þÿþÿz%BaseEventStreamParser._do_error_parsec Cs¦|j d¡r¢|D]l}||}|j d¡r|d}|jdkr@|}n.|jdkrX| |j¡}n| |¡} | || ¡}|||<dSq| |d¡} | || ¡} | | ¡dS)NÚeventZ eventpayloadr/ÚblobÚstring)r9r:rQr³rar¼rUr) r r=rr§r¨r^rXr/Z parsed_bodyZ raw_parser¸Ú body_parsedrrrr» s        z$BaseEventStreamParser._parse_payloadc CsZ|d}|D]H}||}|j d¡r ||kr ||}|jdkrL| |d¡}|||<q dS)Nr.rsÚ timestampg@@)r9r:rQr%) r r=rr§r¨r.r^rXr-rrrrº´s  z.BaseEventStreamParser._parse_non_payload_attrscCs tdƒ‚dS©Nr¼©rM©r r·rrrr¼Âsz)BaseEventStreamParser._initial_body_parseN)rrrr8r7r»rºr¼rrrrr¹|s  r¹c@seZdZdd„ZdS)ÚEventStreamJSONParsercCs | |¡Sr ©r­rÄrrrr¼Ìsz)EventStreamJSONParser._initial_body_parseN©rrrr¼rrrrrÅÊsrÅc@seZdZdd„ZdS)ÚEventStreamXMLParsercCs|stjj d¡S| |¡Sro©r€rr‚ÚElementrŠ©r r‡rrrr¼Òsz(EventStreamXMLParser._initial_body_parseNrÇrrrrrÈÐsrÈc@s(eZdZeZdd„Zdd„Zdd„ZdS)Ú JSONParsercCsJi}|dk r6|j}|r&| |||¡}n| |d|¡}| ||d¡|S)Nr/r.)Zevent_stream_nameÚ_handle_event_streamÚ_handle_json_bodyrž)r r=rr>Ú event_namerrrr8ÝszJSONParser._do_parsec Cs^|j|}| ||¡}z | ¡}Wn tk rBd}t|ƒ‚YnX| |j|¡}|||<|S)Nz,First event was not of type initial-response)rur_Zget_initial_responserr"rÎÚpayload) r r=rrÏZevent_stream_shapeZ event_streamr½Ú error_msgr>rrrrÍès   zJSONParser._handle_event_streamcCs| |¡}| ||¡Sr )r­rU)r Zraw_bodyrZ parsed_jsonrrrrÎôs zJSONParser._handle_json_bodyN)rrrrÅr)r8rÍrÎrrrrrÌØs  rÌc@sDeZdZdd„Zdd„Zdd„Zdd„Zd d „Zd d „Zd d„Z dS)ÚBaseRestParsercCsHi}| |¡|d<|dkr|S|j}| ||||¡| ||||¡|Sr¡)Ú_populate_response_metadatarurºr»)r r=rr¨r§rrrr8þsÿÿzBaseRestParser._do_parsecCsJi}|d}d|kr"|d|d<n$d|krF|d|d<| dd¡|d<|S)Nr.r±ršúx-amz-request-idú x-amz-id-2rÚHostId)r:)r r=Úmetadatar.rrrrÓ s z*BaseRestParser._populate_response_metadatac Cs¸d|jkr|jd}||}|j d¡r>| ||¡}|||<q´|jdkrp|d}t|tƒrf| |j¡}|||<q´| |d¡}|  ||¡||<n$| |d¡}|  ||¡} |  | ¡dS)NrÐr1)r¿r¾r/) r9r:r_rQr;Úbytesr³rar¼rUr) r r=rr§r¨Zpayload_member_nameZ body_shaper/r¸rÀrrrr»s&         ÿ  zBaseRestParser._parse_payloadc Cs |d}|D]Ž}||}|j d¡}|dkr0q q |dkrN| ||d¡||<q |dkrh| ||¡||<q |dkr |j d|¡} | |kr | ||| ¡||<q dS)Nr.rrZ statusCoder0Úheaderr^)r9r:rUÚ_parse_header_map) r r=rr§r¨r.r^rXrrÚ header_namerrrrº/s, ÿ ÿ ÿz'BaseRestParser._parse_non_payload_attrscCsNi}|j dd¡ ¡}|D].}| ¡ |¡r|t|ƒd…}||||<q|S)Nr^r)r9r:ÚlowerrAÚlen)r rr.r>ÚprefixrÛr^rrrrÚCsz BaseRestParser._parse_header_mapcCs tdƒ‚dSrÂrÃrÄrrrr¼Psz"BaseRestParser._initial_body_parsecCs,|}t|ƒr(t |¡ |j¡}t |¡}|Sr )rr+r,r³rar´rµ)r rr-r>Údecodedrrrr•Ws  zBaseRestParser._handle_stringN) rrrr8rÓr»rºrÚr¼r•rrrrrÒüs   rÒcs0eZdZeZdd„Z‡fdd„Zdd„Z‡ZS)ÚRestJSONParsercCs | |¡Sr rÆrÄrrrr¼csz"RestJSONParser._initial_body_parsecs"tt|ƒ ||¡}| ||¡|Sr )rcràr7Ú_inject_error_code)r r=rr¯rgrrr7fs zRestJSONParser._do_error_parsecCsr| |d¡}d|dkrB|dd}| d¡d}||dd<n,d|ksRd|krn| d| dd ¡¡|dd<dS) Nr/zx-amzn-errortyper.rtrrFrCr°r)r¼rzr:)r r¯r=r/r°rrrráks   ÿz!RestJSONParser._inject_error_code) rrrrÅr)r¼r7rár˜rrrgrrà_s ràcsDeZdZeZdd„Zdd„Zdd„Zdd„Ze ‡fd d „ƒZ ‡Z S) Ú RestXMLParsercCs|stjj d¡S| |¡SrorÉrËrrrr¼~sz!RestXMLParser._initial_body_parsec CsP|drFz | |¡WStk rD}ztjdddW5d}~XYnX| |¡S)Nr/z2Exception caught when parsing error response body:T)Úexc_info)Ú_parse_error_from_bodyr"r3r4Ú_parse_error_from_http_status)r r=rr‰rrrr7ƒs þzRestXMLParser._do_error_parsecCsHt|dƒtjjj |dd¡dœ|d dd¡|d dd¡dœdœS) Nr0rrBr.rÔrÕ)ršrÖrE)rGrrHrIrJr:rKrrrrås  ÿþþúz+RestXMLParser._parse_error_from_http_statuscCs’|d}| |¡}| |¡}| |¡|jdkr\| |¡}| dd¡| dd¡||dœSd|krvd| d¡i|d<ddddœi}t||ƒ|S) Nr/rFršrrÖrEr2r©)rŠrvr‹rqrÓr›r)r r=rœrˆr>r×Údefaultrrrräªs         z$RestXMLParser._parse_error_from_bodycstt|ƒ ||¡}|Sr )rcrâr•rrgrrr•ÁszRestXMLParser._handle_string) rrrrÈr)r¼r7rårär!r•r˜rrrgrrâzs râ)Zec2Úqueryr´z rest-jsonzrest-xml))r`rdr+r´Úxml.etree.cElementTreer€ÚloggingZbotocore.compatrrZbotocore.eventstreamrrZbotocore.utilsrrrr Ú getLoggerrr3r$Úobjectr rr!Ú Exceptionr"r#rbr™r¤r¦r¹rÅrÈrÌrÒràrârrrrrÚ sDh   .QN$cNû