Skip to main content

This package offers both synchronous and asynchronous implementations of a standardized Python API to communicate with dserver.

Project description

dtool PyPI GitHub tag (latest by date) GitHub Workflow Status Documentation Status

Python API for interacting with dserver.

This package offers a class-based asynchronous lookup API within dtool_lookup_api.core.LookupClient, a simple class-less wrapper around it at dtool_lookup_api.asynchronous, and a synchronous interface on top at dtool_lookup_api.synchronous.

Direct imports of utility functions from dtool_lookup_api in the examples below forward to the synchronous API variant.

Installation

To install the dtool_lookup_api package.

pip install dtool_lookup_api

This package depends on a dserver instance to talk to.

Configuration

The API needs to know the URL of the lookup server

export DSERVER_URL=https://localhost:5000

You may also need specify an access token generated on the server

export DSERVER_TOKEN=$(flask user token testuser)

Instead of specifying the access token directly, it is also possible to provide

export DSERVER_TOKEN_GENERATOR_URL=https://localhost:5001
export DSERVER_USERNAME=my-username
export DSERVER_PASSWORD=my-password

for the API to request a token. This, however, is intended only for testing purposes and strongly discouraged in a production environment, as your password would reside within environment variables or the dtool config file as clear text.

Our recommended setup is a combination of

export DSERVER_URL=https://localhost:5000
export DSERVER_TOKEN_GENERATOR_URL=https://localhost:5001

in the config. If used interactively, the API will then ask for your credentials at the first interaction and cache the provided values for this session, i.e.

In [1]: from dtool_lookup_api import query
   ...: res = query(
   ...:     {
   ...:         'readme.owners.name': {'$regex': '^Testing User$'},
   ...:     }
   ...: )
Authentication URL https://localhost:5001/token username:my-username
Authentication URL https://localhost:5001/token password:

