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

Commit92a893b

Browse files
committed
feat(cli): do not require config file to run CLI
BREAKING CHANGE: A config file is no longer needed to runthe CLI. python-gitlab will default tohttps://gitlab.comwith no authentication if there is no config file provided.python-gitlab will now also only look for configurationin the provided PYTHON_GITLAB_CFG path, instead of mergingit with user- and system-wide config files. If theenvironment variable is defined and the file cannot beopened, python-gitlab will now explicitly fail.
1 parentaf33aff commit92a893b

File tree

6 files changed

+234
-116
lines changed

6 files changed

+234
-116
lines changed

‎.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ repos:
2626
-id:pylint
2727
additional_dependencies:
2828
-argcomplete==1.12.3
29+
-pytest==6.2.5
2930
-requests==2.26.0
3031
-requests-toolbelt==0.9.1
3132
files:'gitlab/'

‎docs/cli-usage.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
``python-gitlab`` provides a:command:`gitlab` command-line tool to interact
66
with GitLab servers. It uses a configuration file to define how to connect to
7-
the servers.
7+
the servers. Without a configuration file, ``gitlab`` will default to
8+
https://gitlab.com and unauthenticated requests.
89

910
.. _cli_configuration:
1011

@@ -16,8 +17,8 @@ Files
1617

1718
``gitlab`` looks up 3 configuration files by default:
1819

19-
``PYTHON_GITLAB_CFG`` environment variable
20-
An environment variable that contains the path to a configuration file
20+
The``PYTHON_GITLAB_CFG`` environment variable
21+
An environment variable that contains the path to a configuration file.
2122

2223
``/etc/python-gitlab.cfg``
2324
System-wide configuration file
@@ -27,6 +28,13 @@ Files
2728

2829
You can use a different configuration file with the ``--config-file`` option.
2930

31+
..warning::
32+
If the ``PYTHON_GITLAB_CFG`` environment variable is defined and the target
33+
file exists, it will be the only configuration file parsed by ``gitlab``.
34+
35+
If the environment variable is defined and the target file cannot be accessed,
36+
``gitlab`` will fail explicitly.
37+
3038
Content
3139
-------
3240

‎gitlab/config.py

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,67 @@
2020
importshlex
2121
importsubprocess
2222
fromos.pathimportexpanduser,expandvars
23+
frompathlibimportPath
2324
fromtypingimportList,Optional,Union
2425

25-
fromgitlab.constimportUSER_AGENT
26+
fromgitlab.constimportDEFAULT_URL,USER_AGENT
2627

