{ "info": { "author": "Josh Kuhn", "author_email": "deontologician@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 6 - Mature", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.0", "Programming Language :: Python :: 3.1", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries", "Topic :: Utilities" ], "description": "REST Navigator\r\n==============\r\n\r\n|Build Status| |Coverage Status| |Pypi Status| |Documentation Status|\r\n\r\nREST Navigator is a python library for interacting with hypermedia apis\r\n(`REST level\r\n3 `__).\r\nRight now, it only supports\r\n`HAL+JSON `__ but it\r\nshould be general enough to extend to other formats eventually. Its\r\nfirst goal is to make interacting with HAL hypermedia apis as painless\r\nas possible, while discouraging REST anti-patterns.\r\n\r\nTo install it, simply use pip:\r\n\r\n.. code:: bash\r\n\r\n $ pip install restnavigator\r\n\r\nContents\r\n--------\r\n\r\n- `How to use it <#how-to-use-it>`__\r\n\r\n - `Links <#links>`__\r\n - `GET requests <#get-requests>`__\r\n - `Link relation docs <#link-relation-docs>`__\r\n - `POST requests <#post-requests>`__\r\n - `Errors <#errors>`__\r\n - `Templated links <#templated-links>`__\r\n - `Authentication <#authentication>`__\r\n\r\n- `Additional Topics <#additional-topics>`__\r\n\r\n - `Identity Map <#identity-map>`__\r\n - `Iterating over a Navigator <#iterating-over-a-navigator>`__\r\n - `Headers (Request vs. Response) <#headers-request-vs-response>`__\r\n - `Bracket mini-language <#bracket-minilanguage>`__\r\n - `Finding the right link <#finding-the-right-link>`__\r\n - `Default curie <#default-curie>`__\r\n - `Specifying an api name <#specifying-an-api-name>`__\r\n - `Embedded documents <#embedded-documents>`__\r\n\r\n- `Development <#development>`__\r\n\r\n - `Testing <#testing>`__\r\n - `Planned for the future <#planned-for-the-future>`__\r\n\r\n.. raw:: html\r\n\r\n \r\n\r\nHow to use it\r\n-------------\r\n\r\nTo begin interacting with a HAL api, you've got to create a HALNavigator\r\nthat points to the api root. Ideally, in a hypermedia API, the root URL\r\nis the only URL that needs to be hardcoded in your application. All\r\nother URLs are obtained from the api responses themselves (think of your\r\napi client as 'clicking on links', rather than having the urls\r\nhardcoded).\r\n\r\nAs an example, we'll connect to the haltalk api.\r\n\r\n.. code:: python\r\n\r\n >>> from restnavigator import Navigator\r\n >>> N = Navigator.hal('http://haltalk.herokuapp.com/', default_curie=\"ht\")\r\n >>> N\r\n HALNavigator(Haltalk)\r\n\r\nLinks\r\n~~~~~\r\n\r\nUsually, with the index (normally at the api root), you're most\r\ninterested in the links. Let's look at those:\r\n\r\n.. code:: python\r\n\r\n >>> N.links()\r\n {u'ht:users': HALNavigator(Haltalk.users),\r\n u'ht:signup': HALNavigator(Haltalk.signup),\r\n u'ht:me': TemplatedThunk(Haltalk.users.{name}),\r\n u'ht:latest-posts': HALNavigator(Haltalk.posts.latest)}\r\n\r\n(This may take a moment because asking for the links causes the\r\nHALNavigator to actually request the resource from the server).\r\n\r\nHere we can see that the links are organized by their relation type (the\r\nkey), and each key corresponds to a new HALNavigator that represents\r\nsome other resource. Relation types are extremely important in restful\r\napis: we need them to be able to determine what a link means in relation\r\nto the current resource, in a way that is automatable.\r\n\r\nGET requests\r\n~~~~~~~~~~~~\r\n\r\nIn addition, the root has some state associated with it which you can\r\nget in two different ways:\r\n\r\n.. code:: python\r\n\r\n >>> N() # cached state of resource (obtained when we looked at N.links)\r\n {u'hint_1': u'You need an account to post stuff..',\r\n u'hint_2': u'Create one by POSTing via the ht:signup link..',\r\n u'hint_3': u'Click the orange buttons on the right to make POST requests..',\r\n u'hint_4': u'Click the green button to follow a link with a GET request..',\r\n u'hint_5': u'Click the book icon to read docs for the link relation.',\r\n u'welcome': u'Welcome to a haltalk server.'}\r\n >>> N.fetch() # will refetch the resource from the server\r\n {u'hint_1': u'You need an account to post stuff..',\r\n u'hint_2': u'Create one by POSTing via the ht:signup link..',\r\n u'hint_3': u'Click the orange buttons on the right to make POST requests..',\r\n u'hint_4': u'Click the green button to follow a link with a GET request..',\r\n u'hint_5': u'Click the book icon to read docs for the link relation.',\r\n u'welcome': u'Welcome to a haltalk server.'}\r\n\r\nCalling a HALNavigator will execute a GET request against the resource\r\nand returns its value (which it will cache).\r\n\r\nLink relation docs\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nLet's register a hal talk account. Unfortunately, we don't really know\r\nhow to do that, so let's look at the documentation. The ``ht:signup``\r\nlink looks promising, let's check that:\r\n\r\n.. code:: python\r\n\r\n >>> N.docsfor('ht:signup')\r\n\r\nA browser will open to http://haltalk.herokuapp.com/rels/signup.\r\n\r\nWhat? Popping up a browser from a library call? Yes, that's how\r\nrest\\_navigator rolls. The way we see it: docs are for humans, and while\r\ncustom rel-types are URIs, they shouldn't automatically be dereferenced\r\nby a program that interacts with the api. So popping up a browser serves\r\ntwo purposes:\r\n\r\n1. It allows easy access to the documentation at the time when you most\r\n need it: when you're mucking about in the command line trying to\r\n figure out how to interact with the api.\r\n2. It reminds you not to try to automatically dereference the rel\r\n documentation and parse it in your application.\r\n\r\nIf you need a more robust way to browse the api and the documentation,\r\n`HAL Browser `__ is probably\r\nyour best bet.\r\n\r\nPOST requests\r\n~~~~~~~~~~~~~\r\n\r\nThe docs for ``ht:signup`` explain the format of the POST request to\r\nsign up. So let's actually sign up. Since we've set ``\"ht\"`` as our\r\ndefault curie, we can skip typing the curie for convenience. (Note:\r\nhaltalk is a toy api for example purposes, don't ever send plaintext\r\npasswords over an unencrypted connection in a real app!):\r\n\r\n.. code:: python\r\n\r\n >>> fred23 = N['signup'].create(\r\n ... {'username': 'fred23',\r\n ... 'password': 'hunter2',\r\n ... 'real_name': 'Fred 23'}\r\n ... )\r\n >>> fred23\r\n HALNavigator(Haltalk.users.fred23)\r\n\r\nErrors\r\n~~~~~~\r\n\r\nIf the user name had already been in use, a 400 would have been returned\r\nfrom the haltalk api. rest\\_navigator follows the Zen of Python\r\nguideline \"Errors should never pass silently\". An exception would have\r\nbeen raised on a 400 or 500 status code. You can squelch this exception\r\nand just have the post call return a ``HALNavigator`` with a 400/500\r\nstatus code if you want:\r\n\r\n.. code:: python\r\n\r\n >>> dup_signup = N['ht:signup'].create({\r\n ... 'username': 'fred23',\r\n ... 'password': 'hunter2',\r\n ... 'real_name': 'Fred Wilson'\r\n ... }, raise_exc=False)\r\n >>> dup_signup\r\n OrphanHALNavigator(Haltalk.signup) # 400!\r\n >>> dup_signup.status\r\n (400, 'Bad Request')\r\n >>> dup_signup.state\r\n {u\"errors\": {u\"username\": [u\"is already taken\"]}}\r\n\r\nTemplated links\r\n~~~~~~~~~~~~~~~\r\n\r\nNow that we've signed up, lets take a look at our profile. The link for\r\na user's profile is a templated link, which restnavigator represents as\r\na ``PartialNavigator``. Similar to python's\r\n`functools.partial `__,\r\na ``PartialNavigator`` is an object that needs a few more arguments to\r\ngive you a full navigator back. Despite its name, it can't talk to the\r\nnetwork by itself. Its job is to to generate new navigators for you. You\r\ncan see what variables it has by looking at its ``.variables`` attribute\r\n(its ``__repr__`` hints at this as well):\r\n\r\n.. code:: python\r\n\r\n >>> N.links().keys()\r\n ['ht:latest-posts', 'ht:me', 'ht:users', 'ht:signup']\r\n >>> N['ht:me']\r\n PartialNavigator(Haltalk.users.{name})\r\n >>> N['ht:me'].variables\r\n set(['name'])\r\n\r\nThe documentation for the ``ht:me`` rel type should tell us how the name\r\nparameter is supposed to work, but in this case it's fairly obvious\r\n(plug in the username). Two provide the template parameters, just call\r\nit with keyword args:\r\n\r\n.. code:: python\r\n\r\n >>> partial_me = N['ht:me']\r\n >>> partial_me.template_uri\r\n 'http://haltalk.herokuapp.com/users/{name}'\r\n >>> Fred = partial_me(name='fred23')\r\n >>> Fred\r\n HALNavigator('haltalk.users.fred23')\r\n\r\nNow that we have a real navigator, we can fetch the resource:\r\n\r\n.. code:: python\r\n\r\n >>> Fred()\r\n {u'bio': None, u'real_name': u'Fred Wilson', u'username': u'fred23'}\r\n\r\nAuthentication\r\n~~~~~~~~~~~~~~\r\n\r\nIn order to post something to haltalk, we need to authenticate with our\r\nnewly created account. HALNavigator allows any `authentication method\r\nthat requests\r\nsupports `__\r\n(so OAuth etc). For basic auth (which haltalk uses), we can just pass a\r\ntuple.\r\n\r\n.. code:: python\r\n\r\n >>> N.authenticate(('fred23', 'hunter2')) # All subsequent calls are authenticated\r\n\r\nThis doesn't send anything to the server, it just sets the\r\nauthentication details that we'll use on the next request. Other\r\nauthentication methods may contact the server immediately.\r\n\r\nNow we can put it all together to create a new post:\r\n\r\n.. code:: python\r\n\r\n >>> N_post = N['me'](name='fred23')['posts'].create({'content': 'My first post'})\r\n >>> N_post\r\n HALNavigator(Haltalk.posts.523670eff0e6370002000001)\r\n >>> N_post()\r\n {'content': 'My first post', 'created_at': '2015-06-13T19:38:59+00:00'}\r\n\r\nIt is also possible to specify a custom requests Session object when creating \r\na new navigator.\r\n\r\nFor example, if you want to talk to a OAuth2 protected api, simply pass \r\nan OAuth2 Session object that will be used for all requests \r\ndone by HALNavigator:\r\n\r\n.. code:: python\r\n\r\n >>> from requests_oauthlib import OAuth2Session\r\n >>> oauth2_session = OAuth2Session(r'client_id', token='token')\r\n >>> N = Navigator.hal('https://api.example.com', session=oauth2_session)\r\n\r\nAdditional Topics\r\n-----------------\r\n\r\nIdentity Map\r\n~~~~~~~~~~~~\r\n\r\nYou don't need to worry about inadvertently having two different\r\nnavigators pointing to the same resource. rest\\_navigator will reuse the\r\nexisting navigator instead of creating a new one\r\n\r\nIterating over a Navigator\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIf a resource has a link with the rel \"next\", the navigator for that\r\nresource can be used as a python iterator. It will automatically raise a\r\nStopIteration exception if a resource in the chain does not have a next\r\nlink. This makes moving through paged resources really simple and\r\npythonic:\r\n\r\n.. code:: python\r\n\r\n post_navigator = fred['ht:posts']\r\n for post in post_navigator:\r\n # the first post will be post_navigator itself\r\n print(post.state)\r\n\r\nHeaders (Request vs. Response)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nHTTP response headers are available in ``N.response.headers``\r\n\r\nHeaders that will be sent on each request can be obtained through the\r\nsession:\r\n\r\n.. code:: python\r\n\r\n >>> N.session.headers\r\n # Cookies, etc\r\n\r\nBracket mini-language\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe bracket (``[]``) operator on Navigators has a lot of power. As we\r\nsaw earlier, the main use is to get a new Navigator from a link\r\nrelation:\r\n\r\n.. code:: python\r\n\r\n >>> N2 = N['curie:link_rel']\r\n\r\nBut, it can also go more than one link deep, which is equivalent to\r\nusing multiple brackets in a row:\r\n\r\n.. code:: python\r\n\r\n >>> N3 = N['curie:first_link', 'curie:second_link']\r\n # equivalent to:\r\n N3 = N['curie:first_link']['curie:second_link']\r\n\r\nAnd of course, if you set a default curie, you can omit it:\r\n\r\n.. code:: python\r\n\r\n >>> N3 = N['first_link', 'second_link']\r\n\r\nInternally, this is completely equivalent to repeatedly applying the\r\nbracket operator, so you can even use it to jump over intermediate\r\nobjects that aren't Navigators themselves:\r\n\r\n.. code:: python\r\n\r\n >>> N['some-link', 3, 'another-link']\r\n\r\nThis would use the ``some-link`` link relation, select the third link\r\nfrom the list, and then follow ``another-link`` from that resource.\r\n\r\nFinding the right link\r\n~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nNormally, you can chain together brackets to jump from one resource to\r\nanother in one go:\r\n\r\n.. code:: python\r\n\r\n >>> N['ht:widget']['ht:gadget']\r\n\r\nThis will return a Navigator for the ``ht:widget`` link relation and\r\nthen immediately fetch the resource and return a Navigator for the\r\n``ht:gadget`` link relation. This works great if you have only one link\r\nper relation, but HAL allows multiple links per relation. Say for\r\ninstance we have some links like the following:\r\n\r\n.. code:: javascript{\r\n\r\n \"ht:some_rel: [\r\n {\r\n \"href\": \"/api/widget/1\",\r\n \"name\": \"widget1\",\r\n \"profile\": \"widget\"\r\n },\r\n {\r\n \"href\": \"/api/widget/2\",\r\n \"name\": \"widget2\",\r\n \"profile\": \"widget\"\r\n },\r\n {\r\n \"href\": \"/api/gadget/1\",\r\n \"name\": \"gadget1\",\r\n \"profile\": \"gadget\"\r\n }\r\n ]\r\n\r\nWhen we go to get the ``ht:some_rel``, we'll get multiple results:\r\n\r\n.. code:: python\r\n\r\n >>> N['ht:some_rel']\r\n [HALNavigator(api.widget[1]),\r\n HALNavigator(api.widget[2]),\r\n HALNavigator(api.gadget[1])]\r\n\r\nHow do we know which one is the one we want? The `HAL\r\nspec `__\r\nsays links with the same rel can be disambiguated by the ``name`` link\r\nproperty:\r\n\r\n.. code:: python\r\n\r\n >>> N.links['ht:some_rel'].get_by('name', 'gadget1')\r\n HALNavigator(api.gadget[1])\r\n >>> N.links['ht:some_rel'].named('gadget1') # same as previous\r\n HALNavigator(api.gadget[1])\r\n\r\nWe could also use other properties to slice and dice the list:\r\n\r\n.. code:: python\r\n\r\n >>> N.links['ht:some_rel'].get_by('profile', 'gadget')\r\n HALNavigator(api.gadget[1])\r\n >>> N.links['ht:some_rel'].getall_by('profile', 'widget')\r\n [HALNavigator(api.widget[1]), HALNavigator(api.widget[2])]\r\n\r\nThis works for any property on links, not just the standard HAL\r\nproperties.\r\n\r\nDefault curie\r\n~~~~~~~~~~~~~\r\n\r\nYou may specify a default curie when creating your Navigator:\r\n\r\n.. code:: python\r\n\r\n >>> N = HALNavigator('http://haltalk.herokuapp.com', curie='ht')\r\n\r\nNow, when you follow links, you may leave off the default curie if you\r\nwant:\r\n\r\n.. code:: python\r\n\r\n >>> N.links\r\n {'ht:users': [HALNavigator(Haltalk.users)],\r\n 'ht:signup': [HALNavigator(Haltalk.signup)],\r\n 'ht:me': [HALNavigator(Haltalk.users.{name})],\r\n 'ht:latest-posts': [HALNavigator(Haltalk.posts.latest)]\r\n }\r\n >>> N['ht:users']\r\n HALNavigator(Haltalk.users)\r\n >>> N['users']\r\n HALNavigator(Haltalk.users)\r\n\r\nThe only exception is where the key being supplied is a `IANA registered\r\nlink\r\nrelation `__,\r\nand there is a conflict (hint: this should be quite rare):\r\n\r\n.. code:: python\r\n\r\n >>> N.links\r\n {'ht:next': HALNavigator(Haltalk.unregistered),\r\n 'next': HALNavigator(Haltalk.registered)}\r\n >>> N['next']\r\n HALNavigator(Haltalk.registered)\r\n\r\nSpecifying an api name\r\n~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nSometimes the automatic api naming guesses poorly. If you'd like to\r\noverride the default name, you can specify it when creating the\r\nnavigator:\r\n\r\n.. code:: python\r\n\r\n >>> N = Navigator.hal('http://api.example.com', apiname='MySpecialAPI')\r\n HALNavigator(MySpecialAPI)\r\n\r\nEmbedded documents\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nIn rest\\_navigator, embedded documents are treated transparently. This\r\nmeans that in many cases you don't need to worry about whether a\r\ndocument is embedded or whether it's just linked.\r\n\r\nAs an example, assume we have a resource like the following:\r\n\r\n.. code:: json\r\n\r\n {\r\n \"_links\": {\r\n ...\r\n \"xx:yams\": {\r\n \"href\": \"/yams\"\r\n }\r\n ...\r\n },\r\n \"_embedded\": {\r\n \"xx:pickles\": {\r\n \"_links\": {\r\n \"self\": {\"href\": \"/pickles\"}\r\n },\r\n \"state\": \"A pickle\"\r\n }\r\n }\r\n ...\r\n }\r\n\r\nFrom here, you would access both the ``yams`` and the ``pickles``\r\nresource with normal bracket syntax:\r\n\r\n.. code:: python\r\n\r\n >>> Yams = N['xx:yams']\r\n >>> Pickles = N['xx:pickles']\r\n\r\nThe only difference here is that ``Yams`` hasn't been fetched yet, while\r\n``Pickles`` is considered \"resolved\" already because we got it as an\r\nembedded document.\r\n\r\n::\r\n\r\n >>> Yams.resolved\r\n False\r\n >>> Yams.state # None\r\n >>> Pickles.resolved\r\n True\r\n >>> Pickles.state\r\n {'state': 'A pickle'}\r\n\r\nIf an embedded document has a self link, you can treat it just like you\r\nwould any other resource. So if you want to refresh the resource, it's\r\nas easy as:\r\n\r\n.. code:: python\r\n\r\n >>> Pickles.fetch()\r\n\r\nThis will fetch the current state of the resource from the uri in its\r\nself link, even if you've never directly requested that uri before. If\r\nan embedded resource doesn't have a self link, it will be an\r\n``OrphanNavigator`` with the parent set to the resource it was embedded\r\nin.\r\n\r\nOf course, if you need to directly distinguish between linked resources\r\nand embedded resources, there is an out:\r\n\r\n.. code:: python\r\n\r\n >>> N.embedded()\r\n {'xx:pickles': HALNavigator(api.pickles)\r\n >>> N.links()\r\n {'xx:yams': HALNavigator(api.yams)\r\n\r\nHowever, when using the ``in`` operator, it will look in both for a key\r\nyou're interested in:\r\n\r\n.. code:: python\r\n\r\n >>> 'yams' in N # default curie is taken into account!\r\n True\r\n >>> 'xx:yams in N\r\n True\r\n >>> 'xx:pickles' in N\r\n True\r\n\r\nDevelopment\r\n-----------\r\n\r\nTesting\r\n~~~~~~~\r\n\r\nTo run tests, first install the `pytest\r\nframework `__:\r\n\r\n::\r\n\r\n $ pip install -U pytest\r\n\r\nTo run tests, execute following from the root of the source directory:\r\n\r\n::\r\n\r\n $ py.test\r\n\r\nPlanned for the future\r\n~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n- Ability to add hooks for different types, rels and profiles. If a\r\n link has one of these properties, it will call your hook when doing a\r\n server call.\r\n- Since HAL doesn't specify what content type POSTs, PUTs, and PATCHes\r\n need to have, you can specify the hooks based on what the server will\r\n accept. This can trigger off either the rel type of the link, or rest\r\n navigator can do content negotiation over HTTP with the server\r\n directly to see what content types that resource will accept.\r\n\r\nContributors\r\n~~~~~~~~~~~~\r\n\r\nThanks very much to rest navigator's contributors:\r\n\r\n- `dudycooly `__\r\n- `bubenkoff `__\r\n- `bbsgfalconer `__\r\n\r\n.. |Build Status| image:: https://img.shields.io/travis/deontologician/restnavigator/next.svg\r\n :target: https://travis-ci.org/deontologician/restnavigator\r\n.. |Coverage Status| image:: https://img.shields.io/coveralls/deontologician/rest_navigator/next.svg\r\n :target: https://coveralls.io/r/deontologician/rest_navigator?branch=next\r\n.. |Documentation Status| image:: https://readthedocs.org/projects/rest-navigator/badge/?version=latest\r\n :target: https://readthedocs.org/projects/rest-navigator/?badge=latest\r\n :alt: Documentation Status\r\n.. |Pypi Status| image:: https://pypip.in/v/restnavigator/badge.png\r\n :target: https://crate.io/packages/restnavigator/\r\n\r\n\r\nChangelog\r\n=========\r\n\r\nUnreleased\r\n----------\r\n\r\n- TBD\r\n\r\n1.0\r\n---\r\n\r\n- Embedded support\r\n- Ability to specify default curies\r\n- Resources with no URL are now represented by a special Navigator type called OrphanNavigators\r\n- IP addresses can be used in the url (@dudycooly)\r\n- All tests pass in python 2.6 -> 3.4 (@bubenkoff), and travis now runs tox to ensure they stay that way\r\n- Support the DELETE, and PATCH methods\r\n- posts allow an empty body (@bbsgfalconer)\r\n- Much improved content negotiation (@bbsgfalconer)\r\n- There was also a major refactoring that changed how Navigators are created and internally cleaned up a\r\n lot of really messy code.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/deontologician/rest_navigator", "keywords": "REST,HAL,json,http", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "restnavigator", "package_url": "https://pypi.org/project/restnavigator/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/restnavigator/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/deontologician/rest_navigator" }, "release_url": "https://pypi.org/project/restnavigator/1.0.1/", "requires_dist": null, "requires_python": null, "summary": "A python library for interacting with HAL+JSON APIs", "version": "1.0.1" }, "last_serial": 1535797, "releases": { "0.2.0": [ { "comment_text": "", "digests": { "md5": "44267e37502ded162dcbf313c2df57a4", "sha256": "c5f537927c5ed380c284470834a7c57ac9d1844a26040cfc669b2402f0ea93df" }, "downloads": -1, "filename": "restnavigator-0.2.0.tar.gz", "has_sig": false, "md5_digest": "44267e37502ded162dcbf313c2df57a4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9831, "upload_time": "2014-01-19T23:46:55", "url": "https://files.pythonhosted.org/packages/9b/c6/af03ffb5a5b77a5033fdd96d3dd9a85b87ab05fbcccb2b8b782b6aebf291/restnavigator-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "82faed71e3916d528cb67b77d515b215", "sha256": "f4879f9c3c5ebd7af04d6d5930f5526eada72538457e3432883c25be65c254dd" }, "downloads": -1, "filename": "restnavigator-0.2.1-py2-none-any.whl", "has_sig": false, "md5_digest": "82faed71e3916d528cb67b77d515b215", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 11978, "upload_time": "2014-08-27T00:58:51", "url": "https://files.pythonhosted.org/packages/8b/85/869366b799b76287431657c6942d8e0261801bdea63ca76edfc8c9bd8153/restnavigator-0.2.1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d07b2ca8f5c070d77f601fadb83a5fbf", "sha256": "6e2af5e1324d62ded32d25f956f50f8ca9d5d75eab3e0023a60f063512892273" }, "downloads": -1, "filename": "restnavigator-0.2.1.tar.gz", "has_sig": false, "md5_digest": "d07b2ca8f5c070d77f601fadb83a5fbf", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9966, "upload_time": "2014-08-27T00:58:49", "url": "https://files.pythonhosted.org/packages/ac/cc/3b5aedb5a9671b006f56e923733b5922f26119f1f79f5c52342210f85d64/restnavigator-0.2.1.tar.gz" } ], "1.0": [ { "comment_text": "", "digests": { "md5": "8bf3452e2eac89d1f8136056ca094d5c", "sha256": "97f09e6f30c7cbd9607a763c40a83389c4b1784b21819a1f9b3aad6cb79d14ee" }, "downloads": -1, "filename": "restnavigator-1.0-py2-none-any.whl", "has_sig": false, "md5_digest": "8bf3452e2eac89d1f8136056ca094d5c", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 18569, "upload_time": "2015-03-09T06:13:11", "url": "https://files.pythonhosted.org/packages/c8/01/13b2db45042327086302f25dd65033bdd6109b5ef510650ed47905c2b96c/restnavigator-1.0-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "da8c8e79006233659dba50aa865067b2", "sha256": "e25c282886beb670bc593111fc249d623911671862f00c91fb78f0052f60ba73" }, "downloads": -1, "filename": "restnavigator-1.0.tar.gz", "has_sig": false, "md5_digest": "da8c8e79006233659dba50aa865067b2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16444, "upload_time": "2015-03-09T06:12:18", "url": "https://files.pythonhosted.org/packages/2f/d0/41515a001d21b9a17b3685c5acdea5a39c7ebdaee8b9fcb786fb92d04272/restnavigator-1.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "f9adf8117d65322b199fd7131dcbfb5d", "sha256": "b18e06c5b8b8acc604ccd2acb0282c2543c17b23c3b7bc472765ec484e93d083" }, "downloads": -1, "filename": "restnavigator-1.0.1-py2-none-any.whl", "has_sig": false, "md5_digest": "f9adf8117d65322b199fd7131dcbfb5d", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 32426, "upload_time": "2015-05-05T16:01:54", "url": "https://files.pythonhosted.org/packages/21/d8/a9f8c30e0e9a9dc371dccc30703151371b193393335470faad85c3f1b095/restnavigator-1.0.1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f82320b852f68bd51e68f67f79081caa", "sha256": "9be39bb32ee58db5a8f8c5983dfcda95f4578eeb88a012a32b82708dc74fb4ca" }, "downloads": -1, "filename": "restnavigator-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "f82320b852f68bd51e68f67f79081caa", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 32423, "upload_time": "2015-05-05T16:05:35", "url": "https://files.pythonhosted.org/packages/5b/f3/4ff43f25ad13ea583e6f5f1f20fb18f5a954cffbd56446e9eedafa814fdd/restnavigator-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "845796126a0aac9dccc800e6dee6283f", "sha256": "b7dde0bd0cf81bb0e0b05a1529c7093a07700cbced0ecacac8c2e9b13c4bcdfa" }, "downloads": -1, "filename": "restnavigator-1.0.1.tar.gz", "has_sig": false, "md5_digest": "845796126a0aac9dccc800e6dee6283f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36551, "upload_time": "2015-05-05T16:01:43", "url": "https://files.pythonhosted.org/packages/30/60/3019c4fc93734e753096fcc18f8dfcd0aef2177ad518c76533af294e4cad/restnavigator-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "f9adf8117d65322b199fd7131dcbfb5d", "sha256": "b18e06c5b8b8acc604ccd2acb0282c2543c17b23c3b7bc472765ec484e93d083" }, "downloads": -1, "filename": "restnavigator-1.0.1-py2-none-any.whl", "has_sig": false, "md5_digest": "f9adf8117d65322b199fd7131dcbfb5d", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 32426, "upload_time": "2015-05-05T16:01:54", "url": "https://files.pythonhosted.org/packages/21/d8/a9f8c30e0e9a9dc371dccc30703151371b193393335470faad85c3f1b095/restnavigator-1.0.1-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f82320b852f68bd51e68f67f79081caa", "sha256": "9be39bb32ee58db5a8f8c5983dfcda95f4578eeb88a012a32b82708dc74fb4ca" }, "downloads": -1, "filename": "restnavigator-1.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "f82320b852f68bd51e68f67f79081caa", "packagetype": "bdist_wheel", "python_version": "3.4", "requires_python": null, "size": 32423, "upload_time": "2015-05-05T16:05:35", "url": "https://files.pythonhosted.org/packages/5b/f3/4ff43f25ad13ea583e6f5f1f20fb18f5a954cffbd56446e9eedafa814fdd/restnavigator-1.0.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "845796126a0aac9dccc800e6dee6283f", "sha256": "b7dde0bd0cf81bb0e0b05a1529c7093a07700cbced0ecacac8c2e9b13c4bcdfa" }, "downloads": -1, "filename": "restnavigator-1.0.1.tar.gz", "has_sig": false, "md5_digest": "845796126a0aac9dccc800e6dee6283f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36551, "upload_time": "2015-05-05T16:01:43", "url": "https://files.pythonhosted.org/packages/30/60/3019c4fc93734e753096fcc18f8dfcd0aef2177ad518c76533af294e4cad/restnavigator-1.0.1.tar.gz" } ] }