Merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt

Proposed by Matias Bordese
Status: Merged
Approved by: Matias Bordese
Approved revision: 9
Merged at revision: 7
Proposed branch: lp:~matiasb/click-toolbelt/added-missing-params
Merge into: lp:click-toolbelt
Diff against target: 418 lines (+113/-81)
5 files modified
README (+3/-1)
click/login.py (+4/-1)
click/tests/test_login.py (+43/-19)
click/tests/test_upload.py (+47/-54)
click/upload.py (+16/-6)
To merge this branch: bzr merge lp:~matiasb/click-toolbelt/added-missing-params
Reviewer Review Type Date Requested Status
Ricardo Kirkner (community) Approve
Review via email: mp+189962@code.launchpad.net

Commit message

- Added missing parameters (architectures, changelog).
- Updated/fixed SSO login integration to work with updated ssoclient.

Description of the change

Added missing parameters (architectures, what's new).
Updated/fixed SSO login integration to work with updated ssoclient.

To post a comment you must log in.
Revision history for this message
Ricardo Kirkner (ricardokirkner) wrote :

LGTM

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt failed. Below is the output from the failed tests.

running test
Checking .pth file support in .
/usr/bin/python -E -c pass
Searching for ssoclient
Reading http://pypi.python.org/simple/ssoclient/
Best match: ssoclient 2.0
Downloading https://pypi.python.org/packages/source/s/ssoclient/ssoclient-2.0.tar.gz#md5=a4c7a3aeb875d5d245b58752aa680494
Processing ssoclient-2.0.tar.gz
Writing /tmp/easy_install-Ky0kSO/ssoclient-2.0/setup.cfg
Running ssoclient-2.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-Ky0kSO/ssoclient-2.0/egg-dist-tmp-lw9Ja6

Installed /mnt/tarmac/cache/click-toolbelt/trunk/ssoclient-2.0-py2.7.egg
Searching for requests-oauthlib
Reading http://pypi.python.org/simple/requests-oauthlib/
Best match: requests-oauthlib 0.4.0
Downloading https://pypi.python.org/packages/source/r/requests-oauthlib/requests-oauthlib-0.4.0.tar.gz#md5=2649560205a02473998e0e7dd881ee5a
Processing requests-oauthlib-0.4.0.tar.gz
Writing /tmp/easy_install-X3uWbE/requests-oauthlib-0.4.0/setup.cfg
Running requests-oauthlib-0.4.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-X3uWbE/requests-oauthlib-0.4.0/egg-dist-tmp-mfVvSD

Installed /mnt/tarmac/cache/click-toolbelt/trunk/requests_oauthlib-0.4.0-py2.7.egg
Searching for cliff
Reading http://pypi.python.org/simple/cliff/
Best match: cliff 1.4.5
Downloading https://pypi.python.org/packages/source/c/cliff/cliff-1.4.5.tar.gz#md5=21743e9e800cf526e88e420f366a4c5c
Processing cliff-1.4.5.tar.gz
Writing /tmp/easy_install-GPmDRK/cliff-1.4.5/setup.cfg
Running cliff-1.4.5/setup.py -q bdist_egg --dist-dir /tmp/easy_install-GPmDRK/cliff-1.4.5/egg-dist-tmp-_Iaxba

Installed /mnt/tarmac/cache/click-toolbelt/trunk/cliff-1.4.5-py2.7.egg

zip_safe flag not set; analyzing archive contents...
warning: no files found matching '*.py' under directory 'tests'
Traceback (most recent call last):
  File "setup.py", line 69, in <module>
    tests_require=['mock'],
  File "/usr/lib/python2.7/distutils/core.py", line 152, in setup
    dist.run_commands()
  File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command
    cmd_obj.run()
  File "/usr/lib/python2.7/dist-packages/setuptools/command/test.py", line 128, in run
    self.distribution.fetch_build_eggs(self.distribution.install_requires)
  File "/usr/lib/python2.7/dist-packages/setuptools/dist.py", line 245, in fetch_build_eggs
    parse_requirements(requires), installer=self.fetch_build_egg
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 598, in resolve
    raise VersionConflict(dist,req) # XXX put more info here
pkg_resources.VersionConflict: (requests 1.2.3 (/usr/lib/python2.7/dist-packages), Requirement.parse('requests>=2.0.0'))

Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~matiasb/click-toolbelt/added-missing-params into lp:click-toolbelt failed. Below is the output from the failed tests.

rm -rf .env
rm -rf cliff-*.egg
rm -rf cmd2-*.egg
rm -rf mock-*.egg
rm -rf oauthlib-*.egg
rm -rf prettytable-*.egg
rm -rf pyparsing-*.egg
rm -rf pyxdg-*.egg
rm -rf requests-*.egg
rm -rf requests_oauthlib-*.egg
rm -rf ssoclient-*.egg
rm -rf click.egg-info
virtualenv .env

make: virtualenv: Command not found
make: *** [env] Error 127

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2013-09-18 08:15:02 +0000
3+++ README 2013-10-09 14:25:04 +0000
4@@ -34,7 +34,7 @@
5
6 ::
7
8- $ click upload <namespace>.<app_name> <version> <filename>
9+ $ click upload <namespace>.<app_name> <version> <filename> <changelog> [architecture]
10
11
12 For this command to be successful, certain criteria must be met::
13@@ -42,3 +42,5 @@
14 - user is registered as a developer on myapps.developer.ubuntu.com
15 - the application has been previously created on myapps.developer.ubuntu.com
16 - user owns the application for which he's upload the click package
17+ - if not specified, architecture will default to 'all'
18+ (possible values: 'armhf', 'i386', 'amd64', 'all'; you can specify more than one)
19
20=== modified file 'click/login.py'
21--- click/login.py 2013-09-18 12:00:15 +0000
22+++ click/login.py 2013-10-09 14:25:04 +0000
23@@ -7,6 +7,7 @@
24 from cliff.command import Command
25 from ssoclient.v2 import (
26 ApiException,
27+ UnexpectedApiError,
28 V2ApiClient,
29 )
30 from xdg.BaseDirectory import save_config_path
31@@ -59,10 +60,12 @@
32 client = V2ApiClient(endpoint=api_endpoint)
33 try:
34 response = client.login(data=data)
35+ result['body'] = response
36 result['success'] = True
37- result['body'] = response.json()
38 except ApiException as err:
39 result['body'] = err.body
40+ except UnexpectedApiError as err:
41+ result['body'] = err.json_body
42 return result
43
44 def take_action(self, parsed_args):
45
46=== modified file 'click/tests/test_login.py'
47--- click/tests/test_login.py 2013-09-18 12:00:15 +0000
48+++ click/tests/test_login.py 2013-10-09 14:25:04 +0000
49@@ -112,8 +112,8 @@
50 self.assertTrue(cfg.has_section('login.ubuntu.com'))
51 self.assertEqual(dict(cfg.items('login.ubuntu.com')), data)
52
53- @patch('click.login.V2ApiClient.login')
54- def test_take_action_successful(self, mock_login):
55+ @patch('ssoclient.v2.http.requests.Session.request')
56+ def test_take_action_successful(self, mock_post):
57 token_data = {
58 'consumer_key': 'consumer-key',
59 'consumer_secret': 'consumer-secret',
60@@ -123,32 +123,56 @@
61 response = Response()
62 response.status_code = 201
63 response._content = json.dumps(token_data).encode('utf-8')
64- mock_login.return_value = response
65+ mock_post.return_value = response
66
67 data = {
68 'email': 'foo@foo.com',
69 'password': 'password',
70+ 'token_name': 'consumer-key',
71 }
72 result = self.command.login(data)
73 expected = {'success': True, 'body': token_data}
74 self.assertEqual(result, expected)
75
76- @patch('click.login.V2ApiClient.login')
77- def test_take_action_unsuccessful(self, mock_login):
78- error_data = {
79- 'message': 'Error during login.',
80- 'code': 'UNAUTHORISED',
81- 'extra': {},
82- }
83- response = Response()
84- response.status_code = 401
85- response.reason = 'UNAUTHORISED'
86- response._content = json.dumps(error_data).encode('utf-8')
87- mock_login.side_effect = ApiException(response, response.json())
88-
89- data = {
90- 'email': 'foo@foo.com',
91- 'password': 'password',
92+ @patch('ssoclient.v2.http.requests.Session.request')
93+ def test_take_action_unsuccessful_api_exception(self, mock_post):
94+ error_data = {
95+ 'message': 'Error during login.',
96+ 'code': 'INVALID_CREDENTIALS',
97+ 'extra': {},
98+ }
99+ response = Response()
100+ response.status_code = 401
101+ response.reason = 'UNAUTHORISED'
102+ response._content = json.dumps(error_data).encode('utf-8')
103+ mock_post.return_value = response
104+
105+ data = {
106+ 'email': 'foo@foo.com',
107+ 'password': 'password',
108+ 'token_name': 'consumer-key',
109+ }
110+ result = self.command.login(data)
111+ expected = {'success': False, 'body': error_data}
112+ self.assertEqual(result, expected)
113+
114+ @patch('ssoclient.v2.http.requests.Session.request')
115+ def test_take_action_unsuccessful_unexpected_error(self, mock_post):
116+ error_data = {
117+ 'message': 'Error during login.',
118+ 'code': 'UNEXPECTED_ERROR_CODE',
119+ 'extra': {},
120+ }
121+ response = Response()
122+ response.status_code = 401
123+ response.reason = 'UNAUTHORISED'
124+ response._content = json.dumps(error_data).encode('utf-8')
125+ mock_post.return_value = response
126+
127+ data = {
128+ 'email': 'foo@foo.com',
129+ 'password': 'password',
130+ 'token_name': 'consumer-key',
131 }
132 result = self.command.login(data)
133 expected = {'success': False, 'body': error_data}
134
135=== modified file 'click/tests/test_upload.py'
136--- click/tests/test_upload.py 2013-09-13 13:32:25 +0000
137+++ click/tests/test_upload.py 2013-10-09 14:25:04 +0000
138@@ -27,7 +27,9 @@
139 args = None
140 self.command = Upload(app, args)
141 self.app_name = 'namespace.package_name'
142+ self.app_archs = ['all']
143 self.app_version = '1'
144+ self.app_changelog = 'Initial version.'
145 self.uploaded_callback_url = MYAPPS_API_ROOT_URL + 'click-uploaded/1/'
146
147 # setup patches
148@@ -144,7 +146,9 @@
149 self.mock_post.return_value = response
150
151 data = self.command.get_upload_data(self.app_name,
152- self.app_version)
153+ self.app_version,
154+ self.app_changelog,
155+ self.app_archs)
156 self.assertEqual(data, expected)
157
158 def test_get_upload_data_for_unknown_app(self):
159@@ -155,7 +159,9 @@
160 self.mock_post.return_value = response
161
162 data = self.command.get_upload_data(self.app_name,
163- self.app_version)
164+ self.app_version,
165+ self.app_changelog,
166+ self.app_archs)
167 self.assertEqual(data, {
168 'success': False,
169 'message': 'The requested Click Package could not be found.'})
170@@ -164,23 +170,33 @@
171 self.mock_post.side_effect = Exception('some http error')
172
173 data = self.command.get_upload_data(self.app_name,
174- self.app_version)
175+ self.app_version,
176+ self.app_changelog,
177+ self.app_archs)
178 self.assertEqual(data, {'success': False,
179 'message': 'some http error'})
180
181 def test_get_upload_data_uses_default_myapps_url(self):
182 upload_url = MYAPPS_API_ROOT_URL + "click-upload/%s/" % (
183 self.app_name,)
184- self.command.get_upload_data(self.app_name, self.app_version)
185+ self.command.get_upload_data(
186+ self.app_name, self.app_version, self.app_changelog,
187+ self.app_archs)
188 self.mock_post.assert_called_once_with(
189- upload_url, data={'version': self.app_version})
190+ upload_url, data={'version': self.app_version,
191+ 'architectures': self.app_archs,
192+ 'changelog': self.app_changelog})
193
194 def test_get_upload_data_uses_environment_variable(self):
195 upload_url = MYAPPS_API_ROOT_URL + "click-upload/%s/" % (
196 self.app_name,)
197- self.command.get_upload_data(self.app_name, self.app_version)
198+ self.command.get_upload_data(
199+ self.app_name, self.app_version, self.app_changelog,
200+ self.app_archs)
201 self.mock_post.assert_called_once_with(
202- upload_url, data={'version': self.app_version})
203+ upload_url, data={'version': self.app_version,
204+ 'architectures': self.app_archs,
205+ 'changelog': self.app_changelog})
206
207
208 class MarkUploadedTestCase(BaseUploadCommandTestCase):
209@@ -286,21 +302,28 @@
210 patcher.start()
211 self.addCleanup(patcher.stop)
212
213+ parsed_args = namedtuple(
214+ 'parsed_args',
215+ 'application_name, version, filename, changelog, arch')
216+
217+ filename = 'package.click'
218+ self.args = parsed_args(
219+ self.app_name, self.app_version, filename, self.app_changelog,
220+ self.app_archs)
221+
222 def test_parser(self):
223 parser = self.command.get_parser(__namespace__)
224- for i, name in enumerate(['application_name', 'version', 'filename']):
225+ cmd_args = ['application_name', 'version', 'filename', 'changelog']
226+ for i, name in enumerate(cmd_args):
227 # argument 0 is builtin --help
228 # start comparing from first extra argument
229 self.assertEqual(parser._actions[i + 1].dest, name)
230- self.assertFalse(parser._actions[i + 1].required)
231+ self.assertTrue(parser._actions[i + 1].required)
232+ # arch is optional, and last param
233+ self.assertEqual(parser._actions[-1].dest, 'arch')
234+ self.assertFalse(parser._actions[-1].required)
235
236 def test_take_action(self):
237- parsed_args = namedtuple('parsed_args',
238- 'application_name, version, filename')
239-
240- filename = 'package.click'
241- args = parsed_args(self.app_name, self.app_version, filename)
242-
243 response = Response()
244 response.status_code = 200
245 response._content = json.dumps({'successful': True}).encode(
246@@ -309,12 +332,13 @@
247
248 with patch('click.upload.Upload.mark_uploaded') as mock_mark_uploaded:
249 mock_mark_uploaded.return_value = {'success': True}
250- self.command.take_action(args)
251+ self.command.take_action(self.args)
252
253 # assert request was made to get upload data
254 # followed by an updown upload request
255 self.mock_get_upload_data.assert_called_once_with(
256- self.app_name, self.app_version)
257+ self.app_name, self.app_version, self.app_changelog,
258+ self.app_archs)
259 self.mock_post.assert_called_with(
260 CLICK_UPDOWN_UPLOAD_URL,
261 files={'binary': self.mock_open.return_value},
262@@ -323,54 +347,35 @@
263 'Click Package upload finished successfully.')
264
265 def test_take_action_handles_error_while_getting_upload_data(self):
266- parsed_args = namedtuple('parsed_args',
267- 'application_name, version, filename')
268-
269- filename = 'package.click'
270- args = parsed_args(self.app_name, self.app_version, filename)
271-
272 response = Response()
273 response.status_code = 200
274 self.mock_post.return_value = response
275 self.mock_get_upload_data.return_value = {
276 'success': False, 'message': 'Some error'}
277
278- self.command.take_action(args)
279+ self.command.take_action(self.args)
280
281 self.mock_log.error.assert_called_with(
282 'Error verifying upload request. Uploading not possible.\n'
283 'Reason: %s', 'Some error')
284
285 def test_take_action_handles_error_while_uploading(self):
286- parsed_args = namedtuple('parsed_args',
287- 'application_name, version, filename')
288-
289- filename = 'package.click'
290- args = parsed_args(self.app_name, self.app_version, filename)
291-
292 self.mock_post.side_effect = Exception('foo')
293
294- self.command.take_action(args)
295+ self.command.take_action(self.args)
296
297 self.mock_log.exception.assert_called_with(
298 'There was an error uploading the click package.')
299
300 def test_take_action_handles_unsuccessful_response(self):
301- parsed_args = namedtuple('parsed_args',
302- 'application_name, version, filename')
303-
304- filename = 'package.click'
305- args = parsed_args(self.app_name, self.app_version, filename)
306-
307- upload_response = {'success': True,
308- 'message': None}
309+ upload_response = {'success': True, 'message': None}
310 response = Response()
311 response.status_code = 200
312 response.reason = 'OK'
313 response._content = json.dumps(upload_response).encode('utf-8')
314 self.mock_post.return_value = response
315
316- self.command.take_action(args)
317+ self.command.take_action(self.args)
318
319 self.mock_log.error.assert_called_with(
320 'There was an error uploading the click package.\n'
321@@ -379,19 +384,13 @@
322 'OK', json.dumps(upload_response))
323
324 def test_take_action_handles_error_response(self):
325- parsed_args = namedtuple('parsed_args',
326- 'application_name, version, filename')
327-
328- filename = 'package.click'
329- args = parsed_args(self.app_name, self.app_version, filename)
330-
331 response = Response()
332 response.status_code = 500
333 response.reason = 'INTERNAL SERVER ERROR'
334 response._content = 'This is a traceback.'.encode('utf-8')
335 self.mock_post.return_value = response
336
337- self.command.take_action(args)
338+ self.command.take_action(self.args)
339
340 self.mock_log.error.assert_called_with(
341 'There was an error uploading the click package.\n'
342@@ -400,12 +399,6 @@
343 'INTERNAL SERVER ERROR', 'This is a traceback.')
344
345 def test_take_action_handles_unsuccessful_mark_uploaded_response(self):
346- parsed_args = namedtuple('parsed_args',
347- 'application_name, version, filename')
348-
349- filename = 'package.click'
350- args = parsed_args(self.app_name, self.app_version, filename)
351-
352 upload_response = {'successful': True, 'message': None}
353 response = Response()
354 response.status_code = 200
355@@ -416,7 +409,7 @@
356 with patch('click.upload.Upload.mark_uploaded') as mock_mark_uploaded:
357 mock_mark_uploaded.return_value = {
358 'success': False, 'message': 'Please try again later'}
359- self.command.take_action(args)
360+ self.command.take_action(self.args)
361
362 self.mock_log.error.assert_called_with(
363 'There was an error after uploading the click package.\n'
364
365=== modified file 'click/upload.py'
366--- click/upload.py 2013-09-13 13:32:25 +0000
367+++ click/upload.py 2013-10-09 14:25:04 +0000
368@@ -23,9 +23,13 @@
369
370 def get_parser(self, prog_name):
371 parser = super(Upload, self).get_parser(prog_name)
372- parser.add_argument('application_name', nargs='?')
373- parser.add_argument('version', nargs='?')
374- parser.add_argument('filename', nargs='?')
375+ parser.add_argument('application_name')
376+ parser.add_argument('version')
377+ parser.add_argument('filename')
378+ parser.add_argument('changelog')
379+ parser.add_argument(
380+ 'arch', nargs='*', default='all',
381+ choices=['armhf', 'i386', 'amd64', 'all'])
382 return parser
383
384 def get_config(self):
385@@ -58,7 +62,7 @@
386 session = None
387 return session
388
389- def get_upload_data(self, name, version):
390+ def get_upload_data(self, name, version, changelog, architectures):
391 myapps_url = os.environ.get('MYAPPS_API_ROOT_URL', MYAPPS_API_ROOT_URL)
392 myapps_url = urljoin(myapps_url, 'click-upload/%s/' % name)
393
394@@ -70,7 +74,10 @@
395 return result
396
397 try:
398- response = session.post(myapps_url, data={'version': version})
399+ response = session.post(
400+ myapps_url,
401+ data={'version': version, 'changelog': changelog,
402+ 'architectures': architectures})
403 if response.ok:
404 result.update(response.json())
405 message = result.get('message')
406@@ -118,8 +125,11 @@
407 application_name = parsed_args.application_name
408 version = parsed_args.version
409 filename = parsed_args.filename
410+ changelog = parsed_args.changelog
411+ architectures = parsed_args.arch
412
413- data = self.get_upload_data(application_name, version)
414+ data = self.get_upload_data(
415+ application_name, version, changelog, architectures)
416 if not data.get('success', False):
417 error = data.get('message', 'Unknown error')
418 self.log.error(

Subscribers

People subscribed via source and target branches