Coverage for lintro / utils / environment / renderer.py: 22%

60 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Environment report rendering utilities.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import shutil 

7from typing import TYPE_CHECKING 

8 

9from rich.panel import Panel 

10 

11from lintro.utils.environment._protocol import Renderable 

12from lintro.utils.environment.environment_report import EnvironmentReport 

13 

14if TYPE_CHECKING: 

15 from rich.console import Console 

16 

17# Consistent label width for alignment (matches original output) 

18LABEL_WIDTH = 12 

19 

20 

21def render_section(console: Console, section: Renderable | None) -> None: 

22 """Render a single environment section to the console. 

23 

24 Args: 

25 console: Rich console for output. 

26 section: Environment section implementing Renderable protocol, 

27 or None for optional sections. 

28 """ 

29 if section is None: 

30 return 

31 

32 if not section.is_available(): 

33 console.print(f"[bold cyan]{section.section_title}:[/bold cyan]") 

34 console.print(" [dim](not installed)[/dim]") 

35 console.print() 

36 return 

37 

38 console.print(f"[bold cyan]{section.section_title}:[/bold cyan]") 

39 for label, value in section.to_display_rows(): 

40 # Pad label for alignment 

41 padded_label = f"{label}:".ljust(LABEL_WIDTH) 

42 console.print(f" {padded_label} {value}") 

43 console.print() 

44 

45 

46# Default truncation length for PATH; override via terminal width if available 

47_DEFAULT_PATH_TRUNCATE_LEN = 60 

48 

49 

50def _get_path_truncate_len() -> int: 

51 """Get the truncation length for PATH based on terminal width.""" 

52 try: 

53 terminal_width = shutil.get_terminal_size().columns 

54 # Use 70% of terminal width, with a minimum of 40 and maximum of 120 

55 return max(40, min(120, int(terminal_width * 0.7))) 

56 except (ValueError, OSError): 

57 return _DEFAULT_PATH_TRUNCATE_LEN 

58 

59 

60def _render_env_vars(console: Console, env_vars: dict[str, str | None]) -> None: 

61 """Render environment variables section. 

62 

63 Args: 

64 console: Rich console for output. 

65 env_vars: Dictionary of environment variable names to values. 

66 """ 

67 console.print("[bold cyan]Environment Variables:[/bold cyan]") 

68 for var_name, var_value in env_vars.items(): 

69 display_value: str 

70 if var_name == "PATH" and var_value: 

71 # Truncate PATH for readability, keeping whole path entries 

72 path_truncate_len = _get_path_truncate_len() 

73 path_entries = var_value.split(os.pathsep) 

74 if len(var_value) > path_truncate_len: 

75 # Build truncated string by adding whole entries 

76 shown_parts: list[str] = [] 

77 current_len = 0 

78 for entry in path_entries: 

79 # Account for separator length (except for first entry) 

80 entry_len = len(entry) + (1 if shown_parts else 0) 

81 if current_len + entry_len > path_truncate_len: 

82 break 

83 shown_parts.append(entry) 

84 current_len += entry_len 

85 # Always show at least one entry even if it exceeds truncate length 

86 if not shown_parts and path_entries: 

87 shown_parts.append(path_entries[0]) 

88 remaining = len(path_entries) - len(shown_parts) 

89 truncated = os.pathsep.join(shown_parts) 

90 entry_word = "entry" if remaining == 1 else "entries" 

91 display_value = ( 

92 f"{truncated}... ({remaining} more {entry_word})" 

93 if remaining > 0 

94 else truncated 

95 ) 

96 else: 

97 display_value = var_value 

98 else: 

99 display_value = var_value or "(not set)" 

100 console.print(f" {var_name}: {display_value}", markup=False) 

101 console.print() 

102 

103 

104def render_environment_report(console: Console, env: EnvironmentReport) -> None: 

105 """Render complete environment report to the console. 

106 

107 This is a drop-in replacement for the old _print_environment_report() 

108 function, providing identical output using the Renderable protocol. 

109 

110 Args: 

111 console: Rich console for output. 

112 env: Complete environment report. 

113 """ 

114 console.print( 

115 Panel.fit( 

116 "[bold]Lintro Environment Report[/bold]", 

117 border_style="cyan", 

118 ), 

119 ) 

120 console.print() 

121 

122 # Render each section in order 

123 sections: list[Renderable | None] = [ 

124 env.lintro, 

125 env.system, 

126 env.python, 

127 env.node, 

128 env.rust, 

129 env.go, 

130 env.ruby, 

131 env.project, 

132 env.ci, 

133 ] 

134 

135 for section in sections: 

136 render_section(console, section) 

137 

138 # Environment variables are handled specially (dict, not dataclass) 

139 _render_env_vars(console, env.env_vars)