codekingpro/portable-devtools
114k
1from operator import itemgetter2from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Sequence3 4from . import errors5from .protocol import is_renderable, rich_cast6 7if TYPE_CHECKING:8 from .console import Console, ConsoleOptions, RenderableType9 10 11class Measurement(NamedTuple):12 """Stores the minimum and maximum widths (in characters) required to render an object."""13 14 minimum: int15 """Minimum number of cells required to render."""16 maximum: int17 """Maximum number of cells required to render."""18 19 @property20 def span(self) -> int:21 """Get difference between maximum and minimum."""22 return self.maximum - self.minimum23 24 def normalize(self) -> "Measurement":25 """Get measurement that ensures that minimum <= maximum and minimum >= 026 27 Returns:28 Measurement: A normalized measurement.29 """30 minimum, maximum = self31 minimum = min(max(0, minimum), maximum)32 return Measurement(max(0, minimum), max(0, max(minimum, maximum)))33 34 def with_maximum(self, width: int) -> "Measurement":35 """Get a RenderableWith where the widths are <= width.36 37 Args:38 width (int): Maximum desired width.39 40 Returns:41 Measurement: New Measurement object.42 """43 minimum, maximum = self44 return Measurement(min(minimum, width), min(maximum, width))45 46 def with_minimum(self, width: int) -> "Measurement":47 """Get a RenderableWith where the widths are >= width.48 49 Args:50 width (int): Minimum desired width.51 52 Returns:53 Measurement: New Measurement object.54 """55 minimum, maximum = self56 width = max(0, width)57 return Measurement(max(minimum, width), max(maximum, width))58 59 def clamp(60 self, min_width: Optional[int] = None, max_width: Optional[int] = None61 ) -> "Measurement":62 """Clamp a measurement within the specified range.63 64 Args:65 min_width (int): Minimum desired width, or ``None`` for no minimum. Defaults to None.66 max_width (int): Maximum desired width, or ``None`` for no maximum. Defaults to None.67 68 Returns:69 Measurement: New Measurement object.70 """71 measurement = self72 if min_width is not None:73 measurement = measurement.with_minimum(min_width)74 if max_width is not None:75 measurement = measurement.with_maximum(max_width)76 return measurement77 78 @classmethod79 def get(80 cls, console: "Console", options: "ConsoleOptions", renderable: "RenderableType"81 ) -> "Measurement":82 """Get a measurement for a renderable.83 84 Args:85 console (~rich.console.Console): Console instance.86 options (~rich.console.ConsoleOptions): Console options.87 renderable (RenderableType): An object that may be rendered with Rich.88 89 Raises:90 errors.NotRenderableError: If the object is not renderable.91 92 Returns:93 Measurement: Measurement object containing range of character widths required to render the object.94 """95 _max_width = options.max_width96 if _max_width < 1:97 return Measurement(0, 0)98 if isinstance(renderable, str):99 renderable = console.render_str(100 renderable, markup=options.markup, highlight=False101 )102 renderable = rich_cast(renderable)103 if is_renderable(renderable):104 get_console_width: Optional[105 Callable[["Console", "ConsoleOptions"], "Measurement"]106 ] = getattr(renderable, "__rich_measure__", None)107 if get_console_width is not None:108 render_width = (109 get_console_width(console, options)110 .normalize()111 .with_maximum(_max_width)112 )113 if render_width.maximum < 1:114 return Measurement(0, 0)115 return render_width.normalize()116 else:117 return Measurement(0, _max_width)118 else:119 raise errors.NotRenderableError(120 f"Unable to get render width for {renderable!r}; "121 "a str, Segment, or object with __rich_console__ method is required"122 )123 124 125def measure_renderables(126 console: "Console",127 options: "ConsoleOptions",128 renderables: Sequence["RenderableType"],129) -> "Measurement":130 """Get a measurement that would fit a number of renderables.131 132 Args:133 console (~rich.console.Console): Console instance.134 options (~rich.console.ConsoleOptions): Console options.135 renderables (Iterable[RenderableType]): One or more renderable objects.136 137 Returns:138 Measurement: Measurement object containing range of character widths required to139 contain all given renderables.140 """141 if not renderables:142 return Measurement(0, 0)143 get_measurement = Measurement.get144 measurements = [145 get_measurement(console, options, renderable) for renderable in renderables146 ]147 measured_width = Measurement(148 max(measurements, key=itemgetter(0)).minimum,149 max(measurements, key=itemgetter(1)).maximum,150 )151 return measured_width152 