Module textual.binding
Expand source code
from __future__ import annotations
import sys
from dataclasses import dataclass
from typing import Iterable, MutableMapping
import rich.repr
if sys.version_info >= (3, 10):
from typing import TypeAlias
else: # pragma: no cover
from typing_extensions import TypeAlias
BindingType: TypeAlias = "Binding | tuple[str, str, str]"
class BindingError(Exception):
"""A binding related error."""
class NoBinding(Exception):
"""A binding was not found."""
@dataclass(frozen=True)
class Binding:
key: str
"""Key to bind. This can also be a comma-separated list of keys to map multiple keys to a single action."""
action: str
"""Action to bind to."""
description: str
"""Description of action."""
show: bool = True
"""Show the action in Footer, or False to hide."""
key_display: str | None = None
"""How the key should be shown in footer."""
universal: bool = False
"""Allow forwarding from app to focused widget."""
@rich.repr.auto
class Bindings:
"""Manage a set of bindings."""
def __init__(self, bindings: Iterable[BindingType] | None = None) -> None:
def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]:
for binding in bindings:
# If it's a tuple of length 3, convert into a Binding first
if isinstance(binding, tuple):
if len(binding) != 3:
raise BindingError(
f"BINDINGS must contain a tuple of three strings, not {binding!r}"
)
binding = Binding(*binding)
binding_keys = binding.key.split(",")
if len(binding_keys) > 1:
for key in binding_keys:
new_binding = Binding(
key=key,
action=binding.action,
description=binding.description,
show=binding.show,
key_display=binding.key_display,
universal=binding.universal,
)
yield new_binding
else:
yield binding
self.keys: MutableMapping[str, Binding] = (
{binding.key: binding for binding in make_bindings(bindings)}
if bindings
else {}
)
def __rich_repr__(self) -> rich.repr.Result:
yield self.keys
@classmethod
def merge(cls, bindings: Iterable[Bindings]) -> Bindings:
"""Merge a bindings. Subsequence bound keys override initial keys.
Args:
bindings (Iterable[Bindings]): A number of bindings.
Returns:
Bindings: New bindings.
"""
keys: dict[str, Binding] = {}
for _bindings in bindings:
keys.update(_bindings.keys)
return Bindings(keys.values())
@property
def shown_keys(self) -> list[Binding]:
"""A list of bindings for shown keys.
Returns:
list[Binding]: Shown bindings.
"""
keys = [binding for binding in self.keys.values() if binding.show]
return keys
def bind(
self,
keys: str,
action: str,
description: str = "",
show: bool = True,
key_display: str | None = None,
universal: bool = False,
) -> None:
all_keys = [key.strip() for key in keys.split(",")]
for key in all_keys:
self.keys[key] = Binding(
key,
action,
description,
show=show,
key_display=key_display,
universal=universal,
)
def get_key(self, key: str) -> Binding:
"""Get a binding if it exists.
Args:
key (str): Key to look up.
Raises:
NoBinding: If the binding does not exist.
Returns:
Binding: A binding object for the key,
"""
try:
return self.keys[key]
except KeyError:
raise NoBinding(f"No binding for {key}") from None
Classes
class Binding (key: str, action: str, description: str, show: bool = True, key_display: str | None = None, universal: bool = False)
-
Binding(key: 'str', action: 'str', description: 'str', show: 'bool' = True, key_display: 'str | None' = None, universal: 'bool' = False)
Expand source code
class Binding: key: str """Key to bind. This can also be a comma-separated list of keys to map multiple keys to a single action.""" action: str """Action to bind to.""" description: str """Description of action.""" show: bool = True """Show the action in Footer, or False to hide.""" key_display: str | None = None """How the key should be shown in footer.""" universal: bool = False """Allow forwarding from app to focused widget."""
Class variables
var action : str
-
Action to bind to.
var description : str
-
Description of action.
var key : str
-
Key to bind. This can also be a comma-separated list of keys to map multiple keys to a single action.
var key_display : str | None
-
How the key should be shown in footer.
var show : bool
-
Show the action in Footer, or False to hide.
var universal : bool
-
Allow forwarding from app to focused widget.
class BindingError (*args, **kwargs)
-
A binding related error.
Expand source code
class BindingError(Exception): """A binding related error."""
Ancestors
- builtins.Exception
- builtins.BaseException
class Bindings (bindings: Iterable[BindingType] | None = None)
-
Manage a set of bindings.
Expand source code
class Bindings: """Manage a set of bindings.""" def __init__(self, bindings: Iterable[BindingType] | None = None) -> None: def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]: for binding in bindings: # If it's a tuple of length 3, convert into a Binding first if isinstance(binding, tuple): if len(binding) != 3: raise BindingError( f"BINDINGS must contain a tuple of three strings, not {binding!r}" ) binding = Binding(*binding) binding_keys = binding.key.split(",") if len(binding_keys) > 1: for key in binding_keys: new_binding = Binding( key=key, action=binding.action, description=binding.description, show=binding.show, key_display=binding.key_display, universal=binding.universal, ) yield new_binding else: yield binding self.keys: MutableMapping[str, Binding] = ( {binding.key: binding for binding in make_bindings(bindings)} if bindings else {} ) def __rich_repr__(self) -> rich.repr.Result: yield self.keys @classmethod def merge(cls, bindings: Iterable[Bindings]) -> Bindings: """Merge a bindings. Subsequence bound keys override initial keys. Args: bindings (Iterable[Bindings]): A number of bindings. Returns: Bindings: New bindings. """ keys: dict[str, Binding] = {} for _bindings in bindings: keys.update(_bindings.keys) return Bindings(keys.values()) @property def shown_keys(self) -> list[Binding]: """A list of bindings for shown keys. Returns: list[Binding]: Shown bindings. """ keys = [binding for binding in self.keys.values() if binding.show] return keys def bind( self, keys: str, action: str, description: str = "", show: bool = True, key_display: str | None = None, universal: bool = False, ) -> None: all_keys = [key.strip() for key in keys.split(",")] for key in all_keys: self.keys[key] = Binding( key, action, description, show=show, key_display=key_display, universal=universal, ) def get_key(self, key: str) -> Binding: """Get a binding if it exists. Args: key (str): Key to look up. Raises: NoBinding: If the binding does not exist. Returns: Binding: A binding object for the key, """ try: return self.keys[key] except KeyError: raise NoBinding(f"No binding for {key}") from None
Static methods
def merge(bindings: Iterable[Bindings]) ‑> Bindings
-
Merge a bindings. Subsequence bound keys override initial keys.
Args
bindings
:Iterable[Bindings]
- A number of bindings.
Returns
Bindings
- New bindings.
Expand source code
@classmethod def merge(cls, bindings: Iterable[Bindings]) -> Bindings: """Merge a bindings. Subsequence bound keys override initial keys. Args: bindings (Iterable[Bindings]): A number of bindings. Returns: Bindings: New bindings. """ keys: dict[str, Binding] = {} for _bindings in bindings: keys.update(_bindings.keys) return Bindings(keys.values())
Instance variables
var shown_keys : list[Binding]
-
Expand source code
@property def shown_keys(self) -> list[Binding]: """A list of bindings for shown keys. Returns: list[Binding]: Shown bindings. """ keys = [binding for binding in self.keys.values() if binding.show] return keys
Methods
def bind(self, keys: str, action: str, description: str = '', show: bool = True, key_display: str | None = None, universal: bool = False) ‑> None
-
Expand source code
def bind( self, keys: str, action: str, description: str = "", show: bool = True, key_display: str | None = None, universal: bool = False, ) -> None: all_keys = [key.strip() for key in keys.split(",")] for key in all_keys: self.keys[key] = Binding( key, action, description, show=show, key_display=key_display, universal=universal, )
def get_key(self, key: str) ‑> Binding
-
Get a binding if it exists.
Args
key
:str
- Key to look up.
Raises
NoBinding
- If the binding does not exist.
Returns
Binding
- A binding object for the key,
Expand source code
def get_key(self, key: str) -> Binding: """Get a binding if it exists. Args: key (str): Key to look up. Raises: NoBinding: If the binding does not exist. Returns: Binding: A binding object for the key, """ try: return self.keys[key] except KeyError: raise NoBinding(f"No binding for {key}") from None
class NoBinding (*args, **kwargs)
-
A binding was not found.
Expand source code
class NoBinding(Exception): """A binding was not found."""
Ancestors
- builtins.Exception
- builtins.BaseException