Merge lp:~cjwatson/lazr.restful/py3-pretty-print-helpers into lp:lazr.restful

Proposed by Colin Watson
Status: Merged
Merged at revision: 259
Proposed branch: lp:~cjwatson/lazr.restful/py3-pretty-print-helpers
Merge into: lp:lazr.restful
Diff against target: 263 lines (+90/-55)
5 files modified
NEWS.rst (+9/-0)
src/lazr/restful/docs/webservice.rst (+45/-38)
src/lazr/restful/example/base/tests/collection.txt (+10/-10)
src/lazr/restful/example/multiversion/tests/operation.txt (+6/-5)
src/lazr/restful/testing/webservice.py (+20/-2)
To merge this branch: bzr merge lp:~cjwatson/lazr.restful/py3-pretty-print-helpers
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+390200@code.launchpad.net

Commit message

Make pprint_entry/pprint_collection format strings in Python 3 style.

Description of the change

Change lazr.restful.testing.webservice.pprint_entry and lazr.restful.testing.webservice.pprint_collection to print text string representations in the Python 3 style ('text' rather than u'text') on both Python 2 and 3. This makes it easier to write bilingual doctests, although existing callers need to change.

Use these helpers in a few more doctests.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS.rst'
--- NEWS.rst 2020-09-02 08:23:06 +0000
+++ NEWS.rst 2020-09-02 22:01:19 +0000
@@ -2,6 +2,15 @@
2NEWS for lazr.restful2NEWS for lazr.restful
3=====================3=====================
44
50.23.0
6======
7
8Change ``lazr.restful.testing.webservice.pprint_entry`` and
9``lazr.restful.testing.webservice.pprint_collection`` to print text string
10representations in the Python 3 style (``'text'`` rather than ``u'text'``)
11on both Python 2 and 3. This makes it easier to write bilingual doctests,
12although existing callers need to change.
13
50.22.2 (2020-09-02)140.22.2 (2020-09-02)
6===================15===================
716
817
=== modified file 'src/lazr/restful/docs/webservice.rst'
--- src/lazr/restful/docs/webservice.rst 2020-08-21 16:16:09 +0000
+++ src/lazr/restful/docs/webservice.rst 2020-09-02 22:01:19 +0000
@@ -1409,21 +1409,22 @@
1409but since that is not a required field our application used the default1409but since that is not a required field our application used the default
1410value (Brazilian) specified in ``CookbookFactoryOperation`` for it.1410value (Brazilian) specified in ``CookbookFactoryOperation`` for it.
14111411
1412 >>> sorted(feijoada.items())1412 >>> from lazr.restful.testing.webservice import (
1413 [(u'author_link',1413 ... pprint_collection,
1414 u'http://api.cookbooks.dev/beta/authors/Fernando%20Yokota'),1414 ... pprint_entry,
1415 (u'comments_collection_link',1415 ... )
1416 u'http://api.cookbooks.dev/beta/cookbooks/Feijoada/comments'),1416
1417 (u'cover_link',1417 >>> pprint_entry(feijoada)
1418 u'http://api.cookbooks.dev/beta/cookbooks/Feijoada/cover'),1418 author_link: 'http://api.cookbooks.dev/beta/authors/Fernando%20Yokota'
1419 (u'cuisine', u'Brazilian'),1419 comments_collection_link:
1420 (u'http_etag', u'...'),1420 'http://api.cookbooks.dev/beta/cookbooks/Feijoada/comments'
1421 (u'name', u'Feijoada'),1421 cover_link: 'http://api.cookbooks.dev/beta/cookbooks/Feijoada/cover'
1422 (u'recipes_collection_link',1422 cuisine: 'Brazilian'
1423 u'http://api.cookbooks.dev/beta/cookbooks/Feijoada/recipes'),1423 name: 'Feijoada'
1424 (u'resource_type_link',1424 recipes_collection_link:
1425 u'http://api.cookbooks.dev/beta/#cookbook'),1425 'http://api.cookbooks.dev/beta/cookbooks/Feijoada/recipes'
1426 (u'self_link', u'http://api.cookbooks.dev/beta/cookbooks/Feijoada')]1426 resource_type_link: 'http://api.cookbooks.dev/beta/#cookbook'
1427 self_link: 'http://api.cookbooks.dev/beta/cookbooks/Feijoada'
14271428
1428You can also traverse from an entry to an item in a scoped collection:1429You can also traverse from an entry to an item in a scoped collection:
14291430
@@ -1431,16 +1432,14 @@
1431 ... quote('/beta/cookbooks/The Joy of Cooking/recipes/Roast chicken'))1432 ... quote('/beta/cookbooks/The Joy of Cooking/recipes/Roast chicken'))
1432 >>> chicken_recipe_resource = request.traverse(app)1433 >>> chicken_recipe_resource = request.traverse(app)
1433 >>> chicken_recipe = load_json(chicken_recipe_resource())1434 >>> chicken_recipe = load_json(chicken_recipe_resource())
1434 >>> sorted(chicken_recipe.items())1435 >>> pprint_entry(chicken_recipe)
1435 [(u'comments_collection_link',1436 comments_collection_link:
1436 u'http://api...Joy%20of%20Cooking/recipes/Roast%20chicken/comments'),1437 'http://api...Joy%20of%20Cooking/recipes/Roast%20chicken/comments'
1437 (u'cookbook_link',1438 cookbook_link:
1438 u'http://api.cookbooks.dev/beta/cookbooks/The%20Joy%20of%20Cooking'),1439 'http://api.cookbooks.dev/beta/cookbooks/The%20Joy%20of%20Cooking'
1439 (u'dish_link', u'http://api.cookbooks.dev/beta/dishes/Roast%20chicken'),1440 dish_link: 'http://api.cookbooks.dev/beta/dishes/Roast%20chicken'
1440 (u'http_etag', u'...'),1441 instructions: 'Draw, singe, stuff, and truss...'
1441 (u'instructions', u'Draw, singe, stuff, and truss...'),1442 self_link: 'http://api.../The%20Joy%20of%20Cooking/recipes/Roast%20chicken'
1442 (u'self_link',
1443 u'http://api.../The%20Joy%20of%20Cooking/recipes/Roast%20chicken')]
14441443
1445Another example traversing to a comment:1444Another example traversing to a comment:
14461445
@@ -1457,12 +1456,11 @@
1457 ... roast_chicken_comments_url + '/1')1456 ... roast_chicken_comments_url + '/1')
1458 >>> comment_one_resource = request.traverse(app)1457 >>> comment_one_resource = request.traverse(app)
1459 >>> comment_one = load_json(comment_one_resource())1458 >>> comment_one = load_json(comment_one_resource())
1460 >>> sorted(comment_one.items())1459 >>> pprint_entry(comment_one)
1461 [(u'http_etag', u'...'),1460 resource_type_link: 'http://api.cookbooks.dev/beta/#comment'
1462 (u'resource_type_link', u'http://api.cookbooks.dev/beta/#comment'),1461 self_link:
1463 (u'self_link',1462 'http://api...Joy%20of%20Cooking/recipes/Roast%20chicken/comments/1'
1464 u'http://api...Joy%20of%20Cooking/recipes/Roast%20chicken/comments/1'),1463 text: 'Clear and concise.'
1465 (u'text', u'Clear and concise.')]
14661464
1467An entry may expose a number of custom operations through GET. The1465An entry may expose a number of custom operations through GET. The
1468recipe entry exposes a custom GET operation called1466recipe entry exposes a custom GET operation called
@@ -1579,11 +1577,14 @@
1579 >>> recipes = DummyResultSet()1577 >>> recipes = DummyResultSet()
1580 >>> request, operation = make_dummy_operation_request(recipes)1578 >>> request, operation = make_dummy_operation_request(recipes)
1581 >>> response = operation()1579 >>> response = operation()
1582 >>> for key, value in sorted(simplejson.loads(response).items()):1580 >>> pprint_collection(simplejson.loads(response))
1583 ... print('%s: %s' % (key, value))
1584 entries: [{...}, {...}]
1585 start: ...1581 start: ...
1586 total_size: 21582 total_size: 2
1583 ---
1584 ...
1585 ---
1586 ...
1587 ---
15871588
1588When a named operation returns an object that has an ``ICollection``1589When a named operation returns an object that has an ``ICollection``
1589implementation, the result is similar: we return a JSON hash describing one1590implementation, the result is similar: we return a JSON hash describing one
@@ -1591,11 +1592,17 @@
15911592
1592 >>> request, operation = make_dummy_operation_request(DishSet())1593 >>> request, operation = make_dummy_operation_request(DishSet())
1593 >>> response = operation()1594 >>> response = operation()
1594 >>> for key, value in sorted(simplejson.loads(response).items()):1595 >>> pprint_collection(simplejson.loads(response))
1595 ... print('%s: %s' % (key, value))1596 resource_type_link: 'http://api.cookbooks.dev/beta/#dishes'
1596 entries: ...
1597 start: ...1597 start: ...
1598 total_size: ...1598 total_size: 3
1599 ---
1600 ...
1601 ---
1602 ...
1603 ---
1604 ...
1605 ---
15991606
1600If the return value can't be converted into JSON, you'll get an1607If the return value can't be converted into JSON, you'll get an
1601exception.1608exception.
16021609
=== modified file 'src/lazr/restful/example/base/tests/collection.txt'
--- src/lazr/restful/example/base/tests/collection.txt 2020-02-04 13:17:32 +0000
+++ src/lazr/restful/example/base/tests/collection.txt 2020-09-02 22:01:19 +0000
@@ -125,19 +125,19 @@
125 >>> url = quote("/cookbooks/The Joy of Cooking")125 >>> url = quote("/cookbooks/The Joy of Cooking")
126 >>> cookbook = webservice.get(url).jsonBody()126 >>> cookbook = webservice.get(url).jsonBody()
127 >>> pprint_entry(cookbook)127 >>> pprint_entry(cookbook)
128 confirmed: u'tag:launchpad.net:2008:redacted'128 confirmed: 'tag:launchpad.net:2008:redacted'
129 copyright_date: u'1995-01-01'129 copyright_date: '1995-01-01'
130 cover_link: u'http://.../cookbooks/The%20Joy%20of%20Cooking/cover'130 cover_link: 'http://.../cookbooks/The%20Joy%20of%20Cooking/cover'
131 cuisine: u'General'131 cuisine: 'General'
132 description: u''132 description: ''
133 last_printing: None133 last_printing: None
134 name: u'The Joy of Cooking'134 name: 'The Joy of Cooking'
135 price: 20135 price: 20
136 recipes_collection_link: u'http://.../cookbooks/The%20Joy%20of%20Cooking/recipes'136 recipes_collection_link: 'http://.../cookbooks/The%20Joy%20of%20Cooking/recipes'
137 resource_type_link: u'http://...#cookbook'137 resource_type_link: 'http://...#cookbook'
138 revision_number: 0138 revision_number: 0
139 self_link: u'http://.../cookbooks/The%20Joy%20of%20Cooking'139 self_link: 'http://.../cookbooks/The%20Joy%20of%20Cooking'
140 web_link: u'http://dummyurl/'140 web_link: 'http://dummyurl/'
141141
142A collection may be scoped to an element:142A collection may be scoped to an element:
143143
144144
=== modified file 'src/lazr/restful/example/multiversion/tests/operation.txt'
--- src/lazr/restful/example/multiversion/tests/operation.txt 2020-02-04 11:52:59 +0000
+++ src/lazr/restful/example/multiversion/tests/operation.txt 2020-09-02 22:01:19 +0000
@@ -12,7 +12,10 @@
12collections always send a 'total_size' containing the total size of a12collections always send a 'total_size' containing the total size of a
13collection.13collection.
1414
15 >>> from lazr.restful.testing.webservice import WebServiceCaller15 >>> from lazr.restful.testing.webservice import (
16 ... pprint_collection,
17 ... WebServiceCaller,
18 ... )
16 >>> webservice = WebServiceCaller(domain='multiversion.dev')19 >>> webservice = WebServiceCaller(domain='multiversion.dev')
1720
18In the example web service, named operations always send 'total_size'21In the example web service, named operations always send 'total_size'
@@ -52,12 +55,10 @@
52 >>> print(sorted(get_collection('3.0', 'by_value', size=100).keys()))55 >>> print(sorted(get_collection('3.0', 'by_value', size=100).keys()))
53 [u'entries', u'start', u'total_size']56 [u'entries', u'start', u'total_size']
5457
55 >>> for key, value in sorted(58 >>> pprint_collection(get_collection('3.0', 'by_value', 'no-such-value'))
56 ... get_collection('3.0', 'by_value', 'no-such-value').items()):
57 ... print('%s: %s' % (key, value))
58 entries: []
59 start: 059 start: 0
60 total_size: 060 total_size: 0
61 ---
6162
62Mutators as named operations63Mutators as named operations
63----------------------------64----------------------------
6465
=== modified file 'src/lazr/restful/testing/webservice.py'
--- src/lazr/restful/testing/webservice.py 2020-08-17 11:46:51 +0000
+++ src/lazr/restful/testing/webservice.py 2020-09-02 22:01:19 +0000
@@ -13,6 +13,7 @@
13 'FakeResponse',13 'FakeResponse',
14 'IGenericEntry',14 'IGenericEntry',
15 'IGenericCollection',15 'IGenericCollection',
16 'pprint_collection',
16 'pprint_entry',17 'pprint_entry',
17 'simple_renderer',18 'simple_renderer',
18 'WebServiceTestCase',19 'WebServiceTestCase',
@@ -174,6 +175,23 @@
174 return default175 return default
175176
176177
178def pformat_value(value):
179 """Pretty-format a single value.
180
181 This is similar to `repr()`, but for doctest compatibility we format
182 text and bytes in a way that looks the same on both Python 2 and 3.
183 JSON strings are always Unicode, never bytes, so this doesn't introduce
184 ambiguity.
185 """
186 if isinstance(value, six.text_type):
187 if "'" in value and '"' not in value:
188 return '"%s"' % value
189 else:
190 return "'%s'" % value.replace("'", "\\'")
191 else:
192 return repr(value)
193
194
177def pprint_entry(json_body):195def pprint_entry(json_body):
178 """Pretty-print a webservice entry JSON representation.196 """Pretty-print a webservice entry JSON representation.
179197
@@ -182,7 +200,7 @@
182 """200 """
183 for key, value in sorted(json_body.items()):201 for key, value in sorted(json_body.items()):
184 if key != 'http_etag':202 if key != 'http_etag':
185 print('%s: %r' % (key, value))203 print('%s: %s' % (key, pformat_value(value)))
186204
187205
188def pprint_collection(json_body):206def pprint_collection(json_body):
@@ -191,7 +209,7 @@
191 if key == 'total_size_link':209 if key == 'total_size_link':
192 continue210 continue
193 if key != 'entries':211 if key != 'entries':
194 print('%s: %r' % (key, value))212 print('%s: %s' % (key, pformat_value(value)))
195 print('---')213 print('---')
196 for entry in json_body['entries']:214 for entry in json_body['entries']:
197 pprint_entry(entry)215 pprint_entry(entry)

Subscribers

People subscribed via source and target branches