diff options
author | John L. Villalovos <john@sodarock.com> | 2021-11-24 17:43:46 -0800 |
---|---|---|
committer | John L. Villalovos <john@sodarock.com> | 2021-11-24 20:41:36 -0800 |
commit | 308210b3dee15c844cd5db4ddd8c7404bb188990 (patch) | |
tree | 4df88a7a967e209afcf2efea9efc5fd8be358128 | |
parent | ab82fd84ba3cecf766915112dcb46fdfab383e73 (diff) | |
download | gitlab-jlvillal/mypy_strict.tar.gz |
WIP: work on enable mypy 'strict' modejlvillal/mypy_strict
-rw-r--r-- | gitlab/__init__.py | 4 | ||||
-rw-r--r-- | gitlab/base.py | 21 | ||||
-rw-r--r-- | gitlab/client.py | 15 | ||||
-rw-r--r-- | gitlab/mixins.py | 9 | ||||
-rw-r--r-- | gitlab/utils.py | 2 | ||||
-rw-r--r-- | gitlab/v4/objects/files.py | 5 | ||||
-rw-r--r-- | gitlab/v4/objects/keys.py | 2 | ||||
-rw-r--r-- | gitlab/v4/objects/packages.py | 2 | ||||
-rw-r--r-- | gitlab/v4/objects/projects.py | 6 | ||||
-rw-r--r-- | gitlab/v4/objects/users.py | 1 | ||||
-rw-r--r-- | pyproject.toml | 17 | ||||
-rw-r--r-- | tests/functional/api/test_users.py | 4 | ||||
-rw-r--r-- | tests/meta/test_ensure_type_hints.py | 15 | ||||
-rw-r--r-- | tests/unit/test_base.py | 11 |
14 files changed, 90 insertions, 24 deletions
diff --git a/gitlab/__init__.py b/gitlab/__init__.py index 7b79f22..34f6619 100644 --- a/gitlab/__init__.py +++ b/gitlab/__init__.py @@ -32,3 +32,7 @@ from gitlab.const import * # noqa: F401,F403 from gitlab.exceptions import * # noqa: F401,F403 warnings.filterwarnings("default", category=DeprecationWarning, module="^gitlab") + +__all__ = [ + "Gitlab", +] diff --git a/gitlab/base.py b/gitlab/base.py index 5e5f57b..8dcae7d 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -17,7 +17,17 @@ import importlib from types import ModuleType -from typing import Any, Dict, Iterable, NamedTuple, Optional, Tuple, Type +from typing import ( + Any, + Dict, + Iterable, + NamedTuple, + Optional, + Tuple, + Type, + TYPE_CHECKING, + Union, +) from gitlab import types as g_types from gitlab.exceptions import GitlabParsingError @@ -172,15 +182,18 @@ class RESTObject(object): self.__dict__["_updated_attrs"] = {} self.__dict__["_attrs"] = new_attrs - def get_id(self) -> Any: + def get_id(self) -> Optional[Union[int, str]]: """Returns the id of the resource.""" if self._id_attr is None or not hasattr(self, self._id_attr): return None - return getattr(self, self._id_attr) + id_val = getattr(self, self._id_attr) + if TYPE_CHECKING: + assert isinstance(id_val, (int, str)) + return id_val @property def attributes(self) -> Dict[str, Any]: - d = self.__dict__["_updated_attrs"].copy() + d: Dict[str, Any] = self.__dict__["_updated_attrs"].copy() d.update(self.__dict__["_attrs"]) d.update(self.__dict__["_parent_attrs"]) return d diff --git a/gitlab/client.py b/gitlab/client.py index 295712c..467c5dc 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -337,6 +337,7 @@ class Gitlab(object): data = self.http_post("/markdown", post_data=post_data, **kwargs) if TYPE_CHECKING: assert not isinstance(data, requests.Response) + assert isinstance(data["html"], str) return data["html"] @gitlab.exceptions.on_http_error(gitlab.exceptions.GitlabLicenseError) @@ -614,10 +615,13 @@ class Gitlab(object): cur_retries = 0 while True: result = self.session.send(prepped, timeout=timeout, **settings) + print("result type:", type(result)) + print("result:", result) self._check_redirects(result) if 200 <= result.status_code < 300: + print("Returning result:", result) return result retry_transient_errors = kwargs.get( @@ -694,7 +698,8 @@ class Gitlab(object): and not raw ): try: - return result.json() + data: Dict[str, Any] = result.json() + return data except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" @@ -791,7 +796,10 @@ class Gitlab(object): ) try: if result.headers.get("Content-Type", None) == "application/json": - return result.json() + print("data.text:", result.text) + data: Dict[str, Any] = result.json() + print("data type:", type(data)) + return data except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" @@ -839,7 +847,8 @@ class Gitlab(object): **kwargs, ) try: - return result.json() + data: Dict[str, Any] = result.json() + return data except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" diff --git a/gitlab/mixins.py b/gitlab/mixins.py index 0159ecd..785e3c5 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -573,9 +573,11 @@ class ObjectDeleteMixin(_RestObjectBase): GitlabAuthenticationError: If authentication is not correct GitlabDeleteError: If the server cannot perform the request """ + id_val = self.get_id() if TYPE_CHECKING: assert isinstance(self.manager, DeleteMixin) - self.manager.delete(self.get_id(), **kwargs) + assert id_val is not None + self.manager.delete(id_val, **kwargs) class UserAgentDetailMixin(_RestObjectBase): @@ -652,7 +654,7 @@ class DownloadMixin(_RestObjectBase): def download( self, streamed: bool = False, - action: Optional[Callable] = None, + action: Optional[Callable[[bytes], None]] = None, chunk_size: int = 1024, **kwargs: Any, ) -> Optional[bytes]: @@ -779,7 +781,8 @@ class TimeTrackingMixin(_RestObjectBase): # Use the existing time_stats attribute if it exist, otherwise make an # API call if "time_stats" in self.attributes: - return self.attributes["time_stats"] + time_stats: Dict[str, Any] = self.attributes["time_stats"] + return time_stats path = f"{self.manager.path}/{self.get_id()}/time_stats" result = self.manager.gitlab.http_get(path, **kwargs) diff --git a/gitlab/utils.py b/gitlab/utils.py index 220a8c9..196210c 100644 --- a/gitlab/utils.py +++ b/gitlab/utils.py @@ -29,7 +29,7 @@ class _StdoutStream(object): def response_content( response: requests.Response, streamed: bool, - action: Optional[Callable], + action: Optional[Callable[[bytes], None]], chunk_size: int, ) -> Optional[bytes]: if streamed is False: diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index ce7317d..fa4d744 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -76,7 +76,10 @@ class ProjectFile(SaveMixin, ObjectDeleteMixin, RESTObject): GitlabAuthenticationError: If authentication is not correct GitlabDeleteError: If the server cannot perform the request """ - file_path = self.get_id().replace("/", "%2F") + id_val = self.get_id() + if TYPE_CHECKING: + assert isinstance(id_val, str) + file_path = id_val.replace("/", "%2F") self.manager.delete(file_path, branch, commit_message, **kwargs) diff --git a/gitlab/v4/objects/keys.py b/gitlab/v4/objects/keys.py index 46f6894..c03dced 100644 --- a/gitlab/v4/objects/keys.py +++ b/gitlab/v4/objects/keys.py @@ -31,4 +31,4 @@ class KeyManager(GetMixin, RESTManager): server_data = self.gitlab.http_get(self.path, **kwargs) if TYPE_CHECKING: assert isinstance(server_data, dict) - return cast(Key, self._obj_cls(self, server_data)) + return self._obj_cls(self, server_data) diff --git a/gitlab/v4/objects/packages.py b/gitlab/v4/objects/packages.py index 0062067..55e45a3 100644 --- a/gitlab/v4/objects/packages.py +++ b/gitlab/v4/objects/packages.py @@ -103,7 +103,7 @@ class GenericPackageManager(RESTManager): package_version: str, file_name: str, streamed: bool = False, - action: Optional[Callable] = None, + action: Optional[Callable[[bytes], None]] = None, chunk_size: int = 1024, **kwargs: Any, ) -> Optional[bytes]: diff --git a/gitlab/v4/objects/projects.py b/gitlab/v4/objects/projects.py index c5ce717..6fd60f4 100644 --- a/gitlab/v4/objects/projects.py +++ b/gitlab/v4/objects/projects.py @@ -436,7 +436,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO self, wiki: bool = False, streamed: bool = False, - action: Optional[Callable] = None, + action: Optional[Callable[[bytes], None]] = None, chunk_size: int = 1024, **kwargs: Any, ) -> Optional[bytes]: @@ -531,7 +531,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO ref_name: str, job: str, streamed: bool = False, - action: Optional[Callable] = None, + action: Optional[Callable[[bytes], None]] = None, chunk_size: int = 1024, **kwargs: Any, ) -> Optional[bytes]: @@ -574,7 +574,7 @@ class Project(RefreshMixin, SaveMixin, ObjectDeleteMixin, RepositoryMixin, RESTO artifact_path: str, job: str, streamed: bool = False, - action: Optional[Callable] = None, + action: Optional[Callable[[bytes], None]] = None, chunk_size: int = 1024, **kwargs: Any, ) -> Optional[bytes]: diff --git a/gitlab/v4/objects/users.py b/gitlab/v4/objects/users.py index bf7d283..bced3fc 100644 --- a/gitlab/v4/objects/users.py +++ b/gitlab/v4/objects/users.py @@ -181,6 +181,7 @@ class User(SaveMixin, ObjectDeleteMixin, RESTObject): """ path = f"/users/{self.id}/block" server_data = self.manager.gitlab.http_post(path, **kwargs) + print(type(server_data)) if server_data is True: self._attrs["state"] = "blocked" return server_data diff --git a/pyproject.toml b/pyproject.toml index a19b28e..e70d3a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,23 @@ disallow_incomplete_defs = true disallow_untyped_defs = true files = "." +# strict = true is +check_untyped_defs = true +disallow_any_generics = true +# disallow_incomplete_defs = true +disallow_subclassing_any = true +# disallow_untyped_calls = true +disallow_untyped_decorators = true +# disallow_untyped_defs = true +no_implicit_optional = true +# no_implicit_reexport = true +strict_equality = true +warn_redundant_casts = true +warn_return_any = true +warn_unused_configs = true +warn_unused_ignores = true + + [[tool.mypy.overrides]] # Overrides for currently untyped modules module = [ "docs.*", diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py index 1ef237c..a21298e 100644 --- a/tests/functional/api/test_users.py +++ b/tests/functional/api/test_users.py @@ -33,7 +33,9 @@ def test_create_user(gl, avatar_path): def test_block_user(gl, user): - user.block() + result = user.block() + result = user.block() + assert result == "dkdkdkdk" users = gl.users.list(blocked=True) assert user in users diff --git a/tests/meta/test_ensure_type_hints.py b/tests/meta/test_ensure_type_hints.py index 2449324..0de9af2 100644 --- a/tests/meta/test_ensure_type_hints.py +++ b/tests/meta/test_ensure_type_hints.py @@ -7,7 +7,7 @@ Original notes by John L. Villalovos import dataclasses import functools import inspect -from typing import Optional, Type +from typing import Optional, Type, Union, TYPE_CHECKING import _pytest @@ -19,7 +19,7 @@ import gitlab.v4.objects @dataclasses.dataclass(frozen=True) class ClassInfo: name: str - type: Type + type: Type[Union[gitlab.mixins.GetMixin, gitlab.mixins.GetWithoutIdMixin]] def __lt__(self, other: object) -> bool: if not isinstance(other, ClassInfo): @@ -108,7 +108,7 @@ class TestTypeHints: def get_check_helper( self, *, - base_type: Type, + base_type: Type[Union[gitlab.mixins.GetMixin, gitlab.mixins.GetWithoutIdMixin]], class_info: ClassInfo, method_template: str, optional_return: bool, @@ -121,6 +121,11 @@ class TestTypeHints: return obj_cls = class_info.type._obj_cls + if TYPE_CHECKING: + assert obj_cls is not None + assert issubclass( + obj_cls, (gitlab.mixins.GetMixin, gitlab.mixins.GetWithoutIdMixin) + ) signature = inspect.signature(class_info.type.get) filename = inspect.getfile(class_info.type) @@ -131,7 +136,9 @@ class TestTypeHints: f"Recommend adding the followinng method:\n" ) fail_message += method_template.format(obj_cls=obj_cls) - check_type = obj_cls + check_type: Optional[ + Type[Union[gitlab.mixins.GetMixin, gitlab.mixins.GetWithoutIdMixin]] + ] = obj_cls if optional_return: check_type = Optional[obj_cls] assert check_type == signature.return_annotation, fail_message diff --git a/tests/unit/test_base.py b/tests/unit/test_base.py index 137f480..902d9b4 100644 --- a/tests/unit/test_base.py +++ b/tests/unit/test_base.py @@ -117,8 +117,8 @@ class TestRESTObject: obj.id = 42 assert 42 == obj.get_id() - obj.id = None - assert obj.get_id() is None + obj.id = "hello" + assert "hello" == obj.get_id() def test_custom_id_attr(self, fake_manager): class OtherFakeObject(FakeObject): @@ -127,6 +127,13 @@ class TestRESTObject: obj = OtherFakeObject(fake_manager, {"foo": "bar"}) assert "bar" == obj.get_id() + def test_custom_id_attr_missing(self, fake_manager): + class OtherFakeObject(FakeObject): + _id_attr = "spam" + + obj = OtherFakeObject(fake_manager, {"foo": "bar"}) + assert obj.get_id() is None + def test_update_attrs(self, fake_manager): obj = FakeObject(fake_manager, {"foo": "bar"}) obj.bar = "baz" |