Skip to content
Open
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
3 changes: 0 additions & 3 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,3 @@ jobs:

- name: Deploy to pypi
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/TerminusDB?style=social)](https://www.reddit.com/r/TerminusDB/)
[![Twitter](https://img.shields.io/twitter/follow/terminusdb?color=skyblue&label=Follow%20on%20Twitter&logo=twitter&style=flat)](https://twitter.com/TerminusDB)

[![release version](https://img.shields.io/pypi/v/terminusdb-client.svg?logo=pypi)](https://pypi.python.org/pypi/terminusdb-client/)
[![downloads](https://img.shields.io/pypi/dm/terminusdb-client.svg?logo=pypi)](https://pypi.python.org/pypi/terminusdb-client/)
[![release version](https://img.shields.io/pypi/v/terminusdb.svg?logo=pypi)](https://pypi.python.org/pypi/terminusdb/)
[![downloads](https://img.shields.io/pypi/dm/terminusdb.svg?logo=pypi)](https://pypi.python.org/pypi/terminusdb/)

[![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.org/docs/python)
Expand All @@ -18,6 +18,10 @@

> Python client for TerminusDB and TerminusCMS.

> **Migrating from `terminusdb-client`?** This package was formerly known as
> `terminusdb-client`. Simply install `terminusdb` instead — both `import terminusdb`
> and `import terminusdb_client` continue to work, so no code changes are required.

[**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, with full git-for-data version control.
Expand All @@ -28,26 +32,26 @@ graph all through a simple document API, with full git-for-data version control.

## Requirements

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

## Release Notes and Previous Versions

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.
TerminusDB Client v12 works with TerminusDB v12 onwards 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:
`python -m pip install terminusdb-client`
`python -m pip install terminusdb`

This only includes the core Python Client (Client) and WOQLQuery.

If you want to use woqlDataframe or the import and export CSV function in the Scaffolding CLI tool:

`python -m pip install terminusdb-client[dataframe]`
`python -m pip install terminusdb[dataframe]`

*if you are installing from `zsh` you have to quote the argument like this:*

`python -m pip install 'terminusdb-client[dataframe]'`
`python -m pip install 'terminusdb[dataframe]'`

- Install from source:

Expand All @@ -66,19 +70,21 @@ If you want to use woqlDataframe or the import and export CSV function in the Sc
Connect to local host

```Python
from terminusdb_client import Client
from terminusdb import Client

client = Client("http://127.0.0.1:6363/")
client.connect()
```

The previous import path `from terminusdb_client import Client` also continues to work.

Connect to TerminusDB in the cloud

*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
from terminusdb import Client

team="MyTeam"
client = Client(f"https://studio.dfrnt.com/api/hosted/{team}/")
Expand All @@ -94,7 +100,7 @@ client.create_database("MyDatabase")
#### Create a schema

```Python
from terminusdb_client.schema import Schema, DocumentTemplate, RandomKey
from terminusdb.schema import Schema, DocumentTemplate, RandomKey

my_schema = Schema()

Expand Down
10 changes: 7 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
[tool.poetry]
name = "terminusdb-client"
name = "terminusdb"
version = "12.0.3"
description = "Python client for Terminus DB"
authors = ["TerminusDB group"]
description = "Terminus DB Python client"
authors = ["TerminusDB group", "DFRNT AB"]
license = "Apache Software License"
readme = "README.md"
packages = [
{include = "terminusdb_client"},
{include = "terminusdb"},
]

[tool.poetry.dependencies]
python = ">=3.9.0,<3.13"
Expand Down
4 changes: 4 additions & 0 deletions terminusdb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Re-export everything from terminusdb_client for the new import path.
# Both `import terminusdb` and `import terminusdb_client` are supported.
from terminusdb_client import * # noqa
from terminusdb_client import Client, WOQLClient, WOQLQuery, Var, Vars, Patch, GraphType, WOQLDataFrame, WOQLSchema # noqa
2 changes: 2 additions & 0 deletions terminusdb/client/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from terminusdb_client.client import * # noqa
from terminusdb_client.client import Client, GraphType, Patch # noqa
1 change: 1 addition & 0 deletions terminusdb/query_syntax/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from terminusdb_client.query_syntax import * # noqa
1 change: 1 addition & 0 deletions terminusdb/schema/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from terminusdb_client.schema import * # noqa
28 changes: 17 additions & 11 deletions terminusdb_client/scripts/scripts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import builtins
import datetime as dt
import enum
import json
Expand Down Expand Up @@ -439,7 +438,6 @@ def importcsv(
embedded = [x.lower().replace(" ", "_") for x in embedded]
try:
pd = import_module("pandas")
np = import_module("numpy")
except ImportError:
raise ImportError(
"Library 'pandas' is required to import csv, either install 'pandas' or install woqlDataframe requirements as follows: python -m pip install -U terminus-client-python[dataframe]"
Expand All @@ -455,19 +453,27 @@ def importcsv(

def _df_to_schema(class_name, df):
class_dict = {"@type": "Class", "@id": class_name}
np_to_buildin = {
v: getattr(builtins, k)
for k, v in np.sctypeDict.items()
if k in vars(builtins)
}
np_to_buildin[np.datetime64] = dt.datetime
for col, dtype in dict(df.dtypes).items():
if embedded and col in embedded:
converted_type = class_name
else:
converted_type = np_to_buildin[dtype.type]
if converted_type is object:
converted_type = str # pandas treats all string as objects
# Map pandas/numpy dtype to Python type
# Uses dtype.kind for compatibility with numpy 2.0+ and pandas 3.0+
dtype_kind = getattr(dtype, "kind", "O")
if dtype.type is str or dtype_kind in ("U", "O", "S", "T"):
converted_type = str
elif dtype_kind in ("i", "u"):
converted_type = int
elif dtype_kind == "f":
converted_type = float
elif dtype_kind == "b":
converted_type = bool
elif dtype_kind == "M":
converted_type = dt.datetime
elif dtype_kind == "m":
converted_type = dt.timedelta
else:
converted_type = str
converted_type = wt.to_woql_type(converted_type)

if id_ and col == id_:
Expand Down
19 changes: 9 additions & 10 deletions terminusdb_client/tests/integration_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,29 @@
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 200 (success) or 404 (not found but server is up)
# 401 (unauthorized) also indicates server is running but needs auth
return response.status_code in [200, 404]
requests.get("http://127.0.0.1:6363", timeout=2)
# Any HTTP response means the server is running
return True
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]
requests.get("http://127.0.0.1:6366", timeout=2)
# Any HTTP response means the server is running
return True
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]
requests.get("http://127.0.0.1:6367", timeout=2)
# Any HTTP response means the server is running
return True
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
return False

Expand Down
42 changes: 18 additions & 24 deletions terminusdb_client/tests/integration_tests/test_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,18 @@ class TestServerDetection:
"""Test server detection helper functions"""

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_local_server_running_200(self, mock_get):
"""Test local server detection returns True for HTTP 200"""
mock_response = Mock()
mock_response.status_code = 200
mock_get.return_value = mock_response
def test_local_server_running_any_response(self, mock_get):
"""Test local server detection returns True for any HTTP response"""
mock_get.return_value = Mock()

assert is_local_server_running() is True
mock_get.assert_called_once_with("http://127.0.0.1:6363", timeout=2)

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_local_server_running_404(self, mock_get):
"""Test local server detection returns True for HTTP 404"""
def test_local_server_running_401(self, mock_get):
"""Test local server detection returns True for HTTP 401 (unauthorized)"""
mock_response = Mock()
mock_response.status_code = 404
mock_response.status_code = 401
mock_get.return_value = mock_response

assert is_local_server_running() is True
Expand All @@ -47,20 +45,18 @@ def test_local_server_not_running_timeout(self, mock_get):
assert is_local_server_running() is False

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_docker_server_running_200(self, mock_get):
"""Test Docker server detection returns True for HTTP 200"""
mock_response = Mock()
mock_response.status_code = 200
mock_get.return_value = mock_response
def test_docker_server_running_any_response(self, mock_get):
"""Test Docker server detection returns True for any HTTP response"""
mock_get.return_value = Mock()

assert is_docker_server_running() is True
mock_get.assert_called_once_with("http://127.0.0.1:6366", timeout=2)

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_docker_server_running_404(self, mock_get):
"""Test Docker server detection returns True for HTTP 404"""
def test_docker_server_running_401(self, mock_get):
"""Test Docker server detection returns True for HTTP 401 (unauthorized)"""
mock_response = Mock()
mock_response.status_code = 404
mock_response.status_code = 401
mock_get.return_value = mock_response

assert is_docker_server_running() is True
Expand All @@ -73,20 +69,18 @@ def test_docker_server_not_running(self, mock_get):
assert is_docker_server_running() is False

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_jwt_server_running_200(self, mock_get):
"""Test JWT server detection returns True for HTTP 200"""
mock_response = Mock()
mock_response.status_code = 200
mock_get.return_value = mock_response
def test_jwt_server_running_any_response(self, mock_get):
"""Test JWT server detection returns True for any HTTP response"""
mock_get.return_value = Mock()

assert is_jwt_server_running() is True
mock_get.assert_called_once_with("http://127.0.0.1:6367", timeout=2)

@patch("terminusdb_client.tests.integration_tests.conftest.requests.get")
def test_jwt_server_running_404(self, mock_get):
"""Test JWT server detection returns True for HTTP 404"""
def test_jwt_server_running_401(self, mock_get):
"""Test JWT server detection returns True for HTTP 401 (unauthorized)"""
mock_response = Mock()
mock_response.status_code = 404
mock_response.status_code = 401
mock_get.return_value = mock_response

assert is_jwt_server_running() is True
Expand Down
Loading