Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
Expand Down
26 changes: 11 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,28 @@
[![downloads](https://img.shields.io/pypi/dm/terminusdb-client.svg?logo=pypi)](https://pypi.python.org/pypi/terminusdb-client/)

[![build status](https://img.shields.io/github/workflow/status/terminusdb/terminusdb-client-python/Python%20package?logo=github)](https://github.com/terminusdb/terminusdb-client-python/actions)
[![documentation](https://img.shields.io/github/deployments/terminusdb/terminusdb-client-python/github-pages?label=documentation&logo=github)](https://terminusdb.com/docs/python)
[![documentation](https://img.shields.io/github/deployments/terminusdb/terminusdb-client-python/github-pages?label=documentation&logo=github)](https://terminusdb.org/docs/python)
[![code coverage](https://codecov.io/gh/terminusdb/terminusdb-client-python/branch/main/graph/badge.svg?token=BclAUaOPnQ)](https://codecov.io/gh/terminusdb/terminusdb-client-python)
[![license](https://img.shields.io/github/license/terminusdb/terminusdb-client-python?color=pink&logo=apache)](https://github.com/terminusdb/terminusdb-client-python/blob/main/LICENSE)

> Python client for TerminusDB and TerminusCMS.

[**TerminusDB**][terminusdb] is an [open-source][terminusdb-repo] graph database
and document store. It allows you to link JSON documents in a powerful knowledge
graph all through a simple document API.
graph all through a simple document API, with full git-for-data version control.

[terminusdb]: https://terminusdb.com/
[terminusdb-docs]: https://terminusdb.com/docs/
[terminusdb]: https://terminusdb.org/
[terminusdb-docs]: https://terminusdb.org/docs/
[terminusdb-repo]: https://github.com/terminusdb/terminusdb

[**TerminusCMS**](https://terminusdb.com/terminuscms/) is a hosted headless content management system. It is built upon TerminusDB and is a developer-focused data management platform for complex data and content infrastructure. [Sign up and clone a demo project to see how it works][dashboard].

[dashboard]: https://dashboard.terminusdb.com/

## Requirements

- [TerminusDB v10.0](https://github.com/terminusdb/terminusdb-server)
- [TerminusDB v11.1](https://github.com/terminusdb/terminusdb-server)
- [Python >=3.8](https://www.python.org/downloads)

## Release Notes and Previous Versions

TerminusDB Client v10.0 works with TerminusDB v10.0 and TerminusCMS. Please check the [Release Notes](RELEASE_NOTES.md) to find out what has changed.
TerminusDB Client v11.1 works with TerminusDB v11.1 and the [DFRNT cloud service](https://dfrnt.com). Please check the [Release Notes](RELEASE_NOTES.md) to find out what has changed.

## Installation
- TerminusDB Client can be downloaded from PyPI using pip:
Expand Down Expand Up @@ -76,16 +72,16 @@ client = Client("http://127.0.0.1:6363/")
client.connect()
```

Connect to TerminusCMS
Connect to TerminusDB in the cloud

*check the documentation for TerminusCMS about how to add the [API token](https://terminusdb.com/docs/how-to-connect-terminuscms) to the environment variable*
*check the documentation on the DFRNT support page about how to add your [API token](https://support.dfrnt.com/portal/en/kb/articles/api) to the environment variable*


```Python
from terminusdb_client import Client

team="MyTeam"
client = Client(f"https://dashboard.terminusdb.com/{team}/")
client = Client(f"https://studio.dfrnt.com/api/hosted/{team}/")
client.connect(team="MyTeam", use_token=True)
```

Expand Down Expand Up @@ -185,11 +181,11 @@ Do you want to delete 'mydb'? WARNING: This operation is non-reversible. [y/N]:
mydb deleted.
```

### Please check the [full Documentation](https://terminusdb.com/docs/python) for more information.
### Please check the [full Documentation](https://terminusdb.org/docs/python) for more information.

## Guides & Tutorials

Visit our documentation for a range of short how-to guides, [how-to use the Python Client](https://terminusdb.com/docs/use-the-python-client) and [how to use the collaboration features with the Python Client](https://terminusdb.com/docs/collaboration-with-python-client). Alternatively, undertake the [Getting Started with the Python Client Tutorial Series.](https://github.com/terminusdb/terminusdb-tutorials/blob/main/getting_started/python-client/README.md).
Visit our documentation for a range of short how-to guides, [how-to use the Python Client](https://terminusdb.org/docs/use-the-python-client) and [how to use the collaboration features with the Python Client](https://terminusdb.org/docs/collaboration-with-python-client). Alternatively, undertake the [Getting Started with the Python Client Tutorial Series.](https://github.com/terminusdb/terminusdb-tutorials/blob/main/getting_started/python-client/README.md).

## Testing

Expand Down
9 changes: 9 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# TerminusDB Python Client Release Notes

## v11.1.0

- Add support for python 3.12
- Aligned with TerminusDB 11.1 overall and general preparation for v11.2.0

### Bug fixes

- Fix schema parameter to database construction

## v10.2.6

### Bug fixes
Expand Down
51 changes: 51 additions & 0 deletions docs/release_steps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Release Steps

## Quick Release

1. Update release notes (`RELEASE_NOTES.md`)
2. Bump version (bumpversion [patch|minor|major])
3. Push new tag (`git push origin main --tags`)

### Bump version (updates files + creates tag)

To not create a tag, use `--no-tag`.

```bash
bumpversion [patch|minor|major]
```

### Create and push tag (triggers automated PyPI deployment)

```bash
git push origin main --tags
```

**Monitor**: https://github.com/terminusdb/terminusdb-client-python/actions

## Details

### What bumpversion updates
- `terminusdb_client/__version__.py`
- `pyproject.toml`
- `.bumpversion.cfg`

### Automated deployment
Pushing a tag triggers GitHub Actions to:
- Run tests (Python 3.8-3.12)
- Build with Poetry
- Publish to PyPI

### Manual deployment (if needed)
```bash
poetry build
poetry publish
```

### Troubleshooting

**Version conflicts:** Never delete published PyPI versions. Create a new patch release instead.

## Prerequisites

- Install: `pip install bumpversion`
- PyPI publishing handled via `PYPI_API_TOKEN` secret in GitHub Actions
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ license = "Apache Software License"
readme = "README.md"

[tool.poetry.dependencies]
python = ">=3.8.0,<3.12"
python = ">=3.8.0,<3.13"
requests = "^2.31.0"
numpy = ">= 1.13.0"
numpydoc = "*"
Expand Down
76 changes: 69 additions & 7 deletions terminusdb_client/tests/integration_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,38 @@
import pytest
import requests

MAX_CONTAINER_STARTUP_TIME = 30
MAX_CONTAINER_STARTUP_TIME = 120 # Increased from 30 to 120 seconds for slower systems


# Check if a local TerminusDB test server is already running
def is_local_server_running():
"""Check if local TerminusDB server is running at http://127.0.0.1:6363"""
try:
response = requests.get("http://127.0.0.1:6363", timeout=2)
# Server responds with 404 for root path, which means it's running
return response.status_code in [200, 404]
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
return False


def is_docker_server_running():
"""Check if Docker TerminusDB server is already running at http://127.0.0.1:6366"""
try:
response = requests.get("http://127.0.0.1:6366", timeout=2)
# Server responds with 404 for root path, which means it's running
return response.status_code in [200, 404]
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
return False


def is_jwt_server_running():
"""Check if JWT Docker TerminusDB server is already running at http://127.0.0.1:6367"""
try:
response = requests.get("http://127.0.0.1:6367", timeout=2)
# Server responds with 404 for root path, which means it's running
return response.status_code in [200, 404]
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
return False


def is_docker_installed():
Expand Down Expand Up @@ -39,6 +70,13 @@ def docker_url_jwt(pytestconfig):
# we are using subprocess in case we need to access some of the outputs
# most likely
jwt_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InRlc3RrZXkifQ.eyJodHRwOi8vdGVybWludXNkYi5jb20vc2NoZW1hL3N5c3RlbSNhZ2VudF9uYW1lIjoiYWRtaW4iLCJodHRwOi8vdGVybWludXNkYi5jb20vc2NoZW1hL3N5c3RlbSN1c2VyX2lkZW50aWZpZXIiOiJhZG1pbkB1c2VyLmNvbSIsImlzcyI6Imh0dHBzOi8vdGVybWludXNodWIuZXUuYXV0aDAuY29tLyIsInN1YiI6ImFkbWluIiwiYXVkIjpbImh0dHBzOi8vdGVybWludXNodWIvcmVnaXN0ZXJVc2VyIiwiaHR0cHM6Ly90ZXJtaW51c2h1Yi5ldS5hdXRoMC5jb20vdXNlcmluZm8iXSwiaWF0IjoxNTkzNzY5MTgzLCJhenAiOiJNSkpuZEdwMHpVZE03bzNQT1RRUG1SSkltWTJobzBhaSIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwifQ.Ru03Bi6vSIQ57bC41n6fClSdxlb61m0xX6Q34Yh91gql0_CyfYRWTuqzqPMFoCefe53hPC5E-eoSFdID_u6w1ih_pH-lTTqus9OWgi07Qou3QNs8UZBLiM4pgLqcBKs0N058jfg4y6h9GjIBGVhX9Ni2ez3JGNcz1_U45BhnreE"

# Check if JWT server is already running (port 6367)
if is_jwt_server_running():
print("\n✓ Using existing JWT Docker TerminusDB server at http://127.0.0.1:6367")
yield ("http://127.0.0.1:6367", jwt_token)
return # Don't clean up - server was already running

pytestconfig.getoption("docker_compose")
output = subprocess.run(
[
Expand Down Expand Up @@ -79,7 +117,8 @@ def docker_url_jwt(pytestconfig):
if service.stdout == b"terminusdb-server\n":
try:
response = requests.get(test_url)
assert response.status_code == 200
# Server responds with 404 for root path, which means it's running
assert response.status_code in [200, 404]
break
except (requests.exceptions.ConnectionError, AssertionError):
pass
Expand All @@ -89,16 +128,37 @@ def docker_url_jwt(pytestconfig):

if seconds_waited > MAX_CONTAINER_STARTUP_TIME:
clean_up_container()
raise RuntimeError("Container was to slow to startup")
raise RuntimeError(f"JWT Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)")

yield (test_url, jwt_token)
clean_up_container()


@pytest.fixture(scope="module")
def docker_url(pytestconfig):
# we are using subprocess in case we need to access some of the outputs
# most likely
"""
Provides a TerminusDB server URL for integration tests.
Prefers local test server if running, otherwise starts Docker container.

NOTE: This fixture returns just the URL. Tests expect AUTOLOGIN mode (no authentication).
If using local server with authentication, use TERMINUSDB_AUTOLOGIN=true when starting it.
"""
# Check if local test server is already running (port 6363)
if is_local_server_running():
print("\n✓ Using existing local TerminusDB test server at http://127.0.0.1:6363")
print("⚠️ WARNING: Local server should be started with TERMINUSDB_AUTOLOGIN=true")
print(" Or use: TERMINUSDB_SERVER_AUTOLOGIN=true ./tests/terminusdb-test-server.sh restart")
yield "http://127.0.0.1:6363"
return # Don't clean up - server was already running

# Check if Docker container is already running (port 6366)
if is_docker_server_running():
print("\n✓ Using existing Docker TerminusDB server at http://127.0.0.1:6366")
yield "http://127.0.0.1:6366"
return # Don't clean up - server was already running

# No server found, start Docker container
print("\n⚠ No server found, starting Docker container with AUTOLOGIN...")
pytestconfig.getoption("docker_compose")
output = subprocess.run(
[
Expand Down Expand Up @@ -138,7 +198,9 @@ def docker_url(pytestconfig):
if service.stdout == b"terminusdb-server\n":
try:
response = requests.get(test_url)
assert response.status_code == 200
# Server responds with 404 for root path, which means it's running
assert response.status_code in [200, 404]
print(f"✓ Docker container started successfully after {seconds_waited}s")
break
except (requests.exceptions.ConnectionError, AssertionError):
pass
Expand All @@ -148,7 +210,7 @@ def docker_url(pytestconfig):

if seconds_waited > MAX_CONTAINER_STARTUP_TIME:
clean_up_container()
raise RuntimeError("Container was to slow to startup")
raise RuntimeError(f"Container was too slow to startup (waited {MAX_CONTAINER_STARTUP_TIME}s)")

yield test_url
clean_up_container()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3"

volumes:
terminusdb_storage:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3"

volumes:
terminusdb_storage:

Expand Down
Loading