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

Commit9783207

Browse files
author
Gauvain Pocentek
committed
[v4] CLI support is back
1 parenta4f0c52 commit9783207

File tree

4 files changed

+407
-6
lines changed

4 files changed

+407
-6
lines changed

‎gitlab/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,13 @@ def get_id(self):
607607
returnNone
608608
returngetattr(self,self._id_attr)
609609

610+
@property
611+
defattributes(self):
612+
d=self.__dict__['_updated_attrs'].copy()
613+
d.update(self.__dict__['_attrs'])
614+
d.update(self.__dict__['_parent_attrs'])
615+
returnd
616+
610617

611618
classRESTObjectList(object):
612619
"""Generator object representing a list of RESTObject's.

‎gitlab/cli.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818

1919
from __future__importprint_function
20-
from __future__importabsolute_import
2120
importargparse
21+
importfunctools
2222
importimportlib
2323
importre
2424
importsys
@@ -27,6 +27,36 @@
2727

2828
camel_re=re.compile('(.)([A-Z])')
2929

30+
# custom_actions = {
31+
# cls: {
32+
# action: (mandatory_args, optional_args, in_obj),
33+
# },
34+
# }
35+
custom_actions= {}
36+
37+
38+
defregister_custom_action(cls_name,mandatory=tuple(),optional=tuple()):
39+
defwrap(f):
40+
@functools.wraps(f)
41+
defwrapped_f(*args,**kwargs):
42+
returnf(*args,**kwargs)
43+
44+
# in_obj defines whether the method belongs to the obj or the manager
45+
in_obj=True
46+
final_name=cls_name
47+
ifcls_name.endswith('Manager'):
48+
final_name=cls_name.replace('Manager','')
49+
in_obj=False
50+
iffinal_namenotincustom_actions:
51+
custom_actions[final_name]= {}
52+
53+
action=f.__name__
54+
55+
custom_actions[final_name][action]= (mandatory,optional,in_obj)
56+
57+
returnwrapped_f
58+
returnwrap
59+
3060

3161
defdie(msg,e=None):
3262
ife:
@@ -51,6 +81,9 @@ def _get_base_parser():
5181
parser.add_argument("-v","--verbose","--fancy",
5282
help="Verbose mode",
5383
action="store_true")
84+
parser.add_argument("-d","--debug",
85+
help="Debug mode (display HTTP requests",
86+
action="store_true")
5487
parser.add_argument("-c","--config-file",action='append',
5588
help=("Configuration file to use. Can be used "
5689
"multiple times."))
@@ -84,12 +117,13 @@ def main():
84117
config_files=args.config_file
85118
gitlab_id=args.gitlab
86119
verbose=args.verbose
120+
debug=args.debug
87121
action=args.action
88122
what=args.what
89123

90124
args=args.__dict__
91125
# Remove CLI behavior-related args
92-
foritemin ('gitlab','config_file','verbose','what','action',
126+
foritemin ('gitlab','config_file','verbose','debug','what','action',
93127
'version'):
94128
args.pop(item)
95129
args= {k:vfork,vinargs.items()ifvisnotNone}
@@ -100,6 +134,9 @@ def main():
100134
exceptExceptionase:
101135
die(str(e))
102136

137+
ifdebug:
138+
gl.enable_debug()
139+
103140
cli_module.run(gl,what,action,args,verbose)
104141

105142
sys.exit(0)

‎gitlab/v4/cli.py

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (C) 2013-2017 Gauvain Pocentek <gauvain@pocentek.net>
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU Lesser General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
from __future__importprint_function
20+
importinspect
21+
importoperator
22+
23+
importsix
24+
25+
importgitlab
26+
importgitlab.base
27+
fromgitlabimportcli
28+
importgitlab.v4.objects
29+
30+
31+
classGitlabCLI(object):
32+
def__init__(self,gl,what,action,args):
33+
self.cls_name=cli.what_to_cls(what)
34+
self.cls=gitlab.v4.objects.__dict__[self.cls_name]
35+
self.what=what.replace('-','_')
36+
self.action=action.lower().replace('-','')
37+
self.gl=gl
38+
self.args=args
39+
self.mgr_cls=getattr(gitlab.v4.objects,
40+
self.cls.__name__+'Manager')
41+
# We could do something smart, like splitting the manager name to find
42+
# parents, build the chain of managers to get to the final object.
43+
# Instead we do something ugly and efficient: interpolate variables in
44+
# the class _path attribute, and replace the value with the result.
45+
self.mgr_cls._path=self.mgr_cls._path%self.args
46+
self.mgr=self.mgr_cls(gl)
47+
48+
def__call__(self):
49+
method='do_%s'%self.action
50+
ifhasattr(self,method):
51+
returngetattr(self,method)()
52+
else:
53+
returnself.do_custom()
54+
55+
defdo_custom(self):
56+
in_obj=cli.custom_actions[self.cls_name][self.action][2]
57+
58+
# Get the object (lazy), then act
59+
ifin_obj:
60+
data= {}
61+
ifhasattr(self.mgr,'_from_parent_attrs'):
62+
forkinself.mgr._from_parent_attrs:
63+
data[k]=self.args[k]
64+
ifgitlab.mixins.GetWithoutIdMixinnotininspect.getmro(self.cls):
65+
data[self.cls._id_attr]=self.args.pop(self.cls._id_attr)
66+
o=self.cls(self.mgr,data)
67+
returngetattr(o,self.action)(**self.args)
68+
else:
69+
returngetattr(self.mgr,self.action)(**self.args)
70+
71+
defdo_create(self):
72+
try:
73+
returnself.mgr.create(self.args)
74+
exceptExceptionase:
75+
cli.die("Impossible to create object",e)
76+
77+
defdo_list(self):
78+
try:
79+
returnself.mgr.list(**self.args)
80+
exceptExceptionase:
81+
cli.die("Impossible to list objects",e)
82+
83+
defdo_get(self):
84+
id=None
85+
ifgitlab.mixins.GetWithoutIdMixinnotininspect.getmro(self.cls):
86+
id=self.args.pop(self.cls._id_attr)
87+
88+
try:
89+
returnself.mgr.get(id,**self.args)
90+
exceptExceptionase:
91+
cli.die("Impossible to get object",e)
92+
93+
defdo_delete(self):
94+
id=self.args.pop(self.cls._id_attr)
95+
try:
96+
self.mgr.delete(id,**self.args)
97+
exceptExceptionase:
98+
cli.die("Impossible to destroy object",e)
99+
100+
defdo_update(self):
101+
id=self.args.pop(self.cls._id_attr)
102+
try:
103+
returnself.mgr.update(id,self.args)
104+
exceptExceptionase:
105+
cli.die("Impossible to update object",e)
106+
107+
108+
def_populate_sub_parser_by_class(cls,sub_parser):
109+
mgr_cls_name=cls.__name__+'Manager'
110+
mgr_cls=getattr(gitlab.v4.objects,mgr_cls_name)
111+
112+
foraction_namein ['list','get','create','update','delete']:
113+
ifnothasattr(mgr_cls,action_name):
114+
continue
115+
116+
sub_parser_action=sub_parser.add_parser(action_name)
117+
ifhasattr(mgr_cls,'_from_parent_attrs'):
118+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
119+
required=True)
120+
forxinmgr_cls._from_parent_attrs]
121+
sub_parser_action.add_argument("--sudo",required=False)
122+
123+
ifaction_name=="list":
124+
ifhasattr(mgr_cls,'_list_filters'):
125+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
126+
required=False)
127+
forxinmgr_cls._list_filters]
128+
129+
sub_parser_action.add_argument("--page",required=False)
130+
sub_parser_action.add_argument("--per-page",required=False)
131+
sub_parser_action.add_argument("--all",required=False,
132+
action='store_true')
133+
134+
ifaction_name=='delete':
135+
id_attr=cls._id_attr.replace('_','-')
136+
sub_parser_action.add_argument("--%s"%id_attr,required=True)
137+
138+
ifaction_name=="get":
139+
ifgitlab.mixins.GetWithoutIdMixinnotininspect.getmro(cls):
140+
ifcls._id_attrisnotNone:
141+
id_attr=cls._id_attr.replace('_','-')
142+
sub_parser_action.add_argument("--%s"%id_attr,
143+
required=True)
144+
145+
ifhasattr(mgr_cls,'_optional_get_attrs'):
146+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
147+
required=False)
148+
forxinmgr_cls._optional_get_attrs]
149+
150+
ifaction_name=="create":
151+
ifhasattr(mgr_cls,'_create_attrs'):
152+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
153+
required=True)
154+
forxinmgr_cls._create_attrs[0]ifx!=cls._id_attr]
155+
156+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
157+
required=False)
158+
forxinmgr_cls._create_attrs[1]ifx!=cls._id_attr]
159+
160+
ifaction_name=="update":
161+
ifcls._id_attrisnotNone:
162+
id_attr=cls._id_attr.replace('_','-')
163+
sub_parser_action.add_argument("--%s"%id_attr,
164+
required=True)
165+
166+
ifhasattr(mgr_cls,'_update_attrs'):
167+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
168+
required=True)
169+
forxinmgr_cls._update_attrs[0]ifx!=cls._id_attr]
170+
171+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
172+
required=False)
173+
forxinmgr_cls._update_attrs[1]ifx!=cls._id_attr]
174+
175+
ifcls.__name__incli.custom_actions:
176+
name=cls.__name__
177+
foraction_nameincli.custom_actions[name]:
178+
sub_parser_action=sub_parser.add_parser(action_name)
179+
# Get the attributes for URL/path construction
180+
ifhasattr(mgr_cls,'_from_parent_attrs'):
181+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
182+
required=True)
183+
forxinmgr_cls._from_parent_attrs]
184+
sub_parser_action.add_argument("--sudo",required=False)
185+
186+
# We need to get the object somehow
187+
ifgitlab.mixins.GetWithoutIdMixinnotininspect.getmro(cls):
188+
ifcls._id_attrisnotNone:
189+
id_attr=cls._id_attr.replace('_','-')
190+
sub_parser_action.add_argument("--%s"%id_attr,
191+
required=True)
192+
193+
required,optional,dummy=cli.custom_actions[name][action_name]
194+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
195+
required=True)
196+
forxinrequiredifx!=cls._id_attr]
197+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
198+
required=False)
199+
forxinoptionalifx!=cls._id_attr]
200+
201+
ifmgr_cls.__name__incli.custom_actions:
202+
name=mgr_cls.__name__
203+
foraction_nameincli.custom_actions[name]:
204+
sub_parser_action=sub_parser.add_parser(action_name)
205+
ifhasattr(mgr_cls,'_from_parent_attrs'):
206+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
207+
required=True)
208+
forxinmgr_cls._from_parent_attrs]
209+
sub_parser_action.add_argument("--sudo",required=False)
210+
211+
required,optional,dummy=cli.custom_actions[name][action_name]
212+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
213+
required=True)
214+
forxinrequiredifx!=cls._id_attr]
215+
[sub_parser_action.add_argument("--%s"%x.replace('_','-'),
216+
required=False)
217+
forxinoptionalifx!=cls._id_attr]
218+
219+
220+
defextend_parser(parser):
221+
subparsers=parser.add_subparsers(title='object',dest='what',
222+
help="Object to manipulate.")
223+
subparsers.required=True
224+
225+
# populate argparse for all Gitlab Object
226+
classes= []
227+
forclsingitlab.v4.objects.__dict__.values():
228+
try:
229+
ifgitlab.base.RESTManagerininspect.getmro(cls):
230+
ifcls._obj_clsisnotNone:
231+
classes.append(cls._obj_cls)
232+
exceptAttributeError:
233+
pass
234+
classes.sort(key=operator.attrgetter("__name__"))
235+
236+
forclsinclasses:
237+
arg_name=cli.cls_to_what(cls)
238+
object_group=subparsers.add_parser(arg_name)
239+
240+
object_subparsers=object_group.add_subparsers(
241+
dest='action',help="Action to execute.")
242+
_populate_sub_parser_by_class(cls,object_subparsers)
243+
object_subparsers.required=True
244+
245+
returnparser
246+
247+
248+
classLegacyPrinter(object):
249+
defdisplay(self,obj,verbose=False,padding=0):
250+
defdisplay_dict(d):
251+
forkinsorted(d.keys()):
252+
v=d[k]
253+
ifisinstance(v,dict):
254+
print('%s%s:'% (' '*padding,k))
255+
new_padding=padding+2
256+
self.display(v,True,new_padding)
257+
continue
258+
print('%s%s: %s'% (' '*padding,k,v))
259+
260+
ifverbose:
261+
ifisinstance(obj,dict):
262+
display_dict(obj)
263+
return
264+
265+
# not a dict, we assume it's a RESTObject
266+
id=getattr(obj,obj._id_attr)
267+
print('%s: %s'% (obj._id_attr,id))
268+
attrs=obj.attributes
269+
attrs.pop(obj._id_attr)
270+
display_dict(attrs)
271+
print('')
272+
273+
else:
274+
id=getattr(obj,obj._id_attr)
275+
print('%s: %s'% (obj._id_attr,id))
276+
ifhasattr(obj,'_short_print_attr'):
277+
value=getattr(obj,obj._short_print_attr)
278+
print('%s: %s'% (obj._short_print_attr,value))
279+
280+
281+
defrun(gl,what,action,args,verbose):
282+
g_cli=GitlabCLI(gl,what,action,args)
283+
ret_val=g_cli()
284+
285+
printer=LegacyPrinter()
286+
287+
ifisinstance(ret_val,list):
288+
foroinret_val:
289+
ifisinstance(o,gitlab.base.RESTObject):
290+
printer.display(o,verbose)
291+
else:
292+
print(o)
293+
elifisinstance(ret_val,gitlab.base.RESTObject):
294+
printer.display(ret_val,verbose)
295+
elifisinstance(ret_val,six.string_types):
296+
print(ret_val)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp