
    hGP              	          d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
ZddlZddlZddlZej                  j                   j#                  dg g g g d      Zi Zi Zej                  j*                  j-                  ddd      Z ej0                  dd	      Zej4                  j7                         r G d
 de      Zne	j<                  j:                  ZddZddZ d Z!d Z" G d de#      Z$ G d d ej0                  dg d            Z% G d de	jL                        Z'd Z(d Z)d Z*d Z+d dZ,d Z-d Z.d Z/e,Z0e-Z1e+Z2y)!aD  
Helper functions for testing.

Our **stylistic_issues**, **pyflakes_issues**, and **type_check_issues**
respect a 'exclude_paths' in our test config, excluding any absolute paths
matching those regexes. Issue strings can start or end with an asterisk
to match just against the prefix or suffix. For instance...

::

  exclude_paths .*/stem/test/data/.*

.. versionadded:: 1.2.0

::

  TimedTestRunner - test runner that tracks test runtimes
  test_runtimes - provides runtime of tests excuted through TimedTestRunners
  clean_orphaned_pyc - delete *.pyc files without corresponding *.py

  is_pyflakes_available - checks if pyflakes is available
  is_pycodestyle_available - checks if pycodestyle is available

  pyflakes_issues - static checks for problems via pyflakes
  stylistic_issues - checks for PEP8 and other stylistic issues
    Ntest)pep8.ignorepycodestyle.ignorepyflakes.ignoreexclude_pathsPENDINGRUNNINGFINISHEDAsyncResultztype msgc                       e Zd ZdZy)SkipTestz Notes that the test was skipped.N__name__
__module____qualname____doc__     ^/var/www/betterdocs.net/sherlock_api/venv/lib/python3.12/site-packages/stem/util/test_tools.pyr   r   ?   s    &r   r   c                 B    | |k7  rt        |d| d|d      |      y)z
  Function form of a TestCase's assertEqual.

  .. versionadded:: 1.6.0

  :param object expected: expected value
  :param object actual: actual value
  :param str msg: message if assertion fails

  :raises: **AssertionError** if values aren't equal
  N
Expected 'z' but was ''AssertionErrorexpectedactualmsgs      r   assert_equalr   E   s.     
ck6J
cc_b
cc r   c                 @    | |vrt        |d| d|d      |      y)a  
  Asserts that a given value is within this content.

  .. versionadded:: 1.6.0

  :param object expected: expected value
  :param object actual: actual value
  :param str msg: message if assertion fails

  :raises: **AssertionError** if the expected value isn't in the actual
  Nr   z' to be within 'r   r   r   s      r   	assert_inr!   V   s0     V
SVS^hO
hhdg
hh r   c                     t        |       )z
  Function form of a TestCase's skipTest.

  .. versionadded:: 1.6.0

  :param str msg: reason test is being skipped

  :raises: **unittest.case.SkipTest** for this reason
  )r   )r   s    r   skipr#   g   s     	r   c                     t         j                  j                  j                  |       }|t        |j
                  <   |j                  S N)stemutil
test_tools	AsyncTestASYNC_TESTSnamemethod)funcr   s     r   asynchronousr.   u   s4    				'	'	-$+dii	r   c                   0    e Zd ZdZddZd Zd Zd Zd Zy)	r)   a  
  Test that's run asychronously. These are functions (no self reference)
  performed like the following...

  ::

    class MyTest(unittest.TestCase):
      @staticmethod
      def run_tests():
        MyTest.test_addition = stem.util.test_tools.AsyncTest(MyTest.test_addition).method

      @staticmethod
      def test_addition():
        if 1 + 1 != 2:
          raise AssertionError('tisk, tisk')

    MyTest.run()

  .. versionadded:: 1.6.0
  Nc                 
    |j                   d|j                   _        | _        | _        | _         fd _        d  _        d  _        t        j                          _        d  _        t        j                   _        y )N.c                 &    j                  |       S r%   result)r   selfs    r   <lambda>z$AsyncTest.__init__.<locals>.<lambda>   s    t{{40 r   )r   r   r+   _runner_runner_args	_threadedr,   _process_process_pipe	threadingRLock_process_lock_resultAsyncStatusr   _status)r5   runnerargsthreadeds   `   r   __init__zAsyncTest.__init__   sh    !,,foo>DIDLDDN0DKDMD"*DDL&&DLr   c                    t         j                  j                         ry d }| j                  5  | j                  t
        j                  k(  r|r|| _        d|v r
|d   | _        t        j                         \  | _        }| j                  r]t        j                  ||| j                  | j                  fd| j                  z        | _        | j                   j#                  d       n3t        j$                  ||| j                  | j                  f      | _        | j                   j'                          t
        j(                  | _        d d d        y # 1 sw Y   y xY w)Nc           	         t        j                  d       	 |r || n |        | j                  t        dd              | j                          y # t        $ r.}| j                  t        dt        |                   Y d }~Cd }~wt        $ r.}| j                  t        dt        |                   Y d }~xd }~w | j                  t        dt        j                                      Y xY w# | j                          w xY w)N   successfailureskippederror)
osnicesendr   r   strr   	traceback
format_excclose)connrB   rC   excs       r   _wrapperzAsyncTest.run.<locals>._wrapper   s    ggbk
68		+i./ 	

  4		+iS233 4		+iS233@		+gy';';'=>?

s;   )A 	C,$B>C/ C,$B83C/ 82C,*C/ /DrD   zBackground test of %s)targetrC   r+   T)rW   rC   )r&   prereq_is_python_26r>   rA   r@   r   r8   r9   multiprocessingPiper;   r<   Threadr7   r+   r:   	setDaemonProcessstartr	   )r5   runner_argskwargsrV   
child_pipes        r   runzAsyncTest.run   s   {{  " 
		 +	,,	,)$
!*-$.)8)=)=)?&J>>#**d.?.?@*TYY6$- --
!
!$
')118ZY]YeYegkgxgxLyz$-"**-+ + +s   DEEc                     | j                   5  | j                  r"| j                  s| j                  j                  nd cd d d        S # 1 sw Y   y xY wr%   )r>   r:   r9   pidr5   s    r   re   zAsyncTest.pid   s?    			 S#'==T]]dS S Ss   0AAc                 &    | j                  d        y r%   r3   rf   s    r   joinzAsyncTest.join   s    KKr   c                 8   t         j                  j                         ry | j                  5  | j                  t
        j                  k(  r| j                          | j                  t
        j                  k(  rN| j                  j                         | _        | j                  j                          t
        j                  | _        |r?| j                  j                  dk(  r&|j!                  | j                  j"                         n|r?| j                  j                  dk(  r&|j!                  | j                  j"                         n@|r>| j                  j                  dk(  r%|j%                  | j                  j"                         d d d        y # 1 sw Y   y xY w)NrJ   rL   rK   )r&   rX   rY   r>   rA   r@   r   rc   r	   r;   recvr?   r:   rh   r
   typefailr   skipTest)r5   r   s     r   r4   zAsyncTest.result   s   {{  "			 (	,,	,
	,,	,))..0"++	$,,##y0		$,,""#DLL%%0		$,,""#DLL%%2dll&&'( ( (s   EFF)NF)	r   r   r   r   rE   rc   re   rh   r4   r   r   r   r)   r)   {   s"    *' )+VS(r   r)   c                       e Zd ZdZy)Issuez
  Issue encountered by pyflakes or pycodestyle.

  :var int line_number: line number the issue occured on
  :var str message: description of the issue
  :var str line: content of the line the issue is about
  Nr   r   r   r   ro   ro      s    r   ro   )line_numbermessagelinec                   "     e Zd ZdZ fdZ xZS )TimedTestRunnerz
  Test runner that tracks the runtime of individual tests. When tests are run
  with this their runtimes are made available through
  :func:`stem.util.test_tools.test_runtimes`.

  .. versionadded:: 1.6.0
  c                     |j                   D ]!  }t        |       G fdd      }||_        # t        t        |   |      S )Nc                   `     e Zd Zd fd	Z fdZ fdZd Z fdZfdZfdZ	 xZ
