From e86b20e56d1d8be0cfbea4d88cfec7ec7bb2b9e9 Mon Sep 17 00:00:00 2001 From: "Paulo S. Costa" Date: Thu, 22 Apr 2021 20:24:23 -0700 Subject: [PATCH] Fix double Optional --- rich/__init__.py | 2 +- rich/_inspect.py | 2 +- rich/_log_render.py | 2 +- rich/_ratio.py | 4 ++-- rich/color.py | 4 ++-- rich/console.py | 28 ++++++++++++++-------------- rich/live.py | 8 ++++---- rich/logging.py | 4 ++-- rich/markdown.py | 4 ++-- rich/padding.py | 2 +- rich/panel.py | 10 +++++----- rich/pretty.py | 6 +++--- rich/progress.py | 24 ++++++++++++------------ rich/progress_bar.py | 2 +- rich/prompt.py | 2 +- rich/segment.py | 4 ++-- rich/spinner.py | 2 +- rich/status.py | 8 ++++---- rich/style.py | 12 ++++++------ rich/syntax.py | 4 ++-- rich/table.py | 12 ++++++------ rich/tabulate.py | 4 ++-- rich/text.py | 4 ++-- rich/traceback.py | 10 +++++----- rich/tree.py | 2 +- tests/test_console.py | 2 +- tests/test_ratio.py | 2 +- 27 files changed, 85 insertions(+), 85 deletions(-) diff --git a/rich/__init__.py b/rich/__init__.py index 440fca18..af9fae09 100644 --- a/rich/__init__.py +++ b/rich/__init__.py @@ -9,7 +9,7 @@ if TYPE_CHECKING: from .console import Console # Global console used by alternative print -_console: Optional[Optional["Console"]] = None +_console: Optional["Console"] = None _IMPORT_CWD = os.path.abspath(os.getcwd()) diff --git a/rich/_inspect.py b/rich/_inspect.py index 59585950..31997239 100644 --- a/rich/_inspect.py +++ b/rich/_inspect.py @@ -94,7 +94,7 @@ class Inspect(JupyterMixin): except TypeError: return None - source_filename: Optional[Optional[str]] = None + source_filename: Optional[str] = None try: source_filename = getfile(obj) except TypeError: diff --git a/rich/_log_render.py b/rich/_log_render.py index d6713a70..3a77f0c8 100644 --- a/rich/_log_render.py +++ b/rich/_log_render.py @@ -27,7 +27,7 @@ class LogRender: self.time_format = time_format self.omit_repeated_times = omit_repeated_times self.level_width = level_width - self._last_time: Optional[Optional[Text]] = None + self._last_time: Optional[Text] = None def __call__( self, diff --git a/rich/_ratio.py b/rich/_ratio.py index e8c822f9..a44bb7c5 100644 --- a/rich/_ratio.py +++ b/rich/_ratio.py @@ -7,7 +7,7 @@ from typing_extensions import Protocol class Edge(Protocol): """Any object that defines an edge (such as Layout).""" - size: Optional[Optional[int]] = None + size: Optional[int] = None ratio: int = 1 minimum_size: int = 1 @@ -147,7 +147,7 @@ if __name__ == "__main__": # type: ignore @dataclass class E: - size: Optional[Optional[int]] = None + size: Optional[int] = None ratio: int = 1 minimum_size: int = 1 diff --git a/rich/color.py b/rich/color.py index 6a4b6f8f..15512e84 100644 --- a/rich/color.py +++ b/rich/color.py @@ -264,9 +264,9 @@ class Color(NamedTuple): """The name of the color (typically the input to Color.parse).""" type: ColorType """The type of the color.""" - number: Optional[Optional[int]] = None + number: Optional[int] = None """The color number, if a standard color, or None.""" - triplet: Optional[Optional[ColorTriplet]] = None + triplet: Optional[ColorTriplet] = None """A triplet of color components, if an RGB color.""" def __repr__(self) -> str: diff --git a/rich/console.py b/rich/console.py index 26c6896b..a4750d9f 100644 --- a/rich/console.py +++ b/rich/console.py @@ -119,17 +119,17 @@ class ConsoleOptions: """True if the target is a terminal, otherwise False.""" encoding: str """Encoding of terminal.""" - justify: Optional[Optional[JustifyMethod]] = None + justify: Optional[JustifyMethod] = None """Justify value override for renderable.""" - overflow: Optional[Optional[OverflowMethod]] = None + overflow: Optional[OverflowMethod] = None """Overflow value override for renderable.""" no_wrap: Optional[bool] = False """Disable wrapping for text.""" - highlight: Optional[Optional[bool]] = None + highlight: Optional[bool] = None """Highlight override for render_str.""" - markup: Optional[Optional[bool]] = None + markup: Optional[bool] = None """Enable markup when rendering strings.""" - height: Optional[Optional[int]] = None + height: Optional[int] = None """Height available, or None for no height limit.""" @property @@ -283,7 +283,7 @@ class Capture: def __init__(self, console: "Console") -> None: self._console = console - self._result: Optional[Optional[str]] = None + self._result: Optional[str] = None def __enter__(self) -> "Capture": self._console.begin_capture() @@ -404,7 +404,7 @@ class RenderGroup: def __init__(self, *renderables: "RenderableType", fit: bool = True) -> None: self._renderables = renderables self.fit = fit - self._render: Optional[Optional[List[RenderableType]]] = None + self._render: Optional[List[RenderableType]] = None @property def renderables(self) -> List["RenderableType"]: @@ -501,7 +501,7 @@ class RenderHook(ABC): """ -_windows_console_features: Optional[Optional["WindowsConsoleFeatures"]] = None +_windows_console_features: Optional["WindowsConsoleFeatures"] = None def get_windows_console_features() -> "WindowsConsoleFeatures": # pragma: no cover @@ -666,7 +666,7 @@ class Console: ) self._record_buffer: List[Segment] = [] self._render_hooks: List[RenderHook] = [] - self._live: Optional[Optional["Live"]] = None + self._live: Optional["Live"] = None self._is_alt_screen = False def __repr__(self) -> str: @@ -892,8 +892,8 @@ class Console: if self.is_dumb_terminal: return ConsoleDimensions(80, 25) - width: Optional[Optional[int]] = None - height: Optional[Optional[int]] = None + width: Optional[int] = None + height: Optional[int] = None if WINDOWS: # pragma: no cover width, height = shutil.get_terminal_size() else: @@ -1140,9 +1140,9 @@ class Console: def render_lines( self, renderable: RenderableType, - options: Optional[Optional[ConsoleOptions]] = None, + options: Optional[ConsoleOptions] = None, *, - style: Optional[Optional[Style]] = None, + style: Optional[Style] = None, pad: bool = True, new_lines: bool = False, ) -> List[List[Segment]]: @@ -1564,7 +1564,7 @@ class Console: *, width: Optional[int] = 100, extra_lines: int = 3, - theme: Optional[Optional[str]] = None, + theme: Optional[str] = None, word_wrap: bool = False, show_locals: bool = False, ) -> None: diff --git a/rich/live.py b/rich/live.py index 1a6777df..4d59d146 100644 --- a/rich/live.py +++ b/rich/live.py @@ -69,16 +69,16 @@ class Live(JupyterMixin, RenderHook): self._redirect_stdout = redirect_stdout self._redirect_stderr = redirect_stderr - self._restore_stdout: Optional[Optional[IO[str]]] = None - self._restore_stderr: Optional[Optional[IO[str]]] = None + self._restore_stdout: Optional[IO[str]] = None + self._restore_stderr: Optional[IO[str]] = None self._lock = RLock() - self.ipy_widget: Optional[Optional[Any]] = None + self.ipy_widget: Optional[Any] = None self.auto_refresh = auto_refresh self._started: bool = False self.transient = True if screen else transient - self._refresh_thread: Optional[Optional[_RefreshThread]] = None + self._refresh_thread: Optional[_RefreshThread] = None self.refresh_per_second = refresh_per_second self.vertical_overflow = vertical_overflow diff --git a/rich/logging.py b/rich/logging.py index e42c580c..2a7f91b1 100644 --- a/rich/logging.py +++ b/rich/logging.py @@ -68,9 +68,9 @@ class RichHandler(Handler): highlighter: Optional[Highlighter] = None, markup: bool = False, rich_tracebacks: bool = False, - tracebacks_width: Optional[Optional[int]] = None, + tracebacks_width: Optional[int] = None, tracebacks_extra_lines: int = 3, - tracebacks_theme: Optional[Optional[str]] = None, + tracebacks_theme: Optional[str] = None, tracebacks_word_wrap: bool = True, tracebacks_show_locals: bool = False, locals_max_length: int = 10, diff --git a/rich/markdown.py b/rich/markdown.py index 93e29ae0..763c21de 100644 --- a/rich/markdown.py +++ b/rich/markdown.py @@ -322,7 +322,7 @@ class ImageItem(TextElement): def __init__(self, destination: str, hyperlinks: bool) -> None: self.destination = destination self.hyperlinks = hyperlinks - self.link: Optional[Optional[str]] = None + self.link: Optional[str] = None super().__init__() def on_enter(self, context: "MarkdownContext") -> None: @@ -356,7 +356,7 @@ class MarkdownContext: self.style_stack: StyleStack = StyleStack(style) self.stack: Stack[MarkdownElement] = Stack() - self._syntax: Optional[Optional[Syntax]] = None + self._syntax: Optional[Syntax] = None if inline_code_lexer is not None: self._syntax = Syntax("", inline_code_lexer, theme=inline_code_theme) diff --git a/rich/padding.py b/rich/padding.py index 373eed96..da1eb3a8 100644 --- a/rich/padding.py +++ b/rich/padding.py @@ -103,7 +103,7 @@ class Padding(JupyterMixin): if self.right else [_Segment.line()] ) - blank_line: Optional[Optional[List[Segment]]] = None + blank_line: Optional[List[Segment]] = None if self.top: blank_line = [_Segment(f'{" " * width}\n', style)] yield from blank_line * self.top diff --git a/rich/panel.py b/rich/panel.py index f42d3fe7..0fa42d70 100644 --- a/rich/panel.py +++ b/rich/panel.py @@ -42,12 +42,12 @@ class Panel(JupyterMixin): *, title: Optional[TextType] = None, title_align: AlignMethod = "center", - safe_box: Optional[Optional[bool]] = None, + safe_box: Optional[bool] = None, expand: bool = True, style: StyleType = "none", border_style: StyleType = "none", - width: Optional[Optional[int]] = None, - height: Optional[Optional[int]] = None, + width: Optional[int] = None, + height: Optional[int] = None, padding: PaddingDimensions = (0, 1), highlight: bool = False, ) -> None: @@ -72,10 +72,10 @@ class Panel(JupyterMixin): *, title: Optional[TextType] = None, title_align: AlignMethod = "center", - safe_box: Optional[Optional[bool]] = None, + safe_box: Optional[bool] = None, style: StyleType = "none", border_style: StyleType = "none", - width: Optional[Optional[int]] = None, + width: Optional[int] = None, padding: PaddingDimensions = (0, 1), ): """An alternative constructor that sets expand=False.""" diff --git a/rich/pretty.py b/rich/pretty.py index c57e5dcb..1e2f532d 100644 --- a/rich/pretty.py +++ b/rich/pretty.py @@ -158,7 +158,7 @@ class Pretty(JupyterMixin): *, indent_size: int = 4, justify: Optional["JustifyMethod"] = None, - overflow: Optional[Optional["OverflowMethod"]] = None, + overflow: Optional["OverflowMethod"] = None, no_wrap: Optional[bool] = False, indent_guides: bool = False, max_length: Optional[int] = None, @@ -278,7 +278,7 @@ class Node: empty: str = "" last: bool = False is_tuple: bool = False - children: Optional[Optional[List["Node"]]] = None + children: Optional[List["Node"]] = None key_separator = ": " @property @@ -360,7 +360,7 @@ class _Line: """A line in repr output.""" is_root: bool = False - node: Optional[Optional[Node]] = None + node: Optional[Node] = None text: str = "" suffix: str = "" whitespace: str = "" diff --git a/rich/progress.py b/rich/progress.py index b1ebec5d..17ebfdda 100644 --- a/rich/progress.py +++ b/rich/progress.py @@ -83,9 +83,9 @@ class _TrackThread(Thread): def track( sequence: Union[Sequence[ProgressType], Iterable[ProgressType]], description="Working...", - total: Optional[Optional[float]] = None, + total: Optional[float] = None, auto_refresh=True, - console: Optional[Optional[Console]] = None, + console: Optional[Console] = None, transient: bool = False, get_time: Optional[Callable[[], float]] = None, refresh_per_second: float = 10, @@ -151,12 +151,12 @@ def track( class ProgressColumn(ABC): """Base class for a widget to use in progress display.""" - max_refresh: Optional[Optional[float]] = None + max_refresh: Optional[float] = None def __init__(self, table_column: Optional[Column] = None) -> None: self._table_column = table_column self._renderable_cache: Dict[TaskID, Tuple[float, RenderableType]] = {} - self._update_time: Optional[Optional[float]] = None + self._update_time: Optional[float] = None def get_table_column(self) -> Column: """Get a table column, used to build tasks table.""" @@ -455,7 +455,7 @@ class Task: _get_time: GetTimeCallable """Callable to get the current time.""" - finished_time: Optional[Optional[float]] = None + finished_time: Optional[float] = None """float: Time task was finished.""" visible: bool = True @@ -470,7 +470,7 @@ class Task: stop_time: Optional[float] = field(default=None, init=False, repr=False) """Optional[float]: Time this task was stopped, or None if not stopped.""" - finished_speed: Optional[Optional[float]] = None + finished_speed: Optional[float] = None """Optional[float]: The last speed for a finshed task.""" _progress: Deque[ProgressSample] = field( @@ -655,8 +655,8 @@ class Progress(JupyterMixin): def track( self, sequence: Union[Iterable[ProgressType], Sequence[ProgressType]], - total: Optional[Optional[float]] = None, - task_id: Optional[Optional[TaskID]] = None, + total: Optional[float] = None, + task_id: Optional[TaskID] = None, description="Working...", update_period: float = 0.1, ) -> Iterable[ProgressType]: @@ -734,7 +734,7 @@ class Progress(JupyterMixin): self, task_id: TaskID, *, - total: Optional[Optional[float]] = None, + total: Optional[float] = None, completed: Optional[float] = None, advance: Optional[float] = None, description: Optional[str] = None, @@ -794,10 +794,10 @@ class Progress(JupyterMixin): task_id: TaskID, *, start: bool = True, - total: Optional[Optional[float]] = None, + total: Optional[float] = None, completed: int = 0, - visible: Optional[Optional[bool]] = None, - description: Optional[Optional[str]] = None, + visible: Optional[bool] = None, + description: Optional[str] = None, **fields: Any, ) -> None: """Reset a task so completed is 0 and the clock is reset. diff --git a/rich/progress_bar.py b/rich/progress_bar.py index a579cfc8..1797b5f7 100644 --- a/rich/progress_bar.py +++ b/rich/progress_bar.py @@ -52,7 +52,7 @@ class ProgressBar(JupyterMixin): self.pulse_style = pulse_style self.animation_time = animation_time - self._pulse_segments: Optional[Optional[List[Segment]]] = None + self._pulse_segments: Optional[List[Segment]] = None def __repr__(self) -> str: return f"" diff --git a/rich/prompt.py b/rich/prompt.py index ac8ff7a2..8d986822 100644 --- a/rich/prompt.py +++ b/rich/prompt.py @@ -48,7 +48,7 @@ class PromptBase(Generic[PromptType]): ) prompt_suffix = ": " - choices: Optional[Optional[List[str]]] = None + choices: Optional[List[str]] = None def __init__( self, diff --git a/rich/segment.py b/rich/segment.py index 1705c9f5..e7f812b7 100644 --- a/rich/segment.py +++ b/rich/segment.py @@ -47,9 +47,9 @@ class Segment(NamedTuple): text: str = "" """Raw text.""" - style: Optional[Optional[Style]] = None + style: Optional[Style] = None """An optional style.""" - control: Optional[Optional[Sequence[ControlCode]]] = None + control: Optional[Sequence[ControlCode]] = None """Optional sequence of control codes.""" def __repr__(self) -> str: diff --git a/rich/spinner.py b/rich/spinner.py index cb8a69e8..b5e2bbf1 100644 --- a/rich/spinner.py +++ b/rich/spinner.py @@ -37,7 +37,7 @@ class Spinner: self.text = text self.frames = cast(List[str], spinner["frames"])[:] self.interval = cast(float, spinner["interval"]) - self.start_time: Optional[Optional[float]] = None + self.start_time: Optional[float] = None self.style = style self.speed = speed self.time = 0.0 diff --git a/rich/status.py b/rich/status.py index ced28ee0..78b81fc0 100644 --- a/rich/status.py +++ b/rich/status.py @@ -59,11 +59,11 @@ class Status(JupyterMixin): def update( self, - status: Optional[Optional[RenderableType]] = None, + status: Optional[RenderableType] = None, *, - spinner: Optional[Optional[str]] = None, - spinner_style: Optional[Optional[StyleType]] = None, - speed: Optional[Optional[float]] = None, + spinner: Optional[str] = None, + spinner_style: Optional[StyleType] = None, + speed: Optional[float] = None, ): """Update status. diff --git a/rich/style.py b/rich/style.py index ccef2ed9..0e2e91eb 100644 --- a/rich/style.py +++ b/rich/style.py @@ -110,8 +110,8 @@ class Style: overline: Optional[bool] = None, link: Optional[str] = None, ): - self._ansi: Optional[Optional[str]] = None - self._style_definition: Optional[Optional[str]] = None + self._ansi: Optional[str] = None + self._style_definition: Optional[str] = None def _make_color(color: Union[Color, str]) -> Color: return color if isinstance(color, Color) else Color.parse(color) @@ -444,10 +444,10 @@ class Style: "overline": "overline", "o": "overline", } - color: Optional[Optional[str]] = None - bgcolor: Optional[Optional[str]] = None + color: Optional[str] = None + bgcolor: Optional[str] = None attributes: Dict[str, Optional[bool]] = {} - link: Optional[Optional[str]] = None + link: Optional[str] = None words = iter(style_definition.split()) for original_word in words: @@ -625,7 +625,7 @@ class Style: ) return rendered - def test(self, text: Optional[Optional[str]] = None) -> None: + def test(self, text: Optional[str] = None) -> None: """Write text with style directly to terminal. This method is for testing purposes only. diff --git a/rich/syntax.py b/rich/syntax.py index be7b33f4..64b432b0 100644 --- a/rich/syntax.py +++ b/rich/syntax.py @@ -232,7 +232,7 @@ class Syntax(JupyterMixin): start_line: int = 1, line_range: Optional[Tuple[int, int]] = None, highlight_lines: Optional[Set[int]] = None, - code_width: Optional[Optional[int]] = None, + code_width: Optional[int] = None, tab_size: int = 4, word_wrap: bool = False, background_color: Optional[str] = None, @@ -267,7 +267,7 @@ class Syntax(JupyterMixin): line_range: Optional[Tuple[int, int]] = None, start_line: int = 1, highlight_lines: Optional[Set[int]] = None, - code_width: Optional[Optional[int]] = None, + code_width: Optional[int] = None, tab_size: int = 4, word_wrap: bool = False, background_color: Optional[str] = None, diff --git a/rich/table.py b/rich/table.py index c67cd79e..54e39c94 100644 --- a/rich/table.py +++ b/rich/table.py @@ -58,16 +58,16 @@ class Column: overflow: "OverflowMethod" = "ellipsis" """str: Overflow method.""" - width: Optional[Optional[int]] = None + width: Optional[int] = None """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width.""" - min_width: Optional[Optional[int]] = None + min_width: Optional[int] = None """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None.""" - max_width: Optional[Optional[int]] = None + max_width: Optional[int] = None """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None.""" - ratio: Optional[Optional[int]] = None + ratio: Optional[int] = None """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents.""" no_wrap: bool = False @@ -97,7 +97,7 @@ class Column: class Row: """Information regarding a row.""" - style: Optional[Optional[StyleType]] = None + style: Optional[StyleType] = None """Style to apply to row.""" end_section: bool = False @@ -156,7 +156,7 @@ class Table(JupyterMixin): width: Optional[int] = None, min_width: Optional[int] = None, box: Optional[box.Box] = box.HEAVY_HEAD, - safe_box: Optional[Optional[bool]] = None, + safe_box: Optional[bool] = None, padding: PaddingDimensions = (0, 1), collapse_padding: bool = False, pad_edge: bool = True, diff --git a/rich/tabulate.py b/rich/tabulate.py index 46c5fc35..5f7ed216 100644 --- a/rich/tabulate.py +++ b/rich/tabulate.py @@ -13,8 +13,8 @@ def tabulate_mapping( mapping: Mapping, title: Optional[str] = None, caption: Optional[str] = None, - title_justify: Optional[Optional[JustifyMethod]] = None, - caption_justify: Optional[Optional[JustifyMethod]] = None, + title_justify: Optional[JustifyMethod] = None, + caption_justify: Optional[JustifyMethod] = None, ) -> Table: """Generate a simple table from a mapping. diff --git a/rich/text.py b/rich/text.py index f2116719..3f54d06b 100644 --- a/rich/text.py +++ b/rich/text.py @@ -360,7 +360,7 @@ class Text(JupyterMixin): self, style: Union[str, Style], start: int = 0, - end: Optional[Optional[int]] = None, + end: Optional[int] = None, ) -> None: """Apply a style to the text, or a portion of the text. @@ -676,7 +676,7 @@ class Text(JupyterMixin): self, max_width: int, *, - overflow: Optional[Optional["OverflowMethod"]] = None, + overflow: Optional["OverflowMethod"] = None, pad: bool = False, ) -> None: """Truncate text if it is longer that a given width. diff --git a/rich/traceback.py b/rich/traceback.py index f623eec0..87849213 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -43,7 +43,7 @@ def install( console: Optional[Console] = None, width: Optional[int] = 100, extra_lines: int = 3, - theme: Optional[Optional[str]] = None, + theme: Optional[str] = None, word_wrap: bool = False, show_locals: bool = False, indent_guides: bool = True, @@ -104,7 +104,7 @@ def install( exc_tuple = ip._get_exc_info() # do not display trace on syntax error - tb: Optional[Optional[TracebackType]] = None if is_syntax else exc_tuple[2] + tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2] # determine correct tb_offset compiled = tb_data.get("running_compiled_code", False) @@ -161,7 +161,7 @@ class _SyntaxError: class Stack: exc_type: str exc_value: str - syntax_error: Optional[Optional[_SyntaxError]] = None + syntax_error: Optional[_SyntaxError] = None is_cause: bool = False frames: List[Frame] = field(default_factory=list) @@ -205,7 +205,7 @@ class Traceback: trace: Optional[Trace] = None, width: Optional[int] = 100, extra_lines: int = 3, - theme: Optional[Optional[str]] = None, + theme: Optional[str] = None, word_wrap: bool = False, show_locals: bool = False, indent_guides: bool = True, @@ -239,7 +239,7 @@ class Traceback: traceback: Optional[TracebackType], width: Optional[int] = 100, extra_lines: int = 3, - theme: Optional[Optional[str]] = None, + theme: Optional[str] = None, word_wrap: bool = False, show_locals: bool = False, indent_guides: bool = True, diff --git a/rich/tree.py b/rich/tree.py index a7be2fe5..57027ca6 100644 --- a/rich/tree.py +++ b/rich/tree.py @@ -208,7 +208,7 @@ if __name__ == "__main__": # pragma: no cover code = """\ class Segment(NamedTuple): text: str = "" - style: Optional[Optional[Style]] = None + style: Optional[Style] = None is_control: bool = False """ syntax = Syntax(code, "python", theme="monokai", line_numbers=True) diff --git a/tests/test_console.py b/tests/test_console.py index 5921d8cd..1b549357 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -439,7 +439,7 @@ def test_bell() -> None: def test_pager() -> None: console = Console(_environ={}) - pager_content: Optional[Optional[str]] = None + pager_content: Optional[str] = None def mock_pager(content: str) -> None: nonlocal pager_content diff --git a/tests/test_ratio.py b/tests/test_ratio.py index ffdeced9..047ad767 100644 --- a/tests/test_ratio.py +++ b/tests/test_ratio.py @@ -5,7 +5,7 @@ from rich._ratio import ratio_reduce, ratio_resolve class Edge(NamedTuple): - size: Optional[Optional[int]] = None + size: Optional[int] = None ratio: int = 1 minimum_size: int = 1