summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api-objects.rst2
-rw-r--r--docs/gl_objects/clusters.rst11
-rw-r--r--docs/gl_objects/topics.rst2
-rw-r--r--gitlab/base.py12
-rw-r--r--gitlab/cli.py20
-rw-r--r--gitlab/client.py16
-rw-r--r--gitlab/config.py2
-rw-r--r--gitlab/mixins.py5
-rw-r--r--gitlab/v4/objects/topics.py5
-rw-r--r--pyproject.toml2
-rw-r--r--tests/functional/api/test_clusters.py44
-rw-r--r--tests/functional/api/test_topics.py11
-rw-r--r--tests/functional/conftest.py19
-rw-r--r--tests/functional/fixtures/.env2
-rw-r--r--tests/meta/test_v4_objects_imported.py2
-rw-r--r--tests/unit/objects/test_packages.py2
-rw-r--r--tests/unit/objects/test_projects.py2
-rw-r--r--tests/unit/objects/test_topics.py5
-rw-r--r--tests/unit/test_cli.py3
-rw-r--r--tests/unit/test_config.py3
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)