S )	)TimedTestRunner.run.<locals>._TestWrapperc                     t        j                          }t        t        |       |   |      }t        j                          |z
  t        | j                         <   |S r%   )timesuperrk   rc   TEST_RUNTIMESid)r5   r4   
start_time	__class__s      r   rc   z-TimedTestRunner.run.<locals>._TestWrapper.run   sC    yy{*dT.v6&%)YY[:%=-	
"-r   c                 `    t         j                  j                         st        |   |      S y r%   )r&   rX   rY   rz   rm   )r5   rq   r~   original_types     r   rm   z2TimedTestRunner.run.<locals>._TestWrapper.skipTest	  s*    **,6w?? -r   c                     t         j                  j                         r%| j                  t	        |      t	        |             y t
        |   ||      S r%   )r&   rX   rY   assertEqualsetrz   assertItemsEqual)r5   r   r   r~   r   s      r   r   z:TimedTestRunner.run.<locals>._TestWrapper.assertItemsEqual  s?    [[&&(S]CK8>xPPr   c                 ^     | j                   |dt        j                  |      z  |g|i |S )a  
          Asserts the given invokation raises the expected excepiton. This is
          similar to unittest's assertRaises and assertRaisesRegexp, but checks
          for an exact match.

          This method is **not** being vended to external users and may be
          changed without notice. If you want this method to be part of our
          vended API then please let us know.
          z^%s$)assertRaisesRegexpreescape)r5   exc_typeexc_msgr-   rC   ra   s         r   assertRaisesWithz:TimedTestRunner.run.<locals>._TestWrapper.assertRaisesWith  s6     )((6BIIg<N3NPTfW[f_ef
fr   c           	      D   t         j                  j                         r	  ||i | | j                  d|z         y t        | *  |||g|i |S # |$ rG}| j	                  t        j                  |t        |      t
        j                               Y d }~y d }~ww xY w)Nz*Expected a %s to be raised but nothing was)r&   rX   rY   rl   
