# Official Python Driver for [Fauna v10](https://fauna.com) (current)
[](https://pypi.python.org/pypi/fauna)
[](https://raw.githubusercontent.com/fauna/fauna-python/main/LICENSE)
This driver can only be used with FQL v10, and is not compatible with earlier versions
of FQL. To query your databases with earlier API versions, see
the [faunadb](https://pypi.org/project/faunadb/) package.
See the [Fauna Documentation](https://docs.fauna.com/fauna/current/)
for additional information on how to configure and query your databases.
## Installation
Pre-release installations must specify the version you want to install. Find the version you want to install on [PyPI](https://pypi.org/project/fauna/#history).
```bash
pip install fauna==<version>
```
## Compatibility
The following versions of Python are supported:
* Python 3.9
* Python 3.10
* Python 3.11
* Python 3.12
## API reference
API reference documentation for the driver is available at
https://fauna.github.io/fauna-python/. The docs are generated using
[pdoc](https://pdoc.dev/docs/pdoc.html).
## Basic Usage
You can expect a ``Client`` instance to have reasonable defaults, like the Fauna endpoint ``https://db.fauna.com`` and a global HTTP client, but you will always need to configure a secret.
You can configure your secret by passing it directly to the client or by setting an environment variable.
Supported Environment Variables:
* ``FAUNA_ENDPOINT``: The Fauna endpoint to use. For example, ``http://localhost:8443``
* ``FAUNA_SECRET``: The Fauna secret to use.
```python
from fauna import fql
from fauna.client import Client
from fauna.encoding import QuerySuccess
from fauna.errors import FaunaException
client = Client()
# The client defaults to using the value stored FAUNA_SECRET for its secret.
# Either set the FAUNA_SECRET env variable or retrieve it from a secret store.
# As a best practice, don't store your secret directly in your code.
try:
# create a collection
q1 = fql('Collection.create({ name: "Dogs" })')
client.query(q1)
# create a document
q2 = fql('Dogs.create({ name: "Scout" })')
res: QuerySuccess = client.query(q2)
doc = res.data
print(doc)
except FaunaException as e:
# handle errors
print(e)
```
## Query Composition
This driver supports query composition with Python primitives, lists, dicts, and other FQL queries.
For FQL templates, denote variables with ``${}`` and pass variables as kwargs to ``fql()``. You can escape a variable by prepending an additional ``$``.
```python
from fauna import fql
from fauna.client import Client
client = Client()
def add_two(x):
return fql("${x} + 2", x=x)
q = fql("${y} + 4", y=add_two(2))
res = client.query(q)
print(res.data) # 8
```
## Serialization / Deserialization
Serialization and deserialization with user-defined classes is not yet supported.
When building queries, adapt your classes into dicts or lists before using them in composition. When instantiating classes from the query result data, build them from the expected result.
```python
class MyClass:
def __init__ (self, my_prop):
self.my_prop = my_prop
def to_dict(self):
return { 'my_prop': self.my_prop }
@static_method
def from_result(obj):
return MyClass(obj['my_prop'])
```
## Client Configuration
### Max Attempts
The maximum number of times a query will be attempted if a retryable exception is thrown (ThrottlingError). Default 3, inclusive of the initial call. The retry strategy implemented is a simple exponential backoff.
To disable retries, pass max_attempts less than or equal to 1.
### Max Backoff
The maximum backoff in seconds to be observed between each retry. Default 20 seconds.
### Timeouts
There are a few different timeout settings that can be configured; each comes with a default setting. We recommend that most applications use the defaults.
### Query Timeout
The query timeout is the time, as ``datetime.timedelta``, that Fauna will spend executing your query before aborting with a ``QueryTimeoutError``.
The query timeout can be set using the ``query_timeout`` option. The default value if you do not provide one is ``DefaultClientBufferTimeout`` (5 seconds).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(query_timeout=timedelta(seconds=20))
```
The query timeout can also be set to a different value for each query using the ``QueryOptions.query_timeout`` option. Doing so overrides the client configuration when performing this query.
```python
from datetime import timedelta
from fauna.client import Client, QueryOptions
response = client.query(myQuery, QueryOptions(query_timeout=timedelta(seconds=20)))
```
### Client Timeout
The client timeout is the time, as ``datetime.timedelta``, that the client will wait for a network response before canceling the request. If a client timeout occurs, the driver will throw an instance of ``NetworkError``.
The client timeout is always the query timeout plus an additional buffer. This ensures that the client always waits for at least as long Fauna could work on your query and account for network latency.
The client timeout buffer is configured by setting the ``client_buffer_timeout`` option. The default value for the buffer if you do not provide on is ``DefaultClientBufferTimeout`` (5 seconds), therefore the default client timeout is 10 seconds when considering the default query timeout.
```python
from datetime import timedelta
from fauna.client import Client
client = Client(client_buffer_timeout=timedelta(seconds=20))
```
### Idle Timeout
The idle timeout is the time, as ``datetime.timedelta``, that a session will remain open after there is no more pending communication. Once the session idle time has elapsed the session is considered idle and the session is closed. Subsequent requests will create a new session; the session idle timeout does not result in an error.
Configure the idle timeout using the ``http_idle_timeout`` option. The default value if you do not provide one is ``DefaultIdleConnectionTimeout`` (5 seconds).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(http_idle_timeout=timedelta(seconds=6))
```
> **Note**
> Your application process may continue executing after all requests are completed for the duration of the session idle timeout. To prevent this, it is recommended to call ``close()`` once all requests are complete. It is not recommended to set ``http_idle_timeout`` to small values.
### Connect Timeout
The connect timeout is the maximum amount of time, as ``datetime.timedelta``, to wait until a connection to Fauna is established. If the client is unable to connect within this time frame, a ``ConnectTimeout`` exception is raised.
Configure the connect timeout using the ``http_connect_timeout`` option. The default value if you do not provide one is ``DefaultHttpConnectTimeout`` (5 seconds).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(http_connect_timeout=timedelta(seconds=6))
```
### Pool Timeout
The pool timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for acquiring a connection from the connection pool. If the client is unable to acquire a connection within this time frame, a ``PoolTimeout`` exception is raised. This timeout may fire if 20 connections are currently in use and one isn't released before the timeout is up.
Configure the pool timeout using the ``http_pool_timeout`` option. The default value if you do not provide one is ``DefaultHttpPoolTimeout`` (5 seconds).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(http_pool_timeout=timedelta(seconds=6))
```
### Read Timeout
The read timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for a chunk of data to be received (for example, a chunk of the response body). If the client is unable to receive data within this time frame, a ``ReadTimeout`` exception is raised.
Configure the read timeout using the ``http_read_timeout`` option. The default value if you do not provide one is ``DefaultHttpReadTimeout`` (None).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(http_read_timeout=timedelta(seconds=6))
```
### Write Timeout
The write timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for a chunk of data to be sent (for example, a chunk of the request body). If the client is unable to send data within this time frame, a ``WriteTimeout`` exception is raised.
Configure the write timeout using the ``http_write_timeout`` option. The default value if you do not provide one is ``DefaultHttpWriteTimeout`` (5 seconds).
```python
from datetime import timedelta
from fauna.client import Client
client = Client(http_write_timeout=timedelta(seconds=6))
```
## Query Stats
Stats are returned on query responses and ServiceErrors.
```python
from fauna import fql
from fauna.client import Client
from fauna.encoding import QuerySuccess, QueryStats
from fauna.errors import ServiceError
client = Client()
def emit_stats(stats: QueryStats):
print(f"Compute Ops: {stats.compute_ops}")
print(f"Read Ops: {stats.read_ops}")
print(f"Write Ops: {stats.write_ops}")
try:
q = fql('Collection.create({ name: "Dogs" })')
qs: QuerySuccess = client.query(q)
emit_stats(qs.stats)
except ServiceError as e:
if e.stats is not None:
emit_stats(e.stats)
# more error handling...
```
## Pagination
Use the ``paginate()`` method to iterate sets that contain more than one
page of results.
``paginate()`` accepts the same query options as ``query()``.
Change the default items per page using FQL's ``pageSize()`` method.
```python
from datetime import timedelta
from fauna import fql
from fauna.client import Client, QueryOptions
# Adjust `pageSize()` size as needed.
query = fql(
"""
Product
.byName("limes")
.pageSize(60) { description }"""
)
client = Client()
options = QueryOptions(query_timeout=timedelta(seconds=20))
pages = client.paginate(query, options)
for products in pages:
for product in products:
print(products)
```
## Event Feeds (beta)
The driver supports [Event Feeds](https://docs.fauna.com/fauna/current/learn/cdc/#event-feeds).
### Request an Event Feed
An Event Feed asynchronously polls an [event source](https://docs.fauna.com/fauna/current/learn/cdc/#create-an-event-source)
for paginated events.
To get an event source, append ``eventSource()`` or ``eventsOn()`` to a
[supported Set](https://docs.fauna.com/fauna/current/reference/cdc/#sets).
To get paginated events, pass the event source to ``feed()``:
```python
from fauna import fql
from fauna.client import Client
client = Client()
response = client.query(fql('''
let set = Product.all()
{
initialPage: set.pageSize(10),
eventSource: set.eventSource()
}
'''))
initial_page = response.data['initialPage']
event_source = response.data['eventSource']
client.feed(event_source)
```
You can also pass a query that produces an event source directly to ``feed()``:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
client.feed(query)
```
### Iterate on an Event Feed
``feed()`` returns an iterator that emits pages of events. You can use a
generator expression to iterate through the pages:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
feed = client.feed(query)
for page in feed:
print('Page stats: ', page.stats)
for event in page:
event_type = event['type']
if (event_type == 'add'):
print('Add event: ', event)
## ...
elif (event_type == 'update'):
print('Update event: ', event)
## ...
elif (event_type == 'remove'):
print('Remove event: ', event)
## ...
```
Alternatively, you can iterate through events instead of pages with
``flatten()``:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
feed = client.feed(query)
for event in feed.flatten():
event_type = event['type']
## ...
```
The Event Feed iterator stops when there are no more events to poll.
### Error handling
If a non-retryable error occurs when opening or processing an Event Feed, Fauna
raises a ``FaunaException``:
```python
from fauna import fql
from fauna.client import Client
from fauna.errors import FaunaException
client = Client()
try:
feed = client.feed(fql(
'Product.all().eventsOn(.price, .stock)'
))
for event in feed.flatten():
print(event)
# ...
except FaunaException as e:
print('error ocurred with event feed: ', e)
```
Errors can be raised at two different places:
1. At the ``feed`` method call;
2. At the page iteration.
This distinction allows for users to ignore errors originating from event
processing. For example:
```python
from fauna import fql
from fauna.client import Client
from fauna.errors import FaunaException
client = Client()
# Imagine if there are some products with details = null.
# The ones without details will fail due to the toUpperCase call.
feed = client.feed(fql(
'Product.all().map(.details.toUpperCase()).eventSource()'
))
for page in feed:
try:
for event in page:
print(event)
# ...
except FaunaException as e:
# Pages will stop at the first error encountered.
# Therefore, its safe to handle an event failures
# and then pull more pages.
print('error ocurred with event processing: ', e)
```
### Event Feed options
The client configuration sets default options for the ``feed()`` method.
You can pass a ``FeedOptions`` object to override these defaults:
```python
options = FeedOptions(
max_attempts=3,
max_backoff=20,
query_timeout=timedelta(seconds=5),
page_size=None,
cursor=None,
start_ts=None,
)
client.feed(fql('Product.all().eventSource()'), options)
```
## Event Streaming
The driver supports [Event
Streaming](https://docs.fauna.com/fauna/current/reference/cdc/#event-streaming).
### Start a stream
An Event Stream lets you consume events from an [event
source](https://docs.fauna.com/fauna/current/learn/cdc/#create-an-event-source)
as a real-time subscription.
To get an event source, append ``eventSource()`` or ``eventsOn()`` to a
[supported Set](https://docs.fauna.com/fauna/current/reference/cdc/#sets).
To start and subscribe to the stream, pass the event source to ``stream()``:
```python
from fauna import fql
from fauna.client import Client
client = Client()
response = client.query(fql('''
let set = Product.all()
{
initialPage: set.pageSize(10),
eventSource: set.eventSource()
}
'''))
initial_page = response.data['initialPage']
event_source = response.data['eventSource']
client.stream(event_source)
```
You can also pass a query that produces an event source directly to
``stream()``:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
client.stream(query)
```
### Iterate on a stream
``stream()`` returns an iterator that emits events as they occur. You can
use a generator expression to iterate through the events:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
with client.stream(query) as stream:
for event in stream:
event_type = event['type']
if (event_type == 'add'):
print('Add event: ', event)
## ...
elif (event_type == 'update'):
print('Update event: ', event)
## ...
elif (event_type == 'remove'):
print('Remove event: ', event)
## ...
```
### Close a stream
Use ``close()`` to close a stream:
```python
query = fql('Product.all().eventsOn(.price, .stock)')
count = 0
with client.stream(query) as stream:
for event in stream:
print('Stream event', event)
# ...
count+=1
if (count == 2):
stream.close()
```
### Error handling
If a non-retryable error occurs when opening or processing a stream, Fauna
raises a ``FaunaException``:
```python
from fauna import fql
from fauna.client import Client
from fauna.errors import FaunaException
client = Client()
try:
with client.stream(fql(
'Product.all().eventsOn(.price, .stock)'
)) as stream:
for event in stream:
print(event)
# ...
except FaunaException as e:
print('error ocurred with stream: ', e)
```
### Stream options
The client configuration sets default options for the ``stream()``
method.
You can pass a ``StreamOptions`` object to override these defaults:
```python
options = StreamOptions(
max_attempts=3,
max_backoff=20,
start_ts=None,
cursor=None,
status_events=False,
)
client.stream(fql('Product.all().eventSource()'), options)
```
## Logging
Logging is handled using Python's standard `logging` package under the `fauna` namespace. Logs include the HTTP request with body (excluding the `Authorization` header) and the full HTTP response.
To enable logging:
```python
import logging
from fauna.client import Client
from fauna import fql
logging.basicConfig(
level=logging.DEBUG
)
client = Client()
client.query(fql('42'))
```
For configuration options or to set specific log levels, see Python's [Logging HOWTO](https://docs.python.org/3/howto/logging.html).
## Setup
```bash
virtualenv venv
source venv/bin/activate
pip install . .[test] .[lint]
```
## Testing
We use pytest. You can run tests directly or with docker. If you run integration tests directly, you must have fauna running locally.
If you want to run fauna, then run integration tests separately:
```bash
make run-fauna
source venv/bin/activate
make install
make integration-test
```
To run unit tests locally:
```bash
source venv/bin/activate
make install
make unit-test
```
To stand up a container and run all tests at the same time:
```bash
make docker-test
```
See the ``Makefile`` for more.
## Coverage
```bash
source venv/bin/activate
make coverage
```
## Contribute
GitHub pull requests are very welcome.
## License
Copyright 2023 [Fauna, Inc.](https://fauna.com)
Licensed under the Mozilla Public License, Version 2.0 (the
"License"); you may not use this software except in compliance with
the License. You can obtain a copy of the License at
[http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/>)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
Raw data
{
"_id": null,
"home_page": "https://github.com/fauna/fauna-python",
"name": "fauna",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.9",
"maintainer_email": null,
"keywords": "faunadb fauna",
"author": "Fauna, Inc",
"author_email": "priority@fauna.com",
"download_url": null,
"platform": null,
"description": "# Official Python Driver for [Fauna v10](https://fauna.com) (current)\n\n[](https://pypi.python.org/pypi/fauna)\n[](https://raw.githubusercontent.com/fauna/fauna-python/main/LICENSE)\n\nThis driver can only be used with FQL v10, and is not compatible with earlier versions\nof FQL. To query your databases with earlier API versions, see\nthe [faunadb](https://pypi.org/project/faunadb/) package.\n\nSee the [Fauna Documentation](https://docs.fauna.com/fauna/current/)\nfor additional information on how to configure and query your databases.\n\n## Installation\nPre-release installations must specify the version you want to install. Find the version you want to install on [PyPI](https://pypi.org/project/fauna/#history).\n```bash\npip install fauna==<version>\n```\n\n## Compatibility\n\nThe following versions of Python are supported:\n\n* Python 3.9\n* Python 3.10\n* Python 3.11\n* Python 3.12\n\n\n## API reference\n\nAPI reference documentation for the driver is available at\nhttps://fauna.github.io/fauna-python/. The docs are generated using\n[pdoc](https://pdoc.dev/docs/pdoc.html).\n\n\n## Basic Usage\nYou can expect a ``Client`` instance to have reasonable defaults, like the Fauna endpoint ``https://db.fauna.com`` and a global HTTP client, but you will always need to configure a secret.\n\nYou can configure your secret by passing it directly to the client or by setting an environment variable.\n\nSupported Environment Variables:\n\n* ``FAUNA_ENDPOINT``: The Fauna endpoint to use. For example, ``http://localhost:8443``\n* ``FAUNA_SECRET``: The Fauna secret to use.\n\n```python\nfrom fauna import fql\nfrom fauna.client import Client\nfrom fauna.encoding import QuerySuccess\nfrom fauna.errors import FaunaException\n\nclient = Client()\n# The client defaults to using the value stored FAUNA_SECRET for its secret.\n# Either set the FAUNA_SECRET env variable or retrieve it from a secret store.\n# As a best practice, don't store your secret directly in your code.\n\ntry:\n # create a collection\n q1 = fql('Collection.create({ name: \"Dogs\" })')\n client.query(q1)\n\n # create a document\n q2 = fql('Dogs.create({ name: \"Scout\" })')\n res: QuerySuccess = client.query(q2)\n doc = res.data\n print(doc)\nexcept FaunaException as e:\n # handle errors\n print(e)\n```\n\n## Query Composition\n\nThis driver supports query composition with Python primitives, lists, dicts, and other FQL queries.\n\nFor FQL templates, denote variables with ``${}`` and pass variables as kwargs to ``fql()``. You can escape a variable by prepending an additional ``$``.\n\n```python\nfrom fauna import fql\nfrom fauna.client import Client\n\nclient = Client()\n\ndef add_two(x):\n return fql(\"${x} + 2\", x=x)\n\nq = fql(\"${y} + 4\", y=add_two(2))\nres = client.query(q)\nprint(res.data) # 8\n```\n\n## Serialization / Deserialization\n\nSerialization and deserialization with user-defined classes is not yet supported.\n\nWhen building queries, adapt your classes into dicts or lists before using them in composition. When instantiating classes from the query result data, build them from the expected result.\n\n```python\nclass MyClass:\n def __init__ (self, my_prop):\n self.my_prop = my_prop\n\n def to_dict(self):\n return { 'my_prop': self.my_prop }\n\n @static_method\n def from_result(obj):\n return MyClass(obj['my_prop'])\n```\n\n## Client Configuration\n\n### Max Attempts\nThe maximum number of times a query will be attempted if a retryable exception is thrown (ThrottlingError). Default 3, inclusive of the initial call. The retry strategy implemented is a simple exponential backoff.\n\nTo disable retries, pass max_attempts less than or equal to 1.\n\n### Max Backoff\nThe maximum backoff in seconds to be observed between each retry. Default 20 seconds.\n\n### Timeouts\n\nThere are a few different timeout settings that can be configured; each comes with a default setting. We recommend that most applications use the defaults.\n\n### Query Timeout\nThe query timeout is the time, as ``datetime.timedelta``, that Fauna will spend executing your query before aborting with a ``QueryTimeoutError``.\n\nThe query timeout can be set using the ``query_timeout`` option. The default value if you do not provide one is ``DefaultClientBufferTimeout`` (5 seconds).\n\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(query_timeout=timedelta(seconds=20))\n```\n\nThe query timeout can also be set to a different value for each query using the ``QueryOptions.query_timeout`` option. Doing so overrides the client configuration when performing this query.\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client, QueryOptions\n\nresponse = client.query(myQuery, QueryOptions(query_timeout=timedelta(seconds=20)))\n```\n\n### Client Timeout\n\nThe client timeout is the time, as ``datetime.timedelta``, that the client will wait for a network response before canceling the request. If a client timeout occurs, the driver will throw an instance of ``NetworkError``.\n\nThe client timeout is always the query timeout plus an additional buffer. This ensures that the client always waits for at least as long Fauna could work on your query and account for network latency.\n\nThe client timeout buffer is configured by setting the ``client_buffer_timeout`` option. The default value for the buffer if you do not provide on is ``DefaultClientBufferTimeout`` (5 seconds), therefore the default client timeout is 10 seconds when considering the default query timeout.\n\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(client_buffer_timeout=timedelta(seconds=20))\n```\n### Idle Timeout\n\nThe idle timeout is the time, as ``datetime.timedelta``, that a session will remain open after there is no more pending communication. Once the session idle time has elapsed the session is considered idle and the session is closed. Subsequent requests will create a new session; the session idle timeout does not result in an error.\n\nConfigure the idle timeout using the ``http_idle_timeout`` option. The default value if you do not provide one is ``DefaultIdleConnectionTimeout`` (5 seconds).\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(http_idle_timeout=timedelta(seconds=6))\n```\n\n> **Note**\n> Your application process may continue executing after all requests are completed for the duration of the session idle timeout. To prevent this, it is recommended to call ``close()`` once all requests are complete. It is not recommended to set ``http_idle_timeout`` to small values.\n\n### Connect Timeout\n\nThe connect timeout is the maximum amount of time, as ``datetime.timedelta``, to wait until a connection to Fauna is established. If the client is unable to connect within this time frame, a ``ConnectTimeout`` exception is raised.\n\nConfigure the connect timeout using the ``http_connect_timeout`` option. The default value if you do not provide one is ``DefaultHttpConnectTimeout`` (5 seconds).\n\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(http_connect_timeout=timedelta(seconds=6))\n```\n### Pool Timeout\n\nThe pool timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for acquiring a connection from the connection pool. If the client is unable to acquire a connection within this time frame, a ``PoolTimeout`` exception is raised. This timeout may fire if 20 connections are currently in use and one isn't released before the timeout is up.\n\nConfigure the pool timeout using the ``http_pool_timeout`` option. The default value if you do not provide one is ``DefaultHttpPoolTimeout`` (5 seconds).\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(http_pool_timeout=timedelta(seconds=6))\n```\n### Read Timeout\n\nThe read timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for a chunk of data to be received (for example, a chunk of the response body). If the client is unable to receive data within this time frame, a ``ReadTimeout`` exception is raised.\n\nConfigure the read timeout using the ``http_read_timeout`` option. The default value if you do not provide one is ``DefaultHttpReadTimeout`` (None).\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(http_read_timeout=timedelta(seconds=6))\n```\n\n### Write Timeout\n\nThe write timeout specifies the maximum amount of time, as ``datetime.timedelta``, to wait for a chunk of data to be sent (for example, a chunk of the request body). If the client is unable to send data within this time frame, a ``WriteTimeout`` exception is raised.\n\nConfigure the write timeout using the ``http_write_timeout`` option. The default value if you do not provide one is ``DefaultHttpWriteTimeout`` (5 seconds).\n\n```python\nfrom datetime import timedelta\nfrom fauna.client import Client\n\nclient = Client(http_write_timeout=timedelta(seconds=6))\n```\n## Query Stats\n\nStats are returned on query responses and ServiceErrors.\n\n```python\nfrom fauna import fql\nfrom fauna.client import Client\nfrom fauna.encoding import QuerySuccess, QueryStats\nfrom fauna.errors import ServiceError\n\nclient = Client()\n\ndef emit_stats(stats: QueryStats):\n print(f\"Compute Ops: {stats.compute_ops}\")\n print(f\"Read Ops: {stats.read_ops}\")\n print(f\"Write Ops: {stats.write_ops}\")\n\ntry:\n q = fql('Collection.create({ name: \"Dogs\" })')\n qs: QuerySuccess = client.query(q)\n emit_stats(qs.stats)\nexcept ServiceError as e:\n if e.stats is not None:\n emit_stats(e.stats)\n # more error handling...\n```\n\n## Pagination\n\nUse the ``paginate()`` method to iterate sets that contain more than one\npage of results.\n\n``paginate()`` accepts the same query options as ``query()``.\n\nChange the default items per page using FQL's ``pageSize()`` method.\n\n```python\nfrom datetime import timedelta\nfrom fauna import fql\nfrom fauna.client import Client, QueryOptions\n\n# Adjust `pageSize()` size as needed.\nquery = fql(\n \"\"\"\n Product\n .byName(\"limes\")\n .pageSize(60) { description }\"\"\"\n)\n\nclient = Client()\n\noptions = QueryOptions(query_timeout=timedelta(seconds=20))\n\npages = client.paginate(query, options)\n\nfor products in pages:\n for product in products:\n print(products)\n```\n\n## Event Feeds (beta)\n\nThe driver supports [Event Feeds](https://docs.fauna.com/fauna/current/learn/cdc/#event-feeds).\n\n### Request an Event Feed\n\nAn Event Feed asynchronously polls an [event source](https://docs.fauna.com/fauna/current/learn/cdc/#create-an-event-source)\nfor paginated events.\n\nTo get an event source, append ``eventSource()`` or ``eventsOn()`` to a\n[supported Set](https://docs.fauna.com/fauna/current/reference/cdc/#sets).\n\nTo get paginated events, pass the event source to ``feed()``:\n\n```python\n from fauna import fql\n from fauna.client import Client\n\n client = Client()\n\n response = client.query(fql('''\n let set = Product.all()\n {\n initialPage: set.pageSize(10),\n eventSource: set.eventSource()\n }\n '''))\n\n initial_page = response.data['initialPage']\n event_source = response.data['eventSource']\n\n client.feed(event_source)\n```\n\nYou can also pass a query that produces an event source directly to ``feed()``:\n\n```python\n query = fql('Product.all().eventsOn(.price, .stock)')\n\n client.feed(query)\n```\n\n### Iterate on an Event Feed\n\n``feed()`` returns an iterator that emits pages of events. You can use a\ngenerator expression to iterate through the pages:\n\n```python\n query = fql('Product.all().eventsOn(.price, .stock)')\n feed = client.feed(query)\n\n for page in feed:\n print('Page stats: ', page.stats)\n\n for event in page:\n event_type = event['type']\n if (event_type == 'add'):\n print('Add event: ', event)\n ## ...\n elif (event_type == 'update'):\n print('Update event: ', event)\n ## ...\n elif (event_type == 'remove'):\n print('Remove event: ', event)\n ## ...\n```\n\nAlternatively, you can iterate through events instead of pages with\n``flatten()``:\n\n```python\n query = fql('Product.all().eventsOn(.price, .stock)')\n feed = client.feed(query)\n\n for event in feed.flatten():\n event_type = event['type']\n ## ...\n```\n\nThe Event Feed iterator stops when there are no more events to poll.\n\n### Error handling\n\nIf a non-retryable error occurs when opening or processing an Event Feed, Fauna\nraises a ``FaunaException``:\n\n```python\n from fauna import fql\n from fauna.client import Client\n from fauna.errors import FaunaException\n\n client = Client()\n\n try:\n feed = client.feed(fql(\n 'Product.all().eventsOn(.price, .stock)'\n ))\n for event in feed.flatten():\n print(event)\n # ...\n except FaunaException as e:\n print('error ocurred with event feed: ', e)\n```\n\nErrors can be raised at two different places:\n\n1. At the ``feed`` method call;\n2. At the page iteration.\n\nThis distinction allows for users to ignore errors originating from event\nprocessing. For example:\n\n```python\n from fauna import fql\n from fauna.client import Client\n from fauna.errors import FaunaException\n\n client = Client()\n\n # Imagine if there are some products with details = null.\n # The ones without details will fail due to the toUpperCase call.\n feed = client.feed(fql(\n 'Product.all().map(.details.toUpperCase()).eventSource()'\n ))\n\n for page in feed:\n try:\n for event in page:\n print(event)\n # ...\n except FaunaException as e:\n # Pages will stop at the first error encountered.\n # Therefore, its safe to handle an event failures\n # and then pull more pages.\n print('error ocurred with event processing: ', e)\n```\n\n### Event Feed options\n\nThe client configuration sets default options for the ``feed()`` method.\n\nYou can pass a ``FeedOptions`` object to override these defaults:\n\n```python\noptions = FeedOptions(\n max_attempts=3,\n max_backoff=20,\n query_timeout=timedelta(seconds=5),\n page_size=None,\n cursor=None,\n start_ts=None,\n )\n\nclient.feed(fql('Product.all().eventSource()'), options)\n```\n\n## Event Streaming\n\nThe driver supports [Event\nStreaming](https://docs.fauna.com/fauna/current/reference/cdc/#event-streaming).\n\n### Start a stream\n\nAn Event Stream lets you consume events from an [event\nsource](https://docs.fauna.com/fauna/current/learn/cdc/#create-an-event-source)\nas a real-time subscription.\n\nTo get an event source, append ``eventSource()`` or ``eventsOn()`` to a\n[supported Set](https://docs.fauna.com/fauna/current/reference/cdc/#sets).\n\n\nTo start and subscribe to the stream, pass the event source to ``stream()``:\n\n```python\n from fauna import fql\n from fauna.client import Client\n\n client = Client()\n\n response = client.query(fql('''\n let set = Product.all()\n {\n initialPage: set.pageSize(10),\n eventSource: set.eventSource()\n }\n '''))\n\n initial_page = response.data['initialPage']\n event_source = response.data['eventSource']\n\n client.stream(event_source)\n```\n\nYou can also pass a query that produces an event source directly to\n``stream()``:\n\n```python\n query = fql('Product.all().eventsOn(.price, .stock)')\n\n client.stream(query)\n```\n\n### Iterate on a stream\n\n``stream()`` returns an iterator that emits events as they occur. You can\nuse a generator expression to iterate through the events:\n\n```python\nquery = fql('Product.all().eventsOn(.price, .stock)')\n\nwith client.stream(query) as stream:\n for event in stream:\n event_type = event['type']\n if (event_type == 'add'):\n print('Add event: ', event)\n ## ...\n elif (event_type == 'update'):\n print('Update event: ', event)\n ## ...\n elif (event_type == 'remove'):\n print('Remove event: ', event)\n ## ...\n```\n\n### Close a stream\n\nUse ``close()`` to close a stream:\n\n```python\nquery = fql('Product.all().eventsOn(.price, .stock)')\n\ncount = 0\nwith client.stream(query) as stream:\n for event in stream:\n print('Stream event', event)\n # ...\n count+=1\n\n if (count == 2):\n stream.close()\n```\n\n### Error handling\n\nIf a non-retryable error occurs when opening or processing a stream, Fauna\nraises a ``FaunaException``:\n\n```python\nfrom fauna import fql\nfrom fauna.client import Client\nfrom fauna.errors import FaunaException\n\nclient = Client()\n\ntry:\n with client.stream(fql(\n 'Product.all().eventsOn(.price, .stock)'\n )) as stream:\n for event in stream:\n print(event)\n # ...\nexcept FaunaException as e:\n print('error ocurred with stream: ', e)\n```\n\n### Stream options\n\nThe client configuration sets default options for the ``stream()``\nmethod.\n\nYou can pass a ``StreamOptions`` object to override these defaults:\n\n```python\noptions = StreamOptions(\n max_attempts=3,\n max_backoff=20,\n start_ts=None,\n cursor=None,\n status_events=False,\n )\n\nclient.stream(fql('Product.all().eventSource()'), options)\n```\n\n## Logging\n\nLogging is handled using Python's standard `logging` package under the `fauna` namespace. Logs include the HTTP request with body (excluding the `Authorization` header) and the full HTTP response.\n\nTo enable logging:\n```python\nimport logging\nfrom fauna.client import Client\nfrom fauna import fql\n\nlogging.basicConfig(\n level=logging.DEBUG\n)\nclient = Client()\nclient.query(fql('42'))\n```\nFor configuration options or to set specific log levels, see Python's [Logging HOWTO](https://docs.python.org/3/howto/logging.html).\n\n## Setup\n\n```bash\nvirtualenv venv\nsource venv/bin/activate\npip install . .[test] .[lint]\n```\n\n## Testing\n\nWe use pytest. You can run tests directly or with docker. If you run integration tests directly, you must have fauna running locally.\n\nIf you want to run fauna, then run integration tests separately:\n\n```bash\nmake run-fauna\nsource venv/bin/activate\nmake install\nmake integration-test\n```\n\nTo run unit tests locally:\n\n```bash\nsource venv/bin/activate\nmake install\nmake unit-test\n```\n\nTo stand up a container and run all tests at the same time:\n\n\n```bash\nmake docker-test\n```\n\nSee the ``Makefile`` for more.\n\n## Coverage\n\n```bash\nsource venv/bin/activate\nmake coverage\n```\n\n## Contribute\n\nGitHub pull requests are very welcome.\n\n\n## License\n\nCopyright 2023 [Fauna, Inc.](https://fauna.com)\n\nLicensed under the Mozilla Public License, Version 2.0 (the\n\"License\"); you may not use this software except in compliance with\nthe License. You can obtain a copy of the License at\n\n[http://mozilla.org/MPL/2.0/](http://mozilla.org/MPL/2.0/>)\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied. See the License for the specific language governing\npermissions and limitations under the License.\n\n\n",
"bugtrack_url": null,
"license": "MPL 2.0",
"summary": "Fauna Python driver for FQL 10+",
"version": "2.4.0",
"project_urls": {
"Homepage": "https://github.com/fauna/fauna-python"
},
"split_keywords": [
"faunadb",
"fauna"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "91ba370af829e5516716410ee3fe54df278f4e642d637f3d8fab2078e4d7ccbe",
"md5": "f3d77bca3c075b385c06f3ebd11598da",
"sha256": "a0e6e826b6b3ed24bd25db3d038a5cafbd8777bdd9e6a1538c93cc4262911f75"
},
"downloads": -1,
"filename": "fauna-2.4.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "f3d77bca3c075b385c06f3ebd11598da",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": "<4,>=3.9",
"size": 33691,
"upload_time": "2024-12-02T23:03:03",
"upload_time_iso_8601": "2024-12-02T23:03:03.679963Z",
"url": "https://files.pythonhosted.org/packages/91/ba/370af829e5516716410ee3fe54df278f4e642d637f3d8fab2078e4d7ccbe/fauna-2.4.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-02 23:03:03",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "fauna",
"github_project": "fauna-python",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"requirements": [
{
"name": "iso8601",
"specs": [
[
"==",
"2.1.0"
]
]
},
{
"name": "future",
"specs": [
[
"==",
"1.0.0"
]
]
},
{
"name": "httpx",
"specs": [
[
"==",
"0.28.*"
]
]
}
],
"lcname": "fauna"
}