27-
28-
def_env_config()->List[str]:
29-
if"PYTHON_GITLAB_CFG"inos.environ:
30-
return [os.environ["PYTHON_GITLAB_CFG"]]
31-
return []
32-
33-
34-
_DEFAULT_FILES:List[str]=_env_config()+ [
28+
_DEFAULT_FILES:List[str]= [
3529
"/etc/python-gitlab.cfg",
36-
os.path.expanduser("~/.python-gitlab.cfg"),
30+
str(Path.home()/".python-gitlab.cfg"),
3731
]
3832

3933
HELPER_PREFIX="helper:"
4034

4135
HELPER_ATTRIBUTES= ["job_token","http_password","private_token","oauth_token"]
4236

4337

38+
def_resolve_file(filepath:Union[Path,str])->str:
39+
resolved=Path(filepath).resolve(strict=True)
40+
returnstr(resolved)
41+
42+
43+
def_get_config_files(
44+
config_files:Optional[List[str]]=None,
45+
)->Union[str,List[str]]:
46+
"""
47+
Return resolved path(s) to config files if they exist, with precedence:
48+
1. Files passed in config_files
49+
2. File defined in PYTHON_GITLAB_CFG
50+
3. User- and system-wide config files
51+
"""
52+
resolved_files= []
53+
54+
ifconfig_files:
55+
forconfig_fileinconfig_files:
56+
try:
57+
resolved=_resolve_file(config_file)
58+
exceptOSErrorase:
59+
raiseGitlabConfigMissingError(f"Cannot read config from file:{e}")
60+
resolved_files.append(resolved)
61+
62+
returnresolved_files
63+
64+
try:
65+
env_config=os.environ["PYTHON_GITLAB_CFG"]
66+
return_resolve_file(env_config)
67+
exceptKeyError:
68+
pass
69+
exceptOSErrorase:
70+
raiseGitlabConfigMissingError(
71+
f"Cannot read config from PYTHON_GITLAB_CFG:{e}"
72+
)
73+
74+
forconfig_filein_DEFAULT_FILES:
75+
try:
76+
resolved=_resolve_file(config_file)
77+
exceptOSError:
78+
continue
79+
resolved_files.append(resolved)
80+
81+
returnresolved_files
82+
83+
4484
classConfigError(Exception):
4585
pass
4686

@@ -66,155 +106,149 @@ def __init__(
66106
self,gitlab_id:Optional[str]=None,config_files:Optional[List[str]]=None
67107
)->None:
68108
self.gitlab_id=gitlab_id
69-
_files=config_filesor_DEFAULT_FILES
70-
file_exist=False
71-
forfilein_files:
72-
ifos.path.exists(file):
73-
file_exist=True
74-
ifnotfile_exist:
75-
raiseGitlabConfigMissingError(
76-
"Config file not found.\nPlease create one in "
77-
"one of the following locations: {}\nor "
78-
"specify a config file using the '-c' parameter.".format(
79-
", ".join(_DEFAULT_FILES)
80-
)
81-
)
109+
self.http_username:Optional[str]=None
110+
self.http_password:Optional[str]=None
111+
self.job_token:Optional[str]=None
112+
self.oauth_token:Optional[str]=None
113+
self.private_token:Optional[str]=None
114+
115+
self.api_version:str="4"
116+
self.order_by:Optional[str]=None
117+
self.pagination:Optional[str]=None
118+
self.per_page:Optional[int]=None
119+
self.retry_transient_errors:bool=False
120+
self.ssl_verify:Union[bool,str]=True
121+
self.timeout:int=60
122+
self.url:str=DEFAULT_URL
123+
self.user_agent:str=USER_AGENT
82124

83-
self._config=configparser.ConfigParser()
84-
self._config.read(_files)
125+
self._files=_get_config_files(config_files)
126+
ifself._files:
127+
self._parse_config()
128+
129+
def_parse_config(self)->None:
130+
_config=configparser.ConfigParser()
131+
_config.read(self._files)
85132

86133
ifself.gitlab_idisNone:
87134
try:
88-
self.gitlab_id=self._config.get("global","default")
135+
self.gitlab_id=_config.get("global","default")
89136
exceptExceptionase:
90137
raiseGitlabIDError(
91138
"Impossible to get the gitlab id (not specified in config file)"
92139
)frome
93140

94141
try:
95-
self.url=self._config.get(self.gitlab_id,"url")
142+
self.url=_config.get(self.gitlab_id,"url")
96143
exceptExceptionase:
97144
raiseGitlabDataError(
98145
"Impossible to get gitlab details from "
99146
f"configuration ({self.gitlab_id})"
100147
)frome
101148

102-
self.ssl_verify:Union[bool,str]=True
103149
try:
104-
self.ssl_verify=self._config.getboolean("global","ssl_verify")
150+
self.ssl_verify=_config.getboolean("global","ssl_verify")
105151
exceptValueError:
106152
# Value Error means the option exists but isn't a boolean.
107153
# Get as a string instead as it should then be a local path to a
108154
# CA bundle.
109155
try:
110-
self.ssl_verify=self._config.get("global","ssl_verify")
156+
self.ssl_verify=_config.get("global","ssl_verify")
111157
exceptException:
112158
pass
113159
exceptException:
114160
pass
115161
try:
116-
self.ssl_verify=self._config.getboolean(self.gitlab_id,"ssl_verify")
162+
self.ssl_verify=_config.getboolean(self.gitlab_id,"ssl_verify")
117163
exceptValueError:
118164
# Value Error means the option exists but isn't a boolean.
119165
# Get as a string instead as it should then be a local path to a
120166
# CA bundle.
121167
try:
122-
self.ssl_verify=self._config.get(self.gitlab_id,"ssl_verify")
168+
self.ssl_verify=_config.get(self.gitlab_id,"ssl_verify")
123169
exceptException:
124170
pass
125171
exceptException:
126172
pass
127173

128-
self.timeout=60
129174
try:
130-
self.timeout=self._config.getint("global","timeout")
175+
self.timeout=_config.getint("global","timeout")
131176
exceptException:
132177
pass
133178
try:
134-
self.timeout=self._config.getint(self.gitlab_id,"timeout")
179+
self.timeout=_config.getint(self.gitlab_id,"timeout")
135180
exceptException:
136181
pass
137182

138-
self.private_token=None
139183
try:
140-
self.private_token=self._config.get(self.gitlab_id,"private_token")
184+
self.private_token=_config.get(self.gitlab_id,"private_token")
141185
exceptException:
142186
pass
143187

144-
self.oauth_token=None
145188
try:
146-
self.oauth_token=self._config.get(self.gitlab_id,"oauth_token")
189+
self.oauth_token=_config.get(self.gitlab_id,"oauth_token")
147190
exceptException:
148191
pass
149192

150-
self.job_token=None
151193
try:
152-
self.job_token=self._config.get(self.gitlab_id,"job_token")
194+
self.job_token=_config.get(self.gitlab_id,"job_token")
153195
exceptException:
154196
pass
155197

156-
self.http_username=None
157-
self.http_password=None
158198
try:
159-
self.http_username=self._config.get(self.gitlab_id,"http_username")
160-
self.http_password=self._config.get(self.gitlab_id,"http_password")
199+
self.http_username=_config.get(self.gitlab_id,"http_username")
200+
self.http_password=_config.get(self.gitlab_id,"http_password")
161201
exceptException:
162202
pass
163203

164204
self._get_values_from_helper()
165205

166-
self.api_version="4"
167206
try:
168-
self.api_version=self._config.get("global","api_version")
207+
self.api_version=_config.get("global","api_version")
169208
exceptException:
170209
pass
171210
try:
172-
self.api_version=self._config.get(self.gitlab_id,"api_version")
211+
self.api_version=_config.get(self.gitlab_id,"api_version")
173212
exceptException:
174213
pass
175214
ifself.api_versionnotin ("4",):
176215
raiseGitlabDataError(f"Unsupported API version:{self.api_version}")
177216

178-
self.per_page=None
179217
forsectionin ["global",self.gitlab_id]:
180218
try:
181-
self.per_page=self._config.getint(section,"per_page")
219+
self.per_page=_config.getint(section,"per_page")
182220
exceptException:
183221
pass
184222
ifself.per_pageisnotNoneandnot0<=self.per_page<=100:
185223
raiseGitlabDataError(f"Unsupported per_page number:{self.per_page}")
186224

187-
self.pagination=None
188225
try:
189-
self.pagination=self._config.get(self.gitlab_id,"pagination")
226+
self.pagination=_config.get(self.gitlab_id,"pagination")
190227
exceptException:
191228
pass
192229

193-
self.order_by=None
194230
try:
195-
self.order_by=self._config.get(self.gitlab_id,"order_by")
231+
self.order_by=_config.get(self.gitlab_id,"order_by")
196232
exceptException:
197233
pass
198234

199-
self.user_agent=USER_AGENT
200235
try:
201-
self.user_agent=self._config.get("global","user_agent")
236+
self.user_agent=_config.get("global","user_agent")
202237
exceptException:
203238
pass
204239
try:
205-
self.user_agent=self._config.get(self.gitlab_id,"user_agent")
240+
self.user_agent=_config.get(self.gitlab_id,"user_agent")
206241
exceptException:
207242
pass
208243

209-
self.retry_transient_errors=False
210244
try:
211-
self.retry_transient_errors=self._config.getboolean(
245+
self.retry_transient_errors=_config.getboolean(
212246
"global","retry_transient_errors"
213247
)
214248
exceptException:
215249
pass
216250
try:
217-
self.retry_transient_errors=self._config.getboolean(
251+
self.retry_transient_errors=_config.getboolean(
218252
self.gitlab_id,"retry_transient_errors"
219253
)
220254
exceptException:

‎requirements-test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
coverage
22
httmock
3-
pytest
3+
pytest==6.2.5
44
pytest-console-scripts==1.2.1
55
pytest-cov
66
responses

‎tests/functional/cli/test_cli.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
importjson
22

3+
importpytest
4+
importresponses
5+
36
fromgitlabimport__version__
47

58

9+
@pytest.fixture
10+
defresp_get_project():
11+
withresponses.RequestsMock()asrsps:
12+
rsps.add(
13+
method=responses.GET,
14+
url="https://gitlab.com/api/v4/projects/1",
15+
json={"name":"name","path":"test-path","id":1},
16+
content_type="application/json",
17+
status=200,
18+
)
19+
yieldrsps
20+
21+
622
deftest_main_entrypoint(script_runner,gitlab_config):
723
ret=script_runner.run("python","-m","gitlab","--config-file",gitlab_config)
824
assertret.returncode==2
@@ -13,6 +29,29 @@ def test_version(script_runner):
1329
assertret.stdout.strip()==__version__
1430

1531

32+
@pytest.mark.script_launch_mode("inprocess")
33+
deftest_defaults_to_gitlab_com(script_runner,resp_get_project):
34+
# Runs in-process to intercept requests to gitlab.com
35+
ret=script_runner.run("gitlab","project","get","--id","1")
36+
assertret.success
37+
assert"id: 1"inret.stdout
38+
39+
40+
deftest_env_config_missing_file_raises(script_runner,monkeypatch):
41+
monkeypatch.setenv("PYTHON_GITLAB_CFG","non-existent")
42+
ret=script_runner.run("gitlab","project","list")
43+
assertnotret.success
44+
assertret.stderr.startswith("Cannot read config from PYTHON_GITLAB_CFG")
45+
46+
47+
deftest_arg_config_missing_file_raises(script_runner):
48+
ret=script_runner.run(
49+
"gitlab","--config-file","non-existent","project","list"
50+
)
51+
assertnotret.success
52+
assertret.stderr.startswith("Cannot read config from file")
53+
54+
1655
deftest_invalid_config(script_runner):
1756
ret=script_runner.run("gitlab","--gitlab","invalid")
1857
assertnotret.success

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp