From 07e45a426895407edb979b20cb740cce6a9496d3 Mon Sep 17 00:00:00 2001 From: Stuart MacGowan Date: Wed, 23 Oct 2024 13:35:45 +0100 Subject: [PATCH] Prepare for PyPI release --- MANIFEST.in | 3 + README.md | 321 ++++++++++++++++------------------------------------ setup.py | 47 ++++++-- 3 files changed, 140 insertions(+), 231 deletions(-) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..191028f --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE +include README.md +recursive-include slivka_client *.py diff --git a/README.md b/README.md index 54f27cd..2f98d72 100644 --- a/README.md +++ b/README.md @@ -1,281 +1,160 @@ -Introduction -============ -Slivka python client is a convenience wrapper around the popular [requests](https://requests.readthedocs.io/en/latest/) library for Python. -It is designed to provide an interface for communicating with the slivka REST API using Python. +# Slivka Python Client -It includes: +[![PyPI version](https://badge.fury.io/py/slivka-client.svg)](https://badge.fury.io/py/slivka-client) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) -- Programmatic access to slivka API with simple objects -- Command-line interface -- Interactive widgets for Jupyter notebooks +Slivka Python client is a convenience wrapper around the popular [requests](https://requests.readthedocs.io/en/latest/) library. It provides an interface for communicating with the Slivka REST API using Python. -Installation -============ +## Features -The easiest way to install the slivka client is through the conda package manager. -Install it from the *slivka* channel using +- Programmatic access to Slivka API with simple objects. +- Command-line interface. +- Interactive widgets for Jupyter notebooks. -``` -conda install -c slivka slivka-client +--- + +## Installation + +The easiest way to install the Slivka client is via `pip` from PyPI: + +```bash +pip install slivka-client ``` -Alternatively, you can install it from sources. Clone the git repository to your -machine and run +Alternatively, if you prefer to use the conda package manager, you can install it from the Slivka channel: +```bash +conda install -c slivka slivka-client ``` + +To install it from the source code, clone the Git repository to your machine and run: + +```bash python setup.py install ``` -After the installation has completed successfully, you can import slivka_client -from Python or run `slivka-cli` command line tool. +--- -Usage -===== +## Getting Started -To begin, import `slivka_client` library and create a `SlivkaClient` instance using the server URL. +After installing the Slivka client, you can begin by importing it and connecting to a Slivka server: ```python import slivka_client -client = slivka_client.SlivkaClient("http://www.example.org/slivka/") +# Replace with your actual server URL +client = slivka_client.SlivkaClient("https://your-slivka-server-url/") ``` -> [!IMPORTANT] -> Remember that locations typically end with a slash. Missing trailing slash may result -> in resources not being located properly. - -In order to verify the server was found successfully, you may query the version of slivka from the server. +You can verify the connection by checking the server version: ```python ->>> client.version -Version(client='1.2.1b1', server='0.8.1', API='1.1') +print(client.version) ``` -The version displays the current client version, server version and -API compatibility version. +--- -If you see an `HTTPError: 404` exception, check the URL the client tries to access and -make sure the server is available under that address. The error may be caused by a missing -trailing slash in the URL. +## Usage -Services --------- +### Accessing Services -A list of all available services can be retrieved using the `services` property of the `SlivkaClient` object. +To retrieve the list of available services from the Slivka server: ```python ->>> client.services -[Service(...), Service(...), Service(...), ...] +services = client.services ``` -Each element of that list represents a single service. It contains its id, name, description, parameters, etc. -The list is retrieved from the server only once and is cached to improve performance on subsequent access to the services. -If you expect the services list to change on the server side you can force reloading them with the `reload_services()` method. +Each element in the list is a `Service` object containing details such as the service's `id`, `name`, and `description`. + +To access a specific service by its ID, use: ```python -client.reload_services() +service = client.get_service('example_service_id') +# Or +service = client['example_service_id'] ``` -For convenience, you can get a particular service by its id using the `get_service(id)` method or -by a dictionary item access on a client object. +--- + +### Starting Jobs + +Once you've selected the desired service, you can submit jobs using the `submit_job()` method. This requires providing parameters in the form of dictionaries. + +Here’s an example of submitting a job: ```python ->>> client.get_service('example') -Service(id='example', name='Example', ...) +data = { + 'param0': 13, + 'param1': 'foobar' +} + +files = { + 'input0': open("input-file.txt", "rb"), + 'input1': b"data data data data\n" +} ->>> client['example'] -Service(id='example', name='Example', ...) +job = service.submit_job(data=data, files=files) ``` -The `Service` objects provide the following read-only properties: - -
-
id
-
identifier of the service
- -
url
-
location of the service resource
- -
name
-
name of the service
- -
description
-
long description of the service
- -
author
-
one or more authors of the service
- -
version
-
version of the service
- -
license
-
license of the service
- -
classifiers
-
list of classifiers, tags or categories that help identify or group services
- -
parameters
-
list of service parameters represented as Parameter objects (explained below)
- -
presets
-
-list of parameter presets offered for this service - -
-
Preset.id
-
preset identifier
-
Preset.name
-
name of the preset
-
Preset.description
-
long description of the preset
-
Preset.values
-
dictionary of parameter value Dict[str, Any]
-
-
- -
status
-
-service operation status -
-
Status.status
-
status name; one of: OK, WARNING, DOWN
-
Status.message
-
error message
-
Status.timestamp
-
the time the status was last updated
-
-
-
- -### Parameters - -Each object in the *parameters* list describes a parameter that can be provided -for the service. For each parameter present in the list, you can provide one -(sometimes multiple) value when submitting the job. -All parameter objects offer the following read-only properties: - -
-
id
-
parameter identifier; use it as a key in the data dictionary when submitting jobs.
-
type
-
-parameter type; one of: integer, decimal, text, flag, choice, file, undefined, unknown -
-
name
-
human-friendly name of the parameter
-
description
-
longer description of the parameter
-
required
-
if the parameter is required
-
array
-
if multiple values are allowed for the parameter
-
default
-
default value for the parameter that is used if none is provided
-
- -More specialised data structures exist for each type and provide additional properties -specific to that type. - -Starting jobs -------------- - -New jobs are submitted to the server using `submit_job()` method of the *Service* -object. The method takes two arguments *data* and *files* which are both -dictionaries containing parameter ids and corresponding values. -The values are properly encoded and sent as a POST request to the server. -Values for the parameters that require a file as an input need to be provided -through the *files* argument to ensure the HTTP request includes the contents -of the file. - -Values provided in the *data* dictionary are converted to strings automatically. -Values provided in the *files* dictionary can be open files or streams, -in which case the content of the stream is sent to the server, or bytes, which are sent directly. - -> [!IMPORTANT] -> Files and streams should be opened in binary mode to avoid potential issues with -> non-ascii characters. - -On successful job submission the method returns a *Job* object containing -submission data. This object is used to check job status and retrieve -results. -If the input parameter are not valid, the method throws a *SubmissionError* -containing a list of errors encountered during input processing. - -Example: +This method returns a `Job` object that allows you to monitor the status and retrieve results. + +### Polling Jobs and Retrieving Results + +You can check the status of a submitted job using: ```python ->>> service = client['example'] ->>> job = service.submit_job( -... data={ -... 'param0': 13, -... 'param1': 'foobar' -... }, -... files={ -... 'input0': open("input-file.txt", "rb"), -... 'input1': b"data data data data\n" -... } -... ) +print(job.status) ``` -Polling jobs and retrieving results ------------------------------------ - -The *Job* object returned by the `submit_job()` method provides the capability -to inspect submission data and poll the status of the submitted job and -retrieve result files from the server. +To retrieve the results: -The 'id' property of the job object holds a server-generated identifier, uniquely denoting the job within the server's context. -The job additionally holds *service*, *parameters* and *submission_time* information. +```python +for result in job.results: + result.dump("output_file.txt") +``` -The current job status can be obtained using a *status* property which gets updated -from the server every time it is accessed but no more frequently than once every five seconds. +### File Handling -The result files can be accessed with a *results* property. Just like *status*, the *results* -is updated from the server every time it is accessed. It returns a list of *slivka_client.File* -objects which can be used to inspect file metadata and download their content. +The `File` object provides a convenient way to download results: -> [!NOTE] -> If you lose the *Job* object either by deleting a variable or restarting -> the Python interpreter, you can re-create that object using *Client.get_job()* -> method providing it with the job id. +```python +result = job.results[0] +result.dump("output_file.txt") +``` -### File +The `File` object properties include: -The file objects you receive from the slivka client are not actual files you could -read from. Instead, they are referencing the resources located on the server and can -be dumped to actual files or streams instead using `dump()` method. -The argument to the `dump()` method can be a stream or a file opened in either -text or binary mode, or a file name. In case of streams or files, the result -is downloaded from the server and appended to the stream. If a file name is -provided, a new file is created, replacing the existing one if exists, and -the content is written to that file. +- **url**: Location of the resource. +- **content_url**: Location of the file content. +- **id**: Identifier of the file (can be used as input for other jobs). +- **job_id**: ID of the job this file is a result of. +- **path**: Real path and name of the file. +- **label**: Name describing the file. +- **media_type**: Media type of the file. -Example: +--- -```python ->>> result = job.results[0] +## Command-Line Interface (CLI) ->>> # dumping to open file ->>> result.dump(open("out.txt", "w")) +The `slivka-cli` command-line tool allows for interaction with the Slivka API without writing Python scripts. ->>> # dumping to in-memory stream ->>> input_stream = io.BytesIO() ->>> result.dump(input_stream) +To get started: ->>> # creating new file ->>> result.dump("out.txt") +```bash +slivka-cli --help ``` -Additionally, the *File* object provides the following properties: +This will provide you with usage instructions and available commands. + +--- + +## License + +This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. + +--- -| Property | Description | -|---------------|------------------------------------------------------------------| -| *url* | Location of the resource | -| *content_url* | Location of the file content | -| *id* | Identifier of the file which can be used as input for other jobs | -| *job_id* | Id of the job this file is a result of | -| *path* | Real path and name of the file | -| *label* | Name describing the file | -| *media_type* | Media type of the file | +## Support +If you have any questions or need assistance, please open an issue on our [GitHub repository](https://github.com/bartongroup/slivka-python-client/issues). diff --git a/setup.py b/setup.py index 6b7b365..7ab4ef0 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ import os.path - -from setuptools import setup - +from setuptools import setup, find_packages def get_version(path): here = os.path.abspath(os.path.dirname(__file__)) @@ -13,19 +11,48 @@ def get_version(path): else: raise RuntimeError("Unable to find version string.") - setup( name='slivka-client', version=get_version('slivka_client/__init__.py'), - packages=['slivka_client'], + author='Mateusz Warowny', + author_email='m.m.z.warowny@dundee.ac.uk', + maintainer='Stuart MacGowan', + maintainer_email='smacgowan@dundee.ac.uk', + description='A Python client for Slivka services', + long_description=open('README.md', encoding='utf-8').read(), + long_description_content_type='text/markdown', + url='https://github.com/bartongroup/slivka-python-client', + packages=find_packages(), install_requires=[ - 'attrs>=19.0', - 'click>=7.1.2', + 'attrs>=19.3', + 'click>=7.0', 'requests>=2.13.0' ], entry_points={ 'console_scripts': [ - "slivka-cli = slivka_client.__main__:main" - ] - } + 'slivka-cli = slivka_client.__main__:main', + ], + }, + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Science/Research', + 'Topic :: Scientific/Engineering :: Bio-Informatics', + 'Programming Language :: Python :: 3', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: OS Independent', + ], + python_requires='>=3.6', + keywords=[ + 'slivka', + 'client', + 'bioinformatics', + 'computational biology', + 'REST API' + ], + project_urls={ + 'Documentation': 'https://github.com/bartongroup/slivka-python-client#readme', + 'Source': 'https://github.com/bartongroup/slivka-python-client', + 'Tracker': 'https://github.com/bartongroup/slivka-python-client/issues', + 'Organization': 'https://www.compbio.dundee.ac.uk/drsasp.html', + }, )