Skip to content

Commit 63987d5

Browse files
[MINOR] Fix possible 404 errors + get rid of unnecessary arguments (#18)
* fix * actual fix + simplify code * update README.md * [PATCH] Manually trigger version bump * fix lint * [PATCH] Sync tests with updated test repo & manually trigger version bump * fix test_objects.py
1 parent afc43a0 commit 63987d5

File tree

9 files changed

+97
-133
lines changed

9 files changed

+97
-133
lines changed

README.md

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ iterate_subprojects(
122122
gl: Union[Gitlab, ProjectManager],
123123
ref: Optional[str] = None,
124124
only_gitlab_subprojects: bool = False,
125-
self_managed_gitlab_host: Optional[str] = None,
126-
get_latest_commit_possible_if_not_found: bool = False,
127-
get_latest_commit_possible_ref: Optional[str] = None
125+
self_managed_gitlab_host: Optional[str] = None
128126
) -> Generator[Subproject, None, None]
129127
```
130128
Parameters:
@@ -140,17 +138,6 @@ Parameters:
140138
- `self_managed_gitlab_host`: (optional) if some submodules are hosted on a
141139
self-managed GitLab instance, you should pass its url here otherwise it
142140
may be impossible to know from the URL that it's a GitLab project.
143-
- `get_latest_commit_possible_if_not_found`: (optional) in some rare cases,
144-
there won't be any `Subproject commit ...` info in the diff of the last
145-
commit that updated the submodules. Set this option to `True` if you want to
146-
get instead the most recent commit in the subproject that is anterior to the
147-
commit that updated the submodules of the project. If your goal is to
148-
check that your submodules are up-to-date, you might want to use this.
149-
- `get_latest_commit_possible_ref`: (optional) in case you set
150-
`get_latest_commit_possible_if_not_found` to `True`, you can specify a ref for the
151-
subproject (for instance your submodule could point to a different branch
152-
than the main one). By default, the main branch of the subproject will be
153-
used.
154141

155142
Returns: Generator of `Subproject` objects
156143

@@ -169,8 +156,6 @@ Attributes:
169156
- `commit: Union[gitlab.v4.objects.ProjectCommit, Commit]`: the commit that
170157
the submodule points to (if the submodule is not hosted on GitLab, it will
171158
be a dummy `Commit` object with a single attribute `id`)
172-
- `commit_is_exact: bool`: `True` most of the time, `False` only if the commit
173-
had to be guessed via the `get_latest_commit_possible_if_not_found` option
174159

175160
Example `str()` output:
176161
```
@@ -216,16 +201,19 @@ Example `str()` output:
216201
Converts a `Submodule` object to a [`Subproject`](#class-subproject) object, assuming it's
217202
hosted on Gitlab.
218203

204+
Raises as `FileNotFoundError` if the path of the submodule actually doesn't
205+
exist in the host repo or if the url of the submodule doesn't link to an
206+
existing repo (both can happen if you modify the `.gitmodules` file without
207+
using one of the `git submodule` commands)
208+
219209
```python
220210
submodule_to_subproject(
221211
gitmodules_submodule: Submodule,
222212
gl: Union[Gitlab, ProjectManager],
223213
self_managed_gitlab_host: Optional[str] = None,
224-
get_latest_commit_possible_if_not_found: bool = False,
225-
get_latest_commit_possible_ref: Optional[str] = None
226214
) -> Subproject
227215
```
228-
Parameters: See [`iterate_subprojects(...)`](#iterate_subprojects)
216+
Parameter details: See [`iterate_subprojects(...)`](#iterate_subprojects)
229217

230218

231219
## Contributing

gitlab_submodule/gitlab_submodule.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,48 +23,45 @@ def _get_project_manager(
2323
def submodule_to_subproject(
2424
gitmodules_submodule: Submodule,
2525
gl: Union[Gitlab, ProjectManager],
26-
self_managed_gitlab_host: Optional[str] = None,
27-
get_latest_commit_possible_if_not_found: bool = False,
28-
get_latest_commit_possible_ref: Optional[str] = None
26+
self_managed_gitlab_host: Optional[str] = None
2927
) -> Subproject:
30-
submodule_project = submodule_to_project(
31-
gitmodules_submodule,
32-
_get_project_manager(gl),
33-
self_managed_gitlab_host
34-
)
35-
submodule_commit, commit_is_exact = get_submodule_commit(
36-
gitmodules_submodule,
37-
submodule_project,
38-
get_latest_commit_possible_if_not_found,
39-
get_latest_commit_possible_ref
40-
)
41-
return Subproject(
42-
gitmodules_submodule,
43-
submodule_project,
44-
submodule_commit,
45-
commit_is_exact
46-
)
28+
try:
29+
submodule_project = submodule_to_project(
30+
gitmodules_submodule,
31+
_get_project_manager(gl),
32+
self_managed_gitlab_host
33+
)
34+
submodule_commit = get_submodule_commit(
35+
gitmodules_submodule,
36+
submodule_project,
37+
)
38+
return Subproject(
39+
gitmodules_submodule,
40+
submodule_project,
41+
submodule_commit,
42+
)
43+
except FileNotFoundError:
44+
raise
4745

4846

4947
def iterate_subprojects(
5048
project: Project,
5149
gl: Union[Gitlab, ProjectManager],
5250
ref: Optional[str] = None,
5351
only_gitlab_subprojects: bool = False,
54-
self_managed_gitlab_host: Optional[str] = None,
55-
get_latest_commit_possible_if_not_found: bool = False,
56-
get_latest_commit_possible_ref: Optional[str] = None
52+
self_managed_gitlab_host: Optional[str] = None
5753
) -> Generator[Subproject, None, None]:
5854
for gitmodules_submodule in iterate_submodules(project, ref):
59-
subproject: Subproject = submodule_to_subproject(
60-
gitmodules_submodule,
61-
_get_project_manager(gl),
62-
self_managed_gitlab_host,
63-
get_latest_commit_possible_if_not_found,
64-
get_latest_commit_possible_ref
65-
)
66-
if not (only_gitlab_subprojects and not subproject.project):
67-
yield subproject
55+
try:
56+
subproject: Subproject = submodule_to_subproject(
57+
gitmodules_submodule,
58+
_get_project_manager(gl),
59+
self_managed_gitlab_host,
60+
)
61+
if not (only_gitlab_subprojects and not subproject.project):
62+
yield subproject
63+
except FileNotFoundError:
64+
pass
6865

6966

7067
def list_subprojects(*args, **kwargs) -> List[Subproject]:

gitlab_submodule/objects.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,10 @@ class Subproject:
6161
def __init__(self,
6262
submodule: Submodule,
6363
project: Optional[Project],
64-
commit: Union[ProjectCommit, Commit],
65-
commit_is_exact: bool):
64+
commit: Union[ProjectCommit, Commit]):
6665
self.submodule = submodule
6766
self.project = project
6867
self.commit = commit
69-
self.commit_is_exact = commit_is_exact
7068

7169
def __getattribute__(self, item: str):
7270
try:
Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,34 @@
1-
from typing import Optional, Tuple, Union
1+
from typing import Optional, Union
22

33
import re
44

55
from gitlab.v4.objects import Project, ProjectCommit
6+
from gitlab.exceptions import GitlabGetError
67

78
from gitlab_submodule.objects import Submodule, Commit
89

910

1011
def get_submodule_commit(
1112
submodule: Submodule,
1213
submodule_project: Optional[Project] = None,
13-
*args,
14-
**kwargs
15-
) -> Tuple[Union[ProjectCommit, Commit], bool]:
16-
commit_id, is_exact = _get_submodule_commit_id(
14+
) -> Union[ProjectCommit, Commit]:
15+
commit_id = _get_submodule_commit_id(
1716
submodule.parent_project,
1817
submodule.path,
1918
submodule.parent_ref,
20-
submodule_project,
21-
*args,
22-
**kwargs
2319
)
2420
if submodule_project is not None:
2521
commit = submodule_project.commits.get(commit_id)
2622
else:
2723
commit = Commit(commit_id)
28-
return commit, is_exact
24+
return commit
2925

3026

3127
def _get_submodule_commit_id(
3228
project: Project,
3329
submodule_path: str,
3430
ref: Optional[str] = None,
35-
submodule_project: Optional[Project] = None,
36-
get_latest_commit_possible_if_not_found: bool = False,
37-
get_latest_commit_possible_ref: Optional[str] = None
38-
) -> Tuple[str, bool]:
31+
) -> str:
3932
"""This uses a trick:
4033
- The .gitmodules files doesn't contain the actual commit sha that the
4134
submodules points to.
@@ -46,16 +39,17 @@ def _get_submodule_commit_id(
4639
=> We use that info to get the diff of the last commit that updated the
4740
submodule commit
4841
=> We parse the diff to get the new submodule commit sha
49-
50-
NOTE: in some weird cases I observed without really understanding,
51-
a commit which created a .gitmodules file can contain zero submodule
52-
commit sha in its entire diff.
53-
In that case, we can only try to guess which was the latest commit in
54-
the submodule project at the datetime of the commit.
5542
"""
56-
submodule_dir = project.files.get(
57-
submodule_path,
58-
ref=ref if ref else project.default_branch)
43+
try:
44+
submodule_dir = project.files.get(
45+
submodule_path,
46+
ref=ref if ref else project.default_branch)
47+
except GitlabGetError:
48+
raise FileNotFoundError(
49+
f'Local submodule path "{submodule_path}" was not found for '
50+
f'project at url "{project.web_url}" - check if your .gitmodules '
51+
f'file is up-to-date.')
52+
5953
last_commit_id = submodule_dir.last_commit_id
6054
update_submodule_commit = project.commits.get(last_commit_id)
6155

@@ -68,26 +62,11 @@ def _get_submodule_commit_id(
6862
matches = re.findall(submodule_commit_regex, diff_file['diff'])
6963
# submodule commit id was updated
7064
if len(matches) == 2:
71-
return matches[1], True
65+
return matches[1]
7266
# submodule was added
7367
if len(matches) == 1:
74-
return matches[0], True
75-
76-
# If the commit diff doesn't contain the submodule commit info, we still
77-
# know the date of the last commit in the project that updated the
78-
# submodule, so we can fallback to the last commit in the submodule that
79-
# was created before this date.
80-
# This requires a Project object for the submodule so if it wasn't
81-
# passed we cannot guess anything.
82-
if not (get_latest_commit_possible_if_not_found
83-
and submodule_project is not None):
84-
raise ValueError(
85-
f'Could not find commit id for submodule {submodule_path} of '
86-
f'project {project.path_with_namespace}.')
68+
return matches[0]
8769

88-
last_subproject_commits = submodule_project.commits.list(
89-
ref_name=(get_latest_commit_possible_ref
90-
if get_latest_commit_possible_ref
91-
else submodule_project.default_branch),
92-
until=update_submodule_commit.created_at)
93-
return last_subproject_commits[0].id, False
70+
# should never happen
71+
raise RuntimeError(f'Did not find any commit id for submodule '
72+
f'"{submodule_path}" at url "{project.web_url}"')

gitlab_submodule/submodule_to_project.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from giturlparse import parse, GitUrlParsed
66

77
from gitlab.v4.objects import Project, ProjectManager
8+
from gitlab.exceptions import GitlabGetError
89

910
from gitlab_submodule.objects import Submodule
1011
from gitlab_submodule.string_utils import lstrip, rstrip
@@ -22,8 +23,15 @@ def submodule_to_project(
2223
self_managed_gitlab_host)
2324
if not submodule_project_path_with_namespace:
2425
return None
25-
submodule_project = project_manager.get(
26-
submodule_project_path_with_namespace)
26+
try:
27+
submodule_project = project_manager.get(
28+
submodule_project_path_with_namespace)
29+
except GitlabGetError:
30+
# Repo doesn't actually exist (possible because you can modify
31+
# .gitmodules without using `git submodule add`)
32+
raise FileNotFoundError(
33+
'No repo found at url "{}" for submodule at path "{}" - Check if '
34+
'the repo was deleted.'.format(submodule.url, submodule.path))
2735
return submodule_project
2836

2937

tests/test_gitmodules_to_project.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,20 @@ def test_get_submodules_as_projects_with_gitlab_relative_urls(self):
3333
project = self.gl.projects.get(
3434
'python-gitlab-submodule-test/test-projects/gitlab-relative-urls')
3535
submodules = list_project_submodules(project, ref='main')
36-
submodule_projects = [
36+
37+
existing_submodule_projects = [
3738
submodule_to_project(submodule, self.gl.projects)
38-
for submodule in submodules]
39+
for submodule in submodules[:4]]
3940
self.assertTrue(all(
4041
isinstance(project, Project)
41-
for project in submodule_projects))
42+
for project in existing_submodule_projects))
4243
self.assertEqual(
4344
{'1', '2', '3', '4'},
44-
{project.name for project in submodule_projects})
45+
{project.name for project in existing_submodule_projects})
46+
47+
for submodule in submodules[4:]:
48+
with self.assertRaises(FileNotFoundError):
49+
submodule_to_project(submodule, self.gl.projects)
4550

4651
def test_get_submodules_as_projects_with_external_urls(self):
4752
project = self.gl.projects.get(

tests/test_objects.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ def test_Subproject_get_attr(self):
9898
mock_commit = DictMock()
9999
mock_commit.id = '123456789'
100100

101-
project_submodule = Subproject(
102-
submodule, mock_project, mock_commit, commit_is_exact=True)
101+
project_submodule = Subproject(submodule, mock_project, mock_commit)
103102

104103
self.assertEqual(project_submodule.project, mock_project)
105104
self.assertEqual(project_submodule.project.name, 'project')
@@ -130,8 +129,7 @@ def test_Subproject_set_attr(self):
130129
mock_commit = DictMock()
131130
mock_commit.id = '123456789'
132131

133-
project_submodule = Subproject(
134-
submodule, mock_project, mock_commit, commit_is_exact=True)
132+
project_submodule = Subproject(submodule, mock_project, mock_commit)
135133

136134
project_submodule.project_name = 'project2'
137135
self.assertEqual(project_submodule.project_name, 'project2')
@@ -168,8 +166,7 @@ def test_Subproject_str(self):
168166
mock_commit = DictMock()
169167
mock_commit.id = '123456789'
170168

171-
project_submodule = Subproject(
172-
submodule, mock_project, mock_commit, commit_is_exact=True)
169+
project_submodule = Subproject(submodule, mock_project, mock_commit)
173170

174171
str_lines = str(project_submodule).split('\n')
175172
self.assertEqual(
@@ -191,8 +188,7 @@ def test_Subproject_str(self):
191188
str_lines[2]
192189
)
193190
self.assertEqual(
194-
" 'commit': <class 'DictMock'> => {'id': '123456789', "
195-
"'is_exact': True}",
191+
" 'commit': <class 'DictMock'> => {'id': '123456789'}",
196192
str_lines[3]
197193
)
198194
self.assertEqual('}', str_lines[4])
@@ -214,8 +210,7 @@ def test_Subproject_repr(self):
214210
mock_commit = DictMock()
215211
mock_commit.id = '123456789'
216212

217-
project_submodule = Subproject(
218-
submodule, mock_project, mock_commit, commit_is_exact=True)
213+
project_submodule = Subproject(submodule, mock_project, mock_commit)
219214

220215
str_lines = repr(project_submodule).split('\n')
221216
self.assertEqual(
@@ -234,7 +229,7 @@ def test_Subproject_repr(self):
234229
str_lines[2]
235230
)
236231
self.assertEqual(
237-
" {'id': '123456789', 'is_exact': True}",
232+
" {'id': '123456789'}",
238233
str_lines[3]
239234
)
240235
self.assertEqual(')', str_lines[4])

tests/test_read_gitmodules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def test_gitmodules_with_gitlab_relative_urls(self):
3131
{'../../dummy-projects/1.git',
3232
'../../../python-gitlab-submodule-test/dummy-projects/2.git',
3333
'./../../../python-gitlab-submodule-test/dummy-projects/3.git',
34-
'./../../dummy-projects/4.git'},
34+
'./../../dummy-projects/4.git',
35+
'./../../missing-repos/5.git'},
3536
{submodule.url for submodule in submodules})
3637

3738
def test_gitmodules_with_external_urls(self):

0 commit comments

Comments
 (0)