diff options
-rw-r--r-- | docs/api-objects.rst | 2 | ||||
-rw-r--r-- | docs/gl_objects/clusters.rst | 11 | ||||
-rw-r--r-- | docs/gl_objects/topics.rst | 2 | ||||
-rw-r--r-- | gitlab/base.py | 12 | ||||
-rw-r--r-- | gitlab/cli.py | 20 | ||||
-rw-r--r-- | gitlab/client.py | 16 | ||||
-rw-r--r-- | gitlab/config.py | 2 | ||||
-rw-r--r-- | gitlab/mixins.py | 5 | ||||
-rw-r--r-- | gitlab/v4/objects/topics.py | 5 | ||||
-rw-r--r-- | pyproject.toml | 2 | ||||
-rw-r--r-- | tests/functional/api/test_clusters.py | 44 | ||||
-rw-r--r-- | tests/functional/api/test_topics.py | 11 | ||||
-rw-r--r-- | tests/functional/conftest.py | 19 | ||||
-rw-r--r-- | tests/functional/fixtures/.env | 2 | ||||
-rw-r--r-- | tests/meta/test_v4_objects_imported.py | 2 | ||||
-rw-r--r-- | tests/unit/objects/test_packages.py | 2 | ||||
-rw-r--r-- | tests/unit/objects/test_projects.py | 2 | ||||
-rw-r--r-- | tests/unit/objects/test_topics.py | 5 | ||||
-rw-r--r-- | tests/unit/test_cli.py | 3 | ||||
-rw-r--r-- | tests/unit/test_config.py | 3 |
20 files changed, 99 insertions, 71 deletions
diff --git a/docs/api-objects.rst b/docs/api-objects.rst index 3c76d77..349b7cf 100644 --- a/docs/api-objects.rst +++ b/docs/api-objects.rst @@ -11,7 +11,6 @@ API examples gl_objects/emojis gl_objects/badges gl_objects/branches - gl_objects/clusters gl_objects/messages gl_objects/ci_lint gl_objects/commits @@ -63,3 +62,4 @@ API examples gl_objects/variables gl_objects/sidekiq gl_objects/wikis + gl_objects/clusters diff --git a/docs/gl_objects/clusters.rst b/docs/gl_objects/clusters.rst index 96edd82..ff39dcc 100644 --- a/docs/gl_objects/clusters.rst +++ b/docs/gl_objects/clusters.rst @@ -1,6 +1,11 @@ -############ -Clusters -############ +##################### +Clusters (DEPRECATED) +##################### + +.. warning:: + Cluster support was deprecated in GitLab 14.5 and disabled by default as of + GitLab 15.0 + Reference --------- diff --git a/docs/gl_objects/topics.rst b/docs/gl_objects/topics.rst index 0ca46d7..c9a3573 100644 --- a/docs/gl_objects/topics.rst +++ b/docs/gl_objects/topics.rst @@ -30,7 +30,7 @@ Get a specific topic by its ID:: Create a new topic:: - topic = gl.topics.create({"name": "my-topic"}) + topic = gl.topics.create({"name": "my-topic", "title": "my title"}) Update a topic:: diff --git a/gitlab/base.py b/gitlab/base.py index e813fcd..c2df65a 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -21,7 +21,7 @@ import json import pprint import textwrap from types import ModuleType -from typing import Any, Dict, Iterable, Optional, Type, Union +from typing import Any, Dict, Iterable, Optional, Type, TYPE_CHECKING, Union import gitlab from gitlab import types as g_types @@ -245,14 +245,20 @@ class RESTObject: """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 id_val is None or isinstance(id_val, (int, str)) + return id_val @property def _repr_value(self) -> Optional[str]: """Safely returns the human-readable resource name if present.""" if self._repr_attr is None or not hasattr(self, self._repr_attr): return None - return getattr(self, self._repr_attr) + repr_val = getattr(self, self._repr_attr) + if TYPE_CHECKING: + assert isinstance(repr_val, str) + return repr_val @property def encoded_id(self) -> Optional[Union[int, str]]: diff --git a/gitlab/cli.py b/gitlab/cli.py index fd519c3..294d6a8 100644 --- a/gitlab/cli.py +++ b/gitlab/cli.py @@ -23,7 +23,18 @@ import os import re import sys from types import ModuleType -from typing import Any, Callable, cast, Dict, Optional, Tuple, Type, TypeVar, Union +from typing import ( + Any, + Callable, + cast, + Dict, + Optional, + Tuple, + Type, + TYPE_CHECKING, + TypeVar, + Union, +) from requests.structures import CaseInsensitiveDict @@ -113,8 +124,11 @@ def gitlab_resource_to_cls( ) -> Type[RESTObject]: classes = CaseInsensitiveDict(namespace.__dict__) lowercase_class = gitlab_resource.replace("-", "") - - return classes[lowercase_class] + class_type = classes[lowercase_class] + if TYPE_CHECKING: + assert isinstance(class_type, type) + assert issubclass(class_type, RESTObject) + return class_type def cls_to_gitlab_resource(cls: RESTObject) -> str: diff --git a/gitlab/client.py b/gitlab/client.py index e7c44ae..97ca636 100644 --- a/gitlab/client.py +++ b/gitlab/client.py @@ -439,6 +439,7 @@ class Gitlab: 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) @@ -808,7 +809,10 @@ class Gitlab: and not raw ): try: - return result.json() + json_result = result.json() + if TYPE_CHECKING: + assert isinstance(json_result, dict) + return json_result except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" @@ -989,7 +993,10 @@ class Gitlab: ) try: if result.headers.get("Content-Type", None) == "application/json": - return result.json() + json_result = result.json() + if TYPE_CHECKING: + assert isinstance(json_result, dict) + return json_result except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" @@ -1037,7 +1044,10 @@ class Gitlab: **kwargs, ) try: - return result.json() + json_result = result.json() + if TYPE_CHECKING: + assert isinstance(json_result, dict) + return json_result except Exception as e: raise gitlab.exceptions.GitlabParsingError( error_message="Failed to parse the server message" diff --git a/gitlab/config.py b/gitlab/config.py index dc577fc..152ec7e 100644 --- a/gitlab/config.py +++ b/gitlab/config.py @@ -135,7 +135,7 @@ class GitlabConfigParser: def _parse_config(self) -> None: _config = configparser.ConfigParser() - _config.read(self._files) + _config.read(self._files, encoding="utf-8") if self.gitlab_id and not _config.has_section(self.gitlab_id): raise GitlabDataError( diff --git a/gitlab/mixins.py b/gitlab/mixins.py index a48c032..d58227a 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -755,7 +755,10 @@ 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 = self.attributes["time_stats"] + if TYPE_CHECKING: + assert isinstance(time_stats, dict) + return time_stats path = f"{self.manager.path}/{self.encoded_id}/time_stats" result = self.manager.gitlab.http_get(path, **kwargs) diff --git a/gitlab/v4/objects/topics.py b/gitlab/v4/objects/topics.py index 57b104e..143759d 100644 --- a/gitlab/v4/objects/topics.py +++ b/gitlab/v4/objects/topics.py @@ -19,7 +19,10 @@ class TopicManager(CRUDMixin, RESTManager): _path = "/topics" _obj_cls = Topic _create_attrs = RequiredOptional( - required=("name",), optional=("avatar", "description") + # NOTE: The `title` field was added and is required in GitLab 15.0 or + # newer. But not present before that. + required=("name",), + optional=("avatar", "description", "title"), ) _update_attrs = RequiredOptional(optional=("avatar", "description", "name")) _types = {"avatar": types.ImageAttribute} diff --git a/pyproject.toml b/pyproject.toml index 43359e9..544543b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ disallow_untyped_defs = true no_implicit_reexport = true strict_equality = true warn_redundant_casts = true +warn_return_any = true warn_unused_configs = true warn_unused_ignores = true @@ -23,7 +24,6 @@ warn_unused_ignores = true # disallow_any_generics = true # disallow_untyped_calls = true # no_implicit_optional = true -# warn_return_any = true [[tool.mypy.overrides]] # Overrides for currently untyped modules module = [ diff --git a/tests/functional/api/test_clusters.py b/tests/functional/api/test_clusters.py deleted file mode 100644 index 32d1488..0000000 --- a/tests/functional/api/test_clusters.py +++ /dev/null @@ -1,44 +0,0 @@ -def test_project_clusters(project): - cluster = project.clusters.create( - { - "name": "cluster1", - "platform_kubernetes_attributes": { - "api_url": "http://url", - "token": "tokenval", - }, - } - ) - clusters = project.clusters.list() - assert cluster in clusters - - cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} - cluster.save() - - cluster = project.clusters.list()[0] - assert cluster.platform_kubernetes["api_url"] == "http://newurl" - - cluster.delete() - assert cluster not in project.clusters.list() - - -def test_group_clusters(group): - cluster = group.clusters.create( - { - "name": "cluster1", - "platform_kubernetes_attributes": { - "api_url": "http://url", - "token": "tokenval", - }, - } - ) - clusters = group.clusters.list() - assert cluster in clusters - - cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} - cluster.save() - - cluster = group.clusters.list()[0] - assert cluster.platform_kubernetes["api_url"] == "http://newurl" - - cluster.delete() - assert cluster not in group.clusters.list() diff --git a/tests/functional/api/test_topics.py b/tests/functional/api/test_topics.py index 7ad71a5..0d6a3ef 100644 --- a/tests/functional/api/test_topics.py +++ b/tests/functional/api/test_topics.py @@ -4,11 +4,18 @@ https://docs.gitlab.com/ce/api/topics.html """ -def test_topics(gl): +def test_topics(gl, gitlab_version): assert not gl.topics.list() - topic = gl.topics.create({"name": "my-topic", "description": "My Topic"}) + create_dict = {"name": "my-topic", "description": "My Topic"} + if gitlab_version.major >= 15: + create_dict["title"] = "my topic title" + topic = gl.topics.create( + {"name": "my-topic", "title": "my topic title", "description": "My Topic"} + ) assert topic.name == "my-topic" + if gitlab_version.major >= 15: + assert topic.title == "my topic title" assert gl.topics.list() topic.description = "My Updated Topic" diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index 2767b9d..dc4422e 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -1,3 +1,4 @@ +import dataclasses import logging import tempfile import time @@ -12,6 +13,24 @@ import gitlab.base from tests.functional import helpers +@dataclasses.dataclass +class GitlabVersion: + major: int + minor: int + patch: str + revision: str + + def __post_init__(self): + self.major, self.minor = int(self.major), int(self.minor) + + +@pytest.fixture(scope="session") +def gitlab_version(gl) -> GitlabVersion: + version, revision = gl.version() + major, minor, patch = version.split(".") + return GitlabVersion(major=major, minor=minor, patch=patch, revision=revision) + + @pytest.fixture(scope="session") def fixture_dir(test_dir): return test_dir / "functional" / "fixtures" diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env index e7be6c9..9be02fe 100644 --- a/tests/functional/fixtures/.env +++ b/tests/functional/fixtures/.env @@ -1,2 +1,2 @@ GITLAB_IMAGE=gitlab/gitlab-ee -GITLAB_TAG=14.9.2-ee.0 +GITLAB_TAG=15.2.0-ee.0 diff --git a/tests/meta/test_v4_objects_imported.py b/tests/meta/test_v4_objects_imported.py index 083443a..f158a33 100644 --- a/tests/meta/test_v4_objects_imported.py +++ b/tests/meta/test_v4_objects_imported.py @@ -13,7 +13,7 @@ def test_verify_v4_objects_imported() -> None: assert len(gitlab.v4.objects.__path__) == 1 init_files: Set[str] = set() - with open(gitlab.v4.objects.__file__, "r") as in_file: + with open(gitlab.v4.objects.__file__, "r", encoding="utf-8") as in_file: for line in in_file.readlines(): if line.startswith("from ."): init_files.add(line.rstrip()) diff --git a/tests/unit/objects/test_packages.py b/tests/unit/objects/test_packages.py index 79f1d1b..cee3fa8 100644 --- a/tests/unit/objects/test_packages.py +++ b/tests/unit/objects/test_packages.py @@ -276,7 +276,7 @@ def test_delete_project_package_file_from_package_file_object( def test_upload_generic_package(tmp_path, project, resp_upload_generic_package): path = tmp_path / file_name - path.write_text(file_content) + path.write_text(file_content, encoding="utf-8") package = project.generic_packages.upload( package_name=package_name, package_version=package_version, diff --git a/tests/unit/objects/test_projects.py b/tests/unit/objects/test_projects.py index 6134382..1e5e7d1 100644 --- a/tests/unit/objects/test_projects.py +++ b/tests/unit/objects/test_projects.py @@ -431,7 +431,7 @@ def resp_start_housekeeping(): rsps.add( method=responses.POST, url="http://localhost/api/v4/projects/1/housekeeping", - json="0ee4c430667fb7be8461f310", + json={}, content_type="application/json", status=201, ) diff --git a/tests/unit/objects/test_topics.py b/tests/unit/objects/test_topics.py index 14b2cfd..46e964e 100644 --- a/tests/unit/objects/test_topics.py +++ b/tests/unit/objects/test_topics.py @@ -8,10 +8,12 @@ import responses from gitlab.v4.objects import Topic name = "GitLab" +topic_title = "topic title" new_name = "gitlab-test" topic_content = { "id": 1, "name": name, + "title": topic_title, "description": "GitLab is an open source end-to-end software development platform.", "total_projects_count": 1000, "avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon", @@ -102,9 +104,10 @@ def test_get_topic(gl, resp_get_topic): def test_create_topic(gl, resp_create_topic): - topic = gl.topics.create({"name": name}) + topic = gl.topics.create({"name": name, "title": topic_title}) assert isinstance(topic, Topic) assert topic.name == name + assert topic.title == topic_title def test_update_topic(gl, resp_update_topic): diff --git a/tests/unit/test_cli.py b/tests/unit/test_cli.py index ef33b5d..30693d9 100644 --- a/tests/unit/test_cli.py +++ b/tests/unit/test_cli.py @@ -24,6 +24,7 @@ from contextlib import redirect_stderr # noqa: H302 import pytest +import gitlab.base from gitlab import cli from gitlab.exceptions import GitlabError @@ -43,7 +44,7 @@ def test_gitlab_resource_to_cls(gitlab_resource, expected_class): def _namespace(): pass - ExpectedClass = type(expected_class, (), {}) + ExpectedClass = type(expected_class, (gitlab.base.RESTObject,), {}) _namespace.__dict__[expected_class] = ExpectedClass assert cli.gitlab_resource_to_cls(gitlab_resource, _namespace) == ExpectedClass diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index c4fcbef..0e3324a 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -290,7 +290,8 @@ def test_data_from_helper(m_open, monkeypatch, tmp_path): #!/bin/sh echo "secret" """ - ) + ), + encoding="utf-8", ) helper.chmod(0o755) |