Skip to content

Commit b07eece

Browse files
chore: replace usage of utils._url_encode() with utils.EncodedId()
utils.EncodedId() has basically the same functionalityy of using utils._url_encode(). So remove utils._url_encode() as we don't need it.
1 parent a2e7c38 commit b07eece

File tree

9 files changed

+28
-113
lines changed

9 files changed

+28
-113
lines changed

gitlab/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def encoded_id(self) -> Any:
223223
path"""
224224
obj_id = self.get_id()
225225
if isinstance(obj_id, str):
226-
obj_id = gitlab.utils._url_encode(obj_id)
226+
obj_id = gitlab.utils.EncodedId(obj_id)
227227
return obj_id
228228

229229
@property

gitlab/mixins.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ def get(
9999
GitlabAuthenticationError: If authentication is not correct
100100
GitlabGetError: If the server cannot perform the request
101101
"""
102-
id = utils._url_encode(id)
102+
if isinstance(id, str):
103+
id = utils.EncodedId(id)
103104
path = f"{self.path}/{id}"
104105
if TYPE_CHECKING:
105106
assert self._obj_cls is not None
@@ -390,7 +391,7 @@ def update(
390391
if id is None:
391392
path = self.path
392393
else:
393-
path = f"{self.path}/{utils._url_encode(id)}"
394+
path = f"{self.path}/{utils.EncodedId(id)}"
394395

395396
self._check_missing_update_attrs(new_data)
396397
files = {}
@@ -443,7 +444,7 @@ def set(self, key: str, value: str, **kwargs: Any) -> base.RESTObject:
443444
Returns:
444445
The created/updated attribute
445446
"""
446-
path = f"{self.path}/{utils._url_encode(key)}"
447+
path = f"{self.path}/{utils.EncodedId(key)}"
447448
data = {"value": value}
448449
server_data = self.gitlab.http_put(path, post_data=data, **kwargs)
449450
if TYPE_CHECKING:
@@ -476,7 +477,7 @@ def delete(self, id: Union[str, int], **kwargs: Any) -> None:
476477
if id is None:
477478
path = self.path
478479
else:
479-
path = f"{self.path}/{utils._url_encode(id)}"
480+
path = f"{self.path}/{utils.EncodedId(id)}"
480481
self.gitlab.http_delete(path, **kwargs)
481482

482483

gitlab/utils.py

+10-75
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

1818
import urllib.parse
19-
from typing import Any, Callable, Dict, Optional, overload, Union
19+
from typing import Any, Callable, Dict, Optional, Union
2020

2121
import requests
2222

@@ -71,83 +71,18 @@ class EncodedId(str):
7171
https://docs.gitlab.com/ee/api/index.html#path-parameters
7272
"""
7373

74-
# `original_str` will contain the original string value that was used to create the
75-
# first instance of EncodedId. We will use this original value to generate the
76-
# URL-encoded value each time.
77-
original_str: str
78-
79-
def __new__(cls, value: Union[str, int, "EncodedId"]) -> "EncodedId":
80-
# __new__() gets called before __init__()
81-
if isinstance(value, int):
82-
value = str(value)
83-
# Make sure isinstance() for `EncodedId` comes before check for `str` as
84-
# `EncodedId` is an instance of `str` and would pass that check.
85-
elif isinstance(value, EncodedId):
86-
# We use the original string value to URL-encode
87-
value = value.original_str
88-
elif isinstance(value, str):
89-
pass
90-
else:
91-
raise ValueError(f"Unsupported type received: {type(value)}")
92-
# Set the value our string will return
74+
# mypy complains if return type other than the class type. So we ignore issue.
75+
def __new__( # type: ignore
76+
cls, value: Union[str, int, "EncodedId"]
77+
) -> Union[int, "EncodedId"]:
78+
if isinstance(value, (int, EncodedId)):
79+
return value
80+
81+
if not isinstance(value, str):
82+
raise TypeError(f"Unsupported type received: {type(value)}")
9383
value = urllib.parse.quote(value, safe="")
9484
return super().__new__(cls, value)
9585

96-
def __init__(self, value: Union[int, str]) -> None:
97-
# At this point `super().__str__()` returns the URL-encoded value. Which means
98-
# when using this as a `str` it will return the URL-encoded value.
99-
#
100-
# But `value` contains the original value passed in `EncodedId(value)`. We use
101-
# this to always keep the original string that was received so that no matter
102-
# how many times we recurse we only URL-encode our original string once.
103-
if isinstance(value, int):
104-
value = str(value)
105-
# Make sure isinstance() for `EncodedId` comes before check for `str` as
106-
# `EncodedId` is an instance of `str` and would pass that check.
107-
elif isinstance(value, EncodedId):
108-
# This is the key part as we are always keeping the original string even
109-
# through multiple recursions.
110-
value = value.original_str
111-
elif isinstance(value, str):
112-
pass
113-
else:
114-
raise ValueError(f"Unsupported type received: {type(value)}")
115-
self.original_str = value
116-
super().__init__()
117-
118-
119-
@overload
120-
def _url_encode(id: int) -> int:
121-
...
122-
123-
124-
@overload
125-
def _url_encode(id: Union[str, EncodedId]) -> EncodedId:
126-
...
127-
128-
129-
def _url_encode(id: Union[int, str, EncodedId]) -> Union[int, EncodedId]:
130-
"""Encode/quote the characters in the string so that they can be used in a path.
131-
132-
Reference to documentation on why this is necessary.
133-
134-
https://docs.gitlab.com/ee/api/index.html#namespaced-path-encoding
135-
136-
If using namespaced API requests, make sure that the NAMESPACE/PROJECT_PATH is
137-
URL-encoded. For example, / is represented by %2F
138-
139-
https://docs.gitlab.com/ee/api/index.html#path-parameters
140-
141-
Path parameters that are required to be URL-encoded must be followed. If not, it
142-
doesn’t match an API endpoint and responds with a 404. If there’s something in front
143-
of the API (for example, Apache), ensure that it doesn’t decode the URL-encoded path
144-
parameters.
145-
146-
"""
147-
if isinstance(id, (int, EncodedId)):
148-
return id
149-
return EncodedId(id)
150-
15186

15287
def remove_none_from_dict(data: Dict[str, Any]) -> Dict[str, Any]:
15388
return {k: v for k, v in data.items() if v is not None}

gitlab/v4/cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def _process_from_parent_attrs(self) -> None:
7575
if key not in self.args:
7676
continue
7777

78-
self.parent_args[key] = gitlab.utils._url_encode(self.args[key])
78+
self.parent_args[key] = gitlab.utils.EncodedId(self.args[key])
7979
# If we don't delete it then it will be added to the URL as a query-string
8080
del self.args[key]
8181

gitlab/v4/objects/features.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def set(
5252
Returns:
5353
The created/updated attribute
5454
"""
55-
name = utils._url_encode(name)
55+
name = utils.EncodedId(name)
5656
path = f"{self.path}/{name}"
5757
data = {
5858
"value": value,

gitlab/v4/objects/files.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def save( # type: ignore
5656
"""
5757
self.branch = branch
5858
self.commit_message = commit_message
59-
self.file_path = utils._url_encode(self.file_path)
59+
self.file_path = utils.EncodedId(self.file_path)
6060
super(ProjectFile, self).save(**kwargs)
6161

6262
@exc.on_http_error(exc.GitlabDeleteError)
@@ -144,7 +144,7 @@ def create(
144144
assert data is not None
145145
self._check_missing_create_attrs(data)
146146
new_data = data.copy()
147-
file_path = utils._url_encode(new_data.pop("file_path"))
147+
file_path = utils.EncodedId(new_data.pop("file_path"))
148148
path = f"{self.path}/{file_path}"
149149
server_data = self.gitlab.http_post(path, post_data=new_data, **kwargs)
150150
if TYPE_CHECKING:
@@ -173,7 +173,7 @@ def update( # type: ignore
173173
"""
174174
new_data = new_data or {}
175175
data = new_data.copy()
176-
file_path = utils._url_encode(file_path)
176+
file_path = utils.EncodedId(file_path)
177177
data["file_path"] = file_path
178178
path = f"{self.path}/{file_path}"
179179
self._check_missing_update_attrs(data)
@@ -203,7 +203,7 @@ def delete( # type: ignore
203203
GitlabAuthenticationError: If authentication is not correct
204204
GitlabDeleteError: If the server cannot perform the request
205205
"""
206-
file_path = utils._url_encode(file_path)
206+
file_path = utils.EncodedId(file_path)
207207
path = f"{self.path}/{file_path}"
208208
data = {"branch": branch, "commit_message": commit_message}
209209
self.gitlab.http_delete(path, query_data=data, **kwargs)
@@ -239,7 +239,7 @@ def raw(
239239
Returns:
240240
The file content
241241
"""
242-
file_path = utils._url_encode(file_path)
242+
file_path = utils.EncodedId(file_path)
243243
path = f"{self.path}/{file_path}/raw"
244244
query_data = {"ref": ref}
245245
result = self.gitlab.http_get(
@@ -266,7 +266,7 @@ def blame(self, file_path: str, ref: str, **kwargs: Any) -> List[Dict[str, Any]]
266266
Returns:
267267
A list of commits/lines matching the file
268268
"""
269-
file_path = utils._url_encode(file_path)
269+
file_path = utils.EncodedId(file_path)
270270
path = f"{self.path}/{file_path}/blame"
271271
query_data = {"ref": ref}
272272
result = self.gitlab.http_list(path, query_data, **kwargs)

gitlab/v4/objects/repositories.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def update_submodule(
3939
GitlabPutError: If the submodule could not be updated
4040
"""
4141

42-
submodule = utils._url_encode(submodule)
42+
submodule = utils.EncodedId(submodule)
4343
path = f"/projects/{self.encoded_id}/repository/submodules/{submodule}"
4444
data = {"branch": branch, "commit_sha": commit_sha}
4545
if "commit_message" in kwargs:

tests/functional/api/test_lazy_objects.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def lazy_project(gl, project):
1212
def test_lazy_id(project, lazy_project):
1313
assert isinstance(lazy_project.id, str)
1414
assert isinstance(lazy_project.id, gitlab.utils.EncodedId)
15-
assert lazy_project.id == gitlab.utils._url_encode(project.path_with_namespace)
15+
assert lazy_project.id == gitlab.utils.EncodedId(project.path_with_namespace)
1616

1717

1818
def test_refresh_after_lazy_get_with_path(project, lazy_project):

tests/unit/test_utils.py

+2-23
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,10 @@
2020
from gitlab import utils
2121

2222

23-
def test_url_encode():
24-
src = "nothing_special"
25-
dest = "nothing_special"
26-
assert dest == utils._url_encode(src)
27-
28-
src = "foo#bar/baz/"
29-
dest = "foo%23bar%2Fbaz%2F"
30-
assert dest == utils._url_encode(src)
31-
32-
src = "foo%bar/baz/"
33-
dest = "foo%25bar%2Fbaz%2F"
34-
assert dest == utils._url_encode(src)
35-
36-
# periods/dots should not be modified
37-
src = "docs/README.md"
38-
dest = "docs%2FREADME.md"
39-
assert dest == utils._url_encode(src)
40-
41-
4223
class TestEncodedId:
4324
def test_init_str(self):
4425
obj = utils.EncodedId("Hello")
26+
assert "Hello" == obj
4527
assert "Hello" == str(obj)
4628
assert "Hello" == f"{obj}"
4729

@@ -51,6 +33,7 @@ def test_init_str(self):
5133

5234
def test_init_int(self):
5335
obj = utils.EncodedId(23)
36+
assert 23 == obj
5437
assert "23" == str(obj)
5538
assert "23" == f"{obj}"
5639

@@ -60,12 +43,10 @@ def test_init_encodeid_str(self):
6043
obj = utils.EncodedId(obj_init)
6144
assert value == str(obj)
6245
assert value == f"{obj}"
63-
assert value == obj.original_str
6446

6547
value = "we got/a/path"
6648
expected = "we%20got%2Fa%2Fpath"
6749
obj_init = utils.EncodedId(value)
68-
assert value == obj_init.original_str
6950
assert expected == str(obj_init)
7051
assert expected == f"{obj_init}"
7152
# Show that no matter how many times we recursively call it we still only
@@ -75,8 +56,6 @@ def test_init_encodeid_str(self):
7556
)
7657
assert expected == str(obj)
7758
assert expected == f"{obj}"
78-
# We have stored a copy of our original string
79-
assert value == obj.original_str
8059

8160
# Show assignments still only encode once
8261
obj2 = obj

0 commit comments

Comments
 (0)