From a6eed1a44639aabcb3afeecf96f75cdb2e052a9b Mon Sep 17 00:00:00 2001 From: Marcel Wilson Date: Thu, 15 Feb 2024 15:44:24 -0600 Subject: [PATCH] using `Self` now that it's available in typing_extensions (#48) * using `Self` now that it's available in typing_extensions * updated ruff configuration to move all possible imports into TYPE_CHECKING --- Makefile | 18 +- poetry.lock | 282 +++++++++--------- pyproject.toml | 23 +- screenpy_selenium/abilities/browse_the_web.py | 24 +- screenpy_selenium/actions/clear.py | 19 +- screenpy_selenium/actions/click.py | 21 +- screenpy_selenium/actions/double_click.py | 27 +- screenpy_selenium/actions/enter.py | 37 +-- screenpy_selenium/actions/enter_2fa_token.py | 17 +- screenpy_selenium/actions/hold_down.py | 19 +- screenpy_selenium/actions/move_mouse.py | 37 +-- screenpy_selenium/actions/open.py | 15 +- screenpy_selenium/actions/release.py | 15 +- .../actions/respond_to_the_prompt.py | 13 +- screenpy_selenium/actions/right_click.py | 27 +- screenpy_selenium/actions/save_console_log.py | 17 +- screenpy_selenium/actions/save_screenshot.py | 17 +- screenpy_selenium/actions/select.py | 37 +-- screenpy_selenium/actions/switch_to.py | 15 +- screenpy_selenium/actions/wait.py | 39 ++- screenpy_selenium/common.py | 4 +- screenpy_selenium/questions/list.py | 19 +- screenpy_selenium/questions/number.py | 13 +- screenpy_selenium/questions/selected.py | 23 +- screenpy_selenium/questions/text.py | 19 +- screenpy_selenium/target.py | 33 +- tests/test_actions.py | 14 +- tests/test_questions.py | 6 +- 28 files changed, 370 insertions(+), 480 deletions(-) diff --git a/Makefile b/Makefile index 2054540..b47e564 100644 --- a/Makefile +++ b/Makefile @@ -23,16 +23,10 @@ local_screenpy: black-check: black --check . -black: +black-fix: black . -isort-check: - isort . --check - -isort: - isort . - -ruff: +ruff-check: ruff check . ruff-fix: @@ -41,12 +35,10 @@ ruff-fix: mypy: mypy . -lint: isort-check ruff mypy - -.PHONY: black-check black isort-check isort ruff ruff-fix mypy lint +.PHONY: black-check black-fix ruff-check ruff-fix mypy -pre-check-in: black-check lint +pre-check-in: black-check ruff-check mypy -pre-check-in-fix: black isort ruff-fix mypy +pre-check-in-fix: black-fix ruff-fix mypy .PHONY: pre-check-in pre-check-in-fix diff --git a/poetry.lock b/poetry.lock index 3f52117..5313bf9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -63,33 +63,33 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "black" -version = "24.1.1" +version = "24.2.0" description = "The uncompromising code formatter." optional = true python-versions = ">=3.8" files = [ - {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"}, - {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"}, - {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"}, - {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"}, - {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, - {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, - {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, - {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, - {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"}, - {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"}, - {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"}, - {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"}, - {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"}, - {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"}, - {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"}, - {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"}, - {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"}, - {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"}, - {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"}, - {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"}, - {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, - {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, ] [package.dependencies] @@ -468,13 +468,13 @@ files = [ [[package]] name = "identify" -version = "2.5.33" +version = "2.5.34" description = "File identification library for Python" optional = true python-versions = ">=3.8" files = [ - {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, - {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, + {file = "identify-2.5.34-py2.py3-none-any.whl", hash = "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed"}, + {file = "identify-2.5.34.tar.gz", hash = "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d"}, ] [package.extras] @@ -532,20 +532,6 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = true -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - [[package]] name = "jinja2" version = "3.1.3" @@ -801,18 +787,18 @@ files = [ [[package]] name = "pydantic" -version = "2.6.0" +version = "2.6.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"}, - {file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"}, + {file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"}, + {file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.16.1" +pydantic-core = "2.16.2" typing-extensions = ">=4.6.1" [package.extras] @@ -820,90 +806,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.16.1" +version = "2.16.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"}, - {file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"}, - {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"}, - {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"}, - {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"}, - {file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"}, - {file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"}, - {file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"}, - {file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"}, - {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"}, - {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"}, - {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"}, - {file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"}, - {file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"}, - {file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"}, - {file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"}, - {file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"}, - {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"}, - {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"}, - {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"}, - {file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"}, - {file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"}, - {file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"}, - {file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"}, - {file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"}, - {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"}, - {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"}, - {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"}, - {file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"}, - {file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"}, - {file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"}, - {file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"}, - {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"}, - {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"}, - {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"}, - {file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"}, - {file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"}, - {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"}, - {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"}, - {file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"}, + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"}, + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"}, + {file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"}, + {file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"}, + {file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"}, + {file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"}, + {file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"}, + {file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"}, + {file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"}, + {file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f"}, + {file = "pydantic_core-2.16.2-cp38-none-win32.whl", hash = "sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a"}, + {file = "pydantic_core-2.16.2-cp38-none-win_amd64.whl", hash = "sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2"}, + {file = "pydantic_core-2.16.2-cp39-none-win32.whl", hash = "sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469"}, + {file = "pydantic_core-2.16.2-cp39-none-win_amd64.whl", hash = "sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"}, + {file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"}, ] [package.dependencies] @@ -1128,28 +1114,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.2.0" +version = "0.2.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = true python-versions = ">=3.7" files = [ - {file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:638ea3294f800d18bae84a492cb5a245c8d29c90d19a91d8e338937a4c27fca0"}, - {file = "ruff-0.2.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3ff35433fcf4dff6d610738712152df6b7d92351a1bde8e00bd405b08b3d5759"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf9faafbdcf4f53917019f2c230766da437d4fd5caecd12ddb68bb6a17d74399"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8153a3e4128ed770871c47545f1ae7b055023e0c222ff72a759f5a341ee06483"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a75a98ae989a27090e9c51f763990ad5bbc92d20626d54e9701c7fe597f399"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:87057dd2fdde297130ff99553be8549ca38a2965871462a97394c22ed2dfc19d"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d232f99d3ab00094ebaf88e0fb7a8ccacaa54cc7fa3b8993d9627a11e6aed7a"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3c641f95f435fc6754b05591774a17df41648f0daf3de0d75ad3d9f099ab92"}, - {file = "ruff-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3826fb34c144ef1e171b323ed6ae9146ab76d109960addca730756dc19dc7b22"}, - {file = "ruff-0.2.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eceab7d85d09321b4de18b62d38710cf296cb49e98979960a59c6b9307c18cfe"}, - {file = "ruff-0.2.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:30ad74687e1f4a9ff8e513b20b82ccadb6bd796fe5697f1e417189c5cde6be3e"}, - {file = "ruff-0.2.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a7e3818698f8460bd0f8d4322bbe99db8327e9bc2c93c789d3159f5b335f47da"}, - {file = "ruff-0.2.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:edf23041242c48b0d8295214783ef543847ef29e8226d9f69bf96592dba82a83"}, - {file = "ruff-0.2.0-py3-none-win32.whl", hash = "sha256:e155147199c2714ff52385b760fe242bb99ea64b240a9ffbd6a5918eb1268843"}, - {file = "ruff-0.2.0-py3-none-win_amd64.whl", hash = "sha256:ba918e01cdd21e81b07555564f40d307b0caafa9a7a65742e98ff244f5035c59"}, - {file = "ruff-0.2.0-py3-none-win_arm64.whl", hash = "sha256:3fbaff1ba9564a2c5943f8f38bc221f04bac687cc7485e45237579fee7ccda79"}, - {file = "ruff-0.2.0.tar.gz", hash = "sha256:63856b91837606c673537d2889989733d7dffde553828d3b0f0bacfa6def54be"}, + {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:dd81b911d28925e7e8b323e8d06951554655021df8dd4ac3045d7212ac4ba080"}, + {file = "ruff-0.2.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dc586724a95b7d980aa17f671e173df00f0a2eef23f8babbeee663229a938fec"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c92db7101ef5bfc18e96777ed7bc7c822d545fa5977e90a585accac43d22f18a"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13471684694d41ae0f1e8e3a7497e14cd57ccb7dd72ae08d56a159d6c9c3e30e"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a11567e20ea39d1f51aebd778685582d4c56ccb082c1161ffc10f79bebe6df35"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:00a818e2db63659570403e44383ab03c529c2b9678ba4ba6c105af7854008105"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be60592f9d218b52f03384d1325efa9d3b41e4c4d55ea022cd548547cc42cd2b"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbd2288890b88e8aab4499e55148805b58ec711053588cc2f0196a44f6e3d855"}, + {file = "ruff-0.2.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ef052283da7dec1987bba8d8733051c2325654641dfe5877a4022108098683"}, + {file = "ruff-0.2.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7022d66366d6fded4ba3889f73cd791c2d5621b2ccf34befc752cb0df70f5fad"}, + {file = "ruff-0.2.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0a725823cb2a3f08ee743a534cb6935727d9e47409e4ad72c10a3faf042ad5ba"}, + {file = "ruff-0.2.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0034d5b6323e6e8fe91b2a1e55b02d92d0b582d2953a2b37a67a2d7dedbb7acc"}, + {file = "ruff-0.2.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e5cb5526d69bb9143c2e4d2a115d08ffca3d8e0fddc84925a7b54931c96f5c02"}, + {file = "ruff-0.2.1-py3-none-win32.whl", hash = "sha256:6b95ac9ce49b4fb390634d46d6ece32ace3acdd52814671ccaf20b7f60adb232"}, + {file = "ruff-0.2.1-py3-none-win_amd64.whl", hash = "sha256:e3affdcbc2afb6f5bd0eb3130139ceedc5e3f28d206fe49f63073cb9e65988e0"}, + {file = "ruff-0.2.1-py3-none-win_arm64.whl", hash = "sha256:efababa8e12330aa94a53e90a81eb6e2d55f348bc2e71adbf17d9cad23c03ee6"}, + {file = "ruff-0.2.1.tar.gz", hash = "sha256:3b42b5d8677cd0c72b99fcaf068ffc62abb5a19e71b4a3b9cfa50658a0af02f1"}, ] [[package]] @@ -1217,18 +1203,18 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]} [[package]] name = "setuptools" -version = "69.0.3" +version = "69.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = true python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1544,10 +1530,10 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [extras] dev = ["pre-commit", "pytest", "tox"] -dev-all = ["black", "coverage", "isort", "mypy", "pre-commit", "pytest", "ruff", "sphinx", "tox"] +dev-all = ["black", "coverage", "mypy", "pre-commit", "pytest", "ruff", "sphinx", "tox"] test = ["coverage", "pytest"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "648a64ccefc72da3f713565feda1957416487f7f2576f7e7d520e42c4082425d" +content-hash = "58d914781efe541f9a8b6411a11276d3bcc2817e464a000da361624ff28d5034" diff --git a/pyproject.toml b/pyproject.toml index 9cf0ac3..16d87d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,6 @@ importlib_metadata = {version = "*", python = "3.8.*"} # convenience packages for development of screenpy only black = {version = "*", optional = true} coverage = {version = "*", optional = true} -isort = {version = "*", optional = true} mypy = {version = "*", optional = true} pre-commit = {version = "*", optional = true} pytest = {version = "*", optional = true} @@ -86,7 +85,6 @@ dev = [ dev_all = [ "black", "coverage", - "isort", "mypy", "pre-commit", "pytest", @@ -166,14 +164,11 @@ extend-safe-fixes = [ "EM101", "EM102", "TCH001", "TCH002", "TCH003", "TCH004", -# "SIM108" - # maybe? -# "F841", "C419", "D200", "D205", "D415", "PT003", "PT006", "PT018", "RET504", - "UP007", + "UP006", "UP007", ] [tool.ruff.lint.per-file-ignores] @@ -182,13 +177,10 @@ extend-safe-fixes = [ "FBT", # using a boolean as a test object is useful! "PLR", # likewise using specific numbers and strings in tests. ] -"tests/test_pacing.py" = [ - "FA100", # we are purposely testing pacing without future annotations. -] [tool.ruff.lint.isort] combine-as-imports = true -split-on-trailing-comma = true +split-on-trailing-comma = false known-first-party = ["screenpy_selenium", "tests"] @@ -204,12 +196,5 @@ ignore-overlong-task-comments = true convention = "google" -[tool.isort] -line_length = 88 -multi_line_output = 3 -include_trailing_comma = true -use_parentheses = true -combine_as_imports = true - -skip = [".idea", ".tox", "docs"] -src_paths = ["screenpy_selenium","tests"] +[tool.ruff.lint.flake8-type-checking] +strict = true diff --git a/screenpy_selenium/abilities/browse_the_web.py b/screenpy_selenium/abilities/browse_the_web.py index 7a8cd2a..43b2987 100644 --- a/screenpy_selenium/abilities/browse_the_web.py +++ b/screenpy_selenium/abilities/browse_the_web.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from selenium.webdriver import Chrome, Firefox, Remote, Safari @@ -11,13 +11,11 @@ if TYPE_CHECKING: from selenium.webdriver.remote.webdriver import WebDriver + from typing_extensions import Self DEFAULT_APPIUM_HUB_URL = "http://localhost:4723/wd/hub" -SelfBrowseTheWeb = TypeVar("SelfBrowseTheWeb", bound="BrowseTheWeb") - - class BrowseTheWeb: """Use Selenium to enable browsing the web with a web browser. @@ -35,22 +33,22 @@ class BrowseTheWeb: browser: WebDriver @classmethod - def using_chrome(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: + def using_chrome(cls) -> Self: """Create and use a default Chrome Selenium webdriver instance.""" return cls.using(browser=Chrome()) @classmethod - def using_firefox(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: + def using_firefox(cls) -> Self: """Create and use a default Firefox Selenium webdriver instance.""" return cls.using(browser=Firefox()) @classmethod - def using_safari(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: + def using_safari(cls) -> Self: """Create and use a default Safari Selenium webdriver instance.""" return cls.using(browser=Safari()) @classmethod - def using_ios(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: + def using_ios(cls) -> Self: """ Create and use a default Remote driver instance. @@ -83,7 +81,7 @@ def using_ios(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: return cls.using(browser=Remote(hub_url, IOS_CAPABILITIES)) @classmethod - def using_android(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: + def using_android(cls) -> Self: """ Create and use a default Remote driver instance. @@ -116,19 +114,19 @@ def using_android(cls: type[SelfBrowseTheWeb]) -> SelfBrowseTheWeb: return cls.using(browser=Remote(hub_url, ANDROID_CAPABILITIES)) @classmethod - def using(cls: type[SelfBrowseTheWeb], browser: WebDriver) -> SelfBrowseTheWeb: + def using(cls, browser: WebDriver) -> Self: """Provide an already-set-up WebDriver to use to browse the web.""" return cls(browser=browser) - def forget(self: SelfBrowseTheWeb) -> None: + def forget(self) -> None: """Quit the attached browser.""" self.browser.quit() - def __repr__(self: SelfBrowseTheWeb) -> str: + def __repr__(self) -> str: """Repr.""" return "Browse the Web" __str__ = __repr__ - def __init__(self: SelfBrowseTheWeb, browser: WebDriver) -> None: + def __init__(self, browser: WebDriver) -> None: self.browser = browser diff --git a/screenpy_selenium/actions/clear.py b/screenpy_selenium/actions/clear.py index ea0a621..2fb4a8f 100644 --- a/screenpy_selenium/actions/clear.py +++ b/screenpy_selenium/actions/clear.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import DeliveryError from screenpy.pacing import beat @@ -10,11 +10,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor + from typing_extensions import Self from ..target import Target -SelfClear = TypeVar("SelfClear", bound="Clear") - class Clear: """Clear the text from an input field. @@ -28,7 +27,7 @@ class Clear: """ @classmethod - def the_text_from_the(cls: type[SelfClear], target: Target) -> SelfClear: + def the_text_from_the(cls, target: Target) -> Self: """ Specify the Target from which to clear the text. @@ -39,23 +38,21 @@ def the_text_from_the(cls: type[SelfClear], target: Target) -> SelfClear: return cls(target=target) @classmethod - def the_text_from(cls: type[SelfClear], target: Target) -> SelfClear: + def the_text_from(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Clear.the_text_from_the`.""" return cls.the_text_from_the(target=target) @classmethod - def the_text_from_the_first_of_the( - cls: type[SelfClear], target: Target - ) -> SelfClear: + def the_text_from_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Clear.the_text_from_the`.""" return cls.the_text_from_the(target=target) - def describe(self: SelfClear) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Clear the text from the {self.target}." @beat("{} clears text from the {target}.") - def perform_as(self: SelfClear, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to clear the text from the input field.""" element = self.target.found_by(the_actor) @@ -68,5 +65,5 @@ def perform_as(self: SelfClear, the_actor: Actor) -> None: ) raise DeliveryError(msg) from e - def __init__(self: SelfClear, target: Target) -> None: + def __init__(self, target: Target) -> None: self.target = target diff --git a/screenpy_selenium/actions/click.py b/screenpy_selenium/actions/click.py index c9093ec..547997b 100644 --- a/screenpy_selenium/actions/click.py +++ b/screenpy_selenium/actions/click.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import DeliveryError, UnableToAct from screenpy.pacing import beat @@ -11,11 +11,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor from selenium.webdriver.common.action_chains import ActionChains + from typing_extensions import Self from ..target import Target -SelfClick = TypeVar("SelfClick", bound="Click") - class Click: """Click on an element! @@ -33,7 +32,7 @@ class Click: """ @classmethod - def on_the(cls: type[SelfClick], target: Target) -> SelfClick: + def on_the(cls, target: Target) -> Self: """ Target the element to click on. @@ -44,21 +43,21 @@ def on_the(cls: type[SelfClick], target: Target) -> SelfClick: return cls(target=target) @classmethod - def on(cls: type[SelfClick], target: Target) -> SelfClick: + def on(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Click.on_the`.""" return cls.on_the(target=target) @classmethod - def on_the_first_of_the(cls: type[SelfClick], target: Target) -> SelfClick: + def on_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Click.on_the`.""" return cls.on_the(target=target) - def describe(self: SelfClick) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Click on the {self.target}." @beat("{} clicks on the {target}.") - def perform_as(self: SelfClick, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to click on the element.""" if self.target is None: msg = ( @@ -79,9 +78,7 @@ def perform_as(self: SelfClick, the_actor: Actor) -> None: raise DeliveryError(msg) from e @beat("Click{description}!") - def add_to_chain( - self: SelfClick, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the Click Action to a Chain of Actions.""" if self.target is not None: the_element = self.target.found_by(the_actor) @@ -90,6 +87,6 @@ def add_to_chain( the_chain.click(on_element=the_element) - def __init__(self: SelfClick, target: Target | None = None) -> None: + def __init__(self, target: Target | None = None) -> None: self.target = target self.description = f" on the {target}" if target is not None else "" diff --git a/screenpy_selenium/actions/double_click.py b/screenpy_selenium/actions/double_click.py index c22728a..1a4186c 100644 --- a/screenpy_selenium/actions/double_click.py +++ b/screenpy_selenium/actions/double_click.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat from selenium.webdriver.common.action_chains import ActionChains @@ -11,11 +11,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor + from typing_extensions import Self from ..target import Target -SelfDoubleClick = TypeVar("SelfDoubleClick", bound="DoubleClick") - class DoubleClick: """Double-click on an element, or wherever the cursor currently is. @@ -33,7 +32,7 @@ class DoubleClick: target: Target | None @classmethod - def on_the(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick: + def on_the(cls, target: Target) -> Self: """ Target the element to double-click on. @@ -44,20 +43,16 @@ def on_the(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick: return cls(target=target) @classmethod - def on(cls: type[SelfDoubleClick], target: Target) -> SelfDoubleClick: + def on(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.DoubleClick.on_the`.""" return cls.on_the(target=target) @classmethod - def on_the_first_of_the( - cls: type[SelfDoubleClick], target: Target - ) -> SelfDoubleClick: + def on_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.DoubleClick.on_the`.""" return cls.on_the(target=target) - def _add_action_to_chain( - self: SelfDoubleClick, the_actor: Actor, the_chain: ActionChains - ) -> None: + def _add_action_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Private method to add this Action to the chain.""" if self.target is not None: the_element = self.target.found_by(the_actor) @@ -66,12 +61,12 @@ def _add_action_to_chain( the_chain.double_click(on_element=the_element) - def describe(self: SelfDoubleClick) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Double-click{self.description}." @beat("{} double-clicks{description}.") - def perform_as(self: SelfDoubleClick, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to double-click on the element.""" browser = the_actor.ability_to(BrowseTheWeb).browser the_chain = ActionChains(browser) # type: ignore[arg-type] @@ -79,12 +74,10 @@ def perform_as(self: SelfDoubleClick, the_actor: Actor) -> None: the_chain.perform() @beat("Double-click{description}!") - def add_to_chain( - self: SelfDoubleClick, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the DoubleClick Action to a Chain of Actions.""" self._add_action_to_chain(the_actor, the_chain) - def __init__(self: SelfDoubleClick, target: Target | None = None) -> None: + def __init__(self, target: Target | None = None) -> None: self.target = target self.description = f" on the {target}" if target is not None else "" diff --git a/screenpy_selenium/actions/enter.py b/screenpy_selenium/actions/enter.py index b5c9b0b..3895ca4 100644 --- a/screenpy_selenium/actions/enter.py +++ b/screenpy_selenium/actions/enter.py @@ -3,7 +3,7 @@ from __future__ import annotations from functools import partial -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import DeliveryError, UnableToAct from screenpy.pacing import aside, beat @@ -15,11 +15,10 @@ if TYPE_CHECKING: from screenpy import Actor from selenium.webdriver.common.action_chains import ActionChains + from typing_extensions import Self from ..target import Target -SelfEnter = TypeVar("SelfEnter", bound="Enter") - class Enter: """Enter text into an input field, or press specific keys. @@ -40,7 +39,7 @@ class Enter: text_to_log: str @classmethod - def the_text(cls: type[SelfEnter], text: str) -> SelfEnter: + def the_text(cls, text: str) -> Self: """Provide the text to enter into the field. Aliases: @@ -49,12 +48,12 @@ def the_text(cls: type[SelfEnter], text: str) -> SelfEnter: return cls(text=text) @classmethod - def the_keys(cls: type[SelfEnter], text: str) -> SelfEnter: + def the_keys(cls, text: str) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.the_text`.""" return cls.the_text(text=text) @classmethod - def the_secret(cls: type[SelfEnter], text: str) -> SelfEnter: + def the_secret(cls, text: str) -> Self: """ Provide the text to enter into the field, but mask it in logging. @@ -66,11 +65,11 @@ def the_secret(cls: type[SelfEnter], text: str) -> SelfEnter: return cls(text=text, mask=True) @classmethod - def the_password(cls: type[SelfEnter], text: str) -> SelfEnter: + def the_password(cls, text: str) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.the_secret`.""" return cls.the_secret(text=text) - def into_the(self: SelfEnter, target: Target) -> SelfEnter: + def into_the(self, target: Target) -> Self: """Target the element to enter text into. Aliases: @@ -81,19 +80,19 @@ def into_the(self: SelfEnter, target: Target) -> SelfEnter: self.target = target return self - def into(self: SelfEnter, target: Target) -> SelfEnter: + def into(self, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.into_the`.""" return self.into_the(target) - def on(self: SelfEnter, target: Target) -> SelfEnter: + def on(self, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.into_the`.""" return self.into_the(target) - def into_the_first_of_the(self: SelfEnter, target: Target) -> SelfEnter: + def into_the_first_of_the(self, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.into_the`.""" return self.into_the(target) - def then_hit(self: SelfEnter, *keys: str) -> SelfEnter: + def then_hit(self, *keys: str) -> Self: """Supply additional keys to hit after entering the text. Args: @@ -106,16 +105,16 @@ def then_hit(self: SelfEnter, *keys: str) -> SelfEnter: self.following_keys.extend(keys) return self - def then_press(self: SelfEnter, *keys: str) -> SelfEnter: + def then_press(self, *keys: str) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter.then_hit`.""" return self.then_hit(*keys) - def describe(self: SelfEnter) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f'Enter "{self.text_to_log}" into the {self.target}.' @beat('{} enters "{text_to_log}" into the {target}.') - def perform_as(self: SelfEnter, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to enter the text into the element.""" if self.target is None: msg = ( @@ -139,9 +138,7 @@ def perform_as(self: SelfEnter, the_actor: Actor) -> None: raise DeliveryError(msg) from e @beat(' Enter "{text_to_log}" into the {target}!') - def add_to_chain( - self: SelfEnter, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the Enter Action to a Chain of Actions.""" if self.target is None: send_keys = the_chain.send_keys @@ -154,9 +151,7 @@ def add_to_chain( send_keys(key) @pos_args_deprecated("mask") - def __init__( - self: SelfEnter, text: str, mask: bool = False # noqa: FBT001, FBT002 - ) -> None: + def __init__(self, text: str, mask: bool = False) -> None: # noqa: FBT001, FBT002 self.text = text self.target = None self.following_keys = [] diff --git a/screenpy_selenium/actions/enter_2fa_token.py b/screenpy_selenium/actions/enter_2fa_token.py index c490e01..32c56f8 100644 --- a/screenpy_selenium/actions/enter_2fa_token.py +++ b/screenpy_selenium/actions/enter_2fa_token.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat from screenpy_pyotp.abilities import AuthenticateWith2FA @@ -12,11 +12,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor from selenium.webdriver.common.action_chains import ActionChains + from typing_extensions import Self from ..target import Target -SelfEnter2FAToken = TypeVar("SelfEnter2FAToken", bound="Enter2FAToken") - class Enter2FAToken: """Enter the current two-factor authentication token into an input field. @@ -31,7 +30,7 @@ class Enter2FAToken: """ @classmethod - def into_the(cls: type[SelfEnter2FAToken], target: Target) -> SelfEnter2FAToken: + def into_the(cls, target: Target) -> Self: """ Target the element into which to enter the 2FA token. @@ -41,7 +40,7 @@ def into_the(cls: type[SelfEnter2FAToken], target: Target) -> SelfEnter2FAToken: return cls(target) @classmethod - def into(cls: type[SelfEnter2FAToken], target: Target) -> SelfEnter2FAToken: + def into(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Enter2FAToken.into_the`.""" return cls.into_the(target=target) @@ -50,18 +49,16 @@ def describe(self) -> str: return f"Enter a 2FA token into the {self.target}." @beat("{} enters their 2FA token into the {target}.") - def perform_as(self: SelfEnter2FAToken, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to enter their 2FA token into the element.""" token = the_actor.uses_ability_to(AuthenticateWith2FA).to_get_token() the_actor.attempts_to(Enter.the_text(token).into_the(self.target)) @beat("Enter their 2FA token into the {target}!") - def add_to_chain( - self: SelfEnter2FAToken, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the Enter2FAToken Action to a Chain of Actions.""" token = the_actor.uses_ability_to(AuthenticateWith2FA).to_get_token() the_chain.send_keys_to_element(self.target.found_by(the_actor), token) - def __init__(self: SelfEnter2FAToken, target: Target) -> None: + def __init__(self, target: Target) -> None: self.target = target diff --git a/screenpy_selenium/actions/hold_down.py b/screenpy_selenium/actions/hold_down.py index f19a2f7..32ffb48 100644 --- a/screenpy_selenium/actions/hold_down.py +++ b/screenpy_selenium/actions/hold_down.py @@ -3,7 +3,7 @@ from __future__ import annotations import platform -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import UnableToAct from screenpy.pacing import beat @@ -15,11 +15,10 @@ if TYPE_CHECKING: from screenpy import Actor from selenium.webdriver.common.action_chains import ActionChains + from typing_extensions import Self from ..target import Target -SelfHoldDown = TypeVar("SelfHoldDown", bound="HoldDown") - class HoldDown: """Hold down the specified key or left mouse button. @@ -48,7 +47,7 @@ class HoldDown: description: str @classmethod - def command_or_control_key(cls: type[SelfHoldDown]) -> SelfHoldDown: + def command_or_control_key(cls) -> Self: """ A convenience method for supporting multiple operating systems. @@ -60,25 +59,23 @@ def command_or_control_key(cls: type[SelfHoldDown]) -> SelfHoldDown: return cls(Keys.CONTROL) @classmethod - def left_mouse_button(cls: type[SelfHoldDown]) -> SelfHoldDown: + def left_mouse_button(cls) -> Self: """Hold down the left mouse button.""" return cls(lmb=True) - def on_the(self: SelfHoldDown, target: Target) -> SelfHoldDown: + def on_the(self, target: Target) -> Self: """Target an element to hold down left click on.""" self.target = target return self on = on_the - def describe(self: SelfHoldDown) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Hold down {self.description}." @beat("Hold down {description}!") - def add_to_chain( - self: SelfHoldDown, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the HoldDown Action to a Chain of Actions.""" if self.lmb: element = self.target.found_by(the_actor) if self.target else None @@ -91,7 +88,7 @@ def add_to_chain( @pos_args_deprecated("lmb") def __init__( - self: SelfHoldDown, + self, key: str | None = None, lmb: bool = False, # noqa: FBT001, FBT002 ) -> None: diff --git a/screenpy_selenium/actions/move_mouse.py b/screenpy_selenium/actions/move_mouse.py index 78152ed..0bac49a 100644 --- a/screenpy_selenium/actions/move_mouse.py +++ b/screenpy_selenium/actions/move_mouse.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import UnableToAct from screenpy.pacing import beat @@ -12,11 +12,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor + from typing_extensions import Self from ..target import Target -SelfMoveMouse = TypeVar("SelfMoveMouse", bound="MoveMouse") - class MoveMouse: """Move the mouse to a specific element or by a pixel offset. @@ -48,7 +47,7 @@ class MoveMouse: description: str @classmethod - def to_the(cls: type[SelfMoveMouse], target: Target) -> SelfMoveMouse: + def to_the(cls, target: Target) -> Self: """ Target an element to move the mouse to. @@ -61,46 +60,40 @@ def to_the(cls: type[SelfMoveMouse], target: Target) -> SelfMoveMouse: return cls(target=target, description=f"to the {target}") @classmethod - def on_the(cls: type[SelfMoveMouse], target: Target) -> SelfMoveMouse: + def on_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.MoveMouse.to_the`.""" return cls.to_the(target=target) @classmethod - def over_the(cls: type[SelfMoveMouse], target: Target) -> SelfMoveMouse: + def over_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.MoveMouse.to_the`.""" return cls.to_the(target=target) @classmethod - def over_the_first_of_the( - cls: type[SelfMoveMouse], target: Target - ) -> SelfMoveMouse: + def over_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.MoveMouse.to_the`.""" return cls.to_the(target=target) @classmethod - def to_the_first_of_the(cls: type[SelfMoveMouse], target: Target) -> SelfMoveMouse: + def to_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.MoveMouse.to_the`.""" return cls.to_the(target=target) @classmethod - def by_offset( - cls: type[SelfMoveMouse], x_offset: int, y_offset: int - ) -> SelfMoveMouse: + def by_offset(cls, x_offset: int, y_offset: int) -> Self: """Specify the offset by which to move the mouse.""" return cls( offset=(x_offset, y_offset), description=f"by an offset of ({x_offset}, {y_offset})", ) - def with_offset(self: SelfMoveMouse, x_offset: int, y_offset: int) -> SelfMoveMouse: + def with_offset(self, x_offset: int, y_offset: int) -> Self: """Specify the mouse should be moved to the element with an offset.""" self.offset = (x_offset, y_offset) self.description += f" offset by ({x_offset}, {y_offset})" return self - def _add_action_to_chain( - self: SelfMoveMouse, the_actor: Actor, the_chain: ActionChains - ) -> None: + def _add_action_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Private method to add this Action to the chain.""" if self.target is not None and self.offset is not None: the_chain.move_to_element_with_offset( @@ -117,12 +110,12 @@ def _add_action_to_chain( ) raise UnableToAct(msg) - def describe(self: SelfMoveMouse) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Move the mouse {self.description}." @beat("{} moves the mouse {description}.") - def perform_as(self: SelfMoveMouse, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to move the mouse.""" browser = the_actor.ability_to(BrowseTheWeb).browser the_chain = ActionChains(browser) # type: ignore[arg-type] @@ -130,14 +123,12 @@ def perform_as(self: SelfMoveMouse, the_actor: Actor) -> None: the_chain.perform() @beat("Move the mouse {description}!") - def add_to_chain( - self: SelfMoveMouse, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the MoveMouse Action to a Chain of Actions.""" self._add_action_to_chain(the_actor, the_chain) def __init__( - self: SelfMoveMouse, + self, offset: tuple[int, int] | None = None, target: Target | None = None, description: str = "", diff --git a/screenpy_selenium/actions/open.py b/screenpy_selenium/actions/open.py index 5980d0b..803ad09 100644 --- a/screenpy_selenium/actions/open.py +++ b/screenpy_selenium/actions/open.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat @@ -11,8 +11,7 @@ if TYPE_CHECKING: from screenpy import Actor - -SelfOpen = TypeVar("SelfOpen", bound="Open") + from typing_extensions import Self class Open: @@ -42,7 +41,7 @@ class Open: """ @classmethod - def their_browser_on(cls: type[SelfOpen], location: str | object) -> SelfOpen: + def their_browser_on(cls, location: str | object) -> Self: """ Provide a URL to visit. @@ -52,21 +51,21 @@ def their_browser_on(cls: type[SelfOpen], location: str | object) -> SelfOpen: return cls(location=location) @classmethod - def browser_on(cls: type[SelfOpen], location: str | object) -> SelfOpen: + def browser_on(cls, location: str | object) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Open.their_browser_on`.""" return cls.their_browser_on(location=location) - def describe(self: SelfOpen) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Visit {self.url}." @beat("{} visits {url}") - def perform_as(self: SelfOpen, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to visit the specified URL.""" browser = the_actor.ability_to(BrowseTheWeb).browser browser.get(self.url) - def __init__(self: SelfOpen, location: str | object) -> None: + def __init__(self, location: str | object) -> None: url = getattr(location, "url", location) url = f'{os.getenv("BASE_URL", "")}{url}' self.url = url diff --git a/screenpy_selenium/actions/release.py b/screenpy_selenium/actions/release.py index ca21f1c..2542c09 100644 --- a/screenpy_selenium/actions/release.py +++ b/screenpy_selenium/actions/release.py @@ -3,7 +3,7 @@ from __future__ import annotations import platform -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import UnableToAct from screenpy.pacing import beat @@ -15,8 +15,7 @@ if TYPE_CHECKING: from screenpy import Actor from selenium.webdriver.common.action_chains import ActionChains - -SelfRelease = TypeVar("SelfRelease", bound="Release") + from typing_extensions import Self class Release: @@ -44,7 +43,7 @@ class Release: the_kraken: str @classmethod - def command_or_control_key(cls: type[SelfRelease]) -> SelfRelease: + def command_or_control_key(cls) -> Self: """ A convenience method for supporting multiple operating systems. @@ -56,17 +55,17 @@ def command_or_control_key(cls: type[SelfRelease]) -> SelfRelease: return cls(key=Keys.CONTROL) @classmethod - def left_mouse_button(cls: type[SelfRelease]) -> SelfRelease: + def left_mouse_button(cls) -> Self: """Release the left mouse button.""" return cls(lmb=True) - def describe(self: SelfRelease) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" # darn, it doesn't work quite as well here. :P return f"Release {self.the_kraken}." @beat("Release {the_kraken}!") - def add_to_chain(self: SelfRelease, _: Actor, the_chain: ActionChains) -> None: + def add_to_chain(self, _: Actor, the_chain: ActionChains) -> None: """Add the Release Action to a Chain of Actions.""" if self.lmb: the_chain.release() @@ -78,7 +77,7 @@ def add_to_chain(self: SelfRelease, _: Actor, the_chain: ActionChains) -> None: @pos_args_deprecated("lmb") def __init__( - self: SelfRelease, + self, key: str | None = None, lmb: bool = False, # noqa: FBT001, FBT002 ) -> None: diff --git a/screenpy_selenium/actions/respond_to_the_prompt.py b/screenpy_selenium/actions/respond_to_the_prompt.py index 9d201de..94b4d18 100644 --- a/screenpy_selenium/actions/respond_to_the_prompt.py +++ b/screenpy_selenium/actions/respond_to_the_prompt.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import aside, beat @@ -10,8 +10,7 @@ if TYPE_CHECKING: from screenpy.actor import Actor - -SelfRespondToThePrompt = TypeVar("SelfRespondToThePrompt", bound="RespondToThePrompt") + from typing_extensions import Self class RespondToThePrompt: @@ -28,16 +27,16 @@ class RespondToThePrompt: """ @classmethod - def with_(cls: type[SelfRespondToThePrompt], text: str) -> SelfRespondToThePrompt: + def with_(cls, text: str) -> Self: """Provide the text to enter into the prompt.""" return cls(text) - def describe(self: SelfRespondToThePrompt) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f'Respond to the prompt with "{self.text}".' @beat('{} responds to the prompt with "{text}".') - def perform_as(self: SelfRespondToThePrompt, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to respond to the prompt using the given text.""" browser = the_actor.uses_ability_to(BrowseTheWeb).browser alert = browser.switch_to.alert @@ -45,5 +44,5 @@ def perform_as(self: SelfRespondToThePrompt, the_actor: Actor) -> None: alert.send_keys(self.text) alert.accept() - def __init__(self: SelfRespondToThePrompt, text: str) -> None: + def __init__(self, text: str) -> None: self.text = text diff --git a/screenpy_selenium/actions/right_click.py b/screenpy_selenium/actions/right_click.py index d915370..73f9f20 100644 --- a/screenpy_selenium/actions/right_click.py +++ b/screenpy_selenium/actions/right_click.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat from selenium.webdriver.common.action_chains import ActionChains @@ -11,11 +11,10 @@ if TYPE_CHECKING: from screenpy import Actor + from typing_extensions import Self from ..target import Target -SelfRightClick = TypeVar("SelfRightClick", bound="RightClick") - class RightClick: """Right-click on an element, or wherever the cursor currently is. @@ -38,7 +37,7 @@ class RightClick: target: Target | None @classmethod - def on_the(cls: type[SelfRightClick], target: Target) -> SelfRightClick: + def on_the(cls, target: Target) -> Self: """Target an element to right-click on. Aliases: @@ -48,20 +47,16 @@ def on_the(cls: type[SelfRightClick], target: Target) -> SelfRightClick: return cls(target=target) @classmethod - def on(cls: type[SelfRightClick], target: Target) -> SelfRightClick: + def on(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.RightClick.on_the`.""" return cls.on_the(target=target) @classmethod - def on_the_first_of_the( - cls: type[SelfRightClick], target: Target - ) -> SelfRightClick: + def on_the_first_of_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.RightClick.on_the`.""" return cls.on_the(target=target) - def _add_action_to_chain( - self: SelfRightClick, the_actor: Actor, the_chain: ActionChains - ) -> None: + def _add_action_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Private method to add this Action to the chain.""" if self.target is not None: the_element = self.target.found_by(the_actor) @@ -70,12 +65,12 @@ def _add_action_to_chain( the_chain.context_click(on_element=the_element) - def describe(self: SelfRightClick) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Right-click{self.description}." @beat("{} right-clicks{description}.") - def perform_as(self: SelfRightClick, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to right-click on the element.""" browser = the_actor.ability_to(BrowseTheWeb).browser the_chain = ActionChains(browser) # type: ignore[arg-type] @@ -83,12 +78,10 @@ def perform_as(self: SelfRightClick, the_actor: Actor) -> None: the_chain.perform() @beat("Right-click{description}!") - def add_to_chain( - self: SelfRightClick, the_actor: Actor, the_chain: ActionChains - ) -> None: + def add_to_chain(self, the_actor: Actor, the_chain: ActionChains) -> None: """Add the RightClick Action to a Chain of Actions.""" self._add_action_to_chain(the_actor, the_chain) - def __init__(self: SelfRightClick, target: Target | None = None) -> None: + def __init__(self, target: Target | None = None) -> None: self.target = target self.description = f" on the {target}" if target is not None else "" diff --git a/screenpy_selenium/actions/save_console_log.py b/screenpy_selenium/actions/save_console_log.py index fad9612..d6d5c64 100644 --- a/screenpy_selenium/actions/save_console_log.py +++ b/screenpy_selenium/actions/save_console_log.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any from screenpy.actions import AttachTheFile from screenpy.pacing import beat @@ -12,8 +12,7 @@ if TYPE_CHECKING: from screenpy import Actor - -SelfSaveConsoleLog = TypeVar("SelfSaveConsoleLog", bound="SaveConsoleLog") + from typing_extensions import Self class SaveConsoleLog: @@ -53,12 +52,12 @@ class SaveConsoleLog: path: str filename: str - def describe(self: SelfSaveConsoleLog) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Save browser console log as {self.filename}" @classmethod - def as_(cls: type[SelfSaveConsoleLog], path: str) -> SelfSaveConsoleLog: + def as_(cls, path: str) -> Self: """Supply the name and/or filepath for the saved text file. If only a name is supplied, the text file will appear in the current @@ -66,9 +65,7 @@ def as_(cls: type[SelfSaveConsoleLog], path: str) -> SelfSaveConsoleLog: """ return cls(path=path) - def and_attach_it( - self: SelfSaveConsoleLog, **kwargs: Any # noqa: ANN401 - ) -> SelfSaveConsoleLog: + def and_attach_it(self, **kwargs: Any) -> Self: # noqa: ANN401 """Indicate the console log file should be attached to any reports. This method accepts any additional keywords needed by any adapters @@ -80,7 +77,7 @@ def and_attach_it( and_attach_it_with = and_attach_it @beat("{} saves their browser's console log as {filename}") - def perform_as(self: SelfSaveConsoleLog, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the actor to save their browser's console log.""" browser = the_actor.ability_to(BrowseTheWeb).browser js_log = "\n".join([str(entry) for entry in browser.get_log("browser")]) @@ -91,7 +88,7 @@ def perform_as(self: SelfSaveConsoleLog, the_actor: Actor) -> None: if self.attach_kwargs is not None: the_actor.attempts_to(AttachTheFile(self.path, **self.attach_kwargs)) - def __init__(self: SelfSaveConsoleLog, path: str) -> None: + def __init__(self, path: str) -> None: self.path = path self.filename = path.split(os.path.sep)[-1] self.attach_kwargs = None diff --git a/screenpy_selenium/actions/save_screenshot.py b/screenpy_selenium/actions/save_screenshot.py index d582a4e..7a6821c 100644 --- a/screenpy_selenium/actions/save_screenshot.py +++ b/screenpy_selenium/actions/save_screenshot.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any from screenpy.actions import AttachTheFile from screenpy.pacing import beat @@ -12,8 +12,7 @@ if TYPE_CHECKING: from screenpy import Actor - -SelfSaveScreenshot = TypeVar("SelfSaveScreenshot", bound="SaveScreenshot") + from typing_extensions import Self class SaveScreenshot: @@ -49,12 +48,12 @@ class SaveScreenshot: path: str filename: str - def describe(self: SelfSaveScreenshot) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Save screenshot as {self.filename}" @classmethod - def as_(cls: type[SelfSaveScreenshot], path: str) -> SelfSaveScreenshot: + def as_(cls, path: str) -> Self: """Supply the name and/or filepath for the screenshot. If only a name is supplied, the screenshot will appear in the current @@ -62,9 +61,7 @@ def as_(cls: type[SelfSaveScreenshot], path: str) -> SelfSaveScreenshot: """ return cls(path=path) - def and_attach_it( - self: SelfSaveScreenshot, **kwargs: Any # noqa: ANN401 - ) -> SelfSaveScreenshot: + def and_attach_it(self, **kwargs: Any) -> Self: # noqa: ANN401 """Indicate the screenshot should be attached to any reports. This method accepts any additional keywords needed by any adapters @@ -76,7 +73,7 @@ def and_attach_it( and_attach_it_with = and_attach_it @beat("{} saves a screenshot as {filename}") - def perform_as(self: SelfSaveScreenshot, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the actor to save a screenshot.""" browser = the_actor.ability_to(BrowseTheWeb).browser screenshot = browser.get_screenshot_as_png() @@ -87,7 +84,7 @@ def perform_as(self: SelfSaveScreenshot, the_actor: Actor) -> None: if self.attach_kwargs is not None: the_actor.attempts_to(AttachTheFile(self.path, **self.attach_kwargs)) - def __init__(self: SelfSaveScreenshot, path: str) -> None: + def __init__(self, path: str) -> None: self.path = path self.filename = path.split(os.path.sep)[-1] self.attach_kwargs = None diff --git a/screenpy_selenium/actions/select.py b/screenpy_selenium/actions/select.py index 2c70a0b..8fd9a92 100644 --- a/screenpy_selenium/actions/select.py +++ b/screenpy_selenium/actions/select.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.exceptions import DeliveryError, UnableToAct from screenpy.pacing import beat @@ -11,13 +11,10 @@ if TYPE_CHECKING: from screenpy import Actor + from typing_extensions import Self from ..target import Target -SelfSelectByText = TypeVar("SelfSelectByText", bound="SelectByText") -SelfSelectByIndex = TypeVar("SelfSelectByIndex", bound="SelectByIndex") -SelfSelectByValue = TypeVar("SelfSelectByValue", bound="SelectByValue") - class Select: """Select an option from a dropdown menu. @@ -72,19 +69,19 @@ class SelectByText: target: Target | None text: str - def from_the(self: SelfSelectByText, target: Target) -> SelfSelectByText: + def from_the(self, target: Target) -> Self: """Target the dropdown or multi-select field to select the option from.""" self.target = target return self from_ = from_the_first_of_the = from_the - def describe(self: SelfSelectByText) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f'Select the option "{self.text}" from the {self.target}.' @beat('{} selects the option "{text}" from the {target}.') - def perform_as(self: SelfSelectByText, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to select the option by its text.""" if self.target is None: msg = ( @@ -104,9 +101,7 @@ def perform_as(self: SelfSelectByText, the_actor: Actor) -> None: ) raise DeliveryError(msg) from e - def __init__( - self: SelfSelectByText, text: str, target: Target | None = None - ) -> None: + def __init__(self, text: str, target: Target | None = None) -> None: self.target = target self.text = text @@ -124,19 +119,19 @@ class SelectByIndex: target: Target | None index: int - def from_the(self: SelfSelectByIndex, target: Target) -> SelfSelectByIndex: + def from_the(self, target: Target) -> Self: """Target the dropdown or multi-select field to select the option from.""" self.target = target return self from_ = from_the_first_of_the = from_the - def describe(self: SelfSelectByIndex) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Select the option at index {self.index} from the {self.target}." @beat("{} selects the option at index {index} from the {target}.") - def perform_as(self: SelfSelectByIndex, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to select the option using its index.""" if self.target is None: msg = ( @@ -156,9 +151,7 @@ def perform_as(self: SelfSelectByIndex, the_actor: Actor) -> None: ) raise DeliveryError(msg) from e - def __init__( - self: SelfSelectByIndex, index: int | str, target: Target | None = None - ) -> None: + def __init__(self, index: int | str, target: Target | None = None) -> None: self.target = target self.index = int(index) @@ -176,19 +169,19 @@ class SelectByValue: target: Target | None value: str - def from_the(self: SelfSelectByValue, target: Target) -> SelfSelectByValue: + def from_the(self, target: Target) -> Self: """Target the dropdown or multi-select field to select the option from.""" self.target = target return self from_ = from_the_first_of_the = from_the - def describe(self: SelfSelectByValue) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f'Select the option with value "{self.value}" from the {self.target}.' @beat('{} selects the option with value "{value}" from the {target}.') - def perform_as(self: SelfSelectByValue, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to select the option by its value.""" if self.target is None: msg = ( @@ -208,8 +201,6 @@ def perform_as(self: SelfSelectByValue, the_actor: Actor) -> None: ) raise DeliveryError(msg) from e - def __init__( - self: SelfSelectByValue, value: int | str, target: Target | None = None - ) -> None: + def __init__(self, value: int | str, target: Target | None = None) -> None: self.target = target self.value = str(value) diff --git a/screenpy_selenium/actions/switch_to.py b/screenpy_selenium/actions/switch_to.py index eecde46..63e6d32 100644 --- a/screenpy_selenium/actions/switch_to.py +++ b/screenpy_selenium/actions/switch_to.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat @@ -10,11 +10,10 @@ if TYPE_CHECKING: from screenpy.actor import Actor + from typing_extensions import Self from ..target import Target -SelfSwitchTo = TypeVar("SelfSwitchTo", bound="SwitchTo") - class SwitchTo: """Switch to an element, most likely an iframe, or back to default. @@ -32,21 +31,21 @@ class SwitchTo: """ @classmethod - def the(cls: type[SelfSwitchTo], target: Target) -> SelfSwitchTo: + def the(cls, target: Target) -> Self: """Target an element, probably an iframe, to switch to.""" return cls(target=target, frame_to_log=str(target)) @classmethod - def default(cls: type[SelfSwitchTo]) -> SelfSwitchTo: + def default(cls) -> Self: """Switch back to the default frame, the browser window.""" return cls(target=None, frame_to_log="default frame") - def describe(self: SelfSwitchTo) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Switch to the {self.frame_to_log}." @beat("{} switches to the {frame_to_log}.") - def perform_as(self: SelfSwitchTo, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to switch to an element or back to default.""" browser = the_actor.ability_to(BrowseTheWeb).browser if self.target is None: @@ -54,6 +53,6 @@ def perform_as(self: SelfSwitchTo, the_actor: Actor) -> None: else: browser.switch_to.frame(self.target.found_by(the_actor)) - def __init__(self: SelfSwitchTo, target: Target | None, frame_to_log: str) -> None: + def __init__(self, target: Target | None, frame_to_log: str) -> None: self.target = target self.frame_to_log = frame_to_log diff --git a/screenpy_selenium/actions/wait.py b/screenpy_selenium/actions/wait.py index 94ca3a6..a9c89e7 100644 --- a/screenpy_selenium/actions/wait.py +++ b/screenpy_selenium/actions/wait.py @@ -2,9 +2,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Iterable, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Iterable -from screenpy import Actor, settings +from screenpy import settings from screenpy.exceptions import DeliveryError from screenpy.pacing import beat from selenium.common.exceptions import WebDriverException @@ -14,9 +14,10 @@ from ..abilities import BrowseTheWeb if TYPE_CHECKING: - from ..target import Target + from screenpy import Actor + from typing_extensions import Self -SelfWait = TypeVar("SelfWait", bound="Wait") + from ..target import Target class Wait: @@ -55,7 +56,7 @@ class Wait: log_detail: str | None @classmethod - def for_the(cls: type[SelfWait], target: Target) -> SelfWait: + def for_the(cls, target: Target) -> Self: """Set the Target to wait for. Aliases: @@ -64,11 +65,11 @@ def for_the(cls: type[SelfWait], target: Target) -> SelfWait: return cls(seconds=settings.TIMEOUT, args=[target]) @classmethod - def for_(cls: type[SelfWait], target: Target) -> SelfWait: + def for_(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.Wait.for_the`.""" return cls.for_the(target=target) - def seconds_for_the(self: SelfWait, target: Target) -> SelfWait: + def seconds_for_the(self, target: Target) -> Self: """Set the Target to wait for, after changing the default timeout.""" self.args = [target] return self @@ -76,8 +77,8 @@ def seconds_for_the(self: SelfWait, target: Target) -> SelfWait: second_for = second_for_the = seconds_for = seconds_for_the def using( - self: SelfWait, strategy: Callable[..., Any], log_detail: str | None = None - ) -> SelfWait: + self, strategy: Callable[..., Any], log_detail: str | None = None + ) -> Self: """Use the given strategy to wait for the Target. Args: @@ -94,45 +95,45 @@ def using( to = seconds_using = using - def with_(self: SelfWait, *args: Any) -> SelfWait: # noqa: ANN401 + def with_(self, *args: Any) -> Self: # noqa: ANN401 """Set the arguments to pass in to the wait condition.""" self.args = args return self - def to_appear(self: SelfWait) -> SelfWait: + def to_appear(self) -> Self: """Use Selenium's "visibility of element located" strategy.""" return self.using(EC.visibility_of_element_located, "for the {0} to appear...") - def to_be_clickable(self: SelfWait) -> SelfWait: + def to_be_clickable(self) -> Self: """Use Selenium's "to be clickable" strategy.""" return self.using(EC.element_to_be_clickable, "for the {0} to be clickable...") - def to_disappear(self: SelfWait) -> SelfWait: + def to_disappear(self) -> Self: """Use Selenium's "invisibility of element located" strategy.""" return self.using( EC.invisibility_of_element_located, "for the {0} to disappear..." ) - def to_contain_text(self: SelfWait, text: str) -> SelfWait: + def to_contain_text(self, text: str) -> Self: """Use Selenium's "text to be present in element" strategy.""" return self.using( EC.text_to_be_present_in_element, 'for "{1}" to appear in the {0}...' ).with_(*self.args, text) @property - def log_message(self: SelfWait) -> str: + def log_message(self) -> str: """Format the nice log message, or give back the default.""" if self.log_detail is None: return f"using {self.condition.__name__} with {self.args}" return self.log_detail.format(*self.args) - def describe(self: SelfWait) -> str: + def describe(self) -> str: """Describe the Action in present tense.""" return f"Wait {self.timeout} seconds {self.log_message}." @beat("{} waits up to {timeout} seconds {log_message}") - def perform_as(self: SelfWait, the_actor: Actor) -> None: + def perform_as(self, the_actor: Actor) -> None: """Direct the Actor to wait for the condition to be satisfied.""" browser = the_actor.ability_to(BrowseTheWeb).browser @@ -148,9 +149,7 @@ def perform_as(self: SelfWait, the_actor: Actor) -> None: raise DeliveryError(msg) from e def __init__( - self: SelfWait, - seconds: float | None = None, - args: Iterable[Any] | None = None, + self, seconds: float | None = None, args: Iterable[Any] | None = None ) -> None: self.args = args if args is not None else [] self.timeout = seconds if seconds is not None else settings.TIMEOUT diff --git a/screenpy_selenium/common.py b/screenpy_selenium/common.py index bf85295..c78a26c 100644 --- a/screenpy_selenium/common.py +++ b/screenpy_selenium/common.py @@ -6,9 +6,9 @@ from functools import wraps from typing import TYPE_CHECKING, Callable, TypeVar -from typing_extensions import ParamSpec - if TYPE_CHECKING: + from typing_extensions import ParamSpec + P = ParamSpec("P") T = TypeVar("T") Function = Callable[P, T] diff --git a/screenpy_selenium/questions/list.py b/screenpy_selenium/questions/list.py index d6af340..194e6fc 100644 --- a/screenpy_selenium/questions/list.py +++ b/screenpy_selenium/questions/list.py @@ -2,18 +2,17 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat if TYPE_CHECKING: from screenpy import Actor from selenium.webdriver.remote.webdriver import WebElement + from typing_extensions import Self from ..target import Target -SelfList = TypeVar("SelfList", bound="List") - class List: """Ask for a list of elements. @@ -27,7 +26,7 @@ class List: """ @classmethod - def of_the(cls: type[SelfList], target: Target) -> SelfList: + def of_the(cls, target: Target) -> Self: """Target the element(s) to list. Aliases: @@ -38,28 +37,28 @@ def of_the(cls: type[SelfList], target: Target) -> SelfList: return cls(target=target) @classmethod - def of_all_the(cls: type[SelfList], target: Target) -> SelfList: + def of_all_the(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.List.of_the`.""" return cls.of_the(target=target) @classmethod - def of_all(cls: type[SelfList], target: Target) -> SelfList: + def of_all(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.List.of_the`.""" return cls.of_the(target=target) @classmethod - def of(cls: type[SelfList], target: Target) -> SelfList: + def of(cls, target: Target) -> Self: """Alias for :meth:`~screenpy_selenium.actions.List.of_the`.""" return cls.of_the(target=target) - def describe(self: SelfList) -> str: + def describe(self) -> str: """Describe the Question.""" return f"The list of {self.target}." @beat("{} lists off the {target}.") - def answered_by(self: SelfList, the_actor: Actor) -> list[WebElement]: + def answered_by(self, the_actor: Actor) -> list[WebElement]: """Direct the Actor to rattle off the specified elements.""" return self.target.all_found_by(the_actor) - def __init__(self: SelfList, target: Target) -> None: + def __init__(self, target: Target) -> None: self.target = target diff --git a/screenpy_selenium/questions/number.py b/screenpy_selenium/questions/number.py index 20c3976..2f4b597 100644 --- a/screenpy_selenium/questions/number.py +++ b/screenpy_selenium/questions/number.py @@ -2,17 +2,16 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat if TYPE_CHECKING: from screenpy import Actor + from typing_extensions import Self from ..target import Target -SelfNumber = TypeVar("SelfNumber", bound="Number") - class Number: """Ask how many of a certain element are on the page. @@ -26,18 +25,18 @@ class Number: """ @classmethod - def of(cls: type[SelfNumber], target: Target) -> SelfNumber: + def of(cls, target: Target) -> Self: """Target the element to be counted.""" return cls(target=target) - def describe(self: SelfNumber) -> str: + def describe(self) -> str: """Describe the Question.""" return f"The number of {self.target}." @beat("{} counts the number of {target}.") - def answered_by(self: SelfNumber, the_actor: Actor) -> int: + def answered_by(self, the_actor: Actor) -> int: """Direct the Actor to count the elements.""" return len(self.target.all_found_by(the_actor)) - def __init__(self: SelfNumber, target: Target) -> None: + def __init__(self, target: Target) -> None: self.target = target diff --git a/screenpy_selenium/questions/selected.py b/screenpy_selenium/questions/selected.py index a0030f1..fac9589 100644 --- a/screenpy_selenium/questions/selected.py +++ b/screenpy_selenium/questions/selected.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat from selenium.webdriver.support.ui import Select as SeleniumSelect @@ -11,11 +11,10 @@ if TYPE_CHECKING: from screenpy import Actor + from typing_extensions import Self from ..target import Target -SelfSelected = TypeVar("SelfSelected", bound="Selected") - class Selected: """Ask for the text of selected option(s) in a dropdown or multi-select field. @@ -36,7 +35,7 @@ class Selected: multi: bool @classmethod - def option_from_the(cls: type[SelfSelected], target: Target) -> SelfSelected: + def option_from_the(cls, target: Target) -> Self: """ Get the option. @@ -52,14 +51,12 @@ def option_from_the(cls: type[SelfSelected], target: Target) -> SelfSelected: return cls(target=target) @classmethod - def option_from(cls: type[SelfSelected], target: Target) -> SelfSelected: + def option_from(cls, target: Target) -> Self: """Alias of :meth:`~screenpy_selenium.actions.Selected.option_from_the`.""" return cls.option_from_the(target=target) @classmethod - def options_from_the( - cls: type[SelfSelected], multiselect_target: Target - ) -> SelfSelected: + def options_from_the(cls, multiselect_target: Target) -> Self: """ Get all the options that are currently selected in a multi-select field. @@ -73,18 +70,16 @@ def options_from_the( return cls(target=multiselect_target, multi=True) @classmethod - def options_from( - cls: type[SelfSelected], multiselect_target: Target - ) -> SelfSelected: + def options_from(cls, multiselect_target: Target) -> Self: """Alias of :meth:`~screenpy_selenium.actions.Selected.options_from_the`.""" return cls.options_from_the(multiselect_target=multiselect_target) - def describe(self: SelfSelected) -> str: + def describe(self) -> str: """Describe the Question.""" return f"The selected option(s) from the {self.target}." @beat("{} checks the selected option(s) from the {target}.") - def answered_by(self: SelfSelected, the_actor: Actor) -> str | list[str]: + def answered_by(self, the_actor: Actor) -> str | list[str]: """Direct the Actor to name the selected option(s).""" select = SeleniumSelect(self.target.found_by(the_actor)) @@ -94,7 +89,7 @@ def answered_by(self: SelfSelected, the_actor: Actor) -> str | list[str]: @pos_args_deprecated("multi") def __init__( - self: SelfSelected, target: Target, multi: bool = False # noqa: FBT001, FBT002 + self, target: Target, multi: bool = False # noqa: FBT001, FBT002 ) -> None: self.target = target self.multi = multi diff --git a/screenpy_selenium/questions/text.py b/screenpy_selenium/questions/text.py index 5f09e28..4230dd9 100644 --- a/screenpy_selenium/questions/text.py +++ b/screenpy_selenium/questions/text.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, TypeVar +from typing import TYPE_CHECKING from screenpy.pacing import beat @@ -10,11 +10,10 @@ if TYPE_CHECKING: from screenpy import Actor + from typing_extensions import Self from ..target import Target -SelfText = TypeVar("SelfText", bound="Text") - class Text: """Ask what text appears in an element or elements. @@ -37,7 +36,7 @@ class Text: multi: bool @classmethod - def of_the(cls: type[SelfText], target: Target) -> SelfText: + def of_the(cls, target: Target) -> Self: """Target the element to extract the text from. Aliases: @@ -47,26 +46,26 @@ def of_the(cls: type[SelfText], target: Target) -> SelfText: return cls(target=target) @classmethod - def of(cls: type[SelfText], target: Target) -> SelfText: + def of(cls, target: Target) -> Self: """Alias of :meth:`~screenpy_selenium.actions.Text.of_the`.""" return cls.of_the(target=target) @classmethod - def of_the_first_of_the(cls: type[SelfText], target: Target) -> SelfText: + def of_the_first_of_the(cls, target: Target) -> Self: """Alias of :meth:`~screenpy_selenium.actions.Text.of_the`.""" return cls.of_the(target=target) @classmethod - def of_all(cls: type[SelfText], multi_target: Target) -> SelfText: + def of_all(cls, multi_target: Target) -> Self: """Target the elements, plural, to extract the text from.""" return cls(target=multi_target, multi=True) - def describe(self: SelfText) -> str: + def describe(self) -> str: """Describe the Question.""" return f"The text from the {self.target}." @beat("{} reads the text from the {target}.") - def answered_by(self: SelfText, the_actor: Actor) -> str | list[str]: + def answered_by(self, the_actor: Actor) -> str | list[str]: """Direct the Actor to read off the text of the element(s).""" if self.multi: return [e.text for e in self.target.all_found_by(the_actor)] @@ -74,7 +73,7 @@ def answered_by(self: SelfText, the_actor: Actor) -> str | list[str]: @pos_args_deprecated("multi") def __init__( - self: SelfText, target: Target, multi: bool = False # noqa: FBT001, FBT002 + self, target: Target, multi: bool = False # noqa: FBT001, FBT002 ) -> None: self.target = target self.multi = multi diff --git a/screenpy_selenium/target.py b/screenpy_selenium/target.py index aefe684..0f75106 100644 --- a/screenpy_selenium/target.py +++ b/screenpy_selenium/target.py @@ -8,7 +8,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Iterator, TypeVar +from typing import TYPE_CHECKING, Iterator from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.by import By @@ -19,8 +19,7 @@ if TYPE_CHECKING: from screenpy.actor import Actor from selenium.webdriver.remote.webdriver import WebElement - -SelfTarget = TypeVar("SelfTarget", bound="Target") + from typing_extensions import Self class Target: @@ -42,29 +41,29 @@ class Target: locator: tuple[str, str] | None = None @property - def target_name(self: SelfTarget) -> str | None: + def target_name(self) -> str | None: """Return the description when set or the 2nd half of the locator.""" if self._description is not None: return self._description return self.locator[1] if self.locator else None @target_name.setter - def target_name(self: SelfTarget, value: str) -> None: + def target_name(self, value: str) -> None: self._description = value @target_name.deleter - def target_name(self: SelfTarget) -> None: + def target_name(self) -> None: del self._description @classmethod - def the(cls: type[SelfTarget], desc: str) -> SelfTarget: + def the(cls, desc: str) -> Self: """Name this Target. Beginning with a lower-case letter makes the logs look the nicest. """ return cls(desc=desc) - def located_by(self: SelfTarget, locator: tuple[str, str] | str) -> SelfTarget: + def located_by(self, locator: tuple[str, str] | str) -> Self: """Set the locator for this Target. Possible values for locator: @@ -91,11 +90,11 @@ def located_by(self: SelfTarget, locator: tuple[str, str] | str) -> SelfTarget: return self - def located(self: SelfTarget, locator: tuple[str, str] | str) -> SelfTarget: + def located(self, locator: tuple[str, str] | str) -> Self: """Alias for :meth:~screenpy_selenium.Target.located_by.""" return self.located_by(locator) - def get_locator(self: SelfTarget) -> tuple[str, str]: + def get_locator(self) -> tuple[str, str]: """Return the stored locator. Raises: @@ -109,7 +108,7 @@ def get_locator(self: SelfTarget) -> tuple[str, str]: raise TargetingError(msg) return self.locator - def found_by(self: SelfTarget, the_actor: Actor) -> WebElement: + def found_by(self, the_actor: Actor) -> WebElement: """Retrieve the |WebElement| as viewed by the Actor.""" browser = the_actor.ability_to(BrowseTheWeb).browser try: @@ -118,7 +117,7 @@ def found_by(self: SelfTarget, the_actor: Actor) -> WebElement: msg = f"{e} raised while trying to find {self}." raise TargetingError(msg) from e - def all_found_by(self: SelfTarget, the_actor: Actor) -> list[WebElement]: + def all_found_by(self, the_actor: Actor) -> list[WebElement]: """Retrieve a list of |WebElement| objects as viewed by the Actor.""" browser = the_actor.ability_to(BrowseTheWeb).browser try: @@ -127,24 +126,22 @@ def all_found_by(self: SelfTarget, the_actor: Actor) -> list[WebElement]: msg = f"{e} raised while trying to find {self}." raise TargetingError(msg) from e - def __repr__(self: SelfTarget) -> str: + def __repr__(self) -> str: """A Target is represented by its name.""" return f"{self.target_name}" __str__ = __repr__ - def __iter__(self: SelfTarget) -> Iterator[str]: + def __iter__(self) -> Iterator[str]: """Allow Targets to be treated as ``(By, str)`` tuples.""" return self.get_locator().__iter__() - def __getitem__(self: SelfTarget, index: int) -> str: + def __getitem__(self, index: int) -> str: """Allow Targets to be treated as ``(By, str)`` tuples.""" return self.get_locator()[index] def __init__( - self: SelfTarget, - desc: str | None = None, - locator: tuple[str, str] | None = None, + self, desc: str | None = None, locator: tuple[str, str] | None = None ) -> None: self.target_name = desc self.locator = locator diff --git a/tests/test_actions.py b/tests/test_actions.py index 7e4cf30..e8eb9d6 100644 --- a/tests/test_actions.py +++ b/tests/test_actions.py @@ -2,18 +2,11 @@ import warnings from contextlib import contextmanager -from typing import Generator, cast +from typing import TYPE_CHECKING, Generator, cast from unittest import mock import pytest -from screenpy import ( - Actor, - DeliveryError, - Describable, - Performable, - UnableToAct, - settings, -) +from screenpy import DeliveryError, Describable, Performable, UnableToAct, settings from screenpy.configuration import ScreenPySettings from screenpy_pyotp.abilities import AuthenticateWith2FA from selenium.common.exceptions import WebDriverException @@ -61,6 +54,9 @@ get_mocked_target_and_element, ) +if TYPE_CHECKING: + from screenpy import Actor + FakeTarget = get_mock_target_class() TARGET = FakeTarget() diff --git a/tests/test_questions.py b/tests/test_questions.py index 23dbd1b..aaff6f7 100644 --- a/tests/test_questions.py +++ b/tests/test_questions.py @@ -1,10 +1,11 @@ from __future__ import annotations import warnings +from typing import TYPE_CHECKING from unittest import mock import pytest -from screenpy import Actor, Answerable, Describable, ErrorKeeper, UnableToAnswer +from screenpy import Answerable, Describable, ErrorKeeper, UnableToAnswer from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.alert import Alert as SeleniumAlert from selenium.webdriver.remote.webelement import WebElement @@ -26,6 +27,9 @@ from .useful_mocks import get_mock_target_class, get_mocked_browser, get_mocked_element +if TYPE_CHECKING: + from screenpy import Actor + FakeTarget = get_mock_target_class() TARGET = FakeTarget()