assertTruer   searchrP   	MULTILINErz   r   )	r5   r   r   r-   rC   ra   rU   r~   r   s	          r   r   z<TimedTestRunner.run.<locals>._TestWrapper.assertRaisesRegexp"  s    [[&&(JD#F#iiDxOP @7TXj[_jcijj  JoobiiS2<<HIIJs   A B=BBc                 T    j                   dj                  d| j                  S )Nr1   )r   r   _testMethodNamer5   r   s    r   r|   z,TimedTestRunner.run.<locals>._TestWrapper.id,  s#    ,779O9OQUQeQef
fr   c                 V    | j                   dj                  dj                  dS )Nz (r1   ))r   r   r   r   s    r   __str__z1TimedTestRunner.run.<locals>._TestWrapper.__str__/  s#    !%!5!5}7O7OQ^QgQgh
hr   r%   )r   r   r   rc   rm   r   r   r   r|   r   __classcell__)r~   r   s   @r   _TestWrapperrw      s2    		@	Q	g	k	g	i 	ir   r   )_testsrk   r~   rz   rt   rc   )r5   r   tr   r   r~   s       @r   rc   zTimedTestRunner.run   sM    [[ 6!1gm1i 1if !akm6!p $+D11r   )r   r   r   r   rc   r   )r~   s   @r   rt   rt      s    92 92r   rt   c                       t        t              S )z
  Provides the runtimes of tests executed through TimedTestRunners.

  :returns: **dict** of fully qualified test names to floats for the runtime in
    seconds

  .. versionadded:: 1.6.0
  )dictr{   r   r   r   test_runtimesr   7  s     
m	r   c                 >   g }| D ]  }t         j                  j                  j                  |d      D ]  }|dd }t        j
                  j                  dt        j
                  j                  }||v r\|j                  |d      \  }}|j                  d      sjt        j
                  j                  ||j                  d      d   dz         }t        j
                  j                  |      r|j                  |       t	        j                  |         |S )	as  
  Deletes any file with a \*.pyc extention without a corresponding \*.py. This
  helps to address a common gotcha when deleting python files...

  * You delete module 'foo.py' and run the tests to ensure that you haven't
    broken anything. They pass, however there *are* still some 'import foo'
    statements that still work because the bytecode (foo.pyc) is still around.

  * You push your change.

  * Another developer clones our repository and is confused because we have a
    bunch of ImportErrors.

  :param list paths: paths to search for orphaned pyc files

  :returns: list of absolute paths that were deleted
  z.pycN__pycache__   r1   r   .py)r&   r'   systemfiles_with_suffixrM   pathsepsplitendswithrh   existsappendremove)pathsorphaned_pycr   pyc_pathpy_pathpycache	directorypycache_filenames           r   clean_orphaned_pycr   D  s    & , dII$$66tVD "g
 &(WW[["''++>g	H	&.nnWa&@#	#((0
'',,y*:*@*@*Ea*H5*PQWW^^G$H%
		(%* 
r   c                  2    t        d      xr t        d      S )zk
  Checks if pyflakes is availalbe.

  :returns: **True** if we can use pyflakes and **False** otherwise
  zpyflakes.apizpyflakes.reporter)_module_existsr   r   r   is_pyflakes_availabler   q  s     
	'	ON;N,OOr   c                  \    t        d      rddl} nt        d      rddl} nyt        | d      S )zq
  Checks if pycodestyle is availalbe.

  :returns: **True** if we can use pycodestyle and **False** otherwise
  pycodestyler   Npep8F
BaseReport)r   r   r   hasattr)r   s    r   is_pycodestyle_availabler   {  s*     M"f	l	++r   c           	         i g }g g t         d   t         d   z   D ]  }d|v r|j                  dd      \  }}d|v rT|j                  dd      \  }}j                  |j                         |j                         |j                         f       t|j                         dk(  sj                  |j                                |j                  |        fdt	               rgt        d      rd	d
l}	nd	d
l}	 G fdd|	j                        |	j                  |      }
|
j                  t        t        |                    S )a  
  Checks for stylistic issues that are an issue according to the parts of PEP8
  we conform to. You can suppress pycodestyle issues by making a 'test'
  configuration that sets 'pycodestyle.ignore'.

  For example, with a 'test/settings.cfg' of...

  ::

    # pycodestyle compliance issues that we're ignoreing...
    #
    # * E111 and E121 four space indentations
    # * E501 line is over 79 characters

    pycodestyle.ignore E111
    pycodestyle.ignore E121
    pycodestyle.ignore E501

    pycodestyle.ignore run_tests.py => E402: import stem.util.enum

  ... you can then run tests with...

  ::

    import stem.util.conf

    test_config = stem.util.conf.get_config('test')
    test_config.load('test/settings.cfg')

    issues = stylistic_issues('my_project')

  .. versionchanged:: 1.3.0
     Renamed from get_stylistic_issues() to stylistic_issues(). The old name
     still works as an alias, but will be dropped in Stem version 2.0.0.

  .. versionchanged:: 1.4.0
     Changing tuples in return value to be namedtuple instances, and adding the
     line that had the issue.

  .. versionchanged:: 1.4.0
     Added the prefer_single_quotes option.

  .. versionchanged:: 1.6.0
     Changed 'pycodestyle.ignore' code snippets to only need to match against
     the prefix.

  :param list paths: paths to search for stylistic issues
  :param bool check_newlines: check that we have standard newlines (\n), not
    windows (\r\n) nor classic mac (\r)
  :param bool check_exception_keyword: checks that we're using 'as' for
    exceptions rather than a comma
  :param bool prefer_single_quotes: standardize on using single rather than
    double quotes for strings, when reasonable

  :returns: dict of paths list of :class:`stem.util.test_tools.Issue` instances
  r   r   =>r   :*c                     D ]?  \  }}}| j                  |      s||k(  s|j                         j                  |      s? y D ]  }| j                  |      s y y)NTF)r   strip
startswith)r   rulecodeignored_pathignored_ruleignored_codeignore_all_for_filesignore_for_files         r   
is_ignoredz$stylistic_issues.<locals>.is_ignored  sh    4C 0lL	|	$)=$**,BYBYZfBg - 	|	$ r   r   r   Nc                   <     e Zd Z fdZ fdZ xZS )%stylistic_issues.<locals>.StyleReportc                    t         
|   ||||       sssy d}D ]  }|j                  |      s y  t        |      D ]  \  }}|j	                  dd      d   j                         }	r3d|v r/j                  |g       j                  t        |dz   d|             |	sbd|	v r| }rQ|	j                  d      r@|	j                  d	      r/j                  |g       j                  t        |dz   d
|             s|rd|	v sd|	vsd|	vs|	j                  d      rj                  |g       j                  t        |dz   d|              y )NF#r   r   zcontains a windows newlinez"""exceptz, exc:z(except clause should use 'as', not comma"r   \z$use single rather than double quotes)
rz   	init_filer   	enumerater   r   
setdefaultr   ro   r   )r5   filenamelinesr   line_offsetis_block_commentr   indexrr   contentStyleReportr~   check_exception_keywordcheck_newlinesr   issuesprefer_single_quotess             r   r   z/stylistic_issues.<locals>.StyleReport.init_file  so   k4*8UHkR&=FZ
 0 	L|,	 %U+ "	uKE4JJsA&q)//1'h+225D`bf3ghg#33$););H)E'JZJZ[cJd h+225Dnpt3uv!*:g~#W"4g9MV]VfVfgkVl "-44U519Flnr5stE"	ur   c                    t         |   ||||      }|rlt        j                  | j                  |      } 	| j                  ||      s7
j                  | j                  g       j                  t        |||             y y y r%   )rz   rL   	linecachegetliner   r   r   ro   )r5   rp   offsettextcheckr   rr   r   r~   r   r   s          r   rL   z+stylistic_issues.<locals>.StyleReport.error  sv    [$-k64O""4==+>$DMM46dmmR077k4QU8VW 7 r   )r   r   r   r   rL   r   )r~   r   r   r   r   r   r   r   s   @r   r   r     s    .u .u`X Xr   r   )ignorereporter)CONFIGr   r   r   r   r   r   r   r   
StyleGuidecheck_fileslist_python_files)r   r   r   r   ignore_rulesr   r   
rule_entryr   r   style_checkerr   r   r   r   r   s    ```       @@@@@r   stylistic_issuesr     s8   t &,/)*VM-BB 
 dt|D!,dJ	
	%%c1-
d

djjlDJJLIJ$##DJJL1$
 	 f 8X 8Xk,, 8Xt  **L[*YMd=#789	-r   c                     i t               r\ddl}ddl} G fdd|j                  j                        } |       }t        |       D ]  }|j                  j                  ||         S )aP  
  Performs static checks via pyflakes. False positives can be ignored via
  'pyflakes.ignore' entries in our 'test' config. For instance...

  ::

    pyflakes.ignore stem/util/test_tools.py => 'pyflakes' imported but unused
    pyflakes.ignore stem/util/test_tools.py => 'pycodestyle' imported but unused

  .. versionchanged:: 1.3.0
     Renamed from get_pyflakes_issues() to pyflakes_issues(). The old name
     still works as an alias, but will be dropped in Stem version 2.0.0.

  .. versionchanged:: 1.4.0
     Changing tuples in return value to be namedtuple instances, and adding the
     line that had the issue.

  .. versionchanged:: 1.5.0
     Support matching against prefix or suffix issue strings.

  :param list paths: paths to search for problems

  :returns: dict of paths list of :class:`stem.util.test_tools.Issue` instances
  r   Nc                   6    e Zd Zd Zd Zd Zd Zd Z fdZy)!pyflakes_issues.<locals>.Reporterc                     i | _         t        d   D ]]  }|j                  d      \  }}| j                   j                  |j	                         g       j                  |j	                                _ y )Nr   r   )_ignored_issuesr   r   r   r   r   )r5   rr   r   issues       r   rE   z*pyflakes_issues.<locals>.Reporter.__init__J  s`    !,- 	RD

4(+$



)
)$**,
;
B
B5;;=
Q	Rr   c                 ,    | j                  |d |d        y r%   _register_issue)r5   r   r   s      r   unexpectedErrorz1pyflakes_issues.<locals>.Reporter.unexpectedErrorQ  s    XtS$7r   c                 ,    | j                  ||||       y r%   r   )r5   r   r   linenor   r   s         r   syntaxErrorz-pyflakes_issues.<locals>.Reporter.syntaxErrorT  s    XvsD9r   c                     | j                  |j                  |j                  |j                  |j                  z  d        y r%   )r   r   r   rq   message_args)r5   r   s     r   flakez'pyflakes_issues.<locals>.Reporter.flakeW  s.    S\\3::s{{SEUEU7UW[\r   c                    | j                   j                         D ]  \  }}|j                  |      s||v r y|D cg c]  }|j                  d      s|d d  c}D ]  }|j                  |      s  y |D cg c]  }|j                  d      s|dd   c}D ]  }|j                  |      s  y  yc c}w c c}w )NTr   r   F)r   itemsr   r   )r5   r   r   r   ignored_issuesiprefixsuffixs           r   _is_ignoredz-pyflakes_issues.<locals>.Reporter._is_ignoredZ  s     -1,@,@,F,F,H 	(L.]]<(&*8LQAJJsO1Ra5L !!&) +9NQALL<M1QR5N '	  M Os   B8B8;B=B=c                     | j                  ||      sW|r(|r&|s$t        j                  ||      j                         }j	                  |g       j                  t        |||             y y r%   )r  r   r   r   r   r   ro   )r5   r   rp   r   rr   r   s        r   r   z1pyflakes_issues.<locals>.Reporter._register_issuem  s^    e,k$$$T;7==?D


D"
%
,
,U;t-L
M	 -r   N)	r   r   r   rE   r   r   r  r  r   )r   s   r   Reporterr   I  s$    R8:]&Nr   r
  )r   pyflakes.apipyflakes.reporterr   r
  r   api	checkPath)r   pyflakesr
  r   r   r   s        @r   pyflakes_issuesr  )  si    4 &)N8$$-- )NV zHe$ -llT8,- 
-r   c                 :    	 t        |        y# t        $ r Y yw xY w)z
  Checks if a module exists.

  :param str module_name: module to check existance of

  :returns: **True** if module exists and **False** otherwise
  TF)
__import__ImportError)module_names    r   r   r   |  s$    {	 s    	c              #      K   | D ]b  }t         j                  j                  j                  |d      D ]3  }d}t        d   D ]  }t        j                  ||      sd} n |r0| 5 d y w)Nr   Fr   T)r&   r'   r   r   r   r   match)r   r   	file_pathr#   exclude_paths        r   r   r     st      
dYY%%77eD 		d 1 ,88L),$

 	
s   AA+A+"	A+r%   )FFF)3r   collectionsr   rZ   rM   r   r<   ry   rQ   unitteststem.prereqr&   stem.util.confstem.util.enumstem.util.systemr'   confconfig_dictr   r{   r*   enumUppercaseEnumr@   
namedtupler   rX   rY   	Exceptionr   caser   r!   r#   r.   objectr)   ro   TextTestRunnerrt   r   r   r   r   r   r  r   r   get_stylistic_issuesget_pyflakes_issuesis_pep8_availabler   r   r   <module>r+     sb  6    	 	        		#	#F	- 
 iinn**9iL$k$$]J?
 ;;' ' ]]##(d"i"j( j(Z"K""7,NO B2h-- B2J
*ZP,"ZzPf & ( % , r   