@@ -33,7 +33,8 @@ class TestCursesCompatibility(unittest.TestCase):
33
33
$TERM in the same process, so we subprocess all `curses` tests to get correctly
34
34
set up terminfo."""
35
35
36
- def setUp (self ):
36
+ @classmethod
37
+ def setUpClass (cls ):
37
38
if _curses is None :
38
39
raise unittest .SkipTest (
39
40
"`curses` capability provided to regrtest but `_curses` not importable"
@@ -42,6 +43,11 @@ def setUp(self):
42
43
if not has_subprocess_support :
43
44
raise unittest .SkipTest ("test module requires subprocess" )
44
45
46
+ # we need to ensure there's a terminfo database on the system and that
47
+ # `infocmp` works
48
+ cls .infocmp ("dumb" )
49
+
50
+ def setUp (self ):
45
51
self .original_term = os .environ .get ("TERM" , None )
46
52
47
53
def tearDown (self ):
@@ -50,6 +56,34 @@ def tearDown(self):
50
56
elif "TERM" in os .environ :
51
57
del os .environ ["TERM" ]
52
58
59
+ @classmethod
60
+ def infocmp (cls , term ) -> list [str ]:
61
+ all_caps = []
62
+ try :
63
+ result = subprocess .run (
64
+ ["infocmp" , "-l1" , term ],
65
+ capture_output = True ,
66
+ text = True ,
67
+ check = True ,
68
+ )
69
+ except Exception :
70
+ raise unittest .SkipTest ("calling `infocmp` failed on the system" )
71
+
72
+ for line in result .stdout .splitlines ():
73
+ line = line .strip ()
74
+ if line .startswith ("#" ):
75
+ if "terminfo" not in line and "termcap" in line :
76
+ # PyREPL terminfo doesn't parse termcap databases
77
+ raise unittest .SkipTest (
78
+ "curses using termcap.db: no terminfo database on"
79
+ " the system"
80
+ )
81
+ elif "=" in line :
82
+ cap_name = line .split ("=" )[0 ]
83
+ all_caps .append (cap_name )
84
+
85
+ return all_caps
86
+
53
87
def test_setupterm_basic (self ):
54
88
"""Test basic setupterm functionality."""
55
89
# Test with explicit terminal type
@@ -79,7 +113,7 @@ def test_setupterm_basic(self):
79
113
80
114
# Set up with PyREPL curses
81
115
try :
82
- terminfo .TermInfo (term )
116
+ terminfo .TermInfo (term , fallback = False )
83
117
pyrepl_success = True
84
118
except Exception as e :
85
119
pyrepl_success = False
@@ -120,7 +154,7 @@ def test_setupterm_none(self):
120
154
std_success = ncurses_data ["success" ]
121
155
122
156
try :
123
- terminfo .TermInfo (None )
157
+ terminfo .TermInfo (None , fallback = False )
124
158
pyrepl_success = True
125
159
except Exception :
126
160
pyrepl_success = False
@@ -138,28 +172,7 @@ def test_tigetstr_common_capabilities(self):
138
172
term = "xterm"
139
173
140
174
# Get ALL capabilities from infocmp
141
- all_caps = []
142
- try :
143
- result = subprocess .run (
144
- ["infocmp" , "-1" , term ],
145
- capture_output = True ,
146
- text = True ,
147
- check = True ,
148
- )
149
- for line in result .stdout .splitlines ():
150
- line = line .strip ()
151
- if "=" in line and not line .startswith ("#" ):
152
- cap_name = line .split ("=" )[0 ]
153
- all_caps .append (cap_name )
154
- except :
155
- # If infocmp fails, at least test the critical ones
156
- # fmt: off
157
- all_caps = [
158
- "cup" , "clear" , "el" , "cub1" , "cuf1" , "cuu1" , "cud1" , "bel" ,
159
- "ind" , "ri" , "civis" , "cnorm" , "smkx" , "rmkx" , "cub" , "cuf" ,
160
- "cud" , "cuu" , "home" , "hpa" , "vpa" , "cr" , "nel" , "ht"
161
- ]
162
- # fmt: on
175
+ all_caps = self .infocmp (term )
163
176
164
177
ncurses_code = dedent (
165
178
f"""
@@ -176,7 +189,7 @@ def test_tigetstr_common_capabilities(self):
176
189
results[cap] = -1
177
190
else:
178
191
results[cap] = list(val)
179
- except:
192
+ except BaseException :
180
193
results[cap] = "error"
181
194
print(json.dumps(results))
182
195
"""
@@ -193,7 +206,7 @@ def test_tigetstr_common_capabilities(self):
193
206
194
207
ncurses_data = json .loads (result .stdout )
195
208
196
- ti = terminfo .TermInfo (term )
209
+ ti = terminfo .TermInfo (term , fallback = False )
197
210
198
211
# Test every single capability
199
212
for cap in all_caps :
@@ -255,7 +268,7 @@ def test_tigetstr_input_types(self):
255
268
ncurses_data = json .loads (result .stdout )
256
269
257
270
# PyREPL setup
258
- ti = terminfo .TermInfo (term )
271
+ ti = terminfo .TermInfo (term , fallback = False )
259
272
260
273
# PyREPL behavior with string
261
274
try :
@@ -281,7 +294,7 @@ def test_tigetstr_input_types(self):
281
294
def test_tparm_basic (self ):
282
295
"""Test basic tparm functionality."""
283
296
term = "xterm"
284
- ti = terminfo .TermInfo (term )
297
+ ti = terminfo .TermInfo (term , fallback = False )
285
298
286
299
# Test cursor positioning (cup)
287
300
cup = ti .get ("cup" )
@@ -357,7 +370,7 @@ def test_tparm_basic(self):
357
370
def test_tparm_multiple_params (self ):
358
371
"""Test tparm with capabilities using multiple parameters."""
359
372
term = "xterm"
360
- ti = terminfo .TermInfo (term )
373
+ ti = terminfo .TermInfo (term , fallback = False )
361
374
362
375
# Test capabilities that take parameters
363
376
param_caps = {
@@ -472,7 +485,7 @@ def test_tparm_null_handling(self):
472
485
ncurses_data = json .loads (result .stdout )
473
486
474
487
# PyREPL setup
475
- ti = terminfo .TermInfo (term )
488
+ ti = terminfo .TermInfo (term , fallback = False )
476
489
477
490
# Test with None - both should raise TypeError
478
491
if ncurses_data ["raises_typeerror" ]:
@@ -496,38 +509,9 @@ def test_special_terminals(self):
496
509
]
497
510
498
511
# Get all string capabilities from ncurses
499
- all_caps = []
500
- try :
501
- # Get all capability names from infocmp
502
- result = subprocess .run (
503
- ["infocmp" , "-1" , "xterm" ],
504
- capture_output = True ,
505
- text = True ,
506
- check = True ,
507
- )
508
- for line in result .stdout .splitlines ():
509
- line = line .strip ()
510
- if "=" in line :
511
- cap_name = line .split ("=" )[0 ]
512
- all_caps .append (cap_name )
513
- except :
514
- # Fall back to a core set if infocmp fails
515
- # fmt: off
516
- all_caps = [
517
- "cup" , "clear" , "el" , "cub" , "cuf" , "cud" , "cuu" , "cub1" ,
518
- "cuf1" , "cud1" , "cuu1" , "home" , "bel" , "ind" , "ri" , "nel" , "cr" ,
519
- "ht" , "hpa" , "vpa" , "dch" , "dch1" , "dl" , "dl1" , "ich" , "ich1" ,
520
- "il" , "il1" , "sgr0" , "smso" , "rmso" , "smul" , "rmul" , "bold" ,
521
- "rev" , "blink" , "dim" , "smacs" , "rmacs" , "civis" , "cnorm" , "sc" ,
522
- "rc" , "hts" , "tbc" , "ed" , "kbs" , "kcud1" , "kcub1" , "kcuf1" ,
523
- "kcuu1" , "kdch1" , "khome" , "kend" , "knp" , "kpp" , "kich1" , "kf1" ,
524
- "kf2" , "kf3" , "kf4" , "kf5" , "kf6" , "kf7" , "kf8" , "kf9" , "kf10" ,
525
- "rmkx" , "smkx"
526
- ]
527
- # fmt: on
528
-
529
512
for term in special_terms :
530
513
with self .subTest (term = term ):
514
+ all_caps = self .infocmp (term )
531
515
ncurses_code = dedent (
532
516
f"""
533
517
import _curses
@@ -547,7 +531,7 @@ def test_special_terminals(self):
547
531
else:
548
532
# Convert bytes to list of ints for JSON
549
533
results[cap] = list(val)
550
- except:
534
+ except BaseException :
551
535
results[cap] = "error"
552
536
print(json.dumps(results))
553
537
except Exception as e:
@@ -576,10 +560,10 @@ def test_special_terminals(self):
576
560
if "error" in ncurses_data and len (ncurses_data ) == 1 :
577
561
# ncurses failed to setup this terminal
578
562
# PyREPL should still work with fallback
579
- ti = terminfo .TermInfo (term )
563
+ ti = terminfo .TermInfo (term , fallback = True )
580
564
continue
581
565
582
- ti = terminfo .TermInfo (term )
566
+ ti = terminfo .TermInfo (term , fallback = False )
583
567
584
568
# Compare all capabilities
585
569
for cap in all_caps :
@@ -638,9 +622,9 @@ def test_terminfo_fallback(self):
638
622
639
623
# PyREPL should succeed with fallback
640
624
try :
641
- ti = terminfo .TermInfo (fake_term )
625
+ ti = terminfo .TermInfo (fake_term , fallback = True )
642
626
pyrepl_ok = True
643
- except :
627
+ except Exception :
644
628
pyrepl_ok = False
645
629
646
630
self .assertTrue (
0 commit comments