In [2]: res
Out[2]:
[{'base_uri': 'smb://test-share',
  'created_at': 'Sun, 08 Nov 2020 18:38:40 GMT',
  'creator_username': 'jotelha',
  'dtoolcore_version': '3.17.0',
  'frozen_at': 'Wed, 11 Nov 2020 17:20:30 GMT',
  'name': 'simple_test_dataset',
  'tags': [],
  'type': 'dataset',
  'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
  'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

In [3]: from dtool_lookup_api import all
   ...: all()
Out[4]:
[{'base_uri': 'smb://test-share',
  'created_at': 1604860720.736269,
  'creator_username': 'jotelha',
  'frozen_at': 1604921621.719575,
  'name': 'simple_test_dataset',
  'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
  'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

Credentials caching and interactive prompting are turned off with

In [1]: import dtool_lookup_api.core.config
   ...: dtool_lookup_api.core.config.Config.interactive = False
   ...: dtool_lookup_api.core.config.Config.cache = False

In [2]: from dtool_lookup_api import all
   ...: all()
...
RuntimeError: Authentication failed

For testing purposes, it is possible to disable SSL certificates validation with

export DSERVER_VERIFY_SSL=false

As usual, these settings may be specified within the default dtool configuration file as well, i.e. at ~/.config/dtool/dtool.json

{
    "DSERVER_TOKEN_GENERATOR_URL": "https://localhost:5001/token",
    "DSERVER_URL": "https://localhost:5000"
}

List all datasets

To list all registered datasets

In [1]: from dtool_lookup_api import get_datasets
   ...: res = get_datasets()

In [2]: res
Out[2]:
[{'base_uri': 'smb://test-share',
'created_at': 1604860720.736269,
'creator_username': 'jotelha',
'frozen_at': 1604921621.719575,
'name': 'simple_test_dataset',
'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

Looking up datasets by UUID

To lookup URIs from a dataset UUID within Python

In [1]: from dtool_lookup_api import get_datasets_by_uuid
   ...: uuid = "1a1f9fad-8589-413e-9602-5bbd66bfe675"
   ...: res = get_datasets_by_uuid(uuid)

In [2]: res
Out[2]:
[{'base_uri': 'smb://test-share',
  'created_at': 1604860720.736269,
  'creator_username': 'jotelha',
  'frozen_at': 1604921621.719575,
  'name': 'simple_test_dataset',
  'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
  'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

Full text searching

Full text search for the word “test”

In [1]: from dtool_lookup_api import get_datasets
    ...: res = get_datasets(free_text="test")

In [2]: res
Out[2]:
[{'base_uri': 'smb://test-share',
  'created_at': 1604860720.736,
  'creator_username': 'jotelha',
  'dtoolcore_version': '3.17.0',
  'frozen_at': 1605027357.308,
  'name': 'simple_test_dataset',
  'tags': [],
  'type': 'dataset',
  'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
  'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

Manifest

Request the manifest of a particular dataset by URI

In [1]: from dtool_lookup_api import get_manifest
   ...: uri = 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675'
   ...: res = get_manifest(uri)

In [2]: res
Out[2]:
{'dtoolcore_version': '3.17.0',
 'hash_function': 'md5sum_hexdigest',
 'items': {'eb58eb70ebcddf630feeea28834f5256c207edfd': {'hash': '2f7d9c3e0cfd47e8fcab0c12447b2bf0',
   'relpath': 'simple_text_file.txt',
   'size_in_bytes': 17,
   'utc_timestamp': 1605027357.284966}}}

Readme

Request the readme content of a particular dataset by URI

In [1]: from dtool_lookup_api import get_readme
    ..: res = get_readme('smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675')

In [2]: import yaml
    ..: yaml.safe_load(res)
Out[2]:
{'creation_date': '2020-11-08',
'description': 'testing description',
'expiration_date': '2022-11-08',
'funders': [{'code': 'testing_code',
 'organization': 'testing_organization',
 'program': 'testing_program'}],
'owners': [{'email': 'testing@test.edu',
 'name': 'Testing User',
 'orcid': 'testing_orcid',
 'username': 'testing_user'}],
'project': 'testing project'}

Direct mongo language queries

To list all datasets at a certain base URI with their name matching some regular expression pattern, send a direct mongo language query to the server with

In [15]: from dtool_lookup_api import query
    ...: res = query(
    ...:     {
    ...:         'base_uri': 'smb://test-share',
    ...:         'name': {'$regex': 'test'},
    ...:     }
    ...: )

In [16]: res
Out[16]:
[{'base_uri': 'smb://test-share',
'created_at': 'Sun, 08 Nov 2020 18:38:40 GMT',
'creator_username': 'jotelha',
'dtoolcore_version': '3.17.0',
'frozen_at': 'Tue, 10 Nov 2020 16:55:57 GMT',
'name': 'simple_test_dataset',
'tags': [],
'type': 'dataset',
'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

It is possible to search readme content via

In [21]: from dtool_lookup_api import query
    ...: res = query(
    ...:     {
    ...:         'readme.owners.name': {'$regex': '^Testing User$'},
    ...:     }
    ...: )

In [22]: res
Out[22]:
[{'base_uri': 'smb://test-share',
  'created_at': 'Sun, 08 Nov 2020 18:38:40 GMT',
  'creator_username': 'jotelha',
  'dtoolcore_version': '3.17.0',
  'frozen_at': 'Tue, 10 Nov 2020 16:55:57 GMT',
  'name': 'simple_test_dataset',
  'tags': [],
  'type': 'dataset',
  'uri': 'smb://test-share/1a1f9fad-8589-413e-9602-5bbd66bfe675',
  'uuid': '1a1f9fad-8589-413e-9602-5bbd66bfe675'}]

This requires the server-side dserver-direct-mongo-plugin.

TODO: Response from server-side direct mongo plugin still yields dates as strings. Fix within https://github.com/IMTEK-Simulation/dserver-direct-mongo-plugin.

Usage on Jupyter notebook

The current implementation via asgiref.async_to_sync (https://github.com/django/asgiref) hinders the use of the synchronous interface within Jupyter notebooks. Directly use the asynchronous api instead

import dtool_lookup_api.asynchronous as dl
res = await dl.query({
    'base_uri': 'smb://test-share',
    'name': {'$regex': 'test'},
})

The drawback of the above approach is that the same code doesn’t work in python and in jupyter (await outsite of a function is a syntax error in non-interactive python context). The code below can be executed in both contexts:

import dtool_lookup_api.asynchronous as dl
if asyncio.get_event_loop().is_running():
    # then we are in jupyter notebook
    # this allows nested event loops, i.e. calls to asyncio.run inside the notebook as well
    # This way, the same code works in notebook and python
    import nest_asyncio
    nest_asyncio.apply()

def query(query_dict):
    return asyncio.run(dl.query(query_dict))

query({
    'base_uri': 'smb://test-share',
    'name': {'$regex': 'test'},
})

See https://github.com/jupyter/notebook/issues/3397#issuecomment-419386811, https://ipython.readthedocs.io/en/stable/interactive/autoawait.html

Testing

Install editable with testing requirements with

pip install -e .[test]

By default, tests rely on the demo.dtool.dev demonstrator instance.

This can be changed by configuring

DSERVER_ADDRESS_AND_CREDENTIALS = {
    "DSERVER_URL": "https://demo.dtool.dev/lookup",
    "DSERVER_TOKEN_GENERATOR_URL": "https://demo.dtool.dev/token",
    "DSERVER_USERNAME": "testuser",
    "DSERVER_PASSWORD": "test_password",
    "DSERVER_VERIFY_SSL": False,
}

within tests/conftest.py.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dtool_lookup_api-0.9.1.tar.gz (35.5 kB view details)

Uploaded Source

Built Distribution

dtool_lookup_api-0.9.1-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

File details

Details for the file dtool_lookup_api-0.9.1.tar.gz.

File metadata

  • Download URL: dtool_lookup_api-0.9.1.tar.gz
  • Upload date:
  • Size: 35.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for dtool_lookup_api-0.9.1.tar.gz
Algorithm Hash digest
SHA256 ee89315a129993c007916d82a1ed541dc6368d0ceac35bbdcaa21282db381189
MD5 3b2ff9975d71656ad66f24743df6b6d7
BLAKE2b-256 384a55fd8a6b106779622e407925a383ea5547fc6a9c8b43dae91da6cb4ffbd0

See more details on using hashes here.

File details

Details for the file dtool_lookup_api-0.9.1-py3-none-any.whl.

File metadata

File hashes

Hashes for dtool_lookup_api-0.9.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a3e613ca7156c94d302a088aacf3f2fe6459c107ba69b71dd2d5fe23d79363dd
MD5 a2f4971933be96ac4d7ddbee38548603
BLAKE2b-256 3a6ef0391fbf2f2c040be4932ac5e1c58c97ca237433532a89ef846244720396

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page