Skip to content

Setting up documentation #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
![Logo](docs/static/logo.png)

[![CircleCI](https://dl.circleci.com/status-badge/img/gh/arangodb/python-arango-async/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/arangodb/python-arango-async/tree/main)
[![CodeQL](https://github.com/arangodb/python-arango-async/actions/workflows/codeql.yaml/badge.svg)](https://github.com/arangodb/python-arango-async/actions/workflows/codeql.yaml)
[![Last commit](https://img.shields.io/github/last-commit/arangodb/python-arango-async)](https://github.com/arangodb/python-arango-async/commits/main)

[![PyPI version badge](https://img.shields.io/pypi/v/python-arango-async?color=3775A9&style=for-the-badge&logo=pypi&logoColor=FFD43B)](https://pypi.org/project/python-arango-async/)
[![Python versions badge](https://img.shields.io/badge/3.9%2B-3776AB?style=for-the-badge&logo=python&logoColor=FFD43B&label=Python)](https://pypi.org/project/python-arango-async/)

[![License](https://img.shields.io/github/license/arangodb/python-arango?color=9E2165&style=for-the-badge)](https://github.com/arangodb/python-arango/blob/main/LICENSE)
[![Code style: black](https://img.shields.io/static/v1?style=for-the-badge&label=code%20style&message=black&color=black)](https://github.com/psf/black)
[![Downloads](https://img.shields.io/pepy/dt/python-arango-async?style=for-the-badge&color=282661
)](https://pepy.tech/project/python-arango-async)

# python-arango-async

Python driver for [ArangoDB](https://www.arangodb.com), a scalable multi-model
Expand All @@ -6,9 +20,60 @@ database natively supporting documents, graphs and search.
This is the _asyncio_ alternative of the officially supported [python-arango](https://github.com/arangodb/python-arango)
driver.

**Note**: This driver is still in development and not yet ready for production use.
**Note: This project is still in active development, features might be added or removed.**

## Requirements

- ArangoDB version 3.10+
- ArangoDB version 3.11+
- Python version 3.9+

## Installation

```shell
pip install python-arango-async --upgrade
```

## Getting Started

Here is a simple usage example:

```python
from arangoasync import ArangoClient
from arangoasync.auth import Auth


async def main():
# Initialize the client for ArangoDB.
async with ArangoClient(hosts="http://localhost:8529") as client:
auth = Auth(username="root", password="passwd")

# Connect to "_system" database as root user.
sys_db = await client.db("_system", auth=auth)

# Create a new database named "test".
await sys_db.create_database("test")

# Connect to "test" database as root user.
db = await client.db("test", auth=auth)

# Create a new collection named "students".
students = await db.create_collection("students")

# Add a persistent index to the collection.
await students.add_index(type="persistent", fields=["name"], options={"unique": True})

# Insert new documents into the collection.
await students.insert({"name": "jane", "age": 39})
await students.insert({"name": "josh", "age": 18})
await students.insert({"name": "judy", "age": 21})

# Execute an AQL query and iterate through the result cursor.
cursor = await db.aql.execute("FOR doc IN students RETURN doc")
async with cursor:
student_names = []
async for doc in cursor:
student_names.append(doc["name"])

```

Please see the [documentation](https://python-arango-async.readthedocs.io/en/latest/) for more details.
6 changes: 3 additions & 3 deletions arangoasync/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
import arangoasync.errno as errno # noqa: F401
from arangoasync.client import ArangoClient # noqa: F401
from arangoasync.exceptions import * # noqa: F401 F403

from .version import __version__

logger = logging.getLogger(__name__)
5 changes: 3 additions & 2 deletions arangoasync/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

from jwt import ExpiredSignatureError

from arangoasync import errno, logger
from arangoasync.auth import Auth, JwtToken
from arangoasync.compression import CompressionManager
from arangoasync.errno import HTTP_UNAUTHORIZED
from arangoasync.exceptions import (
AuthHeaderError,
ClientConnectionAbortedError,
Expand All @@ -24,6 +24,7 @@
ServerConnectionError,
)
from arangoasync.http import HTTPClient
from arangoasync.logger import logger
from arangoasync.request import Method, Request
from arangoasync.resolver import HostResolver
from arangoasync.response import Response
Expand Down Expand Up @@ -417,7 +418,7 @@ async def send_request(self, request: Request) -> Response:

resp = await self.process_request(request)
if (
resp.status_code == errno.HTTP_UNAUTHORIZED
resp.status_code == HTTP_UNAUTHORIZED
and self._token is not None
and self._token.needs_refresh(self._expire_leeway)
):
Expand Down
3 changes: 3 additions & 0 deletions arangoasync/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import logging

logger = logging.getLogger("arangoasync")
10 changes: 10 additions & 0 deletions docs/aql.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
AQL
----

**ArangoDB Query Language (AQL)** is used to read and write data. It is similar
to SQL for relational databases, but without the support for data definition
operations such as creating or deleting :doc:`databases <database>`,
:doc:`collections <collection>` or :doc:`indexes <indexes>`. For more
information, refer to `ArangoDB manual`_.

.. _ArangoDB manual: https://docs.arangodb.com
6 changes: 6 additions & 0 deletions docs/async.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Async API Execution
-------------------

In **asynchronous API executions**, python-arango-async sends API requests to ArangoDB in
fire-and-forget style. The server processes the requests in the background, and
the results can be retrieved once available via `AsyncJob` objects.
42 changes: 42 additions & 0 deletions docs/collection.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Collections
-----------

A **collection** contains :doc:`documents <document>`. It is uniquely identified
by its name which must consist only of hyphen, underscore and alphanumeric
characters.

Here is an example showing how you can manage standard collections:

.. code-block:: python

from arangoasync import ArangoClient
from arangoasync.auth import Auth

# Initialize the client for ArangoDB.
async with ArangoClient(hosts="http://localhost:8529") as client:
auth = Auth(username="root", password="passwd")

# Connect to "test" database as root user.
db = await client.db("test", auth=auth)

# List all collections in the database.
await db.collections()

# Create a new collection named "students" if it does not exist.
# This returns an API wrapper for "students" collection.
if await db.has_collection("students"):
students = db.collection("students")
else:
students = await db.create_collection("students")

# Retrieve collection properties.
name = students.name
db_name = students.db_name
properties = await students.properties()
count = await students.count()

# Perform various operations.
await students.truncate()

# Delete the collection.
await db.delete_collection("students")
61 changes: 61 additions & 0 deletions docs/database.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
Databases
---------

ArangoDB server can have an arbitrary number of **databases**. Each database
has its own set of :doc:`collections <collection>` and graphs.
There is a special database named ``_system``, which cannot be dropped and
provides operations for managing users, permissions and other databases. Most
of the operations can only be executed by admin users. See :doc:`user` for more
information.

**Example:**

.. code-block:: python

from arangoasync import ArangoClient
from arangoasync.auth import Auth

# Initialize the client for ArangoDB.
async with ArangoClient(hosts="http://localhost:8529") as client:
auth = Auth(username="root", password="passwd")

# Connect to "_system" database as root user.
sys_db = await client.db("_system", auth=auth)

# List all databases.
await sys_db.databases()

# Create a new database named "test" if it does not exist.
# Only root user has access to it at time of its creation.
if not await sys_db.has_database("test"):
await sys_db.create_database("test")

# Delete the database.
await sys_db.delete_database("test")

# Create a new database named "test" along with a new set of users.
# Only "jane", "john", "jake" and root user have access to it.
if not await sys_db.has_database("test"):
await sys_db.create_database(
name="test",
users=[
{"username": "jane", "password": "foo", "active": True},
{"username": "john", "password": "bar", "active": True},
{"username": "jake", "password": "baz", "active": True},
],
)

# Connect to the new "test" database as user "jane".
db = await client.db("test", auth=Auth("jane", "foo"))

# Make sure that user "jane" has read and write permissions.
await sys_db.update_permission(username="jane", permission="rw", database="test")

# Retrieve various database and server information.
name = db.name
version = await db.version()
status = await db.status()
collections = await db.collections()

# Delete the database. Note that the new users will remain.
await sys_db.delete_database("test")
131 changes: 131 additions & 0 deletions docs/document.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
Documents
---------

In python-arango-async, a **document** is an object with the following
properties:

* Is JSON serializable.
* May be nested to an arbitrary depth.
* May contain lists.
* Contains the ``_key`` field, which identifies the document uniquely within a
specific collection.
* Contains the ``_id`` field (also called the *handle*), which identifies the
document uniquely across all collections within a database. This ID is a
combination of the collection name and the document key using the format
``{collection}/{key}`` (see example below).
* Contains the ``_rev`` field. ArangoDB supports MVCC (Multiple Version
Concurrency Control) and is capable of storing each document in multiple
revisions. Latest revision of a document is indicated by this field. The
field is populated by ArangoDB and is not required as input unless you want
to validate a document against its current revision.

For more information on documents and associated terminologies, refer to
`ArangoDB manual`_. Here is an example of a valid document in "students"
collection:

.. _ArangoDB manual: https://docs.arangodb.com

.. testcode::

{
'_id': 'students/bruce',
'_key': 'bruce',
'_rev': '_Wm3dzEi--_',
'first_name': 'Bruce',
'last_name': 'Wayne',
'address': {
'street' : '1007 Mountain Dr.',
'city': 'Gotham',
'state': 'NJ'
},
'is_rich': True,
'friends': ['robin', 'gordon']
}

Standard documents are managed via collection API wrapper:

.. code-block:: python

from arangoasync import ArangoClient
from arangoasync.auth import Auth

# Initialize the client for ArangoDB.
async with ArangoClient(hosts="http://localhost:8529") as client:
auth = Auth(username="root", password="passwd")

# Connect to "test" database as root user.
db = await client.db("test", auth=auth)

# Get the API wrapper for "students" collection.
students = db.collection("students")

# Create some test documents to play around with.
lola = {"_key": "lola", "GPA": 3.5, "first": "Lola", "last": "Martin"}
abby = {"_key": "abby", "GPA": 3.2, "first": "Abby", "last": "Page"}
john = {"_key": "john", "GPA": 3.6, "first": "John", "last": "Kim"}
emma = {"_key": "emma", "GPA": 4.0, "first": "Emma", "last": "Park"}

# Insert a new document. This returns the document metadata.
metadata = await students.insert(lola)
assert metadata["_id"] == "students/lola"
assert metadata["_key"] == "lola"

# Insert multiple documents.
await students.insert_many([abby, john, emma])

# Check if documents exist in the collection.
assert await students.has("lola")

# Retrieve the total document count.
count = await students.count()

# Retrieve one or more matching documents.
async for student in await students.find({"first": "John"}):
assert student["_key"] == "john"
assert student["GPA"] == 3.6
assert student["last"] == "Kim"

# Retrieve one or more matching documents, sorted by a field.
async for student in await students.find({"first": "John"}, sort=[{"sort_by": "GPA", "sort_order": "DESC"}]):
assert student["_key"] == "john"
assert student["GPA"] == 3.6
assert student["last"] == "Kim"

# Retrieve a document by key.
await students.get("john")

# Retrieve a document by ID.
await students.get("students/john")

# Retrieve a document by body with "_id" field.
await students.get({"_id": "students/john"})

# Retrieve a document by body with "_key" field.
await students.get({"_key": "john"})

# Retrieve multiple documents by ID, key or body.
await students.get_many(["abby", "students/lola", {"_key": "john"}])

# Update a single document.
lola["GPA"] = 2.6
await students.update(lola)

# Update one or more matching documents.
await students.update_match({"last": "Park"}, {"GPA": 3.0})

# Replace a single document.
emma["GPA"] = 3.1
await students.replace(emma)

# Replace one or more matching documents.
becky = {"first": "Becky", "last": "Solis", "GPA": "3.3"}
await students.replace_match({"first": "Emma"}, becky)

# Delete a document by body with "_id" or "_key" field.
await students.delete(emma)

# Delete multiple documents. Missing ones are ignored.
await students.delete_many([abby, emma])

# Delete one or more matching documents.
await students.delete_match({"first": "Emma"})
19 changes: 19 additions & 0 deletions docs/errno.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Error Codes
-----------

Python-Arango-Async provides ArangoDB error code constants for convenience.

**Example**

.. testcode::

from arangoasync import errno

# Some examples
assert errno.NOT_IMPLEMENTED == 9
assert errno.DOCUMENT_REV_BAD == 1239
assert errno.DOCUMENT_NOT_FOUND == 1202

For more information, refer to `ArangoDB manual`_.

.. _ArangoDB manual: https://www.arangodb.com/docs/stable/appendix-error-codes.html
Loading
Loading