Replace old types info with 3.10+ constructs

This commit is contained in:
Arjan Molenaar 2024-08-20 15:46:07 +02:00
parent ad7e00bff7
commit 5611fc048f
22 changed files with 130 additions and 116 deletions

View File

@ -10,7 +10,7 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
from typing import Dict
from __future__ import annotations
# -- Project information -----------------------------------------------------
@ -111,7 +111,7 @@ htmlhelp_basename = "Gaphasdoc"
# -- Options for LaTeX output ------------------------------------------------
latex_elements: Dict[str, str] = {
latex_elements: dict[str, str] = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',

View File

@ -1,5 +1,7 @@
from __future__ import annotations
from functools import singledispatch
from typing import Callable, Optional, Protocol
from typing import Callable, Protocol
from gaphas.connections import Connections
from gaphas.geometry import intersect_line_line
@ -13,14 +15,14 @@ from gaphas.types import Pos, SupportsFloatPos
class ConnectionSinkType(Protocol):
item: Item
port: Optional[Port]
port: Port | None
def __init__(self, item: Item, distance: float = 10):
...
def glue(
self, pos: SupportsFloatPos, secondary_pos: Optional[SupportsFloatPos] = None
) -> Optional[Pos]:
self, pos: SupportsFloatPos, secondary_pos: SupportsFloatPos | None = None
) -> Pos | None:
...
def constraint(self, item: Item, handle: Handle) -> Constraint:
@ -40,10 +42,10 @@ class ItemConnector:
def allow(self, sink):
return True
def secondary_handle(self) -> Optional[Handle]:
def secondary_handle(self) -> Handle | None:
return None
def glue(self, sink: ConnectionSinkType) -> Optional[Pos]:
def glue(self, sink: ConnectionSinkType) -> Pos | None:
"""Glue the Connector handle on the sink's port."""
handle = self.handle
item = self.item
@ -80,7 +82,7 @@ class ItemConnector:
def connect_handle(
self,
sink: ConnectionSinkType,
callback: Optional[Callable[[Item, Handle, Item, Port], None]] = None,
callback: Callable[[Item, Handle, Item, Port], None] | None = None,
) -> None:
"""Create constraint between handle of a line and port of connectable
item.
@ -110,7 +112,7 @@ Connector = singledispatch(ItemConnector)
@Connector.register(Line)
class LineConnector(ItemConnector):
def secondary_handle(self) -> Optional[Handle]:
def secondary_handle(self) -> Handle | None:
item = self.item
handle = self.handle
handles = item.handles()
@ -131,11 +133,11 @@ class ItemConnectionSink:
def __init__(self, item: Item, distance: float = 10) -> None:
self.item = item
self.distance = distance
self.port: Optional[Port] = None
self.port: Port | None = None
def glue(
self, pos: SupportsFloatPos, secondary_pos: Optional[SupportsFloatPos] = None
) -> Optional[Pos]:
self, pos: SupportsFloatPos, secondary_pos: SupportsFloatPos | None = None
) -> Pos | None:
max_dist = self.distance
glue_pos = None
for p in self.item.ports():
@ -161,8 +163,8 @@ ConnectionSink = singledispatch(ItemConnectionSink)
@ConnectionSink.register(Element)
class ElementConnectionSink(ItemConnectionSink):
def glue(
self, pos: SupportsFloatPos, secondary_pos: Optional[SupportsFloatPos] = None
) -> Optional[Pos]:
self, pos: SupportsFloatPos, secondary_pos: SupportsFloatPos | None = None
) -> Pos | None:
if glue_pos := super().glue(pos, secondary_pos):
return glue_pos

View File

@ -24,9 +24,10 @@ New constraint class should derive from Constraint class abstract
class and implement `Constraint.solve_for(Variable)` method to update
a variable with appropriate value.
"""
from __future__ import annotations
import logging
import math
from typing import Optional, Tuple
from gaphas.position import Position
from gaphas.solver import BaseConstraint, Constraint
@ -35,13 +36,14 @@ log = logging.getLogger(__name__)
def constraint(
horizontal: Optional[Tuple[Position, Position]] = None,
vertical: Optional[Tuple[Position, Position]] = None,
left_of: Optional[Tuple[Position, Position]] = None,
above: Optional[Tuple[Position, Position]] = None,
line: Optional[Tuple[Position, Tuple[Position, Position]]] = None,
*,
horizontal: tuple[Position, Position] | None = None,
vertical: tuple[Position, Position] | None = None,
left_of: tuple[Position, Position] | None = None,
above: tuple[Position, Position] | None = None,
line: tuple[Position, tuple[Position, Position]] | None = None,
delta: float = 0.0,
align: Optional[float] = None,
align: float | None = None,
) -> Constraint:
"""Utility (factory) method to create item's internal constraint between
two positions or between a position and a line.
@ -67,7 +69,7 @@ def constraint(
line=(p, l)
Keep position ``p`` on line ``l``.
"""
cc: Optional[Constraint]
cc: Constraint | None
if horizontal:
p1, p2 = horizontal
cc = EqualsConstraint(p1[1], p2[1], delta)

View File

@ -1,5 +1,7 @@
from __future__ import annotations
from functools import singledispatch
from typing import Optional
from typing import Union
from gaphas.connector import Handle
from gaphas.item import Element, Item, Line
@ -9,7 +11,7 @@ DEFAULT_CURSOR = "default"
@singledispatch
def cursor(item: Optional[Item], handle: Optional[Handle], pos: Pos) -> str:
def cursor(item: Union[Item, None], handle: Union[Handle, None], pos: Pos) -> str:
return DEFAULT_CURSOR
@ -17,7 +19,7 @@ ELEMENT_CURSORS = ("nw-resize", "ne-resize", "se-resize", "sw-resize")
@cursor.register
def element_hover(item: Element, handle: Optional[Handle], pos: Pos) -> str:
def element_hover(item: Element, handle: Union[Handle, None], pos: Pos) -> str:
if handle:
index = item.handles().index(handle)
return ELEMENT_CURSORS[index] if index < 4 else DEFAULT_CURSOR
@ -28,5 +30,5 @@ LINE_CURSOR = "move"
@cursor.register
def line_hover(item: Line, handle: Optional[Handle], pos: Pos) -> str:
def line_hover(item: Line, handle: Union[Handle, None], pos: Pos) -> str:
return LINE_CURSOR if handle else DEFAULT_CURSOR

View File

@ -8,10 +8,10 @@ A point is represented as a tuple `(x, y)`.
from __future__ import annotations
from math import sqrt
from typing import Iterator, Tuple
from collections.abc import Iterator
Point = Tuple[float, float] # x, y
Rect = Tuple[float, float, float, float] # x, y, width, height
Point = tuple[float, float] # x, y
Rect = tuple[float, float, float, float] # x, y, width, height
class Rectangle:

View File

@ -9,11 +9,11 @@ state notification capabilities.
from __future__ import annotations
from typing import Callable, SupportsFloat, Tuple
from typing import Callable, SupportsFloat
import cairo
MatrixTuple = Tuple[float, float, float, float, float, float]
Matrixtuple = tuple[float, float, float, float, float, float]
class Matrix:
@ -34,31 +34,31 @@ class Matrix:
matrix: cairo.Matrix | None = None,
) -> None:
self._matrix = matrix or cairo.Matrix(xx, yx, xy, yy, x0, y0)
self._handlers: set[Callable[[Matrix, MatrixTuple], None]] = set()
self._handlers: set[Callable[[Matrix, Matrixtuple], None]] = set()
def add_handler(
self,
handler: Callable[[Matrix, MatrixTuple], None],
handler: Callable[[Matrix, Matrixtuple], None],
) -> None:
self._handlers.add(handler)
def remove_handler(
self,
handler: Callable[[Matrix, MatrixTuple], None],
handler: Callable[[Matrix, Matrixtuple], None],
) -> None:
self._handlers.discard(handler)
def notify(self, old: MatrixTuple) -> None:
def notify(self, old: Matrixtuple) -> None:
for handler in self._handlers:
handler(self, old)
def invert(self) -> None:
old: MatrixTuple = self.tuple()
old: Matrixtuple = self.tuple()
self._matrix.invert()
self.notify(old)
def rotate(self, radians: float) -> None:
old: MatrixTuple = self.tuple()
old: Matrixtuple = self.tuple()
self._matrix.rotate(radians)
self.notify(old)
@ -68,7 +68,7 @@ class Matrix:
self.notify(old)
def translate(self, tx: float, ty: float) -> None:
old: MatrixTuple = self.tuple()
old: Matrixtuple = self.tuple()
self._matrix.translate(tx, ty)
self.notify(old)
@ -116,7 +116,7 @@ class Matrix:
m.invert()
return m
def tuple(self) -> MatrixTuple:
def tuple(self) -> Matrixtuple:
return tuple(self) # type: ignore[arg-type, return-value]
def to_cairo(self) -> cairo.Matrix:
@ -135,7 +135,7 @@ class Matrix:
return Matrix(matrix=self._matrix * other._matrix)
def __imul__(self, other: Matrix) -> Matrix:
old: MatrixTuple = self.tuple()
old: Matrixtuple = self.tuple()
self._matrix *= other._matrix
self.notify(old)
return self

View File

@ -1,6 +1,6 @@
from __future__ import annotations
from typing import Collection
from collections.abc import Collection
from cairo import Context as CairoContext

View File

@ -11,7 +11,7 @@ See: http://stevehanov.ca/blog/index.php?id=33 and
"""
from math import sqrt
from random import Random
from typing import Collection
from collections.abc import Collection
from cairo import Context as CairoContext

View File

@ -1,4 +1,6 @@
from typing import Collection, Optional
from __future__ import annotations
from collections.abc import Collection
from cairo import LINE_JOIN_ROUND
from cairo import Context as CairoContext
@ -8,7 +10,7 @@ from gaphas.selection import Selection
class ItemPainter:
def __init__(self, selection: Optional[Selection] = None) -> None:
def __init__(self, selection: Selection | None = None) -> None:
self.selection = selection or Selection()
def paint_item(self, item: Item, cairo: CairoContext) -> None:

View File

@ -20,11 +20,11 @@ as a Q-tree. All forms of Quadtrees share some common features:
from __future__ import annotations
import operator
from typing import Callable, Generic, Iterable, Tuple, TypeVar
from typing import Callable, Generic, Iterable, TypeVar
from gaphas.geometry import rectangle_contains, rectangle_intersects
from gaphas.geometry import rectangle_contains, rectangle_intersects, Rect
Bounds = Tuple[float, float, float, float] # x, y, width, height
Bounds = Rect
T = TypeVar("T")
D = TypeVar("D")

View File

@ -1,7 +1,8 @@
"""Allow for easily adding segments to lines."""
from __future__ import annotations
from functools import singledispatch
from typing import Optional
from typing import Union
from gaphas.connector import Handle, LinePort
from gaphas.cursor import DEFAULT_CURSOR, LINE_CURSOR, cursor, line_hover
@ -211,7 +212,7 @@ class LineSegmentPainter:
@cursor.register
def line_segment_hover(item: Line, handle: Optional[Handle], pos: Pos) -> str:
def line_segment_hover(item: Line, handle: Union[Handle, None], pos: Pos) -> str:
if not handle:
handles = item.handles()
if any(

View File

@ -1,4 +1,6 @@
from typing import Callable, Collection, Optional, Set
from __future__ import annotations
from collections.abc import Callable, Collection
from gaphas.item import Item
@ -6,20 +8,20 @@ from gaphas.item import Item
class Selection:
def __init__(self):
super().__init__()
self._selected_items: Set[Item] = set()
self._focused_item: Optional[Item] = None
self._hovered_item: Optional[Item] = None
self._handlers: Set[Callable[[Optional[Item]], None]] = set()
self._selected_items: set[Item] = set()
self._focused_item: Item | None = None
self._hovered_item: Item | None = None
self._handlers: set[Callable[[Item | None], None]] = set()
def add_handler(self, handler: Callable[[Optional[Item]], None]) -> None:
def add_handler(self, handler: Callable[[Item | None], None]) -> None:
"""Add a callback handler, triggered when a constraint is resolved."""
self._handlers.add(handler)
def remove_handler(self, handler: Callable[[Optional[Item]], None]) -> None:
def remove_handler(self, handler: Callable[[Item | None], None]) -> None:
"""Remove a previously assigned handler."""
self._handlers.discard(handler)
def notify(self, item: Optional[Item]) -> None:
def notify(self, item: Item | None) -> None:
for handler in self._handlers:
handler(item)
@ -57,11 +59,11 @@ class Selection:
self.focused_item = None
@property
def focused_item(self) -> Optional[Item]:
def focused_item(self) -> Item | None:
return self._focused_item
@focused_item.setter
def focused_item(self, item: Optional[Item]) -> None:
def focused_item(self, item: Item | None) -> None:
if item:
self.select_items(item)
@ -70,11 +72,11 @@ class Selection:
self.notify(item)
@property
def hovered_item(self) -> Optional[Item]:
def hovered_item(self) -> Item | None:
return self._hovered_item
@hovered_item.setter
def hovered_item(self, item: Optional[Item]) -> None:
def hovered_item(self, item: Item | None) -> None:
if item is not self._hovered_item:
self._hovered_item = item
self.notify(item)

View File

@ -32,8 +32,10 @@ every constraint is being asked to solve itself
(`constraint.Constraint.solve_for()` method) changing appropriate
variables to make the constraint valid again.
"""
from __future__ import annotations
import functools
from typing import Callable, Collection, List, Optional, Set
from collections.abc import Callable, Collection
from gaphas.solver.constraint import Constraint, ContainsConstraints
@ -46,11 +48,11 @@ class Solver:
def __init__(self, resolve_limit: int = 16) -> None:
# a dict of constraint -> name/variable mappings
self._constraints: Set[Constraint] = set()
self._marked_cons: List[Constraint] = []
self._constraints: set[Constraint] = set()
self._marked_cons: list[Constraint] = []
self._solving = False
self._resolve_limit = resolve_limit
self._handlers: Set[Callable[[Constraint], None]] = set()
self._handlers: set[Callable[[Constraint], None]] = set()
def add_handler(self, handler: Callable[[Constraint], None]) -> None:
"""Add a callback handler, triggered when a constraint is resolved."""
@ -147,7 +149,7 @@ class Solver:
def find_containing_constraint(
constraint: Constraint, constraints: Collection[Constraint]
) -> Optional[Constraint]:
) -> Constraint | None:
if constraint in constraints:
return constraint

View File

@ -1,29 +1,26 @@
"""Table is a storage class that can be used to store information, like one
would in a database table, with indexes on the desired "columns."."""
from __future__ import annotations
from contextlib import suppress
from functools import reduce
from typing import (
Dict,
Generic,
Iterator,
Protocol,
Sequence,
Set,
Tuple,
Type,
TypeVar,
runtime_checkable,
)
T = TypeVar("T", bound=Tuple, covariant=True)
T = TypeVar("T", bound=tuple, covariant=True)
@runtime_checkable
class NamedTupleish(Protocol):
_fields: Tuple[str, ...]
_fields: tuple[str, ...]
def _make(self, *args: object) -> Tuple[object, ...]:
def _make(self, *args: object) -> tuple[object, ...]:
...
@ -48,7 +45,7 @@ class Table(Generic[T]):
self._fields: Sequence[str] = fields
# create data structure, which acts as cache
self._index: Dict[str, Dict[object, Set[object]]] = {n: {} for n in fields}
self._index: dict[str, dict[object, set[object]]] = {n: {} for n in fields}
@property
def columns(self) -> Type[T]:

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import logging
from typing import Optional, Tuple, Union
from gi.repository import Gdk, Gtk
@ -86,7 +87,7 @@ def on_drag_begin(gesture, start_x, start_y, drag_state):
def find_item_and_handle_at_point(
view: GtkView, pos: Pos
) -> Tuple[Optional[Item], Optional[Handle]]:
) -> tuple[Item | None, Handle | None]:
item, handle = handle_at_point(view, pos)
return item or next(item_at_point(view, pos), None), handle # type: ignore[call-overload]
@ -135,7 +136,7 @@ def order_handles(handles):
def handle_at_point(
view: GtkView, pos: Pos, distance: int = 6
) -> Union[Tuple[Item, Handle], Tuple[None, None]]:
) -> tuple[Item, Handle] | tuple[None, None]:
"""Look for a handle at ``pos`` and return the tuple (item, handle)."""
def find(item):

