Abstract UI components, add UI ordering/sorting (#13)

* Abstract UI components

- Created an ABC for our UI components
- Extract components to its own module
- Add concept of sorting including option and defaults
- Update test for _safe_opt when attr is None
- Change import structure to avoid circular imports
pull/15/head
thomas 2023-03-21 20:31:32 +00:00 committed by GitHub
parent 2d3343c911
commit 18d4199854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 329 additions and 226 deletions

View File

@ -0,0 +1,79 @@
from abc import ABC
from abc import abstractmethod
from functools import partial
import gradio as gr
import aspect_ratio_helper._constants as _constants
import aspect_ratio_helper._settings as _settings
import aspect_ratio_helper._util as _util
class ArhUIComponent(ABC):
def __init__(self, script):
self.script = script
@abstractmethod
def render(self): ...
@staticmethod
@abstractmethod
def should_show() -> bool: ...
class MaxDimensionScaler(ArhUIComponent):
def render(self):
inputs = outputs = [self.script.wc, self.script.hc]
with gr.Row():
max_dimension = gr.inputs.Slider(
minimum=_constants.MIN_DIMENSION,
maximum=_constants.MAX_DIMENSION,
step=1,
default=_settings.safe_opt(
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
),
label='Maximum width or height (whichever is higher)',
)
gr.Button(value='Scale to maximum width or height').click(
fn=_util.scale_dimensions_to,
inputs=[*inputs, max_dimension],
outputs=outputs,
)
@staticmethod
def should_show() -> bool:
return _settings.safe_opt(_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY)
class PredefinedPercentageButtons(ArhUIComponent):
def render(self):
inputs = outputs = [self.script.wc, self.script.hc]
with gr.Column(variant='panel'), gr.Row(variant='compact'):
pps = _settings.safe_opt(_constants.ARH_PREDEFINED_PERCENTAGES_KEY)
percentages = [abs(int(x)) for x in pps.split(',')]
for percentage in percentages:
display = self.display_func(percentage)
gr.Button(value=display).click(
fn=partial(
_util.scale_by_percentage,
pct=percentage / 100,
),
inputs=inputs,
outputs=outputs,
)
@staticmethod
def should_show() -> bool:
return _settings.safe_opt(
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
)
@property
def display_func(self) -> callable:
return _settings.PREDEFINED_PERCENTAGES_DISPLAY_MAP.get(
_settings.safe_opt(
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
),
)

View File

@ -1,23 +1,15 @@
_EXTENSION_NAME = 'Aspect Ratio Helper'
EXTENSION_NAME = 'Aspect Ratio Helper'
_MAX_DIMENSION = 2048
_MIN_DIMENSION = 64
MAX_DIMENSION = 2048
MIN_DIMENSION = 64
_ARH_EXPAND_BY_DEFAULT_KEY = 'arh_expand_by_default'
_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_show_max_width_or_height'
_ARH_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_max_width_or_height'
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY = 'arh_show_predefined_percentages'
_ARH_PREDEFINED_PERCENTAGES_KEY = 'arh_predefined_percentages'
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY \
ARH_EXPAND_BY_DEFAULT_KEY = 'arh_expand_by_default'
ARH_UI_COMPONENT_ORDER_KEY = 'arh_ui_component_order_key'
ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_show_max_width_or_height'
ARH_MAX_WIDTH_OR_HEIGHT_KEY = 'arh_max_width_or_height'
ARH_SHOW_PREDEFINED_PERCENTAGES_KEY = 'arh_show_predefined_percentages'
ARH_PREDEFINED_PERCENTAGES_KEY = 'arh_predefined_percentages'
ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY \
= 'arh_predefined_percentages_display_key'
_DEFAULT_PERCENTAGES_DISPLAY_KEY \
DEFAULT_PERCENTAGES_DISPLAY_KEY \
= 'Incremental/decremental percentage (-50%, +50%)'
_OPT_KEY_TO_DEFAULT_MAP = {
_ARH_EXPAND_BY_DEFAULT_KEY: False,
_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY: True,
_ARH_MAX_WIDTH_OR_HEIGHT_KEY: _MAX_DIMENSION / 2,
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY: True,
_ARH_PREDEFINED_PERCENTAGES_KEY: '25, 50, 75, 125, 150, 175, 200',
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY: _DEFAULT_PERCENTAGES_DISPLAY_KEY,
}

View File

@ -1,95 +1,163 @@
import gradio as gr
from modules import shared
from aspect_ratio_helper._constants import _ARH_EXPAND_BY_DEFAULT_KEY
from aspect_ratio_helper._constants import _ARH_MAX_WIDTH_OR_HEIGHT_KEY
from aspect_ratio_helper._constants import \
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
from aspect_ratio_helper._constants import _ARH_PREDEFINED_PERCENTAGES_KEY
from aspect_ratio_helper._constants import _ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY
from aspect_ratio_helper._constants import _ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
from aspect_ratio_helper._constants import _DEFAULT_PERCENTAGES_DISPLAY_KEY
from aspect_ratio_helper._constants import _EXTENSION_NAME
from aspect_ratio_helper._constants import _MAX_DIMENSION
from aspect_ratio_helper._constants import _MIN_DIMENSION
from aspect_ratio_helper._constants import _OPT_KEY_TO_DEFAULT_MAP
from aspect_ratio_helper._util import _display_minus_and_plus
from aspect_ratio_helper._util import _display_multiplication
from aspect_ratio_helper._util import _display_raw_percentage
from aspect_ratio_helper._util import _safe_opt_util
import aspect_ratio_helper._components as _components
import aspect_ratio_helper._constants as _constants
import aspect_ratio_helper._util as _util
_PREDEFINED_PERCENTAGES_DISPLAY_MAP = {
_DEFAULT_PERCENTAGES_DISPLAY_KEY: _display_minus_and_plus,
'Raw percentage (50%, 150%)': _display_raw_percentage,
'Multiplication (x0.5, x1.5)': _display_multiplication,
PREDEFINED_PERCENTAGES_DISPLAY_MAP = {
_constants.DEFAULT_PERCENTAGES_DISPLAY_KEY: _util.display_minus_and_plus,
'Raw percentage (50%, 150%)': _util.display_raw_percentage,
'Multiplication (x0.5, x1.5)': _util.display_multiplication,
}
ELEMENTS = (
_components.MaxDimensionScaler,
_components.PredefinedPercentageButtons,
)
DEFAULT_UI_COMPONENT_ORDER_KEY = ', '.join(
[e.__name__ for e in ELEMENTS], # noqa
)
OPT_KEY_TO_DEFAULT_MAP = {
_constants.ARH_EXPAND_BY_DEFAULT_KEY: False,
_constants.ARH_UI_COMPONENT_ORDER_KEY:
DEFAULT_UI_COMPONENT_ORDER_KEY,
_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY: True,
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY:
_constants.MAX_DIMENSION / 2,
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY: True,
_constants.ARH_PREDEFINED_PERCENTAGES_KEY:
'25, 50, 75, 125, 150, 175, 200',
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY:
_constants.DEFAULT_PERCENTAGES_DISPLAY_KEY,
}
def _safe_opt(key):
return _safe_opt_util(shared.opts, key)
def safe_opt(key):
return _util.safe_opt_util(shared.opts, key, OPT_KEY_TO_DEFAULT_MAP)
def sort_elements_by_keys(
elements: list[_components.ArhUIComponent],
) -> list[_components.ArhUIComponent]:
ordered_component_keys = safe_opt(
_constants.ARH_UI_COMPONENT_ORDER_KEY,
).split(',')
try:
component_key_to_order_dict = {
key: order for order, key in enumerate(
[k.strip() for k in ordered_component_keys],
)
}
return sorted(
elements,
key=lambda c: component_key_to_order_dict.get(
c.__class__.__name__,
),
)
except ValueError:
print(
f'{_constants.EXTENSION_NAME} UI component order is erroneous. '
f'Defaulting to regular order, to fix this, please use'
f'the intended syntax for the setting, i.e '
f'"{DEFAULT_UI_COMPONENT_ORDER_KEY}"',
)
return elements
def on_ui_settings():
section = 'aspect_ratio_helper', _EXTENSION_NAME
section = 'aspect_ratio_helper', _constants.EXTENSION_NAME
shared.opts.add_option(
key=_ARH_EXPAND_BY_DEFAULT_KEY,
key=_constants.ARH_EXPAND_BY_DEFAULT_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_EXPAND_BY_DEFAULT_KEY],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_EXPAND_BY_DEFAULT_KEY,
),
label='Expand by default',
section=section,
),
)
shared.opts.add_option(
key=_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
key=_constants.ARH_UI_COMPONENT_ORDER_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_UI_COMPONENT_ORDER_KEY,
),
label='UI Component order',
# todo: temporary drop-down to avoid user error!
# we only have two components so 2 possible orders.
# however, this will exponentially grow with more components.
# if that happens, permutations is impractical, revisit then.
component=gr.Dropdown,
component_args=lambda: {
'choices': (
DEFAULT_UI_COMPONENT_ORDER_KEY,
', '.join(
DEFAULT_UI_COMPONENT_ORDER_KEY.split(',')[::-1],
),
),
},
section=section,
),
)
shared.opts.add_option(
key=_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
info=shared.OptionInfo(
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY,
),
label='Show maximum width or height button',
section=section,
),
)
shared.opts.add_option(
key=_ARH_MAX_WIDTH_OR_HEIGHT_KEY,
key=_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_MAX_WIDTH_OR_HEIGHT_KEY],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_MAX_WIDTH_OR_HEIGHT_KEY,
),
label='Maximum width or height default',
component=gr.Slider,
component_args={
'minimum': _MIN_DIMENSION,
'maximum': _MAX_DIMENSION,
'minimum': _constants.MIN_DIMENSION,
'maximum': _constants.MAX_DIMENSION,
'step': 1,
},
section=section,
),
)
shared.opts.add_option(
key=_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
key=_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[
_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_SHOW_PREDEFINED_PERCENTAGES_KEY,
),
label='Show predefined percentage buttons',
section=section,
),
)
shared.opts.add_option(
key=_ARH_PREDEFINED_PERCENTAGES_KEY,
key=_constants.ARH_PREDEFINED_PERCENTAGES_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[_ARH_PREDEFINED_PERCENTAGES_KEY],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_PREDEFINED_PERCENTAGES_KEY,
),
label='Predefined percentage buttons, applied to dimensions (75, '
'125, 150)',
section=section,
),
)
shared.opts.add_option(
key=_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
key=_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
info=shared.OptionInfo(
default=_OPT_KEY_TO_DEFAULT_MAP[
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
],
default=OPT_KEY_TO_DEFAULT_MAP.get(
_constants.ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY,
),
label='Predefined percentage display format',
component=gr.Dropdown,
component_args=lambda: {
'choices': tuple(_PREDEFINED_PERCENTAGES_DISPLAY_MAP.keys()),
'choices': tuple(PREDEFINED_PERCENTAGES_DISPLAY_MAP.keys()),
},
section=section,
),

View File

@ -1,64 +1,66 @@
import contextlib
from aspect_ratio_helper._constants import _MAX_DIMENSION
from aspect_ratio_helper._constants import _MIN_DIMENSION
from aspect_ratio_helper._constants import _OPT_KEY_TO_DEFAULT_MAP
import aspect_ratio_helper._constants as _const
def _safe_opt_util(shared_opts, key):
def safe_opt_util(shared_opts, key, default_key_map: dict[str, object]):
# attempt to retrieve key from shared options
with contextlib.suppress(AttributeError):
return shared_opts.__getattr__(key)
value = shared_opts.__getattr__(key)
if value is not None:
return value
# attempt to retrieve default, and last resort the constant default
return shared_opts.get_default(key) or _OPT_KEY_TO_DEFAULT_MAP.get(key)
return shared_opts.get_default(key) or default_key_map.get(key)
def _display_multiplication(num):
def display_multiplication(num) -> str:
return f'x{round(num / 100, 3)}'
def _display_raw_percentage(num):
def display_raw_percentage(num) -> str:
return f'{num}%'
def _display_minus_and_plus(num):
def display_minus_and_plus(num) -> str:
num -= 100
if num > 0:
return f'+{num}%'
return f'{num}%'
def _scale_by_percentage(width, height, pct):
def scale_by_percentage(width, height, pct) -> tuple[int, int]:
aspect_ratio = float(width) / float(height)
step = (pct - 1.0)
new_width = int(round(width * (1.0 + step)))
new_height = int(round(new_width / aspect_ratio))
return _clamp_to_boundaries(new_width, new_height, aspect_ratio)
return clamp_to_boundaries(new_width, new_height, aspect_ratio)
def _scale_dimensions_to_max_dimension(width, height, max_dim):
def scale_dimensions_to(
width, height, max_dim,
) -> tuple[int, int]:
aspect_ratio = float(width) / float(height)
if width > height:
new_width = max_dim
new_height = max(int(round(max_dim / aspect_ratio)), 1)
new_height = int(round(max_dim / aspect_ratio))
else:
new_height = max_dim
new_width = max(int(round(max_dim * aspect_ratio)), 1)
return _clamp_to_boundaries(new_width, new_height, aspect_ratio)
new_width = int(round(max_dim * aspect_ratio))
return clamp_to_boundaries(new_width, new_height, aspect_ratio)
def _clamp_to_boundaries(width, height, aspect_ratio):
if width > _MAX_DIMENSION:
width = _MAX_DIMENSION
def clamp_to_boundaries(width, height, aspect_ratio) -> tuple[int, int]:
if width > _const.MAX_DIMENSION:
width = _const.MAX_DIMENSION
height = int(round(width / aspect_ratio))
if height > _MAX_DIMENSION:
height = _MAX_DIMENSION
if height > _const.MAX_DIMENSION:
height = _const.MAX_DIMENSION
width = int(round(height * aspect_ratio))
if width < _MIN_DIMENSION:
width = _MIN_DIMENSION
if width < _const.MIN_DIMENSION:
width = _const.MIN_DIMENSION
height = int(round(width / aspect_ratio))
if height < _MIN_DIMENSION:
height = _MIN_DIMENSION
if height < _const.MIN_DIMENSION:
height = _const.MIN_DIMENSION
width = int(round(height * aspect_ratio))
return width, height

View File

@ -1,96 +1,63 @@
from functools import partial
import gradio as gr
from modules import script_callbacks
from modules import scripts
from aspect_ratio_helper._constants import _ARH_EXPAND_BY_DEFAULT_KEY
from aspect_ratio_helper._constants import _ARH_MAX_WIDTH_OR_HEIGHT_KEY
from aspect_ratio_helper._constants import \
_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY
from aspect_ratio_helper._constants import _ARH_PREDEFINED_PERCENTAGES_KEY
from aspect_ratio_helper._constants import _ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY
from aspect_ratio_helper._constants import _ARH_SHOW_PREDEFINED_PERCENTAGES_KEY
from aspect_ratio_helper._constants import _EXTENSION_NAME
from aspect_ratio_helper._constants import _MAX_DIMENSION
from aspect_ratio_helper._constants import _MIN_DIMENSION
from aspect_ratio_helper._settings import _PREDEFINED_PERCENTAGES_DISPLAY_MAP
from aspect_ratio_helper._settings import _safe_opt
from aspect_ratio_helper._settings import on_ui_settings
from aspect_ratio_helper._util import _scale_by_percentage
from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
import aspect_ratio_helper._constants as _constants
import aspect_ratio_helper._settings as _settings
class AspectRatioStepScript(scripts.Script):
def title(self):
return _EXTENSION_NAME
def __init__(self):
self.t2i_w = None
self.t2i_h = None
self.i2i_w = None
self.i2i_h = None
self.wc = None
self.hc = None
def show(self, is_img2img):
def title(self) -> str:
return _constants.EXTENSION_NAME
def show(self, _) -> scripts.AlwaysVisible:
return scripts.AlwaysVisible
def ui(self, is_img2img):
if not any([
_safe_opt(_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY),
_safe_opt(_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY),
]):
return # return early as no 'show' options enabled
with (
gr.Group(),
gr.Accordion(
_EXTENSION_NAME,
open=_safe_opt(_ARH_EXPAND_BY_DEFAULT_KEY),
),
if is_img2img:
self.wc, self.hc = self.i2i_w, self.i2i_h
else:
self.wc, self.hc = self.t2i_h, self.t2i_w
elements = _settings.sort_elements_by_keys(
[element(self) for element in _settings.ELEMENTS],
)
if not any(element.should_show() for element in elements):
return # no elements should render, so just return.
start_expanded: bool = _settings.safe_opt(
_constants.ARH_EXPAND_BY_DEFAULT_KEY,
)
with gr.Group(), gr.Accordion(
_constants.EXTENSION_NAME,
open=start_expanded,
):
if is_img2img:
inputs = outputs = [self.i2i_w, self.i2i_h]
else:
inputs = outputs = [self.t2i_w, self.t2i_h]
for element in elements:
if element.should_show():
element.render()
if _safe_opt(_ARH_SHOW_MAX_WIDTH_OR_HEIGHT_KEY):
with gr.Row():
max_dimension = gr.inputs.Slider(
minimum=_MIN_DIMENSION,
maximum=_MAX_DIMENSION,
step=1,
default=_safe_opt(_ARH_MAX_WIDTH_OR_HEIGHT_KEY),
label='Maximum width or height (whichever is higher)',
)
gr.Button(value='Scale to maximum width or height').click(
fn=_scale_dimensions_to_max_dimension,
inputs=[*inputs, max_dimension],
outputs=outputs,
)
if _safe_opt(_ARH_SHOW_PREDEFINED_PERCENTAGES_KEY):
display_func = _PREDEFINED_PERCENTAGES_DISPLAY_MAP.get(
_safe_opt(_ARH_PREDEFINED_PERCENTAGES_DISPLAY_KEY),
)
with gr.Column(variant='panel'), gr.Row(variant='compact'):
pps = _safe_opt(_ARH_PREDEFINED_PERCENTAGES_KEY)
percentages = [
abs(int(x)) for x in pps.split(',')
]
for percentage in percentages:
gr.Button(value=display_func(percentage)).click(
fn=partial(
_scale_by_percentage, pct=percentage / 100,
),
inputs=inputs,
outputs=outputs,
)
def after_component(self, component, **kwargs):
def after_component(self, component: gr.components.Component, **kwargs):
element_id = kwargs.get('elem_id')
if element_id == 'txt2img_width':
self.t2i_w = component
elif element_id == 'txt2img_height':
self.t2i_h = component
elif element_id == 'img2img_width':
self.i2i_w = component
elif element_id == 'img2img_height':
self.i2i_h = component
if isinstance(component, gr.components.Slider):
if element_id == 'txt2img_width':
self.t2i_w: gr.components.Slider = component
elif element_id == 'txt2img_height':
self.t2i_h: gr.components.Slider = component
elif element_id == 'img2img_width':
self.i2i_w: gr.components.Slider = component
elif element_id == 'img2img_height':
self.i2i_h: gr.components.Slider = component
script_callbacks.on_ui_settings(on_ui_settings)
script_callbacks.on_ui_settings(_settings.on_ui_settings)

View File

@ -1,13 +1,7 @@
import pytest
from aspect_ratio_helper._util import _display_minus_and_plus
from aspect_ratio_helper._util import _display_multiplication
from aspect_ratio_helper._util import _display_raw_percentage
from aspect_ratio_helper._util import _MAX_DIMENSION
from aspect_ratio_helper._util import _MIN_DIMENSION
from aspect_ratio_helper._util import _safe_opt_util
from aspect_ratio_helper._util import _scale_by_percentage
from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
import aspect_ratio_helper._constants as _constants
import aspect_ratio_helper._util as _util
@pytest.mark.parametrize(
@ -20,7 +14,7 @@ from aspect_ratio_helper._util import _scale_dimensions_to_max_dimension
],
)
def test_display_multiplication(num, expected):
assert _display_multiplication(num) == expected
assert _util.display_multiplication(num) == expected
@pytest.mark.parametrize(
@ -34,7 +28,7 @@ def test_display_multiplication(num, expected):
],
)
def test_display_raw_percentage(num, expected):
assert _display_raw_percentage(num) == expected
assert _util.display_raw_percentage(num) == expected
@pytest.mark.parametrize(
@ -48,7 +42,7 @@ def test_display_raw_percentage(num, expected):
],
)
def test_display_minus_and_plus(num, expected_output):
assert _display_minus_and_plus(num) == expected_output
assert _util.display_minus_and_plus(num) == expected_output
@pytest.mark.parametrize(
@ -60,25 +54,25 @@ def test_display_minus_and_plus(num, expected_output):
pytest.param(100, 200, 0.9, (90, 180), id='10_percent_scale_down'),
pytest.param(100, 200, 0.0, (64, 128), id='scale_full_down'),
pytest.param(
_MIN_DIMENSION - 1,
_MIN_DIMENSION - 1,
_constants.MIN_DIMENSION - 1,
_constants.MIN_DIMENSION - 1,
0.5,
(_MIN_DIMENSION, _MIN_DIMENSION),
id='scale_below_min_dimension',
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
id='scale_belowMIN_DIMENSION',
),
pytest.param(
_MAX_DIMENSION + 1,
_MAX_DIMENSION + 1,
_constants.MAX_DIMENSION + 1,
_constants.MAX_DIMENSION + 1,
2.0,
(_MAX_DIMENSION, _MAX_DIMENSION),
id='scale_above_max_dimension',
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
id='scale_aboveMAX_DIMENSION',
),
],
)
def test_scale_by_percentage(
width, height, pct, expected,
):
assert _scale_by_percentage(
assert _util.scale_by_percentage(
width, height, pct,
) == expected
@ -88,66 +82,70 @@ def test_scale_by_percentage(
[
pytest.param(
100, 200, 400, (200, 400),
id='scale_up_to_max_dimension_horizontally',
id='scale_up_toMAX_DIMENSION_horizontally',
),
pytest.param(
200, 100, 400, (400, 200),
id='scale_up_to_max_dimension_vertically',
id='scale_up_toMAX_DIMENSION_vertically',
),
pytest.param(
400, 64, 400, (400, 64),
id='no_scale_up_needed_with_max_dimension_width',
id='no_scale_up_needed_withMAX_DIMENSION_width',
),
pytest.param(
64, 400, 400, (64, 400),
id='no_scale_up_needed_with_max_dimension_height',
id='no_scale_up_needed_withMAX_DIMENSION_height',
),
pytest.param(
_MIN_DIMENSION, _MIN_DIMENSION, _MAX_DIMENSION,
(_MAX_DIMENSION, _MAX_DIMENSION),
_constants.MIN_DIMENSION,
_constants.MIN_DIMENSION,
_constants.MAX_DIMENSION,
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
id='scale_from_min_to_max',
),
pytest.param(
_MAX_DIMENSION, _MAX_DIMENSION, _MIN_DIMENSION,
(_MIN_DIMENSION, _MIN_DIMENSION),
_constants.MAX_DIMENSION,
_constants.MAX_DIMENSION,
_constants.MIN_DIMENSION,
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
id='scale_from_max_to_min',
),
pytest.param(
_MIN_DIMENSION, 32, _MIN_DIMENSION,
(128, _MIN_DIMENSION),
_constants.MIN_DIMENSION, 32, _constants.MIN_DIMENSION,
(128, _constants.MIN_DIMENSION),
id='scale_below_min_height_dimension_clamps_retains_ar',
),
pytest.param(
32, _MIN_DIMENSION, _MIN_DIMENSION,
(_MIN_DIMENSION, 128),
32, _constants.MIN_DIMENSION, _constants.MIN_DIMENSION,
(_constants.MIN_DIMENSION, 128),
id='scale_below_min_width_dimension_clamps_retains_ar',
),
pytest.param(
_MAX_DIMENSION, 4096, _MAX_DIMENSION,
(1024, _MAX_DIMENSION),
_constants.MAX_DIMENSION, 4096, _constants.MAX_DIMENSION,
(1024, _constants.MAX_DIMENSION),
id='scale_above_max_height_dimension_clamps_retains_ar',
),
pytest.param(
4096, _MAX_DIMENSION, _MAX_DIMENSION,
(_MAX_DIMENSION, 1024),
4096, _constants.MAX_DIMENSION, _constants.MAX_DIMENSION,
(_constants.MAX_DIMENSION, 1024),
id='scale_above_max_width_dimension_clamps_retains_ar',
),
pytest.param(
64, 64, _MIN_DIMENSION - 1,
(_MIN_DIMENSION, _MIN_DIMENSION),
id='scale_dimension_below_min_dimension_clamps_retains_ar',
64, 64, _constants.MIN_DIMENSION - 1,
(_constants.MIN_DIMENSION, _constants.MIN_DIMENSION),
id='scale_dimension_belowMIN_DIMENSION_clamps_retains_ar',
),
pytest.param(
64, 64, _MAX_DIMENSION + 1,
(_MAX_DIMENSION, _MAX_DIMENSION),
id='scale_dimension_above_max_dimension_clamps_retains_ar',
64, 64, _constants.MAX_DIMENSION + 1,
(_constants.MAX_DIMENSION, _constants.MAX_DIMENSION),
id='scale_dimension_aboveMAX_DIMENSION_clamps_retains_ar',
),
],
)
def test_scale_dimensions_to_max_dimension(
def test_scale_dimensions_to(
width, height, max_dim, expected,
):
assert _scale_dimensions_to_max_dimension(
assert _util.scale_dimensions_to(
width, height, max_dim,
) == expected
@ -169,36 +167,33 @@ class SharedOpts:
def test_safe_opt_util():
shared_opts = SharedOpts(options={'key': 'value'})
assert _safe_opt_util(shared_opts, 'key') == 'value'
assert _util.safe_opt_util(shared_opts, 'key', {}) == 'value'
def test_safe_opt_util_default_a(monkeypatch):
monkeypatch.setattr(
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
{'key': 'default_b'},
)
def test_safe_opt_util_none():
shared_opts = SharedOpts(options={'key': None}, defaults={'key': 'value'})
assert _util.safe_opt_util(shared_opts, 'key', {}) == 'value'
def testsafe_opt_util_default_a():
shared_opts = SharedOpts(
defaults={'key': 'default_a'},
)
assert _safe_opt_util(shared_opts, 'key') == 'default_a'
assert _util.safe_opt_util(
shared_opts, 'key', {'key': 'default_b'},
) == 'default_a'
def test_safe_opt_util_default_b(monkeypatch):
monkeypatch.setattr(
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
{'key': 'default_b'},
)
def test_safe_opt_util_default_b():
shared_opts = SharedOpts(defaults={'key': None})
assert _safe_opt_util(shared_opts, 'key') == 'default_b'
assert _util.safe_opt_util(
shared_opts, 'key', {'key': 'default_b'},
) == 'default_b'
@pytest.mark.parametrize(
'options', ({'key': None}, {}),
)
def test_safe_opt_safe_return_no_defaults_b(monkeypatch, options):
monkeypatch.setattr(
'aspect_ratio_helper._util._OPT_KEY_TO_DEFAULT_MAP',
{},
)
def test_safe_opt_safe_return_no_defaults_b(options):
shared_opts = SharedOpts(options=options)
assert _safe_opt_util(shared_opts, 'unknown_key') is None
assert _util.safe_opt_util(shared_opts, 'unknown_key', {}) is None