Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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