@@ -398,6 +398,15 @@ def dont_skip_any_doctests(app, what, name, obj, skip, options):
398
398
399
399
class _DummyClass (object ): pass
400
400
401
+ # doctest optionflags for platform-specific tests
402
+ # they are skipped on other platforms
403
+ WINDOWS = doctest .register_optionflag ('WINDOWS' )
404
+ LINUX = doctest .register_optionflag ('LINUX' )
405
+ POSIX = doctest .register_optionflag ('POSIX' )
406
+
407
+ # doctest optionflag for tests that haven't been looked at yet
408
+ TODO = doctest .register_optionflag ('TODO' )
409
+
401
410
class Py2OutputChecker (_DummyClass , doctest .OutputChecker ):
402
411
def check_output (self , want , got , optionflags ):
403
412
sup = super (Py2OutputChecker , self ).check_output
@@ -425,27 +434,86 @@ def check_output(self, want, got, optionflags):
425
434
return False
426
435
return True
427
436
437
+ import sphinx .ext .doctest
438
+
439
+ class PlatformDocTestRunner (sphinx .ext .doctest .SphinxDocTestRunner ):
440
+ def run (self , test , compileflags = None , out = None , clear_globs = True ):
441
+ original_optionflags = self .optionflags | test .globs .get ('doctest_additional_flags' , 0 )
442
+ def filter_platform (example ):
443
+ optionflags = original_optionflags
444
+ if example .options :
445
+ for (optionflag , val ) in example .options .items ():
446
+ if val :
447
+ optionflags |= optionflag
448
+ else :
449
+ optionflags &= ~ optionflag
450
+
451
+ if (optionflags & WINDOWS ) == WINDOWS and sys .platform != 'win32' :
452
+ return False
453
+ if (optionflags & LINUX ) == LINUX and sys .platform != 'linux' :
454
+ return False
455
+ if (optionflags & POSIX ) == POSIX and os .name != 'posix' :
456
+ return False
457
+ return True
458
+
459
+ test .examples [:] = [example for example in test .examples if filter_platform (example )]
460
+
461
+ return super (PlatformDocTestRunner , self ).run (test , compileflags , out , clear_globs )
462
+
463
+ class PlatformDocTestBuilder (sphinx .ext .doctest .DocTestBuilder ):
464
+ _test_runner = None
465
+
466
+ @property
467
+ def test_runner (self ):
468
+ return self ._test_runner
469
+
470
+ @test_runner .setter
471
+ def test_runner (self , value ):
472
+ self ._test_runner = PlatformDocTestRunner (value ._checker , value ._verbose , value .optionflags )
473
+
428
474
def py2_doctest_init (self , checker = None , verbose = None , optionflags = 0 ):
429
475
if checker is None :
430
476
checker = Py2OutputChecker ()
431
477
doctest .DocTestRunner .__init__ (self , checker , verbose , optionflags )
432
478
433
479
if 'doctest' in sys .argv :
434
- def setup (app ):
435
- pass # app.connect('autodoc-skip-member', dont_skip_any_doctests)
436
480
437
481
if sys .version_info [:1 ] < (3 ,):
438
- import sphinx .ext .doctest
439
482
sphinx .ext .doctest .SphinxDocTestRunner .__init__ = py2_doctest_init
440
483
else :
484
+ def setup (app ):
485
+ app .add_builder (PlatformDocTestBuilder , override = True )
486
+ # app.connect('autodoc-skip-member', dont_skip_any_doctests)
441
487
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
442
488
import paramiko .client
443
489
import binascii
444
490
paramiko .client .hexlify = lambda x : binascii .hexlify (x ).decode ()
445
491
paramiko .util .safe_string = lambda x : '' # function result never *actually used*
446
492
class EndlessLoop (Exception ): pass
447
- def alrm_handler (sig , frame ):
448
- signal .alarm (180 ) # three minutes
449
- raise EndlessLoop ()
450
- signal .signal (signal .SIGALRM , alrm_handler )
451
- signal .alarm (600 ) # ten minutes
493
+ if hasattr (signal , 'alarm' ):
494
+ def alrm_handler (sig , frame ):
495
+ signal .alarm (180 ) # three minutes
496
+ raise EndlessLoop ()
497
+ signal .signal (signal .SIGALRM , alrm_handler )
498
+ signal .alarm (600 ) # ten minutes
499
+ else :
500
+ def sigabrt_handler (signum , frame ):
501
+ raise EndlessLoop ()
502
+ # thread.interrupt_main received the signum parameter in Python 3.10
503
+ if sys .version_info >= (3 , 10 ):
504
+ signal .signal (signal .SIGABRT , sigabrt_handler )
505
+ def alrm_handler ():
506
+ try :
507
+ import thread
508
+ except ImportError :
509
+ import _thread as thread
510
+ # pre Python 3.10 this raises a KeyboardInterrupt in the main thread.
511
+ # it might not show a traceback in that case, but it will stop the endless loop.
512
+ thread .interrupt_main (signal .SIGABRT )
513
+ timer = threading .Timer (interval = 180 , function = alrm_handler ) # three minutes
514
+ timer .daemon = True
515
+ timer .start ()
516
+ import threading
517
+ timer = threading .Timer (interval = 600 , function = alrm_handler ) # ten minutes
518
+ timer .daemon = True
519
+ timer .start ()
0 commit comments