Skip to content

Commit f5149c8

Browse files
committed
git operations: use GitPython instead of subprocess
1 parent ccf0a81 commit f5149c8

3 files changed

Lines changed: 110 additions & 25 deletions

File tree

colrev/package_manager/colrev_internal_packages.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import json
77
import os
8-
import subprocess
98
import tempfile
109
from importlib.metadata import distribution
1110
from importlib.metadata import PackageNotFoundError
@@ -14,6 +13,7 @@
1413
from urllib.parse import urlparse
1514

1615
import toml
16+
import git
1717

1818

1919
# pylint: disable=too-many-return-statements
@@ -52,16 +52,10 @@ def _get_local_editable_colrev_path() -> str:
5252

5353
def _clone_colrev_repository() -> Path:
5454
temp_dir = tempfile.mkdtemp()
55-
subprocess.run(
56-
[
57-
"git",
58-
"clone",
59-
"--depth",
60-
"1",
61-
"https://github.com/CoLRev-Environment/colrev",
62-
temp_dir,
63-
],
64-
check=True,
55+
git.Repo.clone_from(
56+
"https://github.com/CoLRev-Environment/colrev",
57+
temp_dir,
58+
depth=1,
6559
)
6660
return Path(temp_dir)
6761

colrev/package_manager/init.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import json
88
import os
99
import re
10-
import subprocess
1110
from abc import ABC
1211
from datetime import datetime
1312
from importlib import import_module
@@ -23,19 +22,27 @@
2322

2423
def _get_default_author() -> dict:
2524
try:
26-
name = (
27-
subprocess.check_output(["git", "config", "--get", "user.name"])
28-
.decode()
29-
.strip()
30-
)
31-
email = (
32-
subprocess.check_output(["git", "config", "--get", "user.email"])
33-
.decode()
34-
.strip()
35-
)
36-
return {"name": name, "email": email}
37-
except subprocess.CalledProcessError as exc:
38-
print(exc)
25+
config_reader = git.Repo(
26+
Path.cwd(), search_parent_directories=True
27+
).config_reader()
28+
except git.exc.InvalidGitRepositoryError:
29+
try:
30+
config_reader = git.GitConfigParser(read_only=True)
31+
except (OSError, ValueError):
32+
return {}
33+
except (git.exc.GitError, OSError, ValueError):
34+
return {}
35+
36+
try:
37+
if not (config_reader.has_option("user", "name")) or not (
38+
config_reader.has_option("user", "email")
39+
):
40+
return {}
41+
return {
42+
"name": config_reader.get_value("user", "name"),
43+
"email": config_reader.get_value("user", "email"),
44+
}
45+
except (git.exc.GitError, OSError, ValueError):
3946
return {}
4047

4148

tests/1_env/packages_test.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
"""Tests for the colrev package manager"""
33

44
from pathlib import Path
5+
from unittest.mock import MagicMock
56

67
import pytest
78

9+
import colrev.package_manager.colrev_internal_packages
810
import colrev.package_manager.init
911
from colrev.constants import EndpointType
1012

@@ -28,6 +30,88 @@ def test_generate_method_signatures(endpoint_type: str, helpers) -> None: # typ
2830
), "Generated module content does not match expected version!"
2931

3032

33+
def test_get_default_author_from_repo_config(monkeypatch: pytest.MonkeyPatch) -> None:
34+
mock_reader = MagicMock()
35+
mock_reader.has_option.side_effect = [True, True]
36+
mock_reader.get_value.side_effect = ["Test User", "test.user@example.org"]
37+
38+
mock_repo = MagicMock()
39+
mock_repo.config_reader.return_value = mock_reader
40+
monkeypatch.setattr(
41+
colrev.package_manager.init.git, "Repo", MagicMock(return_value=mock_repo)
42+
)
43+
44+
author = colrev.package_manager.init._get_default_author()
45+
46+
assert author == {"name": "Test User", "email": "test.user@example.org"}
47+
48+
49+
def test_get_default_author_falls_back_to_global_config(
50+
monkeypatch: pytest.MonkeyPatch,
51+
) -> None:
52+
monkeypatch.setattr(
53+
colrev.package_manager.init.git,
54+
"Repo",
55+
MagicMock(
56+
side_effect=colrev.package_manager.init.git.exc.InvalidGitRepositoryError()
57+
),
58+
)
59+
mock_global_reader = MagicMock()
60+
mock_global_reader.has_option.side_effect = [True, True]
61+
mock_global_reader.get_value.side_effect = ["Global User", "global@example.org"]
62+
monkeypatch.setattr(
63+
colrev.package_manager.init.git,
64+
"GitConfigParser",
65+
MagicMock(return_value=mock_global_reader),
66+
)
67+
68+
author = colrev.package_manager.init._get_default_author()
69+
70+
assert author == {"name": "Global User", "email": "global@example.org"}
71+
72+
73+
def test_get_default_author_returns_empty_dict_without_complete_author(
74+
monkeypatch: pytest.MonkeyPatch,
75+
) -> None:
76+
mock_reader = MagicMock()
77+
mock_reader.has_option.side_effect = [True, False]
78+
mock_repo = MagicMock()
79+
mock_repo.config_reader.return_value = mock_reader
80+
monkeypatch.setattr(
81+
colrev.package_manager.init.git, "Repo", MagicMock(return_value=mock_repo)
82+
)
83+
84+
author = colrev.package_manager.init._get_default_author()
85+
86+
assert author == {}
87+
88+
89+
def test_clone_colrev_repository_uses_gitpython(
90+
monkeypatch: pytest.MonkeyPatch,
91+
) -> None:
92+
temp_dir = "/tmp/test-colrev-clone"
93+
clone_from_mock = MagicMock()
94+
monkeypatch.setattr(
95+
colrev.package_manager.colrev_internal_packages.tempfile,
96+
"mkdtemp",
97+
MagicMock(return_value=temp_dir),
98+
)
99+
monkeypatch.setattr(
100+
colrev.package_manager.colrev_internal_packages.git.Repo,
101+
"clone_from",
102+
clone_from_mock,
103+
)
104+
105+
result = colrev.package_manager.colrev_internal_packages._clone_colrev_repository()
106+
107+
clone_from_mock.assert_called_once_with(
108+
"https://github.com/CoLRev-Environment/colrev",
109+
temp_dir,
110+
depth=1,
111+
)
112+
assert result == Path(temp_dir)
113+
114+
31115
# @pytest.fixture
32116
# def settings() -> colrev.settings.Settings:
33117
# """Fixture returning a settings object"""

0 commit comments

Comments
 (0)