Skip to content

Commit 4308337

Browse files
committed
Merge branch 'collections'
2 parents 2f24a46 + d82a0e5 commit 4308337

20 files changed

+594
-836
lines changed

MANIFEST.in

-2
This file was deleted.

Makefile

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
qa: flake8 test
2+
3+
.PHONY: flake8
4+
flake8:
5+
docker-compose run --rm dev flake8 --config=flake8.cfg --statistics turbasen
6+
17
.PHONY: test
28
test:
39
docker-compose run --rm dev python -m unittest
@@ -13,4 +19,5 @@ publish:
1319
.PHONY: clean
1420
clean:
1521
python setup.py clean -a
16-
find . -name \*.pyc | xargs rm
22+
find . -name \*.pyc -delete
23+
rm -rf .cache/ dist/ *.egg-info/

docs/index.rst

+103-73
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,19 @@
66
turbasen.py
77
=============================
88

9-
Python client for `Nasjonal Turbase <http://www.nasjonalturbase.no/>`_.
10-
11-
This client is opinionated about data fields and unrecognized fields require
12-
explicit handling if wanted.
9+
Python client for `Nasjonal Turbase <http://www.nasjonalturbase.no/>`_,
10+
featuring:
11+
12+
- :ref:`Object model for all datatypes <datatypes>`
13+
- :ref:`Container API on instances to access document fields (dict-like)
14+
<document-fields>`
15+
- :ref:`Exceptions abstract away HTTP status codes <exceptions>`
16+
- :ref:`Automatic iteration over paginated list queries <static-methods>`
17+
- :ref:`Handling partial documents returned from list queries
18+
<partial-documents>`
19+
- :ref:`ETag handling with refresh on expiry <settings>`
20+
- :ref:`Client caching <settings>`
21+
- :ref:`Event triggers <events>`
1322

1423
Installation
1524
-----------------------------
@@ -18,6 +27,7 @@ Installation
1827
1928
pip install turbasen
2029
30+
.. _datatypes:
2131

2232
Datatypes
2333
-----------------------------
@@ -30,7 +40,7 @@ Datatypes
3040
3141
`Groups <http://www.nasjonalturbase.no/data/grupper.html>`_
3242

33-
.. py:class:: turbasen.Omrade
43+
.. py:class:: turbasen.Område
3444
3545
`Areas <http://www.nasjonalturbase.no/data/omrader.html>`_
3646

@@ -51,14 +61,16 @@ Environment variables
5161
``ENDPOINT_URL``
5262
API endpoint. See the ``ENDPOINT_URL`` setting.
5363

64+
.. _settings:
65+
5466
Settings
5567
-----------------------------
5668

5769
``ENDPOINT_URL = https://api.nasjonalturbase.no``
5870
API endpoint. Set to ``https://dev.nasjonalturbase.no`` for development.
5971

6072
``LIMIT = 20``
61-
Objects returned per page. API hard max limit is currently 50. Note that
73+
Documents returned per page. API hard max limit is currently 50. Note that
6274
setting this to a low number when the use case is to retrieve all documents is
6375
inefficient.
6476

@@ -75,122 +87,135 @@ Settings
7587
normally be high.
7688

7789
``ETAG_CACHE_PERIOD = 60 * 60``
78-
Number of seconds to ignore ``Etag`` checks and use local cache blindly.
90+
Number of seconds to ignore ``ETag`` checks and use local cache blindly.
7991

80-
``API_KEY = os.environ.get('API_KEY')``
81-
API key is currently required for access.
92+
``API_KEY = os.environ.get('API_KEY', '')``
93+
Get your API key at
94+
`Nasjonal Turbase Developer <https://developer.nasjonalturbase.no/>`_.
8295

8396

8497

85-
Usage
98+
Example usage
8699
-----------------------------
87100

101+
Initialization:
102+
88103
.. code-block:: python
89104
90-
# Initialization
91105
import turbasen
92106
turbasen.configure(LIMIT=3, ENDPOINT='https://dev.nasjonalturbase.no')
93107
94-
# Lookup partial documents
95-
turbasen.Sted.lookup(pages=1)
96-
# [<Sted: 546b36a511f41a9c00c0d4d9 (partial): En liten hytte>,
97-
# <Sted: 546a051011f41a9c00c0d4cc (partial): Snøhulen>,
98-
# <Sted: 555f1f4206b9ce06003405c5 (partial): Strømfoss>]
108+
List documents, with some parameter filters:
109+
110+
.. code-block:: python
111+
112+
turbasen.Sted.list(pages=1, params={
113+
'tilbyder': 'DNT',
114+
'status': 'Offentlig',
115+
'tags': 'Hytte',
116+
})
99117
100-
# Add filter parameters
101-
turbasen.Sted.lookup(pages=1, params={'tags': 'Hytte'})
102118
# [<Sted: 52407fb375049e561500027d (partial): Øvre Grue>,
103119
# <Sted: 52407fb375049e561500035a (partial): Ravnastua fjellstue>,
104120
# <Sted: 52407fb375049e5615000356 (partial): Lahpoluoppal>]
105121
106-
# Get single document
122+
Get single document:
123+
124+
.. code-block:: python
125+
107126
sted = turbasen.Sted.get('546b36a511f41a9c00c0d4d9')
108127
# <Sted: 546b36a511f41a9c00c0d4d9: En liten hytte>
109-
sted.geojson
110-
# {
111-
# 'coordinates': [8.2912015914917, 60.12502756386393],
112-
# 'type': 'Point'
113-
# }
114-
len(sted.get_data().keys())
115-
# 12
116128
117-
# Save document
118-
sted.save()
129+
sted['navn']
130+
# En liten hytte
131+
132+
len(sted)
133+
# 17
134+
135+
Create and delete document:
136+
137+
.. code-block:: python
119138
120-
# Unrecognized fields are discarded by default and require
121-
# explicit handling explicitly if wanted
122-
len(sted.get_data(include_extra=True).keys())
123-
# 13
124-
{
125-
k: v
126-
for k, v in s.get_data(include_extra=True).items()
127-
if k not in s.get_data()
128-
}
129-
# {'unknown_key': 'foo'}
130-
sted.save(include_extra=True)
131-
132-
# Create and delete document
133139
sted = turbasen.Sted(
134140
lisens='Privat',
135141
status='Kladd',
136142
navn='Testcabin',
137143
beskrivelse='Testcabin',
138144
tags=['Hytte'],
139145
)
146+
140147
sted.save()
141-
# Turbasen POST warning: {
142-
# 'resource': 'Document',
143-
# 'field': 'navngiving',
148+
# API warning: {
144149
# 'code': 'missing_field',
150+
# 'field': 'navngiving',
151+
# 'resource': 'Document'
145152
# }
153+
146154
sted.delete()
147155
148156
149157
API
150158
-----------------------------
151159

160+
.. _static-methods:
161+
152162
Static methods
153163
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
154164

155-
.. py:function:: lookup(pages=None, params=dict())
165+
.. py:function:: list(pages=None, params=dict())
156166
157-
Return an iterator yielding all objects of this class object type. Limit the
158-
number of objects to the number of ``pages`` wanted where each page contains
159-
``LIMIT`` objects from the settings.
167+
Return a list of documents. If ``pages`` is not ``None``, limits the results
168+
to ``pages`` pages with ``LIMIT`` documents on each page.
160169

161-
Parameters passed in the ``params`` dict are forwarded to the API. These may
162-
be used to filter the query, or specify which ``fields`` should be returned
163-
to increase performance, avoiding extra fetches for
164-
:ref:`partial objects <partial-objects>`.
170+
Filter results with ``params``, or specify which ``fields`` should be
171+
returned to increase performance, avoiding extra fetches for
172+
:ref:`partial documents <partial-documents>`. See
173+
`the API documentation <http://www.nasjonalturbase.no/api/>`_.
165174

166175
.. py:function:: get(object_id)
167176
168-
Retrieve a document of this class object type. Raises
169-
``turbasen.exceptions.DocumentNotFound`` if the document doesn't exist.
177+
Retrieve a document of this datatype with the given object id.
178+
179+
.. _instance-methods:
170180

171181
Instance methods
172182
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
173183

174-
.. py:function:: get_data(self, include_common=True, include_extra=False)
184+
.. py:function:: save()
185+
186+
Save this document. If the document doesn't have an ``_id`` field, it will be
187+
assigned.
175188

176-
Return a dictionary of all data in this document.
189+
.. py:function:: delete()
177190
178-
Set ``include_common`` to ``False`` to exclude fields that are common for all
179-
objects, returning only fields specific to the current object type.
191+
Delete this document. It must be saved (ie. have an ``_id`` field).
180192

181-
Set ``include_extra`` to ``True`` to include unrecognized fields.
193+
.. py:function:: get_field(key[, default])
182194
183-
.. py:function:: delete()
195+
See `dict.get <https://docs.python.org/3/library/stdtypes.html?#dict.get>`_
184196

185-
Delete the current object. It must be saved (ie. have an ``object_id``).
197+
.. _document-fields:
198+
199+
Document fields
200+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
201+
202+
Instances are
203+
`collections <https://docs.python.org/3/library/collections.html>`_, so document
204+
fields are accessed as keys on a regular ``dict``. All
205+
`dict methods <https://docs.python.org/3/library/stdtypes.html?#dict>`_ are
206+
implemented, except for
207+
`dict.get <https://docs.python.org/3/library/stdtypes.html?#dict.get>`_ which is
208+
renamed to ``get_field``, see :ref:`instance methods <instance-methods>`.
209+
210+
.. _exceptions:
186211

187212
Exceptions
188213
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
189214

190215
.. py:class:: turbasen.exceptions.DocumentNotFound
191216
192-
Thrown when a ``GET`` request for a document with a given object id isn't
193-
found
217+
Thrown when a request references to a document with an object id that doesn't
218+
exist.
194219

195220
.. py:class:: turbasen.exceptions.Unauthorized
196221
@@ -201,21 +226,26 @@ Exceptions
201226
202227
Thrown when updating or creating a document with invalid data.
203228

204-
.. _partial-objects:
229+
.. py:class:: turbasen.exceptions.ServerError
205230
206-
Partial objects
231+
Thrown when a request results in a 5xx server error response.
232+
233+
.. _partial-documents:
234+
235+
Partial documents
207236
-----------------------------
208237

209-
When using ``lookup``, not all document data is retrieved. The objects returned
210-
are classified as *partial*. On attribute lookup, if the attribute doesn't
211-
exist, a ``GET`` request is automatically performed under the hood to request
212-
the entire document, and if the attribute is found on the complete object, it is
213-
returned as normal.
238+
Documents returned from calling ``list`` are not complete, but classified as
239+
*partial*. When accessing a field on a partial document which does not exist,
240+
a ``GET`` request is automatically performed under the hood to request the
241+
entire document. If the accessed field now exists, it is returned as normal.
214242

215-
If you know you only need a few fields from a lookup, it may be a good idea to
216-
specify those in the params field like this:
243+
If you know you only need a few fields from a ``list`` call, it may be a good
244+
idea to specify those in the params field like this:
217245
``params={'fields': ['field1', 'field2']}`` to avoid performing a ``GET``
218-
request for each of the objects in your list.
246+
request for each of the documents in your list.
247+
248+
.. _events:
219249

220250
Events
221251
-----------------------------

flake8.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[flake8]
2+
max-line-length = 100
3+
ignore = E261, E302

setup.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
packages=[name],
99
version=VERSION,
1010
description='Client for Nasjonal Turbase REST API',
11-
long_description='See https://github.com/Turbasen/turbasen.py/blob/master/README.md',
11+
long_description='Documentation: https://turbasenpy.readthedocs.io/',
1212
author='Ali Kaafarani',
1313
author_email='[email protected]',
1414
url='https://github.com/Turbasen/turbasen.py',
@@ -22,11 +22,10 @@
2222
'Natural Language :: Norwegian',
2323
'Operating System :: OS Independent',
2424
'Programming Language :: Python',
25-
'Programming Language :: Python :: 2',
2625
'Programming Language :: Python :: 3',
2726
],
2827
install_requires=['requests>=2.10.0,<3'],
2928
extras_require={
30-
'dev': ['ipython'],
29+
'dev': ['sphinx', 'ipython', 'flake8'],
3130
}
3231
)

tests/test_cache.py

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# encoding: utf-8
2-
from __future__ import absolute_import, division, print_function, unicode_literals
3-
41
import contextlib
52
import unittest
63

@@ -59,12 +56,12 @@ def test_cache_on_get(self):
5956
with self.configure_cache() as cache:
6057
# Save the object and assert that the cache has been set
6158
self.sted.save()
62-
self.assertIsNotNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % self.sted.object_id))
59+
self.assertIsNotNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % self.sted['_id']))
6360
self.assertEqual(cache.hits, 1)
6461
self.assertEqual(cache.misses, 0)
6562

6663
# Now get it back, and asser that it was retrieved from cache
67-
sted = turbasen.Sted.get(self.sted.object_id)
64+
sted = turbasen.Sted.get(self.sted['_id'])
6865
self.assertEqual(cache.hits, 2)
6966
self.assertEqual(cache.misses, 0)
7067

@@ -74,16 +71,17 @@ def test_cache_on_fetch(self):
7471
self.sted.save()
7572
with self.configure_cache() as cache:
7673
# Prepare a partial object, fetch it, and assert that the cache is set
77-
partial_sted = turbasen.Sted(_meta={'id': self.sted.object_id, 'is_partial': True})
78-
self.assertIsNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % partial_sted.object_id))
74+
partial_sted = turbasen.Sted(_is_partial=True, _id=self.sted['_id'])
75+
self.assertIsNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % partial_sted['_id']))
7976
self.assertEqual(cache.hits, 0)
8077
self.assertEqual(cache.misses, 1)
8178
partial_sted._fetch() # triggers another cache miss
82-
self.assertIsNotNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % partial_sted.object_id))
79+
self.assertIsNotNone(turbasen.settings.Settings.CACHE.get('turbasen.object.%s' % partial_sted['_id']))
8380
self.assertEqual(cache.hits, 1)
8481
self.assertEqual(cache.misses, 2)
8582

86-
# Now fetch it again, and assert that this time it was retrieved from cache
83+
# Now fetch it again as partial, and assert that this time it was retrieved from cache
84+
partial_sted._is_partial = True
8785
partial_sted._fetch()
8886
self.assertEqual(cache.hits, 2)
8987
self.assertEqual(cache.misses, 2)

tests/test_events.py

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# encoding: utf-8
2-
from __future__ import absolute_import, division, print_function, unicode_literals
3-
41
import unittest
52

63
from turbasen.events import trigger

0 commit comments

Comments
 (0)