diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9fba4973..2099a19c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { "recommendations": [ "ms-python.python", - "ms-python.vscode-pylance", + "detachhead.basedpyright", "ms-python.pylint", "ms-python.black-formatter", "ms-python.mypy-type-checker", @@ -11,5 +11,8 @@ "tamasfe.even-better-toml", "redhat.vscode-yaml", "robocorp.robotframework-lsp" + ], + "unwantedRecommendations": [ + "ms-python.vscode-pylance", ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 693452dc..966bf0e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,12 +10,6 @@ "git.useEditorAsCommitInput": false, "diffEditor.ignoreTrimWhitespace": false, "terminal.integrated.persistentSessionReviveProcess": "never", - "mypy-type-checker.args": [ - "--ide", - "--python-version", - "3.8" - ], - "mypy-type-checker.importStrategy": "fromEnvironment", "files.autoSave": "onFocusChange", "search.useIgnoreFiles": true, "git.useCommitInputAsStashMessage": true, @@ -28,9 +22,6 @@ "python.analysis.typeCheckingMode": "off", "python.analysis.autoFormatStrings": true, "python.analysis.gotoDefinitionInStringLiteral": true, - "python.analysis.typeshedPaths": [ - "./.venv/lib/site-packages/mypy/typeshed" - ], "pylint.importStrategy": "fromEnvironment", "pylint.severity": { "convention": "Warning", @@ -56,8 +47,5 @@ "search.exclude": { "pw": true }, - "mypy-type-checker.ignorePatterns": [ - "pw" - ], "python.terminal.activateEnvInCurrentTerminal": true } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8174f90a..acfa3d57 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -34,10 +34,10 @@ "problemMatcher": [] }, { - "label": "basedmypy - all files", + "label": "basedpyright - all files", "type": "shell", "command": "./pw", - "args": ["pdm", "run", "mypy_all"], + "args": ["run", "basedpyright"], "presentation": { "clear": true }, diff --git a/pdm.lock b/pdm.lock index 6ca08ffb..bc79eb26 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "lint", "test", "docs"] strategy = ["cross_platform"] lock_version = "4.4.1" -content_hash = "sha256:f92e05170012c8f9371d06b7c6d97e4a5f5660d11a60afca9366ac3b50669900" +content_hash = "sha256:73ada6b5f26d53ee49eeb32469452c718c7f6b0298c8226fc067cbb7e81c851e" [[package]] name = "astroid" @@ -34,44 +34,15 @@ files = [ ] [[package]] -name = "basedmypy" -version = "2.3.0" +name = "basedpyright" +version = "0.1.0" requires_python = ">=3.8" -summary = "Based static typing for Python" +git = "https://github.com/detachhead/basedpyright.git" +ref = "d0b3dbe33add247adf58b2b24f30953cf15d97e3" +revision = "d0b3dbe33add247adf58b2b24f30953cf15d97e3" +summary = "" dependencies = [ - "basedtyping>=0.0.3", - "mypy-extensions>=1.0.0", - "tomli>=1.1.0; python_version < \"3.11\"", - "typing-extensions>=4.1.0", -] -files = [ - {file = "basedmypy-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c38ea60a186ec5b4e5800c6b30f0d27d907eaf025a647c5d376c6e12b85dadde"}, - {file = "basedmypy-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19f21c43cfe4e8617410a7460863477a1be35b6125b80cc767c1aa1df148de0c"}, - {file = "basedmypy-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b6b752f40e1d21e17500cf8041cd756d430b82cbf4ffce1f1b5b46593f8ce1"}, - {file = "basedmypy-2.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:32da5239645abc17c2773becb6e42bfebf63134ac31db528906e7e7e8ed7067c"}, - {file = "basedmypy-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3636e8d5a61124f2c2a262294ca0d97cc19245a677115ad99b90724087e6443"}, - {file = "basedmypy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b40542cce22b9b84d59631f1c09556b82bc6ca96f075a527390055154b1197b3"}, - {file = "basedmypy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:47e5d1529eccd3d890c66c69f75601d629d2402334d729c94ab4a34dfde250d2"}, - {file = "basedmypy-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d01ebdbc8c0fe55d4493388d00802fa46a8f9a131ab98fa8d068de599f09d484"}, - {file = "basedmypy-2.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b882c8ccf24f3fb4df6c61357e58837dbfef177b235f818a5b258f41ff5b67e"}, - {file = "basedmypy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc90c2b54c7948866699ffb7800c309f3d94ff6174b507ea63d775873a35b63e"}, - {file = "basedmypy-2.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d2a11517384ef58ac02cea60939ee8db9b948c3a19209d811ccd389ce229da32"}, - {file = "basedmypy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e8a3db700f6296ec4fdb3565c8f222d53f7e0f5756eec68ef42303862b8e5e0"}, - {file = "basedmypy-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95507d9f9c93b378f88990efba35a46ad99ff1452f4dae76cec87ab17212ef31"}, - {file = "basedmypy-2.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a19e0cba3573e9f4d3d8f59162e1412d8523fd10e250b53f28907367ec2372c9"}, - {file = "basedmypy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:42cbad82650345b41f13d7a5d86141502c8384f13bcab087550d6cfb5f66fdfb"}, - {file = "basedmypy-2.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:63d3621016dbfd568e464139326eb929532d18c597d6a6033c3f3f45c16f527c"}, - {file = "basedmypy-2.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8e69209909e454b42cd9337af89ad401207dc3dd315a180e3cc3e3905372d297"}, - {file = "basedmypy-2.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96f3f5bfb767b8110345e3c9b1122fc1841433b623fea7849c7ddfdaf82276fd"}, - {file = "basedmypy-2.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:808309efb6a09004b1fe58eddcb337ba68c42a8cf0accda890d0dea203947f87"}, - {file = "basedmypy-2.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9847cc608b4581c1ff8d2364ca0afc014bc222f86d6f06645488352469a25b8"}, - {file = "basedmypy-2.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4109fad62459ecbd88097d8e5340afeda400ace34586c44d87d09b668f9d44d0"}, - {file = "basedmypy-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d8805a62248002ff79ae95c63f06f8df9a18b6d15d2592b843dc351e14be778"}, - {file = "basedmypy-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a724142bcbfe7dcf863457d100eec98ae9412b12f8422121c1708240f57dbd"}, - {file = "basedmypy-2.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fd17acb3eb2da752460442ace69cb71adbb401434724258141770bb97d139fd"}, - {file = "basedmypy-2.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:1c5d471541a1f33dc6fc5e41876e07c0c42dcc93d400df2ab0abbc6d4e2340e8"}, - {file = "basedmypy-2.3.0-py3-none-any.whl", hash = "sha256:8e36e6ad996203d3f524afb6c96adc4b33203d94f35ff8703790629f117675ee"}, - {file = "basedmypy-2.3.0.tar.gz", hash = "sha256:0c4bafc220c65fb75ef6dcf9e44294ec7dbc317e385e96c57e40a8b4eccde4bc"}, + "nodejs-bin>=18.4.0a4", ] [[package]] @@ -402,6 +373,21 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodejs-bin" +version = "18.4.0a4" +requires_python = "~=3.5" +summary = " Node.js is an open-source, cross-platform, back-end JavaScript\nruntime environment that runs on the V8 engine and executes JavaScript code\noutside a web browser." +files = [ + {file = "nodejs_bin-18.4.0a4-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:16cb1abf7fe8c11c574e1e474d9f934a0df49a480290eae6e733d8bb09512e22"}, + {file = "nodejs_bin-18.4.0a4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:068ca987ed83ea1123775fafe5dc22d8f2ff920d7d31571e1bfe6fb1093833eb"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06cfeaa4d26eec94d8edb9927525ce94eb96dadc81f7d1daed42d1a7d003a4c9"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:431ee3529f4fb226ddcfd4f14cb37e7df31238c42dfd051f4bf8f0c21029b133"}, + {file = "nodejs_bin-18.4.0a4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:21f1f77ddc8fe05353bb6d6ee8e5a62edb3a8dcdb2740a5f9307fd8d9eef6691"}, + {file = "nodejs_bin-18.4.0a4-py3-none-win32.whl", hash = "sha256:59671fdc563dabb8be8a0b6dae4169d780482b3c9e0fba3f9aa2b7ee8d2261ac"}, + {file = "nodejs_bin-18.4.0a4-py3-none-win_amd64.whl", hash = "sha256:cbd509218b4b17f75ee7841f9c21d5cacc1626d3b823a652a6627dbad18228ec"}, +] + [[package]] name = "packaging" version = "23.2" diff --git a/pyproject.toml b/pyproject.toml index b6639ff1..ca6aba9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ test = "pdm run pytest -n auto" [tool.pdm.dev-dependencies] lint = [ "black>=23", - "basedmypy>=2.1", + "basedpyright @ git+https://github.com/detachhead/basedpyright.git@d0b3dbe33add247adf58b2b24f30953cf15d97e3", "pylint>=3.0.0a7", "ruff>=0.0.290", "robotframework-robocop>=4.1.0", @@ -44,12 +44,6 @@ lint = [ test = ["lxml>=4.9.3", "lxml-stubs>=0.4.0", "pytest-xdist>=3.5.0"] docs = ["pdoc>=14.1.0"] -# maybe these should be pyprojectx scripts instead once https://github.com/pyprojectx/pyprojectx/issues/26 is fixed -[tool.pdm.scripts] -_mypy_package = { cmd = "pdm run mypy -p pytest_robotframework" } -_mypy_tests = { cmd = "pdm run mypy -p tests" } -mypy_all = { composite = ["_mypy_package", "_mypy_tests"] } - [build-system] requires = ["pdm-backend"] build-backend = "pdm.backend" @@ -268,36 +262,73 @@ addopts = ['-p no:robotframework', '--ignore=tests/fixtures'] xfail_strict = true enable_assertion_pass_hook = true -[tool.mypy] -allow_redefinition = false -enable_error_code = "helpful-string" -default_return = false # enabled for our own code only as 3rd party code is not coompatible -cache_dir = 'nul' # disable cache because it sucks - -[[tool.mypy.overrides]] -module = ['pytest_robotframework.*', 'tests.*'] -default_return = true - -[[tool.mypy.overrides]] -module = ['robot.*'] -no_implicit_reexport = false -ignore_missing_py_typed = true # https://github.com/robotframework/robotframework/issues/4822 - -[[tool.mypy.overrides]] -module = ['deepmerge.*'] -ignore_missing_py_typed = true - - [tool.pyright] -# we dont use pyright for type checking, this is just for vscode import suggestions and reachability checks pythonVersion = "3.8" pythonPlatform = "All" +typeCheckingMode = "strict" +reportMissingTypeStubs = false +strictListInference = true +strictDictionaryInference = true +strictSetInference = true +deprecateTypingAliases = true +enableExperimentalFeatures = true +disableBytesTypePromotions = true +reportPropertyTypeMismatch = true +reportFunctionMemberAccess = true +reportMissingModuleSource = true +reportImportCycles = true +reportUnusedClass = true +reportUnusedFunction = true +reportUnusedVariable = true +reportDuplicateImport = true +reportWildcardImportFromLibrary = true +reportUntypedFunctionDecorator = true +reportUntypedClassDecorator = true +reportUntypedBaseClass = true +reportUntypedNamedTuple = true +reportPrivateUsage = true +reportTypeCommentUsage = true +reportConstantRedefinition = true +reportDeprecated = true +reportIncompatibleMethodOverride = true +reportIncompatibleVariableOverride = true +reportInconsistentConstructor = true +reportOverlappingOverload = true +reportMissingSuperCall = true +reportUninitializedInstanceVariable = true +reportInvalidStringEscapeSequence = true +reportUnknownParameterType = true +reportUnknownArgumentType = true +reportUnknownLambdaType = true +reportUnknownVariableType = true +reportUnknownMemberType = true +reportMissingParameterType = true +reportMissingTypeArgument = true +reportInvalidTypeVarUse = true +reportCallInDefaultInitializer = true +reportUnnecessaryIsInstance = true +reportUnnecessaryCast = true +reportUnnecessaryComparison = true +reportUnnecessaryContains = true +reportAssertAlwaysTrue = true +reportSelfClsParameterName = true +reportImplicitStringConcatenation = false # conflicts with black +reportInvalidStubStatement = true +reportIncompleteStub = true +reportUnsupportedDunderAll = true +reportUnusedCallResult = true +reportUnusedCoroutine = true +reportUnusedExpression = true +reportUnnecessaryTypeIgnoreComment = true +reportMatchNotExhaustive = true +reportImplicitOverride = true +reportShadowedImports = true [tool.ruff] unsafe-fixes = true extend-select = ["ALL"] ignore = [ - "ANN", # flake8-annotations (covered by mypy) + "ANN", # flake8-annotations (covered by pyright) "COM", # flake8-commas (covered by black) "EM", # flake8-errmsg "FIX", # flake8-fixme @@ -307,13 +338,13 @@ ignore = [ "PLR2004", # Magic value used in comparison "PLR1722", # Use `sys.exit()` instead of `exit` "PLW2901", # `for` loop variable overwritten by assignment target - "PLE0605", # Invalid format for `__all__`, must be `tuple` or `list` (covered by mypy) + "PLE0605", # Invalid format for `__all__`, must be `tuple` or `list` (covered by pyright) "PLR0911", # Too many return statements "PLW0603", # Using the global statement is discouraged "PLC0105", # `TypeVar` name does not reflect its covariance - "PLC0414", # Import alias does not rename original package (used by mypy for explicit re-export) - "RUF013", # PEP 484 prohibits implicit Optional (covered by mypy) - "RUF016", # Slice in indexed access to type (covered by mypy) + "PLC0414", # Import alias does not rename original package (used by pyright for explicit re-export) + "RUF013", # PEP 484 prohibits implicit Optional (covered by pyright) + "RUF016", # Slice in indexed access to type (covered by pyright) "TRY002", # Create your own exception "TRY003", # Avoid specifying long messages outside the exception class "D10", # Missing docstring @@ -334,7 +365,6 @@ ignore = [ "D418", # Function/Method decorated with @overload shouldn't contain a docstring (vscode supports it) "PT013", # Found incorrect import of pytest, use simple import pytest instead (only for bad linters that can't check the qualname) "TD002", # Missing author in TODO - "PGH003", # Use specific rule codes when ignoring type issues (covered by mypy) "E701", # Multiple statements on one line (sometimes conflicts with black) "CPY001", # missing-copyright-notice "C901", # max-complexity diff --git a/pytest_robotframework/__init__.py b/pytest_robotframework/__init__.py index ecf4fb95..d103c354 100644 --- a/pytest_robotframework/__init__.py +++ b/pytest_robotframework/__init__.py @@ -29,12 +29,16 @@ from basedtyping import Function, P, T from robot import result, running -from robot.api import SuiteVisitor, deco +from robot.api import deco from robot.api.interfaces import ListenerV2, ListenerV3 from robot.libraries.BuiltIn import BuiltIn +from robot.model.visitor import SuiteVisitor from robot.running.librarykeywordrunner import LibraryKeywordRunner from robot.running.statusreporter import StatusReporter -from robot.utils import getshortdoc, printable_name +from robot.utils import ( + getshortdoc, # pyright:ignore[reportUnknownVariableType] + printable_name, # pyright:ignore[reportUnknownVariableType] +) from typing_extensions import Literal, Never, deprecated, override from pytest_robotframework._internal.cringe_globals import current_item, current_session @@ -54,7 +58,9 @@ if TYPE_CHECKING: from types import TracebackType - from robot.running.context import _ExecutionContext + from robot.running.context import ( + _ExecutionContext, # pyright:ignore[reportPrivateUsage] + ) RobotVariables = Dict[str, object] @@ -83,7 +89,9 @@ def import_resource(path: Path | str): to import libraries, use a regular python import""" if execution_context(): - BuiltIn().import_resource(escape_robot_str(str(path))) + BuiltIn().import_resource( # pyright:ignore[reportUnknownMemberType] + escape_robot_str(str(path)) + ) else: _resources.append(Path(path)) @@ -93,7 +101,7 @@ def import_resource(path: Path | str): if robot_6: @patch_method(LibraryKeywordRunner) - def _runner_for( # noqa: PLR0917 + def _runner_for( # pyright:ignore[reportUnusedFunction] # noqa: PLR0917 old_method: Callable[ [ LibraryKeywordRunner, @@ -121,8 +129,8 @@ class _StaticKeyword(StaticKeyword): # pylint:disable=abstract-method """prevents keywords decorated with `pytest_robotframework.keyword` from being wrapped in two status reporters when called from `.robot` tests""" - @override @property + @override def method(self) -> Function: method = cast(Function, super().method) return cast(Function, getattr(method, _kw_attribute, method)) @@ -154,6 +162,7 @@ def __init__( module: str | None = None, doc: str | None = None, ) -> None: + super().__init__() self.name = name self.tags = tags or () self.module = module @@ -176,21 +185,21 @@ def inner( raise if error: raise error - return result_ + # pyright assumes the assignment to error could raise an exception but that will NEVER + # happen + return result_ # pyright:ignore[reportGeneralTypeIssues,reportUnboundVariable] def call(self, fn: Callable[P, T]) -> Callable[P, T]: - # https://github.com/python/mypy/issues/16024 - if isinstance(fn, _KeywordDecorator): # type:ignore[redundant-expr] - return fn # type:ignore[unreachable] + if isinstance(fn, _KeywordDecorator): + return fn keyword_name = self.name or cast( - str, - printable_name( # type:ignore[no-untyped-call] - fn.__name__, code_style=True - ), + str, printable_name(fn.__name__, code_style=True) ) # this doesn't really do anything in python land but we call the original robot keyword # decorator for completeness - deco.keyword(name=keyword_name, tags=self.tags)(fn) + deco.keyword( # pyright:ignore[reportUnknownMemberType] + name=keyword_name, tags=self.tags + )(fn) @wraps(fn) def inner(*args: P.args, **kwargs: P.kwargs) -> T: @@ -203,12 +212,7 @@ def inner(*args: P.args, **kwargs: P.kwargs) -> T: context = execution_context() data = running.Keyword(name=keyword_name, args=log_args) doc: str = ( - ( - getshortdoc( # type:ignore[no-untyped-call,no-any-expr] - inspect.getdoc(fn) - ) - or "" - ) + (getshortdoc(inspect.getdoc(fn)) or "") if self.doc is None else self.doc ) @@ -222,12 +226,11 @@ def inner(*args: P.args, **kwargs: P.kwargs) -> T: StatusReporter( # type:ignore[assignment] data=data, result=( - # mypy is only run when robot 7 is installed - result.Keyword( # type:ignore[call-arg] - kwname=keyword_name, - libname=self.module, - # https://github.com/KotlinIsland/basedmypy/issues/608 - doc=doc, # type:ignore[no-any-expr] + result.Keyword( + # pyright is only run when robot 7 is installed + kwname=keyword_name, # pyright:ignore[reportGeneralTypeIssues] + libname=self.module, # pyright:ignore[reportGeneralTypeIssues] + doc=doc, args=log_args, tags=self.tags, ) @@ -242,8 +245,7 @@ def inner(*args: P.args, **kwargs: P.kwargs) -> T: result=result.Keyword( name=keyword_name, owner=self.module, - # see issue link above - doc=doc, # type:ignore[no-any-expr] + doc=doc, args=log_args, tags=self.tags, ), @@ -315,45 +317,49 @@ def inner( *args: P.args, **kwargs: P.kwargs, ) -> T: - class WrappedContextManager(ContextManager[T]): + T_WrappedContextManager = TypeVar("T_WrappedContextManager") + + class WrappedContextManager(ContextManager[object]): """defers exiting the status reporter until after the wrapped context manager is finished""" def __init__( self, - wrapped: AbstractContextManager[T], + wrapped: AbstractContextManager[T_WrappedContextManager], status_reporter: ContextManager[object], ) -> None: + super().__init__() self.wrapped = wrapped self.status_reporter = status_reporter @override - def __enter__(self) -> T: + # https://github.com/DetachHead/basedpyright/issues/12 + def __enter__(self) -> object: # pyright:ignore[reportMissingSuperCall] # https://github.com/astral-sh/ruff/issues/9499 - self.status_reporter.__enter__() # noqa: PLC2801 + _ = self.status_reporter.__enter__() # noqa: PLC2801 return self.wrapped.__enter__() # noqa: PLC2801 @override - def __exit__( + def __exit__( # pyright:ignore[reportMissingSuperCall] self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /, ) -> bool: + suppress = False try: suppress = self.wrapped.__exit__(exc_type, exc_value, traceback) except BaseException as e: e.__context__ = exc_value exc_value = e - suppress = False raise finally: error = None if suppress else exc_value if error is None: - self.status_reporter.__exit__(None, None, None) + _ = self.status_reporter.__exit__(None, None, None) else: - self.status_reporter.__exit__( + _ = self.status_reporter.__exit__( type(error), error, error.__traceback__ ) return suppress or False @@ -365,8 +371,8 @@ def __exit__( f" {fn_result!r}" ) # 🚀 independently verified for safety by the overloads - return WrappedContextManager( # type:ignore[return-value] - fn_result, status_reporter + return WrappedContextManager( # pyright:ignore[reportGeneralTypeIssues] + cast(AbstractContextManager[object], fn_result), status_reporter ) def __call__( @@ -405,9 +411,11 @@ def keyword( ) -> _FunctionKeywordDecorator: ... -# prevent functions that return Never from matching the context manager overload @overload -def keyword(fn: Callable[P, Never]) -> Callable[P, Never]: ... +# prevent functions that return Never from matching the context manager overload +def keyword( # pyright:ignore[reportOverlappingOverload] + fn: Callable[P, Never] +) -> Callable[P, Never]: ... @deprecated( @@ -425,11 +433,11 @@ def keyword(fn: Callable[P, T]) -> Callable[P, T]: ... def keyword( # pylint:disable=missing-param-doc fn: Callable[P, T] | None = None, *, - name=None, - tags=None, - module=None, - wrap_context_manager=None, -): + name: str | None = None, + tags: tuple[str, ...] | None = None, + module: str | None = None, + wrap_context_manager: bool | None = None, +) -> _KeywordDecorator | Callable[P, T]: """marks a function as a keyword and makes it show in the robot log. unlike robot's `deco.keyword` decorator, this one will make your function appear as a keyword in @@ -457,11 +465,12 @@ def keyword( # pylint:disable=missing-param-doc return _NonWrappedContextManagerKeywordDecorator( name=name, tags=tags, module=module ) - return keyword( # type:ignore[return-value,type-var] - name=name, tags=tags, module=module, wrap_context_manager=wrap_context_manager - )( - fn # type:ignore[arg-type] - ) + return keyword( # pyright:ignore[reportGeneralTypeIssues,reportUnknownVariableType] + name=name, + tags=tags, + module=module, + wrap_context_manager=wrap_context_manager, # pyright:ignore[reportGeneralTypeIssues] + )(fn) def as_keyword( @@ -528,10 +537,8 @@ def keywordify( name=name, tags=tags, module=module, - wrap_context_manager=wrap_context_manager, - )( - getattr(obj, method_name) # type:ignore[no-any-expr] - ), + wrap_context_manager=wrap_context_manager, # pyright:ignore[reportGeneralTypeIssues] + )(getattr(obj, method_name)), ) @@ -579,16 +586,15 @@ def inner(*args: P.args, **kwargs: P.kwargs) -> T: # the wrapper breaks static methods idk why, but we shouldn't need to wrap them anyway # because robot listeners/suite visitors don't call any static/class methods and not isinstance( - inspect.getattr_static(cls, attr.__name__), # type:ignore[no-any-expr] - (staticmethod, classmethod), + inspect.getattr_static(cls, attr.__name__), (staticmethod, classmethod) ) # only wrap methods that are overwritten on the subclass - and attr.__name__ in vars(cls) # type:ignore[no-any-expr] + and attr.__name__ in vars(cls) # don't wrap private/dunder methods since they'll get called by the public ones and we don't # want to duplicate errors and not attr.__name__.startswith("_"), ): - setattr(cls, name, wrapped(method)) # type:ignore[no-any-expr] + setattr(cls, name, wrapped(method)) setattr(cls, marker, True) return cls @@ -626,10 +632,7 @@ def listener(obj: _T_Listener) -> _T_Listener: it gets registered before robot starts running.""" _RobotClassRegistry.check_too_late(obj) _RobotClassRegistry.listeners.append( - obj - if isinstance(obj, (ListenerV2, ListenerV3)) - # https://github.com/python/mypy/issues/16335 - else catch_errors(obj)() # type:ignore[type-var,operator] + obj if isinstance(obj, (ListenerV2, ListenerV3)) else catch_errors(obj)() ) return obj @@ -646,9 +649,6 @@ def pre_rebot_modifier(obj: _T_SuiteVisitor) -> _T_SuiteVisitor: it gets registered before robot starts running.""" _RobotClassRegistry.check_too_late(obj) _RobotClassRegistry.pre_rebot_modifiers.append( - obj - if isinstance(obj, SuiteVisitor) - # https://github.com/python/mypy/issues/16335 - else catch_errors(obj)() # type:ignore[type-var,operator] + obj if isinstance(obj, SuiteVisitor) else catch_errors(obj)() ) return obj