123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- ***********
- Collections
- ***********
- lazr.restful makes collections of data available through Pythonic
- mechanisms like slices.
- >>> from lazr.restfulclient.tests.example import CookbookWebServiceClient
- >>> service = CookbookWebServiceClient()
- You can iterate through all the items in a collection.
- >>> names = sorted([recipe.dish.name for recipe in service.recipes])
- >>> len(names)
- 5
- >>> names
- [u'Baked beans', ..., u'Roast chicken']
- But it's almost always better to slice them.
- >>> sorted([recipe.dish.name for recipe in service.recipes[:2]])
- [u'Roast chicken', u'Roast chicken']
- You can get a slice of any collection, so long as you provide start
- and end points keyed to the beginning of the list. You can't key a
- slice to the end of the list because it might be expensive to
- calculate how big the list is.
- This set-up code creates a regular Python list of all recipes on the
- site, for comparison with a lazr.restful Collection object
- representing the same list.
- >>> all_recipes = [recipe for recipe in service.recipes]
- >>> recipes = service.recipes
- Calling len() on the Collection object makes sure that the first page
- of representations is cached, which forces this test to test an
- optimization.
- >>> ignored = len(recipes)
- These tests demonstrate that slicing the collection resource gives the
- same results as collecting all the entries in the collection, and
- slicing an ordinary list.
- >>> def slices_match(slice):
- ... """Slice two lists of recipes, then make sure they're the same."""
- ... list1 = recipes[slice]
- ... list2 = all_recipes[slice]
- ... if len(list1) != len(list2):
- ... raise ("Lists are different sizes: %d vs. %d" %
- ... (len(list1), len(list2)))
- ... for index in range(0, len(list1)):
- ... if list1[index].id != list2[index].id:
- ... raise ("%s doesn't match %s in position %d" %
- ... (list1[index].id, list2[index].id, index))
- ... return True
- >>> slices_match(slice(3))
- True
- >>> slices_match(slice(50))
- True
- >>> slices_match(slice(1,2))
- True
- >>> slices_match(slice(2,21))
- True
- >>> slices_match(slice(2,21,3))
- True
- >>> slices_match(slice(0, 200))
- True
- >>> slices_match(slice(30, 200))
- True
- >>> slices_match(slice(60, 100))
- True
- >>> recipes[5:]
- Traceback (most recent call last):
- ...
- ValueError: Collection slices must have a definite, nonnegative end point.
- >>> recipes[10:-1]
- Traceback (most recent call last):
- ...
- ValueError: Collection slices must have a definite, nonnegative end point.
- >>> recipes[-1:]
- Traceback (most recent call last):
- ...
- ValueError: Collection slices must have a nonnegative start point.
- >>> recipes[:]
- Traceback (most recent call last):
- ...
- ValueError: Collection slices must have a definite, nonnegative end point.
- You can slice a collection that's the return value of a named
- operation.
- >>> e_recipes = service.cookbooks.find_recipes(search='e')
- >>> len(e_recipes[1:3])
- 2
- You can also access individual items in this collection by index.
- >>> print e_recipes[1].dish.name
- Foies de voilaille en aspic
- >>> e_recipes[1000]
- Traceback (most recent call last):
- ...
- IndexError: list index out of range
- When are representations fetched?
- =================================
- To avoid unnecessary HTTP requests, a representation of a collection
- is fetched at the last possible moment. Let's see what that means.
- >>> import httplib2
- >>> httplib2.debuglevel = 1
- >>> service = CookbookWebServiceClient()
- send: ...
- ...
- Just accessing a top-level collection doesn't trigger an HTTP request.
- >>> recipes = service.recipes
- >>> dishes = service.dishes
- >>> cookbooks = service.cookbooks
- Getting the length of the collection, or any entry from the
- collection, triggers an HTTP request.
- >>> len(recipes)
- send: 'GET /1.0/recipes...
- ...
- >>> dish = dishes[1]
- send: 'GET /1.0/dishes...
- ...
- Invoking a named operation will also trigger an HTTP request.
- >>> cookbooks.find_recipes(search="foo")
- send: ...
- ...
- Scoped collections work the same way: just getting a reference to the
- collection doesn't trigger an HTTP request.
- >>> recipes = dish.recipes
- But getting any information about the collection triggers an HTTP request.
- >>> len(recipes)
- send: 'GET /1.0/dishes/.../recipes ...
- ...
- Cleanup.
- >>> httplib2.debuglevel = None
|