Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat(api): Use protocols and overload to type hint mixins#3080

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Conversation

igorp-collabora
Copy link
Contributor

Changes

Currently mixins like ListMixin are type hinted to return base RESTObject instead of a specific class likeMergeRequest.

The GetMixin and GetWithoutIdMixin solve this problem by defining a newget method for every defined class. However, this creates a lot of duplicated code.

typing.Protocol can be used to type hint that the mixed in method will return a class matching attribute_obj_cls. The type checker will lookup the mixed in class attribute and adjust the return type accordinly.

Deletetests/unit/meta/test_ensure_type_hints.py file as theget method is no required to be defined for every class.

Closes#2228
Closes#2062

Currently mixins like ListMixin are type hinted to return baseRESTObject instead of a specific class like `MergeRequest`.The GetMixin and GetWithoutIdMixin solve this problem by defininga new `get` method for every defined class. However, this createsa lot of duplicated code.`typing.Protocol` can be used to type hint that the mixed in methodwill return a class matching attribute `_obj_cls`. The type checkerwill lookup the mixed in class attribute and adjust the return typeaccordinly.Delete `tests/unit/meta/test_ensure_type_hints.py` file as the `get`method is no required to be defined for every class.Signed-off-by: Igor Ponomarev <igor.ponomarev@collabora.com>
@igorp-collabora
Copy link
ContributorAuthor

igorp-collabora commentedJan 7, 2025
edited
Loading

This turned out to be a larger commit than I expected. I can probably split it by a mixin. For example, first commit for Get and GetWithoutId mixins, second one for ListMixin...

Also this is a pretty ad-hoc solution but requires the least amount of changes. Ideally making aRESTManager generic would be better as it would remove alloverload. However, this MR can be a starting point to reworking theRESTManager hierarchy.

I made a table of the attributes each mixin uses:#2228 (comment)

Looking at it the RESTManager hierarchy would probably be a base object without_obj_cls and then a subclass with generic_obj_cls: Type[T]

@@ -347,7 +347,6 @@ class RESTManager:
_create_attrs: g_types.RequiredOptional = g_types.RequiredOptional()
_update_attrs: g_types.RequiredOptional = g_types.RequiredOptional()
_path: Optional[str] = None
_obj_cls: Optional[Type[RESTObject]] = None
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The_obj_cls needs to be specific for each class. Having a_obj_cls as None in base class will prevent Protocol from working.

@@ -397,10 +397,11 @@ def auth(self) -> None:
The `user` attribute will hold a `gitlab.objects.CurrentUser` object on
success.
"""
self.user = self._objects.CurrentUserManager(self).get()
user = self._objects.CurrentUserManager(self).get()
self.user = user
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For some reasonself.user will now be type evaluated asOptional[User]. I had to separate user to a new variable that can't be None.

*,
iterator: Literal[True] = True,
**kwargs: Any,
) -> base.RESTObjectList: ...
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I added an overload to the list method so that passingiterator will change if a list is returned or an iterator. This can be separated to another commit and MR.

AlsoRESTObjectList is not generic. I want to create a follow-up MR there it will be made generic to iterate over a specific type.

@@ -104,12 +104,12 @@ def reset_gitlab(gl: gitlab.Gitlab) -> None:
)
continue

fordeploy_token in group.deploytokens.list():
forgroup_deploy_token in group.deploytokens.list():
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Becauselist() now returns a list containing a specific type it would case a type error here because the same variable is used between two different types.

@@ -4,7 +4,9 @@
https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user
"""

from typing import Any, cast, Dict, List, Optional, Union
from __future__ import annotations
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For some reason without delayed annotations type checks failed to resolve types forUserProject.list. This means the quotes around the type annotation had to be removed or managers would not be initialized.

@JohnVillalovos
Copy link
Member

@igorp-collabora Thanks for this. Not ignoring your PRs. Just I haven't had time yet to fully review them. Hopefully this week or next week. Sorry for the delay.

Thanks for the PRs! They look interesting from the description 👍

@codecovCodecov
Copy link

codecovbot commentedJan 7, 2025
edited
Loading

Codecov Report

Attention: Patch coverage is97.63780% with3 lines in your changes missing coverage. Please review.

Project coverage is 97.19%. Comparing base(f4f7d7a) to head(2335cf8).
Report is 39 commits behind head on main.

Files with missing linesPatch %Lines
gitlab/v4/objects/clusters.py33.33%2 Missing⚠️
gitlab/v4/objects/pipelines.py50.00%1 Missing⚠️
Additional details and impacted files
@@            Coverage Diff             @@##             main    #3080      +/-   ##==========================================+ Coverage   96.68%   97.19%   +0.50%==========================================  Files          96       96                Lines        6213     5913     -300     ==========================================- Hits         6007     5747     -260+ Misses        206      166      -40
FlagCoverage Δ
api_func_v483.39% <96.85%> (+0.82%)⬆️
cli_func_v484.32% <92.91%> (+1.62%)⬆️
unit89.86% <94.48%> (+0.84%)⬆️

Flags with carried forward coverage won't be shown.Click here to find out more.

Files with missing linesCoverage Δ
gitlab/base.py100.00% <ø> (ø)
gitlab/client.py98.72% <100.00%> (+<0.01%)⬆️
gitlab/mixins.py93.23% <100.00%> (+0.47%)⬆️
gitlab/v4/cli.py91.80% <100.00%> (ø)
gitlab/v4/objects/appearance.py100.00% <100.00%> (ø)
gitlab/v4/objects/audit_events.py100.00% <ø> (ø)
gitlab/v4/objects/award_emojis.py100.00% <ø> (+10.38%)⬆️
gitlab/v4/objects/badges.py100.00% <ø> (ø)
gitlab/v4/objects/boards.py100.00% <ø> (+4.65%)⬆️
gitlab/v4/objects/branches.py100.00% <ø> (ø)
... and56 more

... and2 files with indirect coverage changes

@igorp-collabora
Copy link
ContributorAuthor

Sorry for the delay.

No worries. I want to experiment with reworking RESTManager hierarchy meanwhile.

@igorp-collabora
Copy link
ContributorAuthor

The#3083 is a cleaner solution but more verbose. This one builds more work-around code on top of existing one.

@nejchnejch closed this in #3083Feb 5, 2025
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Some mixins are not properly type-hinted Type hint on ListMixin.list is too strict
2 participants
@igorp-collabora@JohnVillalovos

[8]ページ先頭

©2009-2025 Movatter.jp