From 33477f057f561eceff5af832b2ec87155cc5d615 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Fri, 6 Sep 2024 09:34:20 -0400 Subject: [PATCH] Add types and ament_mypy to rpyutils. (#12) * types * system check * add dep * add None check Signed-off-by: Michael Carlstrom --- package.xml | 1 + rpyutils/add_dll_directories.py | 12 ++++++++++-- rpyutils/import_c_library.py | 9 ++++++--- test/rpyutils/test_add_dll_directories.py | 4 +++- test/test_copyright.py | 2 +- test/test_flake8.py | 2 +- test/test_mypy.py | 23 +++++++++++++++++++++++ test/test_pep257.py | 2 +- test/test_xmllint.py | 2 +- 9 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 test/test_mypy.py diff --git a/package.xml b/package.xml index ab66fd2..499dc32 100644 --- a/package.xml +++ b/package.xml @@ -13,6 +13,7 @@ ament_copyright ament_flake8 + ament_mypy ament_pep257 ament_xmllint python3-pytest diff --git a/rpyutils/add_dll_directories.py b/rpyutils/add_dll_directories.py index 3a5de20..cac2861 100644 --- a/rpyutils/add_dll_directories.py +++ b/rpyutils/add_dll_directories.py @@ -15,10 +15,18 @@ from contextlib import contextmanager import os import sys +from typing import Any, Generator, List, TYPE_CHECKING + +if TYPE_CHECKING: + if sys.platform == 'win32': + from os import _AddedDllDirectory + else: + _AddedDllDirectory = Any @contextmanager -def add_dll_directories_from_env(env_name: str): +def add_dll_directories_from_env(env_name: str + ) -> 'Generator[List[_AddedDllDirectory], None, None]': """ Add a list of directories from an environment variable to the DLL search path on Windows. @@ -38,7 +46,7 @@ def add_dll_directories_from_env(env_name: str): :param env_name: The name of the environment variable with DLL search paths. :return: A list of handles to directories. """ - dll_dir_handles = [] + dll_dir_handles: 'List[_AddedDllDirectory]' = [] # This function only makes sense on Windows and if the function 'add_dll_directory' exists if sys.platform == 'win32' and hasattr(os, 'add_dll_directory'): env_value = os.environ.get(env_name) diff --git a/rpyutils/import_c_library.py b/rpyutils/import_c_library.py index a91e43c..00358f6 100644 --- a/rpyutils/import_c_library.py +++ b/rpyutils/import_c_library.py @@ -15,12 +15,13 @@ import importlib import os from pathlib import Path +from types import ModuleType from typing import Optional from rpyutils import add_dll_directories_from_env -def import_c_library(name: str, package: Optional[str] = None): +def import_c_library(name: str, package: Optional[str] = None) -> ModuleType: """ Import and return a C extension library using importlib, with consistent error messaging. @@ -41,8 +42,10 @@ def import_c_library(name: str, package: Optional[str] = None): distro = os.environ.get('ROS_DISTRO', 'rolling') if e.path is None: import sysconfig - expected_path = Path(__file__).parents[1] / ( - name[1:] + sysconfig.get_config_var('EXT_SUFFIX')) + config_vars = sysconfig.get_config_var('EXT_SUFFIX') + if config_vars: + expected_path = Path(__file__).parents[1] / ( + name[1:] + config_vars) assert not expected_path.is_file() link = f'https://docs.ros.org/en/{distro}/How-To-Guides/Installation-Troubleshooting.html#import-failing-without-library-present-on-the-system' # noqa: E501 e.msg += \ diff --git a/test/rpyutils/test_add_dll_directories.py b/test/rpyutils/test_add_dll_directories.py index a33b72b..60e787a 100644 --- a/test/rpyutils/test_add_dll_directories.py +++ b/test/rpyutils/test_add_dll_directories.py @@ -12,12 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path import sys +from pytest import MonkeyPatch from rpyutils import add_dll_directories_from_env -def test_add_dll_directories_from_env(monkeypatch, tmp_path): +def test_add_dll_directories_from_env(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: # Test with empty value monkeypatch.delenv('TEST_ENV', raising=False) with add_dll_directories_from_env('TEST_ENV') as dlls: diff --git a/test/test_copyright.py b/test/test_copyright.py index cf0fae3..66a7d63 100644 --- a/test/test_copyright.py +++ b/test/test_copyright.py @@ -18,6 +18,6 @@ @pytest.mark.copyright @pytest.mark.linter -def test_copyright(): +def test_copyright() -> None: rc = main(argv=['.', 'test']) assert rc == 0, 'Found errors' diff --git a/test/test_flake8.py b/test/test_flake8.py index 27ee107..eac16ee 100644 --- a/test/test_flake8.py +++ b/test/test_flake8.py @@ -18,7 +18,7 @@ @pytest.mark.flake8 @pytest.mark.linter -def test_flake8(): +def test_flake8() -> None: rc, errors = main_with_errors(argv=[]) assert rc == 0, \ 'Found %d code style errors / warnings:\n' % len(errors) + \ diff --git a/test/test_mypy.py b/test/test_mypy.py new file mode 100644 index 0000000..97e4f50 --- /dev/null +++ b/test/test_mypy.py @@ -0,0 +1,23 @@ +# Copyright 2024 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ament_mypy.main import main +import pytest + + +@pytest.mark.mypy +@pytest.mark.linter +def test_mypy() -> None: + rc = main(argv=[]) + assert rc == 0, 'Found type errors!' diff --git a/test/test_pep257.py b/test/test_pep257.py index 0e38a6c..4ae521a 100644 --- a/test/test_pep257.py +++ b/test/test_pep257.py @@ -18,6 +18,6 @@ @pytest.mark.linter @pytest.mark.pep257 -def test_pep257(): +def test_pep257() -> None: rc = main(argv=[]) assert rc == 0, 'Found code style errors / warnings' diff --git a/test/test_xmllint.py b/test/test_xmllint.py index f46285e..08bf7fd 100644 --- a/test/test_xmllint.py +++ b/test/test_xmllint.py @@ -18,6 +18,6 @@ @pytest.mark.linter @pytest.mark.xmllint -def test_xmllint(): +def test_xmllint() -> None: rc = main(argv=[]) assert rc == 0, 'Found errors'