mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
calculate message namespace from __qualname__ when not specified (#3940)
* use __qualname__ for the default message namespace * improve tests * update changelog * better, more backwards compatible splitting * Fix syntax * Fix CHANGELOG --------- Co-authored-by: Darren Burns <darrenburns@users.noreply.github.com> Co-authored-by: Darren Burns <darrenb900@gmail.com>
This commit is contained in:
committed by
GitHub
parent
433d78f270
commit
c9bb137c0a
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Fixed `Tree` and `DirectoryTree` horizontal scrolling off-by-2 https://github.com/Textualize/textual/pull/4744
|
||||
- Fixed text-opacity in component styles https://github.com/Textualize/textual/pull/4747
|
||||
- Ensure `Tree.select_node` sends `NodeSelected` message https://github.com/Textualize/textual/pull/4753
|
||||
- Fixed message handlers not working when message types are assigned as the value of class vars https://github.com/Textualize/textual/pull/3940
|
||||
- Fixed `CommandPalette` not focusing the input when opened when `App.AUTO_FOCUS` doesn't match the input https://github.com/Textualize/textual/pull/4763
|
||||
- `SelectionList.SelectionToggled` will now be sent for each option when a bulk toggle is performed (e.g. `toggle_all`). Previously no messages were sent at all. https://github.com/Textualize/textual/pull/4759
|
||||
|
||||
|
||||
@@ -74,8 +74,16 @@ class Message:
|
||||
cls.no_dispatch = no_dispatch
|
||||
if namespace is not None:
|
||||
cls.namespace = namespace
|
||||
name = camel_to_snake(cls.__name__)
|
||||
cls.handler_name = f"on_{namespace}_{name}" if namespace else f"on_{name}"
|
||||
name = f"{namespace}_{camel_to_snake(cls.__name__)}"
|
||||
else:
|
||||
# a class defined inside of a function will have a qualified name like func.<locals>.Class,
|
||||
# so make sure we only use the actual class name(s)
|
||||
qualname = cls.__qualname__.rsplit("<locals>.", 1)[-1]
|
||||
# only keep the last two parts of the qualified name of deeply nested classes
|
||||
# for backwards compatibility, e.g. A.B.C.D becomes C.D
|
||||
namespace = qualname.rsplit(".", 2)[-2:]
|
||||
name = "_".join(camel_to_snake(part) for part in namespace)
|
||||
cls.handler_name = f"on_{name}"
|
||||
|
||||
@property
|
||||
def control(self) -> DOMNode | None:
|
||||
|
||||
@@ -11,7 +11,6 @@ A `MessagePump` is a base class for any object which processes messages, which i
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import inspect
|
||||
import threading
|
||||
from asyncio import CancelledError, Queue, QueueEmpty, Task, create_task
|
||||
from contextlib import contextmanager
|
||||
@@ -36,7 +35,6 @@ from ._context import message_hook as message_hook_context_var
|
||||
from ._context import prevent_message_types_stack
|
||||
from ._on import OnNoWidget
|
||||
from ._time import time
|
||||
from .case import camel_to_snake
|
||||
from .css.match import match
|
||||
from .errors import DuplicateKeyHandlers
|
||||
from .events import Event
|
||||
@@ -78,8 +76,6 @@ class _MessagePumpMeta(type):
|
||||
class_dict: dict[str, Any],
|
||||
**kwargs: Any,
|
||||
) -> _MessagePumpMetaSub:
|
||||
namespace = camel_to_snake(name)
|
||||
isclass = inspect.isclass
|
||||
handlers: dict[
|
||||
type[Message], list[tuple[Callable, dict[str, tuple[SelectorSet, ...]]]]
|
||||
] = class_dict.get("_decorated_handlers", {})
|
||||
@@ -93,13 +89,6 @@ class _MessagePumpMeta(type):
|
||||
] = getattr(value, "_textual_on")
|
||||
for message_type, selectors in textual_on:
|
||||
handlers.setdefault(message_type, []).append((value, selectors))
|
||||
if isclass(value) and issubclass(value, Message):
|
||||
if "namespace" in value.__dict__:
|
||||
value.handler_name = f"on_{value.__dict__['namespace']}_{camel_to_snake(value.__name__)}"
|
||||
else:
|
||||
value.handler_name = (
|
||||
f"on_{namespace}_{camel_to_snake(value.__name__)}"
|
||||
)
|
||||
|
||||
# Look for reactives with public AND private compute methods.
|
||||
prefix = "compute_"
|
||||
|
||||
@@ -24,6 +24,11 @@ async def test_message_inheritance_namespace():
|
||||
class Fired(BaseWidget.Fired):
|
||||
pass
|
||||
|
||||
class DummyWidget(Widget):
|
||||
# ensure that referencing a message type in other class scopes
|
||||
# doesn't break the namespace
|
||||
_event = Left.Fired
|
||||
|
||||
handlers_called = []
|
||||
|
||||
class MessageInheritanceApp(App[None]):
|
||||
|
||||
Reference in New Issue
Block a user