##// END OF EJS Templates
Show proper error on argument parse when using api-cli
marcink -
r2509:955956f4 beta
parent child Browse files
Show More
@@ -1,232 +1,237 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.bin.backup_manager
3 rhodecode.bin.backup_manager
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Api CLI client for RhodeCode
6 Api CLI client for RhodeCode
7
7
8 :created_on: Jun 3, 2012
8 :created_on: Jun 3, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 from __future__ import with_statement
26 from __future__ import with_statement
27 import os
27 import os
28 import sys
28 import sys
29 import random
29 import random
30 import urllib2
30 import urllib2
31 import pprint
31 import pprint
32 import argparse
32 import argparse
33
33
34 try:
34 try:
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 except ImportError:
36 except ImportError:
37 try:
37 try:
38 import simplejson as json
38 import simplejson as json
39 except ImportError:
39 except ImportError:
40 import json
40 import json
41
41
42
42
43 CONFIG_NAME = '.rhodecode'
43 CONFIG_NAME = '.rhodecode'
44 FORMAT_PRETTY = 'pretty'
44 FORMAT_PRETTY = 'pretty'
45 FORMAT_JSON = 'json'
45 FORMAT_JSON = 'json'
46
46
47
47
48 class RcConf(object):
48 class RcConf(object):
49 """
49 """
50 RhodeCode config for API
50 RhodeCode config for API
51
51
52 conf = RcConf()
52 conf = RcConf()
53 conf['key']
53 conf['key']
54
54
55 """
55 """
56
56
57 def __init__(self, autoload=True, autocreate=False, config=None):
57 def __init__(self, autoload=True, autocreate=False, config=None):
58 self._conf_name = CONFIG_NAME
58 self._conf_name = CONFIG_NAME
59 self._conf = {}
59 self._conf = {}
60 if autocreate:
60 if autocreate:
61 self.make_config(config)
61 self.make_config(config)
62 if autoload:
62 if autoload:
63 self._conf = self.load_config()
63 self._conf = self.load_config()
64
64
65 def __getitem__(self, key):
65 def __getitem__(self, key):
66 return self._conf[key]
66 return self._conf[key]
67
67
68 def __nonzero__(self):
68 def __nonzero__(self):
69 if self._conf:
69 if self._conf:
70 return True
70 return True
71 return False
71 return False
72
72
73 def __eq__(self):
73 def __eq__(self):
74 return self._conf.__eq__()
74 return self._conf.__eq__()
75
75
76 def __repr__(self):
76 def __repr__(self):
77 return 'RcConf<%s>' % self._conf.__repr__()
77 return 'RcConf<%s>' % self._conf.__repr__()
78
78
79 def make_config(self, config):
79 def make_config(self, config):
80 """
80 """
81 Saves given config as a JSON dump in the _conf_name location
81 Saves given config as a JSON dump in the _conf_name location
82
82
83 :param config:
83 :param config:
84 :type config:
84 :type config:
85 """
85 """
86 with open(self._conf_name, 'wb') as f:
86 with open(self._conf_name, 'wb') as f:
87 json.dump(config, f, indent=4)
87 json.dump(config, f, indent=4)
88 sys.stdout.write('Updated conf\n')
88 sys.stdout.write('Updated conf\n')
89
89
90 def update_config(self, new_config):
90 def update_config(self, new_config):
91 """
91 """
92 Reads the JSON config updates it's values with new_config and
92 Reads the JSON config updates it's values with new_config and
93 saves it back as JSON dump
93 saves it back as JSON dump
94
94
95 :param new_config:
95 :param new_config:
96 """
96 """
97 config = {}
97 config = {}
98 try:
98 try:
99 with open(self._conf_name, 'rb') as conf:
99 with open(self._conf_name, 'rb') as conf:
100 config = json.load(conf)
100 config = json.load(conf)
101 except IOError, e:
101 except IOError, e:
102 sys.stderr.write(str(e) + '\n')
102 sys.stderr.write(str(e) + '\n')
103
103
104 config.update(new_config)
104 config.update(new_config)
105 self.make_config(config)
105 self.make_config(config)
106
106
107 def load_config(self):
107 def load_config(self):
108 """
108 """
109 Loads config from file and returns loaded JSON object
109 Loads config from file and returns loaded JSON object
110 """
110 """
111 try:
111 try:
112 with open(self._conf_name, 'rb') as conf:
112 with open(self._conf_name, 'rb') as conf:
113 return json.load(conf)
113 return json.load(conf)
114 except IOError, e:
114 except IOError, e:
115 #sys.stderr.write(str(e) + '\n')
115 #sys.stderr.write(str(e) + '\n')
116 pass
116 pass
117
117
118
118
119 def api_call(apikey, apihost, format, method=None, **kw):
119 def api_call(apikey, apihost, format, method=None, **kw):
120 """
120 """
121 Api_call wrapper for RhodeCode
121 Api_call wrapper for RhodeCode
122
122
123 :param apikey:
123 :param apikey:
124 :param apihost:
124 :param apihost:
125 :param format: formatting, pretty means prints and pprint of json
125 :param format: formatting, pretty means prints and pprint of json
126 json returns unparsed json
126 json returns unparsed json
127 :param method:
127 :param method:
128 """
128 """
129 def _build_data(random_id):
129 def _build_data(random_id):
130 """
130 """
131 Builds API data with given random ID
131 Builds API data with given random ID
132
132
133 :param random_id:
133 :param random_id:
134 :type random_id:
134 :type random_id:
135 """
135 """
136 return {
136 return {
137 "id": random_id,
137 "id": random_id,
138 "api_key": apikey,
138 "api_key": apikey,
139 "method": method,
139 "method": method,
140 "args": kw
140 "args": kw
141 }
141 }
142
142
143 if not method:
143 if not method:
144 raise Exception('please specify method name !')
144 raise Exception('please specify method name !')
145 id_ = random.randrange(1, 9999)
145 id_ = random.randrange(1, 9999)
146 req = urllib2.Request('%s/_admin/api' % apihost,
146 req = urllib2.Request('%s/_admin/api' % apihost,
147 data=json.dumps(_build_data(id_)),
147 data=json.dumps(_build_data(id_)),
148 headers={'content-type': 'text/plain'})
148 headers={'content-type': 'text/plain'})
149 if format == FORMAT_PRETTY:
149 if format == FORMAT_PRETTY:
150 sys.stdout.write('calling %s to %s \n' % (req.get_data(), apihost))
150 sys.stdout.write('calling %s to %s \n' % (req.get_data(), apihost))
151 ret = urllib2.urlopen(req)
151 ret = urllib2.urlopen(req)
152 raw_json = ret.read()
152 raw_json = ret.read()
153 json_data = json.loads(raw_json)
153 json_data = json.loads(raw_json)
154 id_ret = json_data['id']
154 id_ret = json_data['id']
155 _formatted_json = pprint.pformat(json_data)
155 _formatted_json = pprint.pformat(json_data)
156 if id_ret == id_:
156 if id_ret == id_:
157 if format == FORMAT_JSON:
157 if format == FORMAT_JSON:
158 sys.stdout.write(str(raw_json))
158 sys.stdout.write(str(raw_json))
159 else:
159 else:
160 sys.stdout.write('rhodecode returned:\n%s\n' % (_formatted_json))
160 sys.stdout.write('rhodecode returned:\n%s\n' % (_formatted_json))
161
161
162 else:
162 else:
163 raise Exception('something went wrong. '
163 raise Exception('something went wrong. '
164 'ID mismatch got %s, expected %s | %s' % (
164 'ID mismatch got %s, expected %s | %s' % (
165 id_ret, id_, _formatted_json))
165 id_ret, id_, _formatted_json))
166
166
167
167
168 def argparser(argv):
168 def argparser(argv):
169 usage = ("rhodecode_api [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] "
169 usage = ("rhodecode_api [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] "
170 "_create_config or METHOD <key:val> <key2:val> ...")
170 "_create_config or METHOD <key:val> <key2:val> ...")
171
171
172 parser = argparse.ArgumentParser(description='RhodeCode API cli',
172 parser = argparse.ArgumentParser(description='RhodeCode API cli',
173 usage=usage)
173 usage=usage)
174
174
175 ## config
175 ## config
176 group = parser.add_argument_group('config')
176 group = parser.add_argument_group('config')
177 group.add_argument('--apikey', help='api access key')
177 group.add_argument('--apikey', help='api access key')
178 group.add_argument('--apihost', help='api host')
178 group.add_argument('--apihost', help='api host')
179
179
180 group = parser.add_argument_group('API')
180 group = parser.add_argument_group('API')
181 group.add_argument('method', metavar='METHOD', type=str,
181 group.add_argument('method', metavar='METHOD', type=str,
182 help='API method name to call followed by key:value attributes',
182 help='API method name to call followed by key:value attributes',
183 )
183 )
184 group.add_argument('--format', dest='format', type=str,
184 group.add_argument('--format', dest='format', type=str,
185 help='output format default: `pretty` can '
185 help='output format default: `pretty` can '
186 'be also `%s`' % FORMAT_JSON,
186 'be also `%s`' % FORMAT_JSON,
187 default=FORMAT_PRETTY
187 default=FORMAT_PRETTY
188 )
188 )
189 args, other = parser.parse_known_args()
189 args, other = parser.parse_known_args()
190 return parser, args, other
190 return parser, args, other
191
191
192
192
193 def main(argv=None):
193 def main(argv=None):
194 """
194 """
195 Main execution function for cli
195 Main execution function for cli
196
196
197 :param argv:
197 :param argv:
198 :type argv:
198 :type argv:
199 """
199 """
200 if argv is None:
200 if argv is None:
201 argv = sys.argv
201 argv = sys.argv
202
202
203 conf = None
203 conf = None
204 parser, args, other = argparser(argv)
204 parser, args, other = argparser(argv)
205
205
206 api_credentials_given = (args.apikey and args.apihost)
206 api_credentials_given = (args.apikey and args.apihost)
207 if args.method == '_create_config':
207 if args.method == '_create_config':
208 if not api_credentials_given:
208 if not api_credentials_given:
209 raise parser.error('_create_config requires --apikey and --apihost')
209 raise parser.error('_create_config requires --apikey and --apihost')
210 conf = RcConf(autocreate=True, config={'apikey': args.apikey,
210 conf = RcConf(autocreate=True, config={'apikey': args.apikey,
211 'apihost': args.apihost})
211 'apihost': args.apihost})
212 sys.stdout.write('Create new config in %s\n' % CONFIG_NAME)
212 sys.stdout.write('Create new config in %s\n' % CONFIG_NAME)
213
213
214 if not conf:
214 if not conf:
215 conf = RcConf(autoload=True)
215 conf = RcConf(autoload=True)
216 if not conf:
216 if not conf:
217 if not api_credentials_given:
217 if not api_credentials_given:
218 parser.error('Could not find config file and missing '
218 parser.error('Could not find config file and missing '
219 '--apikey or --apihost in params')
219 '--apikey or --apihost in params')
220
220
221 apikey = args.apikey or conf['apikey']
221 apikey = args.apikey or conf['apikey']
222 host = args.apihost or conf['apihost']
222 host = args.apihost or conf['apihost']
223 method = args.method
223 method = args.method
224 if method == '_create_config':
224 if method == '_create_config':
225 sys.exit()
225 sys.exit()
226
227 try:
226 margs = dict(map(lambda s: s.split(':', 1), other))
228 margs = dict(map(lambda s: s.split(':', 1), other))
229 except:
230 sys.stderr.write('Error parsing arguments \n')
231 sys.exit()
227
232
228 api_call(apikey, host, args.format, method, **margs)
233 api_call(apikey, host, args.format, method, **margs)
229 return 0
234 return 0
230
235
231 if __name__ == '__main__':
236 if __name__ == '__main__':
232 sys.exit(main(sys.argv))
237 sys.exit(main(sys.argv))
General Comments 0
You need to be logged in to leave comments. Login now