diff --git a/examples/five_by_five.py b/examples/five_by_five.py index 3e668f04d..f3aada19f 100644 --- a/examples/five_by_five.py +++ b/examples/five_by_five.py @@ -1,25 +1,24 @@ +from __future__ import annotations + """Simple version of 5x5, developed for/with Textual.""" from pathlib import Path -from typing import cast -import sys - -if sys.version_info >= (3, 8): - from typing import Final -else: - from typing_extensions import Final - -from textual.containers import Horizontal -from textual.app import App, ComposeResult -from textual.screen import Screen -from textual.widget import Widget -from textual.widgets import Footer, Button, Label -from textual.css.query import DOMQuery -from textual.reactive import reactive -from textual.binding import Binding +from typing import TYPE_CHECKING, cast from rich.markdown import Markdown +from textual.app import App, ComposeResult +from textual.binding import Binding +from textual.containers import Horizontal +from textual.css.query import DOMQuery +from textual.reactive import reactive +from textual.screen import Screen +from textual.widget import Widget +from textual.widgets import Button, Footer, Label + +if TYPE_CHECKING: + from typing_extensions import Final + class Help(Screen): """The help screen for the application.""" diff --git a/poetry.lock b/poetry.lock index c1ee65e43..2c437f4a0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -583,7 +583,7 @@ python-versions = ">=3.7" [[package]] name = "mypy" -version = "0.990" +version = "1.0.0" description = "Optional static typing for Python" category = "dev" optional = false @@ -603,11 +603,11 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" [[package]] name = "nanoid" @@ -869,7 +869,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "67.1.0" +version = "67.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false @@ -982,11 +982,11 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.17.1" +version = "20.18.0" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] distlib = ">=0.3.6,<1" @@ -995,8 +995,8 @@ importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] [[package]] name = "watchdog" @@ -1024,7 +1024,7 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [[package]] name = "zipp" -version = "3.12.0" +version = "3.12.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false @@ -1040,7 +1040,7 @@ dev = ["aiohttp", "click", "msgpack"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "8b9c57d32f9db7d59bacc1e254e46bc5ae523e9e831494c205caf1b5fe7982e4" +content-hash = "efa7c78c6403931436fb415a21c935eaafd7859130a9da8ef7e5d79ddb34b14d" [metadata.files] aiohttp = [ @@ -1625,40 +1625,36 @@ multidict = [ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] mypy = [ - {file = "mypy-0.990-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:aaf1be63e0207d7d17be942dcf9a6b641745581fe6c64df9a38deb562a7dbafa"}, - {file = "mypy-0.990-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d555aa7f44cecb7ea3c0ac69d58b1a5afb92caa017285a8e9c4efbf0518b61b4"}, - {file = "mypy-0.990-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f694d6d09a460b117dccb6857dda269188e3437c880d7b60fa0014fa872d1e9"}, - {file = "mypy-0.990-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:269f0dfb6463b8780333310ff4b5134425157ef0d2b1d614015adaf6d6a7eabd"}, - {file = "mypy-0.990-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8798c8ed83aa809f053abff08664bdca056038f5a02af3660de00b7290b64c47"}, - {file = "mypy-0.990-cp310-cp310-win_amd64.whl", hash = "sha256:47a9955214615108c3480a500cfda8513a0b1cd3c09a1ed42764ca0dd7b931dd"}, - {file = "mypy-0.990-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4a8a6c10f4c63fbf6ad6c03eba22c9331b3946a4cec97f008e9ffb4d3b31e8e2"}, - {file = "mypy-0.990-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd2dd3730ba894ec2a2082cc703fbf3e95a08479f7be84912e3131fc68809d46"}, - {file = "mypy-0.990-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7da0005e47975287a92b43276e460ac1831af3d23032c34e67d003388a0ce8d0"}, - {file = "mypy-0.990-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262c543ef24deb10470a3c1c254bb986714e2b6b1a67d66daf836a548a9f316c"}, - {file = "mypy-0.990-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3ff201a0c6d3ea029d73b1648943387d75aa052491365b101f6edd5570d018ea"}, - {file = "mypy-0.990-cp311-cp311-win_amd64.whl", hash = "sha256:1767830da2d1afa4e62b684647af0ff79b401f004d7fa08bc5b0ce2d45bcd5ec"}, - {file = "mypy-0.990-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6826d9c4d85bbf6d68cb279b561de6a4d8d778ca8e9ab2d00ee768ab501a9852"}, - {file = "mypy-0.990-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46897755f944176fbc504178422a5a2875bbf3f7436727374724842c0987b5af"}, - {file = "mypy-0.990-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0680389c34284287fe00e82fc8bccdea9aff318f7e7d55b90d967a13a9606013"}, - {file = "mypy-0.990-cp37-cp37m-win_amd64.whl", hash = "sha256:b08541a06eed35b543ae1a6b301590eb61826a1eb099417676ddc5a42aa151c5"}, - {file = "mypy-0.990-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:be88d665e76b452c26fb2bdc3d54555c01226fba062b004ede780b190a50f9db"}, - {file = "mypy-0.990-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b8f4a8213b1fd4b751e26b59ae0e0c12896568d7e805861035c7a15ed6dc9eb"}, - {file = "mypy-0.990-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b6f85c2ad378e3224e017904a051b26660087b3b76490d533b7344f1546d3ff"}, - {file = "mypy-0.990-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee5f99817ee70254e7eb5cf97c1b11dda29c6893d846c8b07bce449184e9466"}, - {file = "mypy-0.990-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49082382f571c3186ce9ea0bd627cb1345d4da8d44a8377870f4442401f0a706"}, - {file = "mypy-0.990-cp38-cp38-win_amd64.whl", hash = "sha256:aba38e3dd66bdbafbbfe9c6e79637841928ea4c79b32e334099463c17b0d90ef"}, - {file = "mypy-0.990-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9d851c09b981a65d9d283a8ccb5b1d0b698e580493416a10942ef1a04b19fd37"}, - {file = "mypy-0.990-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d847dd23540e2912d9667602271e5ebf25e5788e7da46da5ffd98e7872616e8e"}, - {file = "mypy-0.990-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc6019808580565040cd2a561b593d7c3c646badd7e580e07d875eb1bf35c695"}, - {file = "mypy-0.990-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a3150d409609a775c8cb65dbe305c4edd7fe576c22ea79d77d1454acd9aeda8"}, - {file = "mypy-0.990-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3227f14fe943524f5794679156488f18bf8d34bfecd4623cf76bc55958d229c5"}, - {file = "mypy-0.990-cp39-cp39-win_amd64.whl", hash = "sha256:c76c769c46a1e6062a84837badcb2a7b0cdb153d68601a61f60739c37d41cc74"}, - {file = "mypy-0.990-py3-none-any.whl", hash = "sha256:8f1940325a8ed460ba03d19ab83742260fa9534804c317224e5d4e5aa588e2d6"}, - {file = "mypy-0.990.tar.gz", hash = "sha256:72382cb609142dba3f04140d016c94b4092bc7b4d98ca718740dc989e5271b8d"}, + {file = "mypy-1.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0626db16705ab9f7fa6c249c017c887baf20738ce7f9129da162bb3075fc1af"}, + {file = "mypy-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ace23f6bb4aec4604b86c4843276e8fa548d667dbbd0cb83a3ae14b18b2db6c"}, + {file = "mypy-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87edfaf344c9401942883fad030909116aa77b0fa7e6e8e1c5407e14549afe9a"}, + {file = "mypy-1.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0ab090d9240d6b4e99e1fa998c2d0aa5b29fc0fb06bd30e7ad6183c95fa07593"}, + {file = "mypy-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:7cc2c01dfc5a3cbddfa6c13f530ef3b95292f926329929001d45e124342cd6b7"}, + {file = "mypy-1.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14d776869a3e6c89c17eb943100f7868f677703c8a4e00b3803918f86aafbc52"}, + {file = "mypy-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb2782a036d9eb6b5a6efcdda0986774bf798beef86a62da86cb73e2a10b423d"}, + {file = "mypy-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cfca124f0ac6707747544c127880893ad72a656e136adc935c8600740b21ff5"}, + {file = "mypy-1.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8845125d0b7c57838a10fd8925b0f5f709d0e08568ce587cc862aacce453e3dd"}, + {file = "mypy-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b1b9e1ed40544ef486fa8ac022232ccc57109f379611633ede8e71630d07d2"}, + {file = "mypy-1.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c7cf862aef988b5fbaa17764ad1d21b4831436701c7d2b653156a9497d92c83c"}, + {file = "mypy-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd187d92b6939617f1168a4fe68f68add749902c010e66fe574c165c742ed88"}, + {file = "mypy-1.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4e5175026618c178dfba6188228b845b64131034ab3ba52acaffa8f6c361f805"}, + {file = "mypy-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2f6ac8c87e046dc18c7d1d7f6653a66787a4555085b056fe2d599f1f1a2a2d21"}, + {file = "mypy-1.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7306edca1c6f1b5fa0bc9aa645e6ac8393014fa82d0fa180d0ebc990ebe15964"}, + {file = "mypy-1.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3cfad08f16a9c6611e6143485a93de0e1e13f48cfb90bcad7d5fde1c0cec3d36"}, + {file = "mypy-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67cced7f15654710386e5c10b96608f1ee3d5c94ca1da5a2aad5889793a824c1"}, + {file = "mypy-1.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a86b794e8a56ada65c573183756eac8ac5b8d3d59daf9d5ebd72ecdbb7867a43"}, + {file = "mypy-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:50979d5efff8d4135d9db293c6cb2c42260e70fb010cbc697b1311a4d7a39ddb"}, + {file = "mypy-1.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ae4c7a99e5153496243146a3baf33b9beff714464ca386b5f62daad601d87af"}, + {file = "mypy-1.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e398652d005a198a7f3c132426b33c6b85d98aa7dc852137a2a3be8890c4072"}, + {file = "mypy-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be78077064d016bc1b639c2cbcc5be945b47b4261a4f4b7d8923f6c69c5c9457"}, + {file = "mypy-1.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92024447a339400ea00ac228369cd242e988dd775640755fa4ac0c126e49bb74"}, + {file = "mypy-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:fe523fcbd52c05040c7bee370d66fee8373c5972171e4fbc323153433198592d"}, + {file = "mypy-1.0.0-py3-none-any.whl", hash = "sha256:2efa963bdddb27cb4a0d42545cd137a8d2b883bd181bbc4525b568ef6eca258f"}, + {file = "mypy-1.0.0.tar.gz", hash = "sha256:f34495079c8d9da05b183f9f7daec2878280c2ad7cc81da686ef0b484cea2ecf"}, ] mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] nanoid = [ {file = "nanoid-2.0.0-py3-none-any.whl", hash = "sha256:90aefa650e328cffb0893bbd4c236cfd44c48bc1f2d0b525ecc53c3187b653bb"}, @@ -1779,8 +1775,8 @@ rich = [ {file = "rich-13.3.1.tar.gz", hash = "sha256:125d96d20c92b946b983d0d392b84ff945461e5a06d3867e9f9e575f8697b67f"}, ] setuptools = [ - {file = "setuptools-67.1.0-py3-none-any.whl", hash = "sha256:a7687c12b444eaac951ea87a9627c4f904ac757e7abdc5aac32833234af90378"}, - {file = "setuptools-67.1.0.tar.gz", hash = "sha256:e261cdf010c11a41cb5cb5f1bf3338a7433832029f559a6a7614bd42a967c300"}, + {file = "setuptools-67.2.0-py3-none-any.whl", hash = "sha256:16ccf598aab3b506593c17378473978908a2734d7336755a8769b480906bec1c"}, + {file = "setuptools-67.2.0.tar.gz", hash = "sha256:b440ee5f7e607bb8c9de15259dba2583dd41a38879a7abc1d43a71c59524da48"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -1900,8 +1896,8 @@ urllib3 = [ {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, ] virtualenv = [ - {file = "virtualenv-20.17.1-py3-none-any.whl", hash = "sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4"}, - {file = "virtualenv-20.17.1.tar.gz", hash = "sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"}, + {file = "virtualenv-20.18.0-py3-none-any.whl", hash = "sha256:9d61e4ec8d2c0345dab329fb825eb05579043766a4b26a2f66b28948de68c722"}, + {file = "virtualenv-20.18.0.tar.gz", hash = "sha256:f262457a4d7298a6b733b920a196bf8b46c8af15bf1fd9da7142995eff15118e"}, ] watchdog = [ {file = "watchdog-2.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260"}, @@ -2010,6 +2006,6 @@ yarl = [ {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"}, ] zipp = [ - {file = "zipp-3.12.0-py3-none-any.whl", hash = "sha256:9eb0a4c5feab9b08871db0d672745b53450d7f26992fd1e4653aa43345e97b86"}, - {file = "zipp-3.12.0.tar.gz", hash = "sha256:73efd63936398aac78fd92b6f4865190119d6c91b531532e798977ea8dd402eb"}, + {file = "zipp-3.12.1-py3-none-any.whl", hash = "sha256:6c4fe274b8f85ec73c37a8e4e3fa00df9fb9335da96fb789e3b96b318e5097b3"}, + {file = "zipp-3.12.1.tar.gz", hash = "sha256:a3cac813d40993596b39ea9e93a18e8a2076d5c378b8bc88ec32ab264e04ad02"}, ] diff --git a/pyproject.toml b/pyproject.toml index 87338775a..fd925a42e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ python = "^3.7" rich = ">12.6.0" #rich = {path="../rich", develop=true} importlib-metadata = "^4.11.3" -typing-extensions = { version = "^4.0.0", python = "<3.10" } +typing-extensions = "^4.0.0" # Dependencies below are required for devtools only aiohttp = { version = ">=3.8.1", optional = true } @@ -50,7 +50,7 @@ dev = ["aiohttp", "click", "msgpack"] [tool.poetry.group.dev.dependencies] pytest = "^7.1.3" black = "^23.1.0" -mypy = "^0.990" +mypy = "^1.0.0" pytest-cov = "^2.12.1" mkdocs = "^1.3.0" mkdocstrings = {extras = ["python"], version = "^0.20.0"} diff --git a/src/textual/__init__.py b/src/textual/__init__.py index 4e233dc04..8f10728b4 100644 --- a/src/textual/__init__.py +++ b/src/textual/__init__.py @@ -4,10 +4,14 @@ import inspect import rich.repr from rich.console import RenderableType +from typing import Callable, TYPE_CHECKING from ._context import active_app from ._log import LogGroup, LogVerbosity -from ._typing import TypeAlias + + +if TYPE_CHECKING: + from typing_extensions import TypeAlias __all__ = ["log", "panic", "__version__"] # type: ignore diff --git a/src/textual/_animator.py b/src/textual/_animator.py index 6ace3dcff..9340e1fd9 100644 --- a/src/textual/_animator.py +++ b/src/textual/_animator.py @@ -1,23 +1,19 @@ from __future__ import annotations import asyncio -import sys from abc import ABC, abstractmethod from dataclasses import dataclass from functools import partial from typing import TYPE_CHECKING, Any, Callable, TypeVar +from typing_extensions import Protocol, runtime_checkable + from . import _clock from ._callback import invoke from ._easing import DEFAULT_EASING, EASING from ._types import CallbackType from .timer import Timer -if sys.version_info >= (3, 8): - from typing import Protocol, runtime_checkable -else: # pragma: no cover - from typing_extensions import Protocol, runtime_checkable - if TYPE_CHECKING: from textual.app import App diff --git a/src/textual/_border.py b/src/textual/_border.py index b14c2c6fd..2feb33df0 100644 --- a/src/textual/_border.py +++ b/src/textual/_border.py @@ -2,13 +2,16 @@ from __future__ import annotations from functools import lru_cache from typing import cast, Tuple, Union +from typing import TYPE_CHECKING from rich.segment import Segment from rich.style import Style from .color import Color from .css.types import EdgeStyle, EdgeType -from ._typing import TypeAlias + +if TYPE_CHECKING: + from typing_extensions import TypeAlias INNER = 1 OUTER = 2 diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index e19762189..a74910d78 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -26,12 +26,12 @@ from . import errors from ._cells import cell_len from ._loop import loop_last from .strip import Strip -from ._typing import TypeAlias from .geometry import NULL_OFFSET, Offset, Region, Size if TYPE_CHECKING: from .widget import Widget + from typing_extensions import TypeAlias class ReflowResult(NamedTuple): diff --git a/src/textual/_layout.py b/src/textual/_layout.py index 86543f322..22c7129cd 100644 --- a/src/textual/_layout.py +++ b/src/textual/_layout.py @@ -4,13 +4,13 @@ from abc import ABC, abstractmethod from typing import ClassVar, NamedTuple, TYPE_CHECKING from .geometry import Region, Size, Spacing -from ._typing import TypeAlias if TYPE_CHECKING: + from typing_extensions import TypeAlias from .widget import Widget ArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget]]" -DockArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget], Spacing]]" +DockArrangeResult: TypeAlias = "tuple[list[WidgetPlacement], set[Widget], Spacing]" class WidgetPlacement(NamedTuple): diff --git a/src/textual/_layout_resolve.py b/src/textual/_layout_resolve.py index 156550114..d94cfec7f 100644 --- a/src/textual/_layout_resolve.py +++ b/src/textual/_layout_resolve.py @@ -1,15 +1,10 @@ from __future__ import annotations from dataclasses import dataclass -import sys from fractions import Fraction -from typing import cast, Sequence +from typing import Sequence, cast - -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from typing_extensions import Protocol # pragma: no cover +from typing_extensions import Protocol class EdgeProtocol(Protocol): diff --git a/src/textual/_resolve.py b/src/textual/_resolve.py index a52578704..5a6961381 100644 --- a/src/textual/_resolve.py +++ b/src/textual/_resolve.py @@ -1,9 +1,10 @@ from __future__ import annotations -import sys from fractions import Fraction from itertools import accumulate -from typing import cast, Sequence, TYPE_CHECKING +from typing import Sequence, cast, TYPE_CHECKING + +from typing_extensions import Literal from .box_model import BoxModel from .css.scalar import Scalar @@ -13,12 +14,6 @@ if TYPE_CHECKING: from .widget import Widget -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - - def resolve( dimensions: Sequence[Scalar], total: int, diff --git a/src/textual/_styles_cache.py b/src/textual/_styles_cache.py index 4a8a74208..77cbfb4de 100644 --- a/src/textual/_styles_cache.py +++ b/src/textual/_styles_cache.py @@ -2,7 +2,7 @@ from __future__ import annotations from functools import lru_cache from sys import intern -from typing import TYPE_CHECKING, Callable, Iterable, List +from typing import TYPE_CHECKING, Callable, Iterable from rich.segment import Segment from rich.style import Style @@ -11,7 +11,6 @@ from ._border import get_box, render_row from ._filter import LineFilter from ._opacity import _apply_opacity from ._segment_tools import line_pad, line_trim -from ._typing import TypeAlias from .color import Color from .geometry import Region, Size, Spacing from .renderables.text_opacity import TextOpacity @@ -19,6 +18,8 @@ from .renderables.tint import Tint from .strip import Strip if TYPE_CHECKING: + from typing import TypeAlias + from .css.styles import StylesBase from .widget import Widget diff --git a/src/textual/_types.py b/src/textual/_types.py index 56a3efd33..5db2e27f3 100644 --- a/src/textual/_types.py +++ b/src/textual/_types.py @@ -1,9 +1,7 @@ -from typing import Awaitable, Callable, List, TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Awaitable, Callable, List, Union from rich.segment import Segment - -from ._typing import Protocol - +from typing_extensions import Protocol if TYPE_CHECKING: from .message import Message diff --git a/src/textual/_typing.py b/src/textual/_typing.py deleted file mode 100644 index 340137d3e..000000000 --- a/src/textual/_typing.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -if sys.version_info >= (3, 10): - from typing import TypeAlias -else: # pragma: no cover - from typing_extensions import TypeAlias - -if sys.version_info >= (3, 8): - from typing import Final, Literal, Protocol, TypedDict -else: - from typing_extensions import Final, Literal, Protocol, TypedDict - -__all__ = ["TypeAlias", "Final", "Literal", "Protocol", "TypedDict"] diff --git a/src/textual/app.py b/src/textual/app.py index 6abf74839..d6a0d766d 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -46,11 +46,12 @@ from ._animator import DEFAULT_EASING, Animatable, Animator, EasingFunction from ._ansi_sequences import SYNC_END, SYNC_START from ._asyncio import create_task from ._callback import invoke -from ._context import active_app, active_message_pump +from ._context import active_app from ._event_broker import NoHandler, extract_handler_actions from ._filter import LineFilter, Monochrome from ._path import _make_path_object_relative -from ._typing import Final, TypeAlias + +from ._wait import wait_for_idle from .actions import SkipAction from .await_remove import AwaitRemove from .binding import Binding, Bindings @@ -68,10 +69,11 @@ from .messages import CallbackType from .reactive import Reactive from .renderables.blank import Blank from .screen import Screen -from ._wait import wait_for_idle from .widget import AwaitMount, Widget if TYPE_CHECKING: + from typing_extensions import Coroutine, Final, TypeAlias + from .devtools.client import DevtoolsClient from .pilot import Pilot diff --git a/src/textual/binding.py b/src/textual/binding.py index 8864a7943..acf6b0345 100644 --- a/src/textual/binding.py +++ b/src/textual/binding.py @@ -1,12 +1,14 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Iterable, MutableMapping +from typing import TYPE_CHECKING, Iterable, MutableMapping import rich.repr -from textual.keys import _character_to_key -from textual._typing import TypeAlias +from .keys import _character_to_key + +if TYPE_CHECKING: + from typing_extensions import TypeAlias BindingType: TypeAlias = "Binding | tuple[str, str, str]" diff --git a/src/textual/css/_help_text.py b/src/textual/css/_help_text.py index 1541706fd..e0f3b60ed 100644 --- a/src/textual/css/_help_text.py +++ b/src/textual/css/_help_text.py @@ -3,10 +3,11 @@ from __future__ import annotations from dataclasses import dataclass from typing import Iterable, Sequence -from textual._typing import Literal -from textual.color import ColorParseError -from textual.css._help_renderables import Example, Bullet, HelpText -from textual.css.constants import ( +from typing_extensions import Literal + +from ..color import ColorParseError +from ._help_renderables import Example, Bullet, HelpText +from .constants import ( VALID_BORDER, VALID_LAYOUT, VALID_ALIGN_HORIZONTAL, @@ -125,7 +126,10 @@ def _spacing_examples(property_name: str) -> ContextSpecificBullets: def property_invalid_value_help_text( - property_name: str, context: StylingContext, *, suggested_property_name: str = None + property_name: str, + context: StylingContext, + *, + suggested_property_name: str | None = None, ) -> HelpText: """Help text to show when the user supplies an invalid value for CSS property property. @@ -300,7 +304,7 @@ def color_property_help_text( property_name: str, context: StylingContext, *, - error: Exception = None, + error: Exception | None = None, ) -> HelpText: """Help text to show when the user supplies an invalid value for a color property. For example, an unparseable color string. diff --git a/src/textual/css/_style_properties.py b/src/textual/css/_style_properties.py index 882be31cf..f3e5908a6 100644 --- a/src/textual/css/_style_properties.py +++ b/src/textual/css/_style_properties.py @@ -10,7 +10,14 @@ when setting and getting. from __future__ import annotations from operator import attrgetter -from typing import TYPE_CHECKING, Generic, Iterable, NamedTuple, TypeVar, cast +from typing import ( + TYPE_CHECKING, + Generic, + Iterable, + NamedTuple, + TypeVar, + cast, +) import rich.errors import rich.repr diff --git a/src/textual/css/constants.py b/src/textual/css/constants.py index 48aa85147..c3c073940 100644 --- a/src/textual/css/constants.py +++ b/src/textual/css/constants.py @@ -2,14 +2,13 @@ from __future__ import annotations import typing from ..geometry import Spacing -from .._typing import Final if typing.TYPE_CHECKING: - from .types import EdgeType + from typing_extensions import Final VALID_VISIBILITY: Final = {"visible", "hidden"} VALID_DISPLAY: Final = {"block", "none"} -VALID_BORDER: Final[set[EdgeType]] = { +VALID_BORDER: Final = { "none", "hidden", "ascii", @@ -33,7 +32,14 @@ VALID_BOX_SIZING: Final = {"border-box", "content-box"} VALID_OVERFLOW: Final = {"scroll", "hidden", "auto"} VALID_ALIGN_HORIZONTAL: Final = {"left", "center", "right"} VALID_ALIGN_VERTICAL: Final = {"top", "middle", "bottom"} -VALID_TEXT_ALIGN: Final = {"start", "end", "left", "right", "center", "justify"} +VALID_TEXT_ALIGN: Final = { + "start", + "end", + "left", + "right", + "center", + "justify", +} VALID_SCROLLBAR_GUTTER: Final = {"auto", "stable"} VALID_STYLE_FLAGS: Final = { "b", @@ -53,4 +59,5 @@ VALID_STYLE_FLAGS: Final = { "uu", } + NULL_SPACING: Final = Spacing.all(0) diff --git a/src/textual/css/parse.py b/src/textual/css/parse.py index 1b98a56f6..265458fa9 100644 --- a/src/textual/css/parse.py +++ b/src/textual/css/parse.py @@ -4,8 +4,6 @@ from functools import lru_cache from pathlib import PurePath from typing import Iterator, Iterable, NoReturn -from rich import print - from .errors import UnresolvedVariableError from .types import Specificity3 from ._styles_builder import StylesBuilder, DeclarationError diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 5f7ff7235..423f1889f 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -4,13 +4,15 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, field from functools import lru_cache from operator import attrgetter -from typing import TYPE_CHECKING, Any, Iterable, NamedTuple, cast +from typing import Iterable, cast +from typing import TYPE_CHECKING, Any, NamedTuple import rich.repr from rich.style import Style +from typing_extensions import TypedDict +from .._animator import DEFAULT_EASING, Animatable, BoundAnimator, EasingFunction from .._types import CallbackType -from .._animator import BoundAnimator, DEFAULT_EASING, Animatable, EasingFunction from ..color import Color from ..geometry import Offset, Spacing from ._style_properties import ( @@ -41,8 +43,8 @@ from .constants import ( VALID_DISPLAY, VALID_OVERFLOW, VALID_SCROLLBAR_GUTTER, - VALID_VISIBILITY, VALID_TEXT_ALIGN, + VALID_VISIBILITY, ) from .scalar import Scalar, ScalarOffset, Unit from .scalar_animation import ScalarAnimation @@ -57,10 +59,9 @@ from .types import ( ScrollbarGutter, Specificity3, Specificity6, - Visibility, TextAlign, + Visibility, ) -from .._typing import TypedDict if TYPE_CHECKING: from .._layout import Layout diff --git a/src/textual/css/types.py b/src/textual/css/types.py index b0f78c796..60a1d2f5f 100644 --- a/src/textual/css/types.py +++ b/src/textual/css/types.py @@ -1,9 +1,9 @@ from __future__ import annotations from typing import Tuple +from typing_extensions import Literal from ..color import Color -from .._typing import Literal Edge = Literal["top", "right", "bottom", "left"] DockEdge = Literal["top", "right", "bottom", "left", ""] diff --git a/src/textual/devtools/renderables.py b/src/textual/devtools/renderables.py index cd1ca7f54..a8ddb166e 100644 --- a/src/textual/devtools/renderables.py +++ b/src/textual/devtools/renderables.py @@ -5,7 +5,6 @@ from pathlib import Path from typing import Iterable from importlib_metadata import version - from rich.align import Align from rich.console import Console, ConsoleOptions, RenderResult from rich.markup import escape @@ -15,8 +14,9 @@ from rich.style import Style from rich.styled import Styled from rich.table import Table from rich.text import Text +from typing_extensions import Literal + from textual._log import LogGroup -from textual._typing import Literal DevConsoleMessageLevel = Literal["info", "warning", "error"] diff --git a/src/textual/dom.py b/src/textual/dom.py index fd2ef1ae4..b6129daed 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -40,8 +40,9 @@ if TYPE_CHECKING: from .css.query import DOMQuery from .screen import Screen from .widget import Widget + from typing_extensions import TypeAlias -from textual._typing import Literal, TypeAlias +from typing_extensions import Literal _re_identifier = re.compile(IDENTIFIER) diff --git a/src/textual/features.py b/src/textual/features.py index 7ac0d9916..9799363cd 100644 --- a/src/textual/features.py +++ b/src/textual/features.py @@ -1,7 +1,11 @@ from __future__ import annotations -from typing import cast -from textual._typing import Final, Literal +from typing import TYPE_CHECKING, cast + +from typing_extensions import Literal + +if TYPE_CHECKING: + from typing_extensions import Final FEATURES: Final = {"devtools", "debug", "headless"} diff --git a/src/textual/geometry.py b/src/textual/geometry.py index 5079244d8..f2f8ef3d8 100644 --- a/src/textual/geometry.py +++ b/src/textual/geometry.py @@ -8,9 +8,20 @@ from __future__ import annotations from functools import lru_cache from operator import attrgetter, itemgetter -from typing import Any, Collection, NamedTuple, Tuple, TypeVar, Union, cast +from typing import ( + Any, + Collection, + NamedTuple, + Tuple, + TypeVar, + Union, + cast, + TYPE_CHECKING, +) + +if TYPE_CHECKING: + from typing_extensions import TypeAlias -from textual._typing import TypeAlias SpacingDimensions: TypeAlias = Union[ int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int] diff --git a/src/textual/screen.py b/src/textual/screen.py index 06222d687..3bed42f18 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterable, Iterator +from typing import Iterable, Iterator, TYPE_CHECKING import rich.repr from rich.console import RenderableType @@ -15,11 +15,12 @@ from .dom import DOMNode from .timer import Timer from ._types import CallbackType from .geometry import Offset, Region, Size -from ._typing import Final from .reactive import Reactive from .renderables.blank import Blank from .widget import Widget +if TYPE_CHECKING: + from typing_extensions import Final # Screen updates will be batched so that they don't happen more often than 120 times per second: UPDATE_PERIOD: Final[float] = 1 / 120 diff --git a/src/textual/widgets/_button.py b/src/textual/widgets/_button.py index 164ca38ef..12518445b 100644 --- a/src/textual/widgets/_button.py +++ b/src/textual/widgets/_button.py @@ -2,6 +2,7 @@ from __future__ import annotations from functools import partial from typing import cast +from typing_extensions import Literal import rich.repr from rich.console import RenderableType @@ -12,7 +13,7 @@ from ..css._error_tools import friendly_list from ..message import Message from ..reactive import Reactive from ..widgets import Static -from .._typing import Literal + ButtonVariant = Literal["default", "primary", "success", "warning", "error"] _VALID_BUTTON_VARIANTS = {"default", "primary", "success", "warning", "error"} diff --git a/src/textual/widgets/_data_table.py b/src/textual/widgets/_data_table.py index 9d7f0dda4..a9e198420 100644 --- a/src/textual/widgets/_data_table.py +++ b/src/textual/widgets/_data_table.py @@ -11,8 +11,6 @@ from typing import ( TypeVar, cast, NamedTuple, - Callable, - Sequence, Any, ) diff --git a/src/textual/widgets/_list_view.py b/src/textual/widgets/_list_view.py index 87675ec47..a4a530e2e 100644 --- a/src/textual/widgets/_list_view.py +++ b/src/textual/widgets/_list_view.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import ClassVar -from textual import events from textual.await_remove import AwaitRemove from textual.binding import Binding, BindingType from textual.containers import Vertical diff --git a/src/textual/widgets/_placeholder.py b/src/textual/widgets/_placeholder.py index 755f85a8b..d111d906b 100644 --- a/src/textual/widgets/_placeholder.py +++ b/src/textual/widgets/_placeholder.py @@ -2,11 +2,13 @@ from __future__ import annotations from itertools import cycle +from typing_extensions import Literal + from .. import events from ..css._error_tools import friendly_list from ..reactive import Reactive, reactive from ..widget import Widget, RenderResult -from .._typing import Literal + PlaceholderVariant = Literal["default", "size", "text"] _VALID_PLACEHOLDER_VARIANTS_ORDERED: list[PlaceholderVariant] = [ diff --git a/src/textual/widgets/_text_log.py b/src/textual/widgets/_text_log.py index 1c116c0b4..f78934e70 100644 --- a/src/textual/widgets/_text_log.py +++ b/src/textual/widgets/_text_log.py @@ -10,10 +10,10 @@ from rich.protocol import is_renderable from rich.segment import Segment from rich.text import Text -from ..reactive import var -from ..geometry import Size, Region -from ..scroll_view import ScrollView from .._cache import LRUCache +from ..geometry import Region, Size +from ..reactive import var +from ..scroll_view import ScrollView from ..strip import Strip diff --git a/src/textual/widgets/_tree.py b/src/textual/widgets/_tree.py index c7bf07781..9421d47ad 100644 --- a/src/textual/widgets/_tree.py +++ b/src/textual/widgets/_tree.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import ClassVar, Generic, NewType, TypeVar +from typing import TYPE_CHECKING, ClassVar, Generic, NewType, TypeVar import rich.repr from rich.style import NULL_STYLE, Style @@ -9,11 +9,10 @@ from rich.text import Text, TextType from .. import events from .._cache import LRUCache +from .._immutable_sequence_view import ImmutableSequenceView from .._loop import loop_last from .._segment_tools import line_pad from .._types import MessageTarget -from .._typing import TypeAlias -from .._immutable_sequence_view import ImmutableSequenceView from ..binding import Binding, BindingType from ..geometry import Region, Size, clamp from ..message import Message @@ -21,6 +20,9 @@ from ..reactive import reactive, var from ..scroll_view import ScrollView from ..strip import Strip +if TYPE_CHECKING: + from typing_extensions import TypeAlias + NodeID = NewType("NodeID", int) TreeDataType = TypeVar("TreeDataType") EventTreeDataType = TypeVar("EventTreeDataType") diff --git a/tests/css/test_stylesheet.py b/tests/css/test_stylesheet.py index be54fe936..c582bc0d8 100644 --- a/tests/css/test_stylesheet.py +++ b/tests/css/test_stylesheet.py @@ -4,8 +4,7 @@ from typing import Any import pytest from textual.color import Color -from textual.css._help_renderables import HelpText -from textual.css.stylesheet import Stylesheet, StylesheetParseError, CssSource +from textual.css.stylesheet import CssSource, Stylesheet, StylesheetParseError from textual.css.tokenizer import TokenError from textual.dom import DOMNode from textual.geometry import Spacing diff --git a/tests/snapshot_tests/conftest.py b/tests/snapshot_tests/conftest.py index 127d53596..c5d9ee21b 100644 --- a/tests/snapshot_tests/conftest.py +++ b/tests/snapshot_tests/conftest.py @@ -118,7 +118,6 @@ def pytest_sessionfinish( diffs: List[SvgSnapshotDiff] = [] num_snapshots_passing = 0 for item in session.items: - # Grab the data our fixture attached to the pytest node num_snapshots_passing += int(item.stash.get(TEXTUAL_SNAPSHOT_PASS, False)) snapshot_svg = item.stash.get(TEXTUAL_SNAPSHOT_SVG_KEY, None) diff --git a/tests/test_immutable_sequence_view.py b/tests/test_immutable_sequence_view.py index 500eaa6d0..fd691798c 100644 --- a/tests/test_immutable_sequence_view.py +++ b/tests/test_immutable_sequence_view.py @@ -3,6 +3,7 @@ import pytest from typing import Sequence from textual._immutable_sequence_view import ImmutableSequenceView + def wrap(source: Sequence[int]) -> ImmutableSequenceView[int]: """Wrap a sequence of integers inside an immutable sequence view.""" return ImmutableSequenceView[int](source)