bgirdZddlZddlZddlZddlZddlZddlZddlZddlm Z ddl m Z ddl m Z ddlmZmZmZmZmZddlmZmZdZGd d ZGd d ZGd dZGddeZGddeZGddeZGddeZGddeZdS)z) Tests for L{pyflakes.scripts.pyflakes}. N)PYPY) UnusedImport)Reporter)maincheck checkPathcheckRecursiveiterSourceCode)TestCaseskipIfctj|c}t_ ||i||t_S#|t_wxYw)z? Call C{f} with C{sys.stderr} redirected to C{stderr}. )sysstderr)rfargskwargsouters g/builddir/build/BUILD/cloudlinux-venv-1.0.7/venv/lib/python3.11/site-packages/pyflakes/test/test_api.py withStderrTorsG:vUCJq$!&!! U s/=ceZdZdZddZdS)Nodez Mock an AST node. rc"||_||_dSN)lineno col_offset)selfrrs r__init__z Node.__init__)s $N)r)__name__ __module__ __qualname____doc__rrrrr%s2%%%%%%rrc$eZdZdZdZdZdZdS)SysStreamCapturingzContext manager capturing sys.stdin, sys.stdout and sys.stderr. The file handles are replaced with a StringIO object. cTtj|pdtj|_dS)Nnewline)ioStringIOoslinesep_stdin)rstdins rrzSysStreamCapturing.__init__4s"k%+2rzBBB rcNtj|_tj|_tj|_|jt_tj tj xt_|_ tj tj xt_|_ |S)Nr()rr/ _orig_stdinstdout _orig_stdoutr _orig_stderrr.r*r+r,r-_stdout_stringio_stderr_stringiors r __enter__zSysStreamCapturing.__enter__7sj9JJK -/[-L-L-LL T*-/[-L-L-LL T* rc|j|_|j|_|jt _|jt _ |j t _ dSr) r5getvalueoutputr6errorr1rr/r3r2r4r)rrs r__exit__zSysStreamCapturing.__exit__BsP+4466 *3355 $ & & rN)rr r!r"rr8r=r#rrr%r%.sN CCC   '''''rr%c*eZdZdZdZdZdZdZdS)LoggingReporterzK Implementation of Reporter that just appends any error to a list. c||_dS)zh Construct a C{LoggingReporter}. @param log: A list to append log messages to. N)log)rrAs rrzLoggingReporter.__init__Ps rcX|jdt|fdS)Nflake)rAappendstr)rmessages rrCzLoggingReporter.flakeXs' #g,,/00000rc@|jd||fdS)NunexpectedErrorrArD)rfilenamerFs rrHzLoggingReporter.unexpectedError[s$ *Hg>?????rcF|jd|||||fdS)N syntaxErrorrI)rrJmsgroffsetlines rrLzLoggingReporter.syntaxError^s) #vvtLMMMMMrN)rr r!r"rrCrHrLr#rrr?r?Ksb111@@@NNNNNrr?cNeZdZdZdZdZdZdZdZdZ dZ d Z d Z d Z d S) TestIterSourceCodez& Tests for L{iterSourceCode}. c6tj|_dSr)tempfilemkdtemptempdirr7s rsetUpzTestIterSourceCode.setUpgs')) rc8tj|jdSrshutilrmtreerUr7s rtearDownzTestIterSourceCode.tearDownj dl#####rc|sJtjj|jg|R}t |d|S)Na)r,pathjoinrUopenclose)rpartsfpaths r makeEmptyFilez TestIterSourceCode.makeEmptyFilemsG u T\2E222 UC    rcr|tt|jggdS)zB There are no Python files in an empty directory. N) assertEquallistr rUr7s rtest_emptyDirectoryz&TestIterSourceCode.test_emptyDirectoryss4 ndl^<<==rBBBBBrc|d}|tt|jg|gdS)zd If the directory contains one Python file, C{iterSourceCode} will find it. foo.pyNrergrhr rU)r childpaths rtest_singleFilez"TestIterSourceCode.test_singleFileysI &&x00  ndl^<<== {KKKKKrc|d|tt|jggdS)zJ Files that are not Python source files are not included. zfoo.pycNrlr7s rtest_onlyPythonSourcez(TestIterSourceCode.test_onlyPythonSourcesH 9%%% ndl^<<==rBBBBBrctjtj|jd|dd}|ddtjtj|jd|dd}|d}|tt|jgt|||gdS)zk If the Python files are hidden deep down in child directories, we will find them. fooa.pyza.py~barb.pyzc.pyN) r,mkdirr_r`rUrergsortedr )rapathbpathcpaths r test_recursesz TestIterSourceCode.test_recursess dlE22333""5&11 5'*** dlE22333""5&11""6**  >4<.11 2 2 E5%( ) ) + + + + +rc tj|jd}t |d5}|ddddn #1swxYwY|dt tj|jdd5}|ddddn #1swxYwYtj|jd}t |d5}|d dddn #1swxYwYtj|jd }t |d5}|d dddn #1swxYwYtj|jd }t |d5}|d dddn #1swxYwYtj|jd}t |d5}|ddddn #1swxYwYtj|jd}t |d5}|ddddn #1swxYwYtj|jd}t |d5}|ddddn #1swxYwY|tt|jgt||||||gdS)zd Find Python files that don't end with `.py`, but contain a Python shebang. r^wz#!/usr/bin/env python Nbcz hello world ez#!/usr/bin/env python3 rz#!/usr/bin/env pythonw gz#!/usr/bin/python3 -u iz#!/usr/local/bin/python3d jz#! /usr/bin/env python3.8m lz#!/bin/sh #!/usr/bin/python ) r,r_r`rUrawriterergrwr ) rpythonfdpython3pythonw python3argspython3d python38mnotfirsts r test_shebangzTestIterSourceCode.test_shebangs dlC00 &#   0" HH. / / / 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 "',,t|S113 7 7 '2 HH% & & & ' ' ' ' ' ' ' ' ' ' ' ' ' ' '',,t|S11 '3   12 HH/ 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1',,t|S11 '3   12 HH/ 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1gll4<55 +s # # 0r HH. / / / 0 0 0 0 0 0 0 0 0 0 0 0 0 0 07<< c22 (C  4B HH2 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4GLLs33 )S ! ! 5R HH3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 57<< c22 (C  7B HH5 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7  >4<.11 2 2 +x       sAAA+C  CC D//D36D3/FFFG33G7:G73IIIJ77J;>J;7LL Lctj|jd}tj|jd}tj||dd}tj||dd}|tt||gt||gdS)zr L{iterSourceCode} can be given multiple directories. It will recurse into each of them. rrrtrsruN) r,r_r`rUrvrergrwr )rfoopathbarpathrxrys rtest_multipleDirectoriesz+TestIterSourceCode.test_multipleDirectoriess ',,t|U33',,t|U33 ""5&11 ""5&11  >7G"455 6 6 E5> " " $ $ $ $ $rc|d}|tt|g|gdS)z If one of the paths given to L{iterSourceCode} is not a directory but a file, it will include that in its output. ze.pyN)rergrhr )repaths rtest_explicitFilesz%TestIterSourceCode.test_explicitFilessR ""6** neW5566 " " " " "rN)rr r!r"rVr[rerirnrpr{rrrr#rrrQrQbs***$$$ CCC LLLCCC + + +,,,\ $ $ $"""""rrQc6eZdZdZdZdZdZdZdZdZ dS) TestReporterz Tests for L{Reporter}. ctj}td|}|dddtjdkrdndd|d |dS) a  C{syntaxError} reports that there was a syntax error in the source file. It reports to the error stream and includes the filename, line number, error message, actual line of source and a caret pointing to where the error is. Nrk a problemrrbad line of sourcez2foo.py:3:8: a problem bad line of source ^ )r*r+rrLr version_inforgr:rerrreporters rtest_syntaxErrorzTestReporter.test_syntaxErrorskmmD#&&X{A"%"2f"<"rrz:1:1: a problem rrs rtest_syntaxErrorNoTextz#TestReporter.test_syntaxErrorNoTexts_ kmmD#&&Y Q4@@@ 4s||~~FFFFFrc tj}ddg}td|}|dddt |ddzd |t jd krd nd}|d |z|d zd zd|dz zzdz| dS)z If there's a multi-line syntax error, then we only report the last line. The offset is adjusted so that it is relative to the start of the last line. rzmore bad lines of sourceNrkrrrr rzfoo.py:3:%d: a problem  ^ ) r*r+rrLlenr`rrrgr:)rrlinesrcolumns rtest_multiLineSyntaxErrorz&TestReporter.test_multiLineSyntaxErrors kmm & D#&&X{As58}}q7H!YYu-- / / /'611q  '& 0 2Y FQJ  "'( LLNN      rctj}td|}|dd|d|dS)zO C{unexpectedError} reports an error processing a source file. Nz source.pyz error messagezsource.py: error message )r*r+rrHrgr:rs rtest_unexpectedErrorz!TestReporter.test_unexpectedError#sYkmmD#&&  o>>> 5s||~~FFFFFrctj}t|d}tdt dd}|||||ddS)z C{flake} reports a code warning from Pyflakes. It is exactly the str() of a L{pyflakes.messages.Message}. Nrk*rtr)r*r+rrrrCrgr:)routrrFs r test_flakezTestReporter.test_flake,sq kmmC&&xb599w G88888rN) rr r!r"rrrrrrr#rrrrs~$    G G G*GGG 9 9 9 9 9rrceZdZdZejdZdZdZdZ dZ dZ dZ d Z d Zd Zd Zd ZeejdkddZdZdZdZdZdZdZdZdS) CheckTestszL Tests for L{check} and L{checkPath} which check a file for flakes. c#hKtj\}} tj|d5}t |ds|d}||dddn #1swxYwY|Vtj|dS#tj|wxYw)zV Make a temporary file containing C{content} and return a path to it. wbdecodeasciiN)rSmkstempr,fdopenhasattrencoderremove)rcontentrnamers r makeTempFilezCheckTests.makeTempFile=s #%%D 2t$$ !w116%nnW55G    ! ! ! ! ! ! ! ! ! ! ! ! ! ! !JJJ IdOOOOOBIdOOOOs.B;A6* B6A::B=A:>BB1ctj}t|t|}|||ft |d|fdS)z Assert that C{path} causes errors. @param path: A path to a file to check. @param errorList: A list of errors expected to be printed to stderr. r'N)r*r+rrrgr:rr`)rr_ errorListrcounts rassertHasErrorszCheckTests.assertHasErrorsLspkmmS)T22  CLLNN #c)nnbggi6H6H%I K K K K KrcLg}t|}t||}||fS)a Get any warnings or errors reported by pyflakes for the file at C{path}. @param path: The path to a Python file on disk that pyflakes will check. @return: C{(count, log)}, where C{count} is the number of warnings or errors generated, and log is a list of those warnings, presented as structured data. See L{LoggingReporter} for more details. )r?r)rr_rArrs r getErrorszCheckTests.getErrorsXs/"3''$))czrcRddlm}||jtdS)Nr)pyflakes)pyflakes.scriptsrassertIsr)rscript_pyflakess rtest_legacyScriptzCheckTests.test_legacyScriptfs1@@@@@@ o/;;;;;rc|d5}||gddddS#1swxYwYdS)z Source which doesn't end with a newline shouldn't cause any exception to be raised nor an error indicator to be returned by L{check}. zdef foo(): pass Nrr)rfNames rtest_missingTrailingNewlinez&CheckTests.test_missingTrailingNewlinejs   7 8 8 ,E   + + + , , , , , , , , , , , , , , , , , , :>>c|d\}}||d||dgdS)z: L{checkPath} handles non-existing files. extremor)rHrzNo such file or directoryN)rrg)rrerrorss rtest_checkPathNonExistingz$CheckTests.test_checkPathNonExistingss_y11 v """   H I K K K K Krc Pd}d} |||n_#t$rR}tsAtjdkr1||jddkYd}~nd}~wwxYw||5}trd}ntjdkrd}nd }tstjdkrd }ntjd krd }nd }| |d|||d|dz zfzgddddS#1swxYwYdS)z Source which includes a syntax error which results in the raised L{SyntaxError.text} containing multiple lines of source are reported with only the last line of that source. zCdef foo(): ''' def bar(): pass def baz(): '''quux''' c$t|dSr)exec)sources revaluatez6CheckTests.test_multilineSyntaxError..evaluates LLLLLrr rrNz=end of file (EOF) while scanning triple-quoted string literalz>unterminated triple-quoted string literal (detected at line 8)zinvalid syntax rr z%s:8:%d: %s '''quux''' %s^ r) fail SyntaxErrorrrr assertTruetextrrr)rrrr sourcePathrFrs rtest_multilineSyntaxErrorz$CheckTests.test_multilineSyntaxError}s      HV    IIKKKK  8 8 8 8C,w66 T 2 2Q 6777 8   v & & :* +Y!W,,Z* s'722!V++  67C6A:$67 89 : : : : : : : : : : : : : : : : : : : : :s( ' BAA>>BA3DD"DcF|d5}trd}ntjdkrd}nd}tstjdkrd}nd}d|d z z}d ||||}|||gd d d d S#1swxYwYd S) The error reported for source files which end prematurely causing a syntax error reflects the cause for the syntax error. zdef foo(zparenthesis is never closedrz'(' was never closedzunexpected EOF while parsingr rrz{}:1:{}: {} def foo( {}^ N)rrrrformatr)rrrMrspacesexpecteds rtest_eofSyntaxErrorzCheckTests.test_eofSyntaxErrors    z * * 9j 53!W,,,4 s'722FQJ'F5<<FCH  hZ 8 8 8% 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9sA3BBBc|d5}|||dgddddS#1swxYwYdS)rzif True: foo =z$:2:7: invalid syntax foo = ^ Nrrrs rtest_eofSyntaxErrorWithTabz%CheckTests.test_eofSyntaxErrorWithTabs   2 3 3 z                        s>AAc d}||5}trtjdkrd}nEtrd}n;tjdkrd}n(tjdkrd}ntjdkrd}nd}d |d z zd z}d |z}||d |||gddddS#1swxYwYdS)z Source which has a non-default argument following a default argument should include the line number of the syntax error. However these exceptions do not include an offset. z def foo(bar=baz, bax): pass rrrrrrrrr%d:zO{}:1:{} non-default argument follows default argument def foo(bar=baz, bax): {}N)rrrrrr)rrrr last_line columnstrs r(test_nonDefaultFollowsDefaultSyntaxErrorz3CheckTests.test_nonDefaultFollowsDefaultSyntaxErrorsC   v & & 1* (F22 !W,,!V++!V++vz*U2II   fZI..0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1sBCCCc Bd}||5}tjdkrd}ntstjdkrd}nd}d|dz zd z}d |z}d }||d ||||gd d d d S#1swxYwYd S)z Source which has a non-keyword argument after a keyword argument should include the line number of the syntax error. However these exceptions do not include an offset. zfoo(bar=baz, bax) rr rrrrz,positional argument follows keyword argumentz{}:1:{} {} foo(bar=baz, bax) {}N)rrrrrr)rrrrrrrFs r&test_nonKeywordAfterKeywordSyntaxErrorz1CheckTests.test_nonKeywordAfterKeywordSyntaxErrors   v & & :*6)) c.&88vz*U2IIDG   fZGY779 : : : : : : : : : : : : : : : : : : : : :sA/BBBc.|d5}d}trtjdkrd}ntrd}ntjdkrd}nd}dd|dz zz}d ||||fz}|||gd d d d S#1swxYwYd S) zI The invalid escape syntax raises ValueError in Python 2 z foo = '\xyz'rrrrz%s^ rzy%s:1:%d: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-%d: truncated \xXX escape foo = '\xyz' %sN)rrrrr)rr position_endrrdecoding_errors rtest_invalidEscapezCheckTests.test_invalidEscapes    / / .:L (F22 !V++3&1*#56I V\95 6N  ^, . . .' . . . . . . . . . . . . . . . . . .sA'B  BBwin32zunsupported on Windowscttjdkr|d|d5}tj|d||\}}||d||d|dfgddddS#1swxYwYdS)za If the source file is not readable, this is reported on standard error. rz8root user can access all files regardless of permissionsr'rrHzPermission deniedN)r,getuidskipTestrchmodrrgrrrrs rtest_permissionDeniedz CheckTests.test_permissionDenied.s! 9;;!   MM( ) ) )   r " " Hj HZ # # # NN:66ME6   UA & & &   #Z1DEF H H H  H H H H H H H H H H H H H H H H H HsAB--B14B1c B|d5}||\}}||d||dtt |t ddfgddddS#1swxYwYdS)zc If the source file has a pyflakes warning, this is reported as a 'flake'. z import foorrCrrN)rrrgrErrrs rtest_pyflakesWarningzCheckTests.test_pyflakesWarning?s   | , , T  NN:66ME6   UA & & &   '3|JQ'O'O#P#PQR T T T T T T T T T T T T T T T T T T T T TsA1BBBctd}d|zd}||5}||gddddS#1swxYwYdS)zU If source file declares the correct encoding, no error is reported. &z# coding: utf-8 x = "%s" utf-8NchrrrrrSNOWMANrrs rtest_encodedFileUTF8zCheckTests.test_encodedFileUTF8Jsf++vg   v & & 1*  R 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1sA!!A%(A%c|d5}||gddddS#1swxYwYdS)zW Source files with Windows CR LF line endings are parsed successfully. zx = 42 Nrrs rtest_CRLFLineEndingszCheckTests.test_CRLFLineEndingsVs  | , , 1  R 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1rctd}d|zd}||5}|||dgddddS#1swxYwYdS)n If a source file contains bytes which cannot be decoded, this is reported on stderr. r# coding: ascii x = "%s" rzU:1:1: 'ascii' codec can't decode byte 0xe2 in position 21: ordinal not in range(128) Nrrs rtest_misencodedFileUTF8z"CheckTests.test_misencodedFileUTF8]s f++vg   v & & y*  vvvw y y y y y y y y y y y y y y y y y y y y yA%%A),A)ctd}d|zd}||5}|||dgddddS#1swxYwYdS)rrrzutf-16z: problem decoding source Nrrs rtest_misencodedFileUTF16z#CheckTests.test_misencodedFileUTF16ls f++vh   v & & J*   GGGH J J J J J J J J J J J J J J J J J J J J Jr ctj} tjtj|dtj|dd}t |d5}|ddddn #1swxYwYtj|d}t |d5}|ddddn #1swxYwYg}t|}t|g|}| |d| t|td tt|td d fd tt|td d fgtj|dS#tj|wxYw) zv L{checkRecursive} descends into each directory, finding Python files and reporting problems. rrzbar.pyrs import baz Nzbaz.pysimport contrabandrCrbaz contraband)rSrTr,rvr_r`rarr?r rgrwrErrrYrZ)rrUfile1rfile2rArwarningss rtest_checkRecursivezCheckTests.test_checkRecursivezs3 "$$ # HRW\\'511 2 2 2GLL%::EeT"" *b))) * * * * * * * * * * * * * * *GLL(33EeT"" /b-... / / / / / / / / / / / / / / /C&s++H%wi::H   Xq ) ) )   s #l5$q''5&I&I"J"JK \%a,GGHHJKLL M M M M' " " " " "FM' " " " "sUA#G8B GBG!B"3GC7+ G7C;;G>C;?CGG.cd}tj}t|t|d}||d|ddd}tjdkrgd}nGtrgd }n;tjd krd g}n'tjd krd g}ntjdkrdg}|||dS)z; L{check} reports syntax errors from stdin z-max(1 for i in range(10), key=lambda x: x+1) rrrNrr)7:1:5: Generator expression must be parenthesized,max(1 for i in range(10), key=lambda x: x+1)z ^)L:1:4: Generator expression must be parenthesized if not sole argumentr-z ^rr,)rrz7:1:4: Generator expression must be parenthesized)rrr.) r*r+rrrgr:splitrrr)rrrrerrlinesexpected_errors rtest_stdinReportsErrorsz"CheckTests.test_stdinReportsErrorssBkmmS%;; """<<>>''--crc2  v % %NN  NN   ' 'INN  ' 'INN  ' '^N >22222rN)rr r!r" contextlibcontextmanagerrrrrrrrrrrrrr rplatformrrrrrr"r*r2r#rrrr8s    K K K   <<<,,,KKK1:1:1:f9992   111@:::8...6 VCLG #%=>>HH?>H T T T 1 1 1111 y y y J J J###4#3#3#3#3#3rrcJeZdZdZdZdZdZd dZdZdZ d Z d Z d Z dS) IntegrationTestszF Tests of the pyflakes script that actually spawn the script. ctj|_tj|jd|_dS)Ntemp)rSrTrUr,r_r` tempfilepathr7s rrVzIntegrationTests.setUps2')) GLLv>>rc8tj|jdSrrXr7s rr[zIntegrationTests.tearDownr\rcddl}tj|j}tj|dddS)z9 Return the path to the pyflakes binary. rNz..binr)rr,r_dirname__file__r`)rr package_dirs rgetPyflakesBinaryz"IntegrationTests.getPyflakesBinarys= gooh&788 w||KujAAArNcttj}tjt j|d<t j|g}| ||rctj ||tj tj tj }| |d\}}nCtj ||tj tj }| \}}|}|d}|d}|||fS)a  Launch a subprocess running C{pyflakes}. @param paths: Command-line arguments to pass to pyflakes. @param stdin: Text to use as stdin. @return: C{(returncode, stdout, stderr)} of the completed pyflakes process. PYTHONPATH)envr/r2rr)rDr2rr)dictr,environpathsepr`rr_ executablerAextend subprocessPopenPIPE communicaterwaitr) rpathsr/rDcommandpr2rrvs r runPyflakeszIntegrationTests.runPyflakess#2:JOOCH55L>4#9#9#;#;<u  / c(2 QQQA }}U\\'-B-BCC VVV c(2 QQQA }} VV VVXXw''w''##rct|jd||jg}||ddS)z When a Python source file is all good, the return code is zero and no messages are printed to either stdout or stderr. r^)r'r'rN)rar:rbrSrg)rds r test_goodFilezIntegrationTests.test_goodFilesX T $$**,,,   d/0 1 1 K(((((rcTt|jd5}|ddddn #1swxYwY||jg}t |jt dd}|||tjddfdS)z When a Python source file has warnings, the return code is non-zero and the warnings are printed to stdout. rsimport contraband Nrr&r') rar:rrSrrrgr,r-)rrrUrs rtest_fileWithFlakesz$IntegrationTests.test_fileWithFlakess $#T * * -b HH+ , , , - - - - - - - - - - - - - - -   d/0 1 1 1477LII 6"*66A>????? 8<<c||jg}d|jtj}||d|dfdS) When pyflakes finds errors with the files it's given, (if they don't exist, say), then the return code is non-zero and the errors are printed to stderr. z{}: No such file or directory{}r'rN)rSr:rr,r-rg)rrU error_msgs rtest_errors_iozIntegrationTests.test_errors_iosa   d/0 1 15<?????rr) rr r!r"rVr[rArSrVrXr]r_rar#rrr7r7s???$$$BBB$$$$4))) @ @ @ 0 0 0 0 0 0@@@@@rr7ceZdZdZddZdS)TestMainz. Tests of the pyflakes main function. NcD t|5}t|dddn #1swxYwYtd#t$rM}||jt t|j}|j|j |fcYd}~Sd}~wwxYw)N)rzSystemExit not raised) r%r RuntimeError SystemExitassertIsInstancecodeboolintr;r<)rrOr/capturerrRs rrSzTestMain.runPyflakes s 8#E** !g%     ! ! ! ! ! ! ! ! ! ! ! ! ! ! !677 7  7 7 7  ! !!&$ / / /QVBNGM26 6 6 6 6 6 6 7s9A. A2A2A BABBBr)rr r!r"rSr#rrrcrcs2 8 8 8 8 8 8rrc) r"r3r*r,rrYrJrSpyflakes.checkerrpyflakes.messagesrpyflakes.reporterr pyflakes.apirrrr r pyflakes.test.harnessr r rrr%r?rQrrr7rcr#rrrqs !!!!!!******&&&&&&32222222%%%%%%%%'''''''':NNNNNNNN.y"y"y"y"y"y"y"y"xW9W9W9W9W98W9W9W9t33333333D _@_@_@_@_@x_@_@_@D 8 8 8 8 8 8 8 8 8 8r