Skip to content

Split widget and introduce base enums #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 4, 2023

Conversation

penguinolog
Copy link
Collaborator

@penguinolog penguinolog commented Aug 17, 2023

Split base widget (widget module), container widgets (container module) and decoration widgets (decoration and graphics modules) into several modules under subpackage widget.

Introduce string-compatible enums for sizing, align, valign, wrap mode and base width-height settings.
Enforce automating imports sorting and code formatting + static validation for introduced changes.

Checklist
  • I've ensured that similar functionality has not already been implemented
  • I've ensured that similar functionality has not earlier been proposed and declined
  • I've branched off the master or python-dual-support branch
  • I've merged fresh upstream into my branch recently
  • I've ran tox successfully in local environment

* Backward compatible (subclass `str`)
* Auto-format and static check for widget
Aleksei Stepanov added 9 commits August 17, 2023 09:11
* Backward compatible (subclass `str`)
* Use enums
* Enforce code style and static checking
* Use backward compatible exports in package
* import to old modules for backward compatibility
* split constants from widget module and extend WHSettings
  (CLIP = WrapMode.CLIP, FLOW = Sizing.FLOW)
* proper normalize_*, simplify_*
  (check for None, partial handle `RELATIVE`)
* use backward compatibility imports in module
* use backward compatibility imports in module
* leave python logo widget untouched
@coveralls
Copy link

coveralls commented Aug 17, 2023

Coverage Status

coverage: 73.544% (+0.09%) from 73.458% when pulling 9e1a128 on penguinolog:split_widget_enums into a8c0d71 on urwid:master.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically after enums introduction and move of sizing helpers it's not constants, but I have no idea about better naming

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was lazy and not splitted this module: it's < 1000 LOC's and can be splitted later if will be too complex

urwid/listbox.py Outdated
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

automatically re-formatted after imports change (no manual work)

@@ -25,6 +26,7 @@ def test_connect(self):
edit.set_edit_text('another text')
handler.assert_not_called()

@unittest.skipIf(sys.implementation.name == "pypy", "WeakRef works differently on PyPy")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is expecially unstable on local PyPy run.
Also manual experiments shown, that everything garbage collector related work can work differently on PyPy

urwid.widget,
urwid.wimp,
urwid.decoration,
urwid.widget.attr_map,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doctests need explicit paths :(

@penguinolog
Copy link
Collaborator Author

Validated compatibility against downstream packages: khal and zulip-terminal

VAlign,
WHSettings,
WrapMode,
normalize_align,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

normalize_* / simplify_* are useful in downstream code, but was never exported "public"

from .widget_decoration import WidgetDecoration, WidgetDisable, WidgetPlaceholder
from .wimp import Button, CheckBox, CheckBoxError, RadioButton, SelectableIcon

__all__ = (
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__all__ needed due to backwards-compatibility constants definition (as it's not used locally, it's defined as "public" explicit)

@penguinolog penguinolog requested review from wardi and removed request for ulidtko August 17, 2023 11:13
@penguinolog penguinolog merged commit c4fd30f into urwid:master Sep 4, 2023
@penguinolog penguinolog deleted the split_widget_enums branch September 4, 2023 07:26
@JC3
Copy link

JC3 commented Sep 21, 2023

"clip" no longer works for the width parameter to Padding because of this commit (specifically, because of the conversion to enums).

  File "/Users/cipriani_pro2023/pyenv-brixel-newprotobuf/lib/python3.9/site-packages/urwid/widget/padding.py", line 137, in __init__
    self._width_type, self._width_amount = normalize_width(width, PaddingError)
  File "/Users/cipriani_pro2023/pyenv-brixel-newprotobuf/lib/python3.9/site-packages/urwid/widget/constants.py", line 269, in normalize_width
    raise err(
urwid.widget.padding.PaddingError: width value 'clip' is not one offixed number of columns, 'pack', ('relative', percentage of total width), 'clip'

The root cause is that WHSettings.CLIP is assigned a value of WrapMode.CLIP, which causes the in statement at https://github.com/penguinolog/urwid/blob/9e1a128bf80805ee84d8cb1af5b5b5017817240d/urwid/widget/constants.py#L259 to always evaluate to False for "clip".

Note that the following example program will print False:

import enum

class OtherTest(str, enum.Enum):
    CLIP = "clip"
    
class Test(str, enum.Enum):
    PACK = "pack"
    CLIP = OtherTest.CLIP
    
print("clip" in (Test.PACK, Test.CLIP))

The fix is to assign WHSettings.CLIP to the actual string value "clip". The same should probably also be done for WHSettings.FLOW.

The consequence at the moment is that release 2.2.0 can't be used in programs that use "clip" for Padding.

@ethanfurman
Copy link

ethanfurman commented Sep 22, 2023

Using the StrEnum in Python 3.12 does function as expected. [Edit: but don't do it this way -- see below.]

@penguinolog
Copy link
Collaborator Author

On python 3.10 synthetic tests it also worked, but release is for python 3.7+ - need to fix now and wait for old python releases EOL.

@ethanfurman
Copy link

Further explanation: When a type is mixed in to an enum, that type is used to generate the .value, so the value of Test.CLIP was generated by str(OtherTest.CLIP) (which is "OtherTest.CLIP"). In Python 3.12 StrEnum was added, and IntEnum and IntFlag modified, so that str(enum_member) was the same as str(enum_member.value) precisely to avoid the problem of constants converted to enums no longer working correctly you are seeing now.

Having said that, using one enum member as the value of another enum member is not intended, and can easily cause confusion:

import enum

class OtherTest(enum.StrEnum):
    CLIP = "clip"

class Test(enum.StrEnum):
    PACK = "pack"
    CLIP = OtherTest.CLIP

"clip" in (Test.PACK, Test.CLIP))
# True
Test.CLIP == OtherTest.CLIP
# True
Test.CLIP is OtherTest.CLIP  # given the above definition, one could expect this to be True
# False

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants