@@ -33,7 +33,8 @@ class TestCursesCompatibility(unittest.TestCase):
3333 $TERM in the same process, so we subprocess all `curses` tests to get correctly
3434 set up terminfo."""
3535
36- def setUp (self ):
36+ @classmethod
37+ def setUpClass (cls ):
3738 if _curses is None :
3839 raise unittest .SkipTest (
3940 "`curses` capability provided to regrtest but `_curses` not importable"
@@ -42,6 +43,11 @@ def setUp(self):
4243 if not has_subprocess_support :
4344 raise unittest .SkipTest ("test module requires subprocess" )
4445
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 ):
4551 self .original_term = os .environ .get ("TERM" , None )
4652
4753 def tearDown (self ):
@@ -50,6 +56,34 @@ def tearDown(self):
5056 elif "TERM" in os .environ :
5157 del os .environ ["TERM" ]
5258
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+
5387 def test_setupterm_basic (self ):
5488 """Test basic setupterm functionality."""
5589 # Test with explicit terminal type
@@ -79,7 +113,7 @@ def test_setupterm_basic(self):
79113
80114 # Set up with PyREPL curses
81115 try :
82- terminfo .TermInfo (term )
116+ terminfo .TermInfo (term , fallback = False )
83117 pyrepl_success = True
84118 except Exception as e :
85119 pyrepl_success = False
@@ -120,7 +154,7 @@ def test_setupterm_none(self):
120154 std_success = ncurses_data ["success" ]
121155
122156 try :
123- terminfo .TermInfo (None )
157+ terminfo .TermInfo (None , fallback = False )
124158 pyrepl_success = True
125159 except Exception :
126160 pyrepl_success = False
@@ -138,28 +172,7 @@ def test_tigetstr_common_capabilities(self):
138172 term = "xterm"
139173
140174 # 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 )
163176
164177 ncurses_code = dedent (
165178 f"""
@@ -176,7 +189,7 @@ def test_tigetstr_common_capabilities(self):
176189 results[cap] = -1
177190 else:
178191 results[cap] = list(val)
179- except:
192+ except BaseException :
180193 results[cap] = "error"
181194 print(json.dumps(results))
182195 """
@@ -193,7 +206,7 @@ def test_tigetstr_common_capabilities(self):
193206
194207 ncurses_data = json .loads (result .stdout )
195208
196- ti = terminfo .TermInfo (term )
209+ ti = terminfo .TermInfo (term , fallback = False )
197210
198211 # Test every single capability
199212 for cap in all_caps :
@@ -255,7 +268,7 @@ def test_tigetstr_input_types(self):
255268 ncurses_data = json .loads (result .stdout )
256269
257270 # PyREPL setup
258- ti = terminfo .TermInfo (term )
271+ ti = terminfo .TermInfo (term , fallback = False )
259272
260273 # PyREPL behavior with string
261274 try :
@@ -281,7 +294,7 @@ def test_tigetstr_input_types(self):
281294 def test_tparm_basic (self ):
282295 """Test basic tparm functionality."""
283296 term = "xterm"
284- ti = terminfo .TermInfo (term )
297+ ti = terminfo .TermInfo (term , fallback = False )
285298
286299 # Test cursor positioning (cup)
287300 cup = ti .get ("cup" )
@@ -357,7 +370,7 @@ def test_tparm_basic(self):
357370 def test_tparm_multiple_params (self ):
358371 """Test tparm with capabilities using multiple parameters."""
359372 term = "xterm"
360- ti = terminfo .TermInfo (term )
373+ ti = terminfo .TermInfo (term , fallback = False )
361374
362375 # Test capabilities that take parameters
363376 param_caps = {
@@ -472,7 +485,7 @@ def test_tparm_null_handling(self):
472485 ncurses_data = json .loads (result .stdout )
473486
474487 # PyREPL setup
475- ti = terminfo .TermInfo (term )
488+ ti = terminfo .TermInfo (term , fallback = False )
476489
477490 # Test with None - both should raise TypeError
478491 if ncurses_data ["raises_typeerror" ]:
@@ -496,38 +509,9 @@ def test_special_terminals(self):
496509 ]
497510
498511 # 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-
529512 for term in special_terms :
530513 with self .subTest (term = term ):
514+ all_caps = self .infocmp (term )
531515 ncurses_code = dedent (
532516 f"""
533517 import _curses
@@ -547,7 +531,7 @@ def test_special_terminals(self):
547531 else:
548532 # Convert bytes to list of ints for JSON
549533 results[cap] = list(val)
550- except:
534+ except BaseException :
551535 results[cap] = "error"
552536 print(json.dumps(results))
553537 except Exception as e:
@@ -576,10 +560,10 @@ def test_special_terminals(self):
576560 if "error" in ncurses_data and len (ncurses_data ) == 1 :
577561 # ncurses failed to setup this terminal
578562 # PyREPL should still work with fallback
579- ti = terminfo .TermInfo (term )
563+ ti = terminfo .TermInfo (term , fallback = True )
580564 continue
581565
582- ti = terminfo .TermInfo (term )
566+ ti = terminfo .TermInfo (term , fallback = False )
583567
584568 # Compare all capabilities
585569 for cap in all_caps :
@@ -638,9 +622,9 @@ def test_terminfo_fallback(self):
638622
639623 # PyREPL should succeed with fallback
640624 try :
641- ti = terminfo .TermInfo (fake_term )
625+ ti = terminfo .TermInfo (fake_term , fallback = True )
642626 pyrepl_ok = True
643- except :
627+ except Exception :
644628 pyrepl_ok = False
645629
646630 self .assertTrue (
0 commit comments