View File

@ -1,4 +1,6 @@
from typing import Callable, Optional
from __future__ import annotations
from collections.abc import Callable
from gi.repository import Gtk
@ -23,7 +25,7 @@ class PlacementState:
def __init__(self, factory: FactoryType, handle_index: int):
self.factory = factory
self.handle_index = handle_index
self.moving: Optional[MoveType] = None
self.moving: MoveType | None = None
def on_drag_begin(gesture, start_x, start_y, placement_state):

View File

@ -1,4 +1,4 @@
from typing import Collection
from collections.abc import Collection
from cairo import Context as CairoContext
from gi.repository import Gtk

View File

@ -1,6 +1,8 @@
"""Simple class containing the tree structure for the canvas items."""
from __future__ import annotations
from contextlib import suppress
from typing import Dict, Generic, Iterable, List, Optional, Sequence, TypeVar, Union
from typing import Generic, Iterable, Sequence, TypeVar
T = TypeVar("T")
@ -16,19 +18,19 @@ class Tree(Generic[T]):
def __init__(self) -> None:
# List of nodes in the tree, sorted in the order they ought to be
# rendered
self._nodes: List[T] = []
self._nodes: list[T] = []
# Per entry a list of children is maintained.
self._children: Dict[Union[T, None], List[T]] = {None: []}
self._children: dict[T | None, list[T]] = {None: []}
# For easy and fast lookups, also maintain a child -> parent mapping
self._parents: Dict[T, T] = {}
self._parents: dict[T, T] = {}
@property
def nodes(self) -> Sequence[T]:
return list(self._nodes)
def get_parent(self, node: T) -> Optional[T]:
def get_parent(self, node: T) -> T | None:
"""Return the parent item of ``node``.
>>> tree = Tree()
@ -39,7 +41,7 @@ class Tree(Generic[T]):
"""
return self._parents.get(node)
def get_children(self, node: Optional[T]) -> Iterable[T]:
def get_children(self, node: T | None) -> Iterable[T]:
"""Return all child objects of ``node``.
>>> tree = Tree()
@ -53,7 +55,7 @@ class Tree(Generic[T]):
"""
return self._children[node]
def get_siblings(self, node: T) -> List[T]:
def get_siblings(self, node: T) -> list[T]:
"""Get all siblings of ``node``, including ``node``.
>>> tree = Tree()
@ -150,7 +152,7 @@ class Tree(Generic[T]):
return (n for n in self._nodes if n in items_set)
def _add_to_nodes(
self, node: T, parent: Optional[T], index: Optional[int] = None
self, node: T, parent: T | None, index: int | None = None
) -> None:
"""Helper method to place nodes on the right location in the nodes list
Called only from add() and move()"""
@ -175,9 +177,7 @@ class Tree(Generic[T]):
else:
nodes.insert(nodes.index(atnode), node)
def _add(
self, node: T, parent: Optional[T] = None, index: Optional[int] = None
) -> None:
def _add(self, node: T, parent: T | None = None, index: int | None = None) -> None:
"""Helper method for both add() and move()."""
assert node not in self._nodes
@ -195,9 +195,7 @@ class Tree(Generic[T]):
if parent:
self._parents[node] = parent
def add(
self, node: T, parent: Optional[T] = None, index: Optional[int] = None
) -> None:
def add(self, node: T, parent: T | None = None, index: int | None = None) -> None:
"""Add node to the tree. parent is the parent node, which may be None
if the item should be added to the root item.
@ -225,7 +223,7 @@ class Tree(Generic[T]):
self.remove(c)
self._remove(node)
def _reparent_nodes(self, node: T, parent: Optional[T]) -> None:
def _reparent_nodes(self, node: T, parent: T | None) -> None:
"""Helper for move().
The _children and _parent trees can be left intact as far as
@ -237,7 +235,7 @@ class Tree(Generic[T]):
for c in self._children[node]:
self._reparent_nodes(c, node)
def move(self, node: T, parent: Optional[T], index: Optional[int] = None) -> None:
def move(self, node: T, parent: T | None, index: int | None = None) -> None:
"""Set new parent for a ``node``. ``Parent`` can be ``None``,
indicating it's added to the top.

View File

@ -1,11 +1,11 @@
from __future__ import annotations
from typing import Protocol, SupportsFloat, Tuple, TypeVar
from typing import Protocol, SupportsFloat, TypeVar
# A primitive position, tuple ``(x, y)``
# Pos = Tuple[Union[float, SupportsFloat], Union[float, SupportsFloat]]
Pos = Tuple[float, float]
SupportsFloatPos = Tuple[SupportsFloat, SupportsFloat]
Pos = tuple[float, float]
SupportsFloatPos = tuple[SupportsFloat, SupportsFloat]
GetT = TypeVar("GetT", covariant=True)
SetT = TypeVar("SetT", contravariant=True)

View File

@ -1,7 +1,7 @@
"""This module contains everything to display a model on a screen."""
from __future__ import annotations
from typing import Collection, Iterable
from collections.abc import Collection, Iterable
import cairo
from gi.repository import Graphene, GLib, GObject, Gtk

View File

@ -1,4 +1,6 @@
from typing import Callable, Optional
from __future__ import annotations
from typing import Callable
from gi.repository import Gtk
@ -11,10 +13,10 @@ class Scrolling:
def __init__(self, scrolling_updated: Callable[[Matrix], None]) -> None:
self._scrolling_updated = scrolling_updated
self.hadjustment: Optional[Gtk.Adjustment] = None
self.vadjustment: Optional[Gtk.Adjustment] = None
self.hscroll_policy: Optional[Gtk.ScrollablePolicy] = None
self.vscroll_policy: Optional[Gtk.ScrollablePolicy] = None
self.hadjustment: Gtk.Adjustment | None = None
self.vadjustment: Gtk.Adjustment | None = None
self.hscroll_policy: Gtk.ScrollablePolicy | None = None
self.vscroll_policy: Gtk.ScrollablePolicy | None = None
self._hadjustment_handler_id = 0
self._vadjustment_handler_id = 0
self._last_hvalue = 0
@ -58,7 +60,7 @@ class Scrolling:
else:
raise AttributeError(f"Unknown property {prop.name}")
def update_adjustments(self, width, height, bounds):
def update_adjustments(self, width: int, height: int, bounds: Rectangle) -> None:
"""Update scroll bar values (adjustments in GTK), and reset the scroll
value to 0.

View File

@ -1,6 +1,7 @@
from __future__ import annotations
import functools
import time
from typing import List
import pytest
from gi.repository import GLib
@ -62,7 +63,7 @@ def test_in_main_context():
def test_function_is_called_when_not_in_main_loop():
called: List[str] = []
called: list[str] = []
async_function(called, "called")
@ -70,7 +71,7 @@ def test_function_is_called_when_not_in_main_loop():
def test_generator_is_called_when_not_in_main_loop():
called: List[str] = []
called: list[str] = []
for _ in generator_function(called, ["one", "two"]):
pass
@ -80,7 +81,7 @@ def test_generator_is_called_when_not_in_main_loop():
@in_main_context
def test_function_is_not_called_directly_in_main_loop():
called: List[str] = []
called: list[str] = []
async_function(called, "called")
@ -88,7 +89,7 @@ def test_function_is_not_called_directly_in_main_loop():
def test_function_is_called_from_main_loop():
called: List[str] = []
called: list[str] = []
@in_main_context
def fn():
@ -101,7 +102,7 @@ def test_function_is_called_from_main_loop():
def test_single_function_is_called_once():
called: List[str] = []
called: list[str] = []
@in_main_context
def fn():
@ -134,7 +135,7 @@ def test_single_method_is_called_once_per_instance():
def test_timeout_function():
called: List[str] = []
called: list[str] = []
@in_main_context
def fn():
@ -151,7 +152,7 @@ def test_timeout_function():
def test_run_generator_to_completion():
called: List[str] = []
called: list[str] = []
@in_main_context
def fn():
@ -164,7 +165,7 @@ def test_run_generator_to_completion():
def test_run_first_generator_to_completion():
called: List[str] = []
called: list[str] = []
@in_main_context
def fn():