
    <iGP              	          S r SSKrSSKrSSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
rSSKrSSKrSSKr\R                  R                   R#                  S/ / / / S.5      r0 r0 r\R                  R*                  R-                  SSS5      r\R0                  " SS	5      r\R4                  R7                  5       (       a   " S
 S\5      rO\	R<                  R:                  rSS jrSS jr S r!S r" " S S\#5      r$ " S S\R0                  " S/ SQ5      5      r% " S S\	RL                  5      r'S r(S r)S r*S r+S S jr,S r-S r.S r/\,r0\-r1\+r2g)!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                       \ rS rSrSrSrg)SkipTest?   z Notes that the test was skipped. N__name__
__module____qualname____firstlineno____doc____static_attributes__r       N/home/james-whalen/.local/lib/python3.13/site-packages/stem/util/test_tools.pyr   r   ?   s    &r   r   c                 H    X:w  a  [        Uc  SU < SU< S35      eU5      eg)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                 H    X;  a  [        Uc  SU < SU< S35      eU5      eg)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   s.     
SVS^hO
hhdg
hh r   c                     [        U 5      e)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                     [         R                  R                  R                  U 5      nU[        UR
                  '   UR                  $ N)stemutil
test_tools	AsyncTestASYNC_TESTSnamemethod)funcr   s     r   asynchronousr1   u   s4    				'	'	-$+dii	r   c                   :    \ rS rSrSrS
S jrS rS rS rS r	S	r
g)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                   ^  UR                   < SUR                  < 3T l        UT l        UT l        UT l        U 4S jT l        S T l        S T l        [        R                  " 5       T l        S T l        [        R                  T l        g )N.c                 &   > TR                  U 5      $ r(   result)r   selfs    r   <lambda>$AsyncTest.__init__.<locals>.<lambda>   s    t{{40r   )r   r   r.   _runner_runner_args	_threadedr/   _process_process_pipe	threadingRLock_process_lock_resultAsyncStatusr   _status)r9   runnerargsthreadeds   `   r   __init__AsyncTest.__init__   sh    !,,foo>DIDLDDN0DKDMD"*DDL&&DLr   c                    [         R                  R                  5       (       a  g S nU R                     U R                  [
        R                  :X  Ga  U(       a  Xl        SU;   a
  US   U l        [        R                  " 5       u  U l        nU R                  (       aZ  [        R                  " UX@R                  U R                  4SU R                  -  S9U l        U R                   R#                  S5        O0[        R$                  " X4U R                  U R                  4S9U l        U R                   R'                  5         [
        R(                  U l        S S S 5        g ! , (       d  f       g = f)Nc           	         [         R                  " S5         U(       a  U" U6 OU" 5         U R                  [        SS 5      5        U R                  5         g ! [         a.  nU R                  [        S[        U5      5      5         S nANDS nAf[         a.  nU R                  [        S[        U5      5      5         S nANzS nAf  U R                  [        S[        R                  " 5       5      5         N= f! U R                  5         f = f)N   successfailureskippederror)
osnicesendr   r   strr   	traceback
format_excclose)connrG   rH   excs       r   _wrapperAsyncTest.run.<locals>._wrapper   s    ggbk
68		+i./ 	

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

s;   .A 
C5!$B
C8 
C5$C ;C8  3C53C8 8D
rI   zBackground test of %s)targetrH   r.   T)r^   rH   )r)   prereq_is_python_26rC   rF   rE   r   r=   r>   multiprocessingPiper@   rA   Threadr<   r.   r?   	setDaemonProcessstartr	   )r9   runner_argskwargsr\   
child_pipes        r   runAsyncTest.run   s   {{  "" 
			,,	,)
!*-$.)8)=)=)?&J>>#**d.?.?@*TYY6$- --
!
!$
')118Y]YeYegkgxgxLyz$-"**- 
		s   D$E!!
E/c                     U R                      U R                  (       a'  U R                  (       d  U R                  R                  OS sS S S 5        $ ! , (       d  f       g = fr(   )rC   r?   r>   pidr9   s    r   rm   AsyncTest.pid   s2    			#'==T]]d 
		s   :A
Ac                 &    U R                  S 5        g r(   r7   rn   s    r   joinAsyncTest.join   s    KKr   c                 t   [         R                  R                  5       (       a  g U R                     U R                  [
        R                  :X  a  U R                  5         U R                  [
        R                  :X  aN  U R                  R                  5       U l        U R                  R                  5         [
        R                  U l        U(       a@  U R                  R                  S:X  a&  UR!                  U R                  R"                  5        OU(       a@  U R                  R                  S:X  a&  UR!                  U R                  R"                  5        OFU(       a?  U R                  R                  S:X  a%  UR%                  U R                  R"                  5        S S S 5        g ! , (       d  f       g = f)NrP   rR   rQ   )r)   r_   r`   rC   rF   rE   r   rj   r	   r@   recvrD   r?   rq   r
   typefailr!   skipTest)r9   r   s     r   r8   AsyncTest.result   s   {{  ""				,,	,
	,,	,))..0"++	$,,##y0		$,,""#DLL%%0		$,,""#DLL%%2dll&&' 
		s   E/F))
F7)
r?   rC   r@   rD   r<   r=   rF   r>   r/   r.   )NF)r   r   r   r   r   rJ   rj   rm   rq   r8   r   r   r   r   r,   r,   {   s"    *' )+VS(r   r,   c                       \ rS rSrSrSrg)Issue   z
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
r   Nr   r   r   r   rz   rz      s    r   rz   )line_numbermessagelinec                   ,   ^  \ rS rSrSrU 4S jrSrU =r$ )TimedTestRunner   z
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                    >^ UR                    H"  n[        U5      m " U4S jST5      nX2l        M$     [        [        U ]  U5      $ )Nc                   v   >^  \ rS rSrS
U 4S jjrU U4S jrU U4S jrS rU U4S jrU4S jr	U4S jr
S	rU =r$ ))TimedTestRunner.run.<locals>._TestWrapper   c                    > [         R                   " 5       n[        [        U 5      U ]  U5      n[         R                   " 5       U-
  [        U R                  5       '   U$ r(   )timesuperru   rj   TEST_RUNTIMESid)r9   r8   
start_time	__class__s      r   rj   -TimedTestRunner.run.<locals>._TestWrapper.run   sC    yy{*dT.v6&%)YY[:%=-	
"-r   c                 j   > [         R                  R                  5       (       d  [        TU ]  U5      $ g r(   )r)   r_   r`   r   rw   )r9   r}   r   original_types     r   rw   2TimedTestRunner.run.<locals>._TestWrapper.skipTest	  s-    **,,6w?? -r   c                    > [         R                  R                  5       (       a%  U R                  [	        U5      [	        U5      5        g [
        TU ]  X5      $ r(   )r)   r_   r`   assertEqualsetr   assertItemsEqual)r9   r   r    r   r   s      r   r   :TimedTestRunner.run.<locals>._TestWrapper.assertItemsEqual  s@    [[&&((S]CK8>xPPr   c                 `    U R                   " US[        R                  " U5      -  U/UQ70 UD6$ )aG  
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)r9   exc_typeexc_msgr0   rH   rh   s         r   assertRaisesWith:TimedTestRunner.run.<locals>._TestWrapper.assertRaisesWith  s4     ((6BIIg<N3NPTfW[f_ef
fr   c           	      R  > [         R                  R                  5       (       a   U" U0 UD6  U R                  SU-  5        g [        TU ]*  " XU/UQ70 UD6$ ! U aH  nU R	                  [
        R                  " U[        U5      [
        R                  5      5         S nAg S nAff = f)Nz*Expected a %s to be raised but nothing was)r)   r_   r`   rv   
assertTruer   searchrV   	MULTILINEr   r   )	r9   r   r   r0   rH   rh   r[   r   r   s	          r   r   <TimedTestRunner.run.<locals>._TestWrapper.assertRaisesRegexp"  s    [[&&((JD#F#iiDxOP @TXj[_jcijj  JoobiiS2<<HIIJs   A B&>B!!B&c                 Z   > TR                   < STR                  < SU R                  < 3$ )Nr5   )r   r   _testMethodNamer9   r   s    r   r   ,TimedTestRunner.run.<locals>._TestWrapper.id,  s#    ,779O9OQUQeQef
fr   c                 \   > U R                   < STR                  < STR                  < S3$ )Nz (r5   ))r   r   r   r   s    r   __str__1TimedTestRunner.run.<locals>._TestWrapper.__str__/  s#    !%!5!5}7O7OQ^QgQgh
hr   r   r(   )r   r   r   r   rj   rw   r   r   r   r   r   r   __classcell__)r   r   s   @r   _TestWrapperr      s2    		@	Q	g	k	g	i 	ir   r   )_testsru   r   r   r   rj   )r9   r   tr   r   r   s       @r   rj   TimedTestRunner.run   sF    [[1gm1i 1if !km p $+D11r   r   )r   r   r   r   r   rj   r   r   )r   s   @r   r   r      s    92 92r   r   c                       [        [        5      $ )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                 b   / nU  GH%  n[         R                  R                  R                  US5       H  nUSS n[        R
                  R                  < S[        R
                  R                  < 3nXS;   a`  UR                  US5      u  pgUR                  S5      (       d  Mr  [        R
                  R                  XgR                  S5      S   S-   5      n[        R
                  R                  U5      (       a  M  UR                  U5        [        R                  " U5        M     GM(     U$ )	a]  
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__   r5   r   .py)r)   r*   systemfiles_with_suffixrS   pathsepsplitendswithrq   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		&.nnWa&@#	((00
'',,y*@*@*Ea*H5*PQWW^^G$$H%
		(% E * 
r   c                  <    [        S5      =(       a    [        S5      $ )ze
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                  p    [        S5      (       a  SSKn O[        S5      (       a  SSKn Og[        U S5      $ )zk
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   {  s0     M""f	l	++r   c           	        ^^^^^^^^ 0 m/ n/ m/ m[         S   [         S   -    H  nSU;   a  UR                  SS5      u  pgSU;   aT  UR                  SS5      u  pXTR                  UR                  5       UR                  5       UR                  5       45        Mw  UR                  5       S:X  a!  TR                  UR                  5       5        M  M  UR                  U5        M     UU4S jm[	        5       (       aj  [        S5      (       a  S	S
Kn	OS	S
Kn	 " UUUUUUU4S jSU	R                  5      mU	R                  UTS9n
U
R                  [        [        U 5      5      5        T$ )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                    > T HK  u  p4nU R                  U5      (       d  M  XA:X  d  M%  UR                  5       R                  U5      (       d  MK    g   T H  nU R                  U5      (       d  M    g   g)NTF)r   strip
startswith)r   rulecodeignored_pathignored_ruleignored_codeignore_all_for_filesignore_for_files         r   
is_ignored$stylistic_issues.<locals>.is_ignored  sd    4C0L	|	$	$)=$**,BYBYZfBgBg 5D -	|	$	$ - r   r   r   Nc                   H   >^  \ rS rSrUU UUUUU4S jrUU UU4S jrSrU =r$ )%stylistic_issues.<locals>.StyleReporti  c                 X  > [         T
U ]  XX45        T(       d  T(       d  T(       d  g SnT H  nUR                  U5      (       d  M    g    [        U5       GHP  u  pxUR	                  SS5      S   R                  5       n	T(       a5  SU;   a/  TR                  U/ 5      R                  [        US-   SU5      5        U	(       d  Mn  SU	;   a  U(       + nT(       a[  U	R                  S5      (       aE  U	R                  S	5      (       a/  TR                  U/ 5      R                  [        US-   S
U5      5        T(       d  M  U(       a  M  SU	;   d  M  SU	;  d  M  SU	;  d  GM  U	R                  S5      (       a  GM!  TR                  U/ 5      R                  [        US-   SU5      5        GMS     g )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)
r   	init_filer   	enumerater   r   
setdefaultr   rz   r   )r9   filenamelinesr   line_offsetis_block_commentr   indexr~   contentStyleReportr   check_exception_keywordcheck_newlinesr   issuesprefer_single_quotess             r   r   /stylistic_issues.<locals>.StyleReport.init_file  so   k4*8HR&=FZ
 0L|,, 1 %U+KEJJsA&q)//1'h+225D`bf3ghg#33$););H)E)E'JZJZ[cJdJd h+225Dnpt3uv!!*:*:g~#W"4g9MV]VfVfgkVlVl "-44U519Flnr5stE ,r   c                   > [         TU ]  XX45      nU(       ap  [        R                  " U R                  U5      nT	" U R                  XV5      (       d6  T
R                  U R                  / 5      R                  [        XU5      5        g g g r(   )r   rR   	linecachegetliner   r   r   rz   )r9   r|   offsettextcheckr   r~   r   r   r   r   s          r   rR   +stylistic_issues.<locals>.StyleReport.error  sq    [$-k4O""4==+>$DMM466dmmR077kQU8VW 7 r   r   )r   r   r   r   r   rR   r   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
    s=   t &,/)*VM-BBdt|D!,d	
	%%c1-


djjlDJJLIJ$##DJJL1 % $ C	 f 8X 8Xk,, 8Xt  **L[*YMd=#789	-r   c                    ^ 0 m[        5       (       a^  SSKnSSKn " U4S jSUR                  R                  5      nU" 5       n[        U 5       H  nUR                  R                  XC5        M      T$ )a0  
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                   @   > \ rS rSrS rS rS rS rS rU 4S jr	Sr
g	)
!pyflakes_issues.<locals>.ReporteriI  c                     0 U l         [        S    H]  nUR                  S5      u  p#U R                   R                  UR	                  5       / 5      R                  UR	                  5       5        M_     g )Nr   r   )_ignored_issuesr  r   r   r   r   )r9   r~   r   issues       r   rJ   *pyflakes_issues.<locals>.Reporter.__init__J  sW    !,-D

4(+$



)
)$**,
;
B
B5;;=
Q .r   c                 ,    U R                  US US 5        g r(   _register_issue)r9   r   r!   s      r   unexpectedError1pyflakes_issues.<locals>.Reporter.unexpectedErrorQ  s    XtS$7r   c                 (    U R                  XX%5        g r(   r  )r9   r   r!   linenor   r   s         r   syntaxError-pyflakes_issues.<locals>.Reporter.syntaxErrorT  s    Xs9r   c                     U R                  UR                  UR                  UR                  UR                  -  S 5        g r(   )r  r   r  r}   message_args)r9   r!   s     r   flake'pyflakes_issues.<locals>.Reporter.flakeW  s.    S\\3::s{{SEUEU7UW[\r   c                    U R                   R                  5        H  u  p4UR                  U5      (       d  M  X$;   a    gU Vs/ s H  oUR                  S5      (       d  M  US S PM!     sn H  nUR                  U5      (       d  M      g   U Vs/ s H  oUR                  S5      (       d  M  USS  PM!     sn H  nUR                  U5      (       d  M      g   M     gs  snf s  snf )NTr   r   F)r  itemsr   r   )r9   r   r  r   ignored_issuesiprefixsuffixs           r   _is_ignored-pyflakes_issues.<locals>.Reporter._is_ignoredZ  s     -1,@,@,F,F,H(L]]<((&*8L.QJJsO51Ra5.L!!&)) M +9N.QLL<M51QR5.N'' O -I  M Os   C 	CC#-	C#c                    > U R                  X5      (       de  U(       a2  U(       a+  U(       d$  [        R                  " X5      R                  5       nTR	                  U/ 5      R                  [        X#U5      5        g g r(   )r%  r   r   r   r   r   rz   )r9   r   r|   r  r~   r   s        r   r  1pyflakes_issues.<locals>.Reporter._register_issuem  s[    ,,k$$$T7==?D


D"
%
,
,U;t-L
M	 -r   )r  N)r   r   r   r   rJ   r  r  r  r%  r  r   )r   s   r   Reporterr  I  s*    R8:]&N Nr   r)  )r   pyflakes.apipyflakes.reporterr  r)  r  api	checkPath)r   pyflakesr)  r  r   r   s        @r   pyflakes_issuesr/  )  se    4 &)N8$$-- )NV zHe$llT, % 
-r   c                 <     [        U 5        g! [         a     gf = f)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              #      #    U  Hr  n[         R                  R                  R                  US5       HA  nSn[        S    H"  n[
        R                  " XB5      (       d  M   Sn  O   U(       a  M=  Uv   MC     Mt     g 7f)Nr   Fr   T)r)   r*   r   r   r  r   match)r   r   	file_pathr&   exclude_paths        r   r  r    sh     dYY%%77eD	d 1,88L,,$
 2
 T E s   AA< A</A<r(   )FFF)3r   collectionsr   ra   rS   r   rA   r   rW   unitteststem.prereqr)   stem.util.confstem.util.enumstem.util.systemr*   confconfig_dictr  r   r-   enumUppercaseEnumrE   
namedtupler   r_   r`   	Exceptionr   caser"   r$   r&   r1   objectr,   rz   TextTestRunnerr   r   r   r   r   r
  r/  r   r  get_stylistic_issuesget_pyflakes_issuesis_pep8_availabler   r   r   <module>rJ     s`  6    	 	        		#	#F	- 
 iinn**9iL$$]J?
 ;;' ' ]]##(d"i"j( j(ZK""7,NO B2h-- B2J
*ZP,"ZzPf & ( % , r   