1
1
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
2
2
3
+ import contextlib
4
+ import functools
3
5
import inspect
4
6
import io
5
7
import operator
@@ -35,6 +37,35 @@ def getvalue(self):
35
37
return self .buffer .raw .getvalue ().decode ('utf-8' )
36
38
37
39
40
+ class StdStreamTest (unittest .TestCase ):
41
+
42
+ def test_skip_invalid_stderr (self ):
43
+ parser = argparse .ArgumentParser ()
44
+ with (
45
+ contextlib .redirect_stderr (None ),
46
+ mock .patch ('argparse._sys.exit' )
47
+ ):
48
+ parser .exit (status = 0 , message = 'foo' )
49
+
50
+ def test_skip_invalid_stdout (self ):
51
+ parser = argparse .ArgumentParser ()
52
+ for func in (
53
+ parser .print_usage ,
54
+ parser .print_help ,
55
+ functools .partial (parser .parse_args , ['-h' ])
56
+ ):
57
+ with (
58
+ self .subTest (func = func ),
59
+ contextlib .redirect_stdout (None ),
60
+ # argparse uses stderr as a fallback
61
+ StdIOBuffer () as mocked_stderr ,
62
+ contextlib .redirect_stderr (mocked_stderr ),
63
+ mock .patch ('argparse._sys.exit' ),
64
+ ):
65
+ func ()
66
+ self .assertRegex (mocked_stderr .getvalue (), r'usage:' )
67
+
68
+
38
69
class TestCase (unittest .TestCase ):
39
70
40
71
def setUp (self ):
@@ -734,6 +765,49 @@ def test_const(self):
734
765
735
766
self .assertIn ("got an unexpected keyword argument 'const'" , str (cm .exception ))
736
767
768
+ def test_deprecated_init_kw (self ):
769
+ # See gh-92248
770
+ parser = argparse .ArgumentParser ()
771
+
772
+ with self .assertWarns (DeprecationWarning ):
773
+ parser .add_argument (
774
+ '-a' ,
775
+ action = argparse .BooleanOptionalAction ,
776
+ type = None ,
777
+ )
778
+ with self .assertWarns (DeprecationWarning ):
779
+ parser .add_argument (
780
+ '-b' ,
781
+ action = argparse .BooleanOptionalAction ,
782
+ type = bool ,
783
+ )
784
+
785
+ with self .assertWarns (DeprecationWarning ):
786
+ parser .add_argument (
787
+ '-c' ,
788
+ action = argparse .BooleanOptionalAction ,
789
+ metavar = None ,
790
+ )
791
+ with self .assertWarns (DeprecationWarning ):
792
+ parser .add_argument (
793
+ '-d' ,
794
+ action = argparse .BooleanOptionalAction ,
795
+ metavar = 'd' ,
796
+ )
797
+
798
+ with self .assertWarns (DeprecationWarning ):
799
+ parser .add_argument (
800
+ '-e' ,
801
+ action = argparse .BooleanOptionalAction ,
802
+ choices = None ,
803
+ )
804
+ with self .assertWarns (DeprecationWarning ):
805
+ parser .add_argument (
806
+ '-f' ,
807
+ action = argparse .BooleanOptionalAction ,
808
+ choices = (),
809
+ )
810
+
737
811
class TestBooleanOptionalActionRequired (ParserTestCase ):
738
812
"""Tests BooleanOptionalAction required"""
739
813
@@ -1505,14 +1579,15 @@ class TestArgumentsFromFile(TempDirMixin, ParserTestCase):
1505
1579
def setUp (self ):
1506
1580
super (TestArgumentsFromFile , self ).setUp ()
1507
1581
file_texts = [
1508
- ('hello' , 'hello world!\n ' ),
1509
- ('recursive' , '-a\n '
1510
- 'A\n '
1511
- '@hello' ),
1512
- ('invalid' , '@no-such-path\n ' ),
1582
+ ('hello' , os .fsencode (self .hello ) + b'\n ' ),
1583
+ ('recursive' , b'-a\n '
1584
+ b'A\n '
1585
+ b'@hello' ),
1586
+ ('invalid' , b'@no-such-path\n ' ),
1587
+ ('undecodable' , self .undecodable + b'\n ' ),
1513
1588
]
1514
1589
for path , text in file_texts :
1515
- with open (path , 'w' , encoding = "utf-8" ) as file :
1590
+ with open (path , 'wb' ) as file :
1516
1591
file .write (text )
1517
1592
1518
1593
parser_signature = Sig (fromfile_prefix_chars = '@' )
@@ -1522,15 +1597,25 @@ def setUp(self):
1522
1597
Sig ('y' , nargs = '+' ),
1523
1598
]
1524
1599
failures = ['' , '-b' , 'X' , '@invalid' , '@missing' ]
1600
+ hello = 'hello world!' + os_helper .FS_NONASCII
1525
1601
successes = [
1526
1602
('X Y' , NS (a = None , x = 'X' , y = ['Y' ])),
1527
1603
('X -a A Y Z' , NS (a = 'A' , x = 'X' , y = ['Y' , 'Z' ])),
1528
- ('@hello X' , NS (a = None , x = ' hello world!' , y = ['X' ])),
1529
- ('X @hello' , NS (a = None , x = 'X' , y = [' hello world!' ])),
1530
- ('-a B @recursive Y Z' , NS (a = 'A' , x = ' hello world!' , y = ['Y' , 'Z' ])),
1531
- ('X @recursive Z -a B' , NS (a = 'B' , x = 'X' , y = [' hello world!' , 'Z' ])),
1604
+ ('@hello X' , NS (a = None , x = hello , y = ['X' ])),
1605
+ ('X @hello' , NS (a = None , x = 'X' , y = [hello ])),
1606
+ ('-a B @recursive Y Z' , NS (a = 'A' , x = hello , y = ['Y' , 'Z' ])),
1607
+ ('X @recursive Z -a B' , NS (a = 'B' , x = 'X' , y = [hello , 'Z' ])),
1532
1608
(["-a" , "" , "X" , "Y" ], NS (a = '' , x = 'X' , y = ['Y' ])),
1533
1609
]
1610
+ if os_helper .TESTFN_UNDECODABLE :
1611
+ undecodable = os_helper .TESTFN_UNDECODABLE .lstrip (b'@' )
1612
+ decoded_undecodable = os .fsdecode (undecodable )
1613
+ successes += [
1614
+ ('@undecodable X' , NS (a = None , x = decoded_undecodable , y = ['X' ])),
1615
+ ('X @undecodable' , NS (a = None , x = 'X' , y = [decoded_undecodable ])),
1616
+ ]
1617
+ else :
1618
+ undecodable = b''
1534
1619
1535
1620
1536
1621
class TestArgumentsFromFileConverter (TempDirMixin , ParserTestCase ):
@@ -1539,10 +1624,10 @@ class TestArgumentsFromFileConverter(TempDirMixin, ParserTestCase):
1539
1624
def setUp (self ):
1540
1625
super (TestArgumentsFromFileConverter , self ).setUp ()
1541
1626
file_texts = [
1542
- ('hello' , 'hello world!\n ' ),
1627
+ ('hello' , b 'hello world!\n ' ),
1543
1628
]
1544
1629
for path , text in file_texts :
1545
- with open (path , 'w' , encoding = "utf-8" ) as file :
1630
+ with open (path , 'wb' ) as file :
1546
1631
file .write (text )
1547
1632
1548
1633
class FromFileConverterArgumentParser (ErrorRaisingArgumentParser ):
@@ -3753,6 +3838,28 @@ class TestHelpUsage(HelpTestCase):
3753
3838
version = ''
3754
3839
3755
3840
3841
+ class TestHelpUsageWithParentheses (HelpTestCase ):
3842
+ parser_signature = Sig (prog = 'PROG' )
3843
+ argument_signatures = [
3844
+ Sig ('positional' , metavar = '(example) positional' ),
3845
+ Sig ('-p' , '--optional' , metavar = '{1 (option A), 2 (option B)}' ),
3846
+ ]
3847
+
3848
+ usage = '''\
3849
+ usage: PROG [-h] [-p {1 (option A), 2 (option B)}] (example) positional
3850
+ '''
3851
+ help = usage + '''\
3852
+
3853
+ positional arguments:
3854
+ (example) positional
3855
+
3856
+ options:
3857
+ -h, --help show this help message and exit
3858
+ -p {1 (option A), 2 (option B)}, --optional {1 (option A), 2 (option B)}
3859
+ '''
3860
+ version = ''
3861
+
3862
+
3756
3863
class TestHelpOnlyUserGroups (HelpTestCase ):
3757
3864
"""Test basic usage messages"""
3758
3865
@@ -5219,6 +5326,13 @@ def test_mixed(self):
5219
5326
self .assertEqual (NS (v = 3 , spam = True , badger = "B" ), args )
5220
5327
self .assertEqual (["C" , "--foo" , "4" ], extras )
5221
5328
5329
+ def test_zero_or_more_optional (self ):
5330
+ parser = argparse .ArgumentParser ()
5331
+ parser .add_argument ('x' , nargs = '*' , choices = ('x' , 'y' ))
5332
+ args = parser .parse_args ([])
5333
+ self .assertEqual (NS (x = []), args )
5334
+
5335
+
5222
5336
# ===========================
5223
5337
# parse_intermixed_args tests
5224
5338
# ===========================
0 commit comments