Skip to main content

OpenDMA REST client for Python

Please see OpenDMA API for Python for an introduction to OpenDMA and it's Python API.

Provides an OpenDMA Adaptor consuming the OpenDMA REST API.

Installation

This project is available on PyPI. You can simply install it with pip:

pip install opendma-remote

Usage

Import this module and use the connect function to establish an OdmaSession with a REST-ful OpenDMA service:

import opendma.remote

session = opendma.remote.connect(endpoint="http://127.0.0.1:8080/opendma/",
                                 username="user",
								 password="secret")

Example

Run the tutorial REST service docker container:

docker run -p 8080:8080 ghcr.io/opendma/tutorial-xmlrepo:0.8

It will provide the tutorial xml repository. Make sure that this service is available by opening
http://localhost:8080/opendma
in a web browser.

Troubleshooting

If the GitHub Container Registry (ghcr.io) is blocked in your environment, you can use our mirror on Docker Hub:

docker run -p 8080:8080 opendma/tutorial-xmlrepo:latest

If the local port 8080 is already in use, you can map to a different port, e.g. 8090:

docker run -p 8090:8080 ghcr.io/opendma/tutorial-xmlrepo:latest

To run in deamon mode in the background:

docker run -d --name opendma-tutorial-xmlrepo -p 8080:8080 ghcr.io/opendma/tutorial-xmlrepo:latest

The following python application will establish a connection to this service and print out the entire repository:

from typing import Optional
from opendma.api import OdmaType, OdmaQName, OdmaId, OdmaObject, OdmaClass, OdmaPropertyInfo, OdmaFolder, OdmaAssociation, OdmaDocument, OdmaContentElement, OdmaDataContentElement, OdmaContent, OdmaVersionCollection, OdmaServiceException
import opendma.remote


def printClassTree(clazz: OdmaClass, indent: int = 0) -> None:
    print(f"{'  ' * indent}{clazz.get_qname()}")
    aspects = clazz.get_aspects()
    if aspects is not None:
        for aspect in aspects:
            print(f"{'  ' * indent}  @{aspect.get_qname()}")
    subclasses = clazz.get_sub_classes()
    if subclasses is not None:
        for subclass in subclasses:
            printClassTree(subclass, indent + 1)


def printObjectInfo(obj: OdmaObject, indent: int, include_props: bool, expand_special: bool):
    print(f"{'  ' * indent}{obj.get_id()} {obj.get_odma_class().get_qname()}")
    if not include_props:
        return
    properties = obj.get_odma_class().get_properties()
    indent = indent + 1
    for propinfo in properties:
        pi: OdmaPropertyInfo = propinfo
        prop = obj.get_property(pi.get_qname())
        type = prop.get_type()
        value = prop.get_value()
        if prop.is_multi_value():
            if type == OdmaType.REFERENCE:
                print(f"{'  ' * indent}{pi.get_qname()}=")
                for pos, item in enumerate(value):
                    id = item.get_id()
                    if '/' in str(id):
                        printObjectInfo(item, indent+1, True, False)
                    else:
                        print(f"{'  ' * (indent+3)}* Object<{id}>")
            else:
                print(f"{'  ' * indent}{pi.get_qname()}=")
                for pos, item in enumerate(value):
                    print(f"{'  ' * (indent+3)}[{pos}] {item}")
        else:
            if value == None:
                print(f"{'  ' * indent}{pi.get_qname()}=None")
            else:
                if type == OdmaType.REFERENCE:
                    if expand_special and (isinstance(value, OdmaVersionCollection) or isinstance(value, OdmaContentElement)):
                        print(f"{'  ' * indent}{pi.get_qname()}=")
                        printObjectInfo(value, indent+1, True, False)
                    else:
                        print(f"{'  ' * indent}{pi.get_qname()}=Object<{value.get_id()}>")
                elif type == OdmaType.CONTENT:
                    cnt: OdmaContent =  value
                    stream = cnt.get_stream()
                    data = stream.read(25)
                    hex_bytes = ' '.join(f'{b:02x}' for b in data)
                    print(f"{'  ' * indent}{pi.get_qname()}= {cnt.get_size()} bytes; first 25 bytes: {hex_bytes}")
                else:
                    print(f"{'  ' * indent}{pi.get_qname()}={value}")


def printFolderTree(folder: OdmaFolder, indent: int = 0) -> None:
    print(f"{'  ' * indent}{folder.get_title()}")
    associations = folder.get_associations()
    if associations is not None:
        for association in associations:
            assoc: OdmaAssociation = association
            print(f"{'  ' * indent}  -{assoc.get_name()}")
            printObjectInfo(assoc.get_containable(), indent + 2, True, True)
    subfolders = folder.get_sub_folders()
    if subfolders is not None:
        for subfolder in subfolders:
            printFolderTree(subfolder, indent + 1)


def printRepo(endpoint: str, username: Optional[str] = None, password: Optional[str] = None, includeClassTree: bool = True, includeFolderTree: bool = True) -> None:
    print(f"Trying to connect...")
    session = opendma.remote.connect(endpoint=endpoint, username=username, password=password, requestTraceLevel = 0)
    print(f"Connected.")
    print(f"Repositories: {session.get_repository_ids()}")
    repoId = session.get_repository_ids()[0]
    print(f"Trying to get repository {repoId}...")
    repo = session.get_repository(repoId)
    print(f"Got repository.")
    print(f"  Id: {repo.get_id()}")
    print(f"  Display Name: {repo.get_display_name()}")
    if includeClassTree:
        print(f"")
        print(f"Class tree:")
        rootClass = repo.get_root_class()
        printClassTree(rootClass)
    if includeFolderTree:
        print(f"")
        print(f"Folder tree:")
        rootFolder = repo.get_root_folder()
        if rootFolder is None:
            print(f"No root folder is in this repository")
        else:
            printFolderTree(rootFolder)


if __name__ == "__main__":
    printRepo(endpoint="http://127.0.0.1:8080/opendma/")

Metadata

Release files for opendma-remote 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opendma-remote 0.8.0
File Size Uploaded Provenance
opendma_remote-0.8.0.tar.gz 11.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for opendma-remote 0.8.0
File Interpreter ABI Platform
opendma_remote-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 21.1 kB

Release files / opendma_remote-0.8.0.tar.gz

Download URL opendma_remote-0.8.0.tar.gz
Size 11.5 kB
Tags Source
SHA-256 checksum
How to use checksums
351008a5439792d43a18c37a6b5234fb363a2f80752970d81b8b4bf6567bf0ed
BLAKE2b-256 checksum
How to use checksums
c0d26786b209084785afa22e10e5503c2c9b5ff25c9e8d1a491f74ef11736103
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release files / opendma_remote-0.8.0-py3-none-any.whl

Download URL opendma_remote-0.8.0-py3-none-any.whl
Size 9.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4a19deeb744636c6f45c605691f594569e02f6f5fee0073f84fa30e4ed008129
BLAKE2b-256 checksum
How to use checksums
6841e3b85bc9ff532d922ce88ca1089fee611944f8694b8900a741d0d9eebf2b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Security for opendma-remote 0.8.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page