Skip to main content

Provide a Python 3 interface to communicate with a sandbox (https://github.com/PremierLangage/sandbox)

Project description

Python package codecov CodeFactor PyPI Version Python 3.7+ License MIT

Python API for WIMS' adm/raw module

A Python 3 interface to communicate with a sandbox

Installation

The latest stable version is available on PyPI :

pip install sandbox-api

or from sources:

git clone https://github.com/qcoumes/sandbox-api
cd sandbox-api
python3 setup.py install

Usage

Sandbox-api is pretty straightforward to use, you just need to import Sandbox and create an instance with the url of the server :

from sandbox_api import Sandbox

sandbox = Sandbox("http://www.my-sandbox.com")

Default request timeout is 60 seconds, use the timeout argument to override :

sandbox = Sandbox("http://www.my-sandbox.com", timeout=2.5)

Specifications

You can obtain the sandbox' host and container with the .specifications() method :

>>> pprint.pp(sandbox.specifications())
{'host': {'cpu': {'core': 6,
                  'logical': 12,
                  'freq_min': 800.0,
                  'freq_max': 4700.0},
          'memory': {'ram': 16723701760,
                     'swap': 9448923136,
                     'storage': {'/dev/sda2': 225603444736,
                                 '/dev/sda1': 313942016,
                                 '/dev/sdc1': 2000396288000}},
          'docker_version': '19.03.8-ce',
          'sandbox_version': '3.0.0'},
 'container': {'count': 20,
               'cpu': {'count': 1, 'period': 1000, 'shares': 1024, 'quota': 0},
               'memory': {'ram': 100000000, 'swap': 100000000, 'storage': -1},
               'io': {'read_iops': {},
                      'read_bps': {},
                      'write_iops': {},
                      'write_bps': {}},
               'process': -1,
               'working_dir_device': '/dev/sda2'}}

Sandbox' Usage

You can obtain the sandbox' usage with the .usage() method :

>>> pprint.pp(sandbox.usage())
{'cpu': {'frequency': 800.0504166666668,
         'usage': 0.085,
         'usage_avg': [0.10416666666666667, 0.09000000000000001, 0.0775]},
 'memory': {'ram': 5376819200,
            'swap': 0,
            'storage': {'/dev/sda2': 60275105792,
                        '/dev/sda1': 286720,
                        '/dev/sdc1': 1294363865088}},
 'io': {'read_iops': {'/dev/sdc1': 0, '/dev/sda1': 0, '/dev/sda2': 0},
        'read_bps': {'/dev/sdc1': 0, '/dev/sda1': 0, '/dev/sda2': 0},
        'write_iops': {'/dev/sdc1': 0, '/dev/sda1': 0, '/dev/sda2': 0},
        'write_bps': {'/dev/sdc1': 0, '/dev/sda1': 0, '/dev/sda2': 2048}},
 'network': {'sent_bytes': 514,
             'received_bytes': 526,
             'sent_packets': 6,
             'received_packets': 6},
 'process': 326,
 'container': 0}

Downloading environments and files

You can download files or environments through the download(uuid, path=None) method. uuid correspond to the ID of the environment on the sandbox.

If only uuid is provided the whole environment will be downloading, if path is also provided, only the file corresponding to path inside the environment will be downloaded.

The downloaded object is returned as a io.BytesIO.

Executing commands

Use the execute(config, environ=None) method to execute commands on the sandbox. Config must be a dictionary corresponding to a valid config as defined here, environ, if provided, must be a binary object implementing the .read() (I.G. file object or io.BytesIO) at position 0 corresponding to a valid environment (again, more information here).

The returned value is a dictionary corresponding to the response's json :

You can check a config dictionnary by calling sandbox_api.utils.validate(config).

>>> pprint.pp(sandbox.execute({
     "commands": [
             "echo $((2+2))"
     ]
}))

{'status': 0,
 'execution': [ {'command': 'echo $((2+2))',
                 'exit_code': 0,
                 'stdout': '4',
                 'stderr': '',
                 'time': 0.28589797019958496
        }
    ],
 'total_time': 0.2871053218841553}

Asynchronous API

Since requests may take sometime before a response is available, an asynchronous interface is available through the class ASandbox :

from sandbox_api import ASandbox

sandbox = ASandbox("http://www.my-sandbox.com")

ASandbox use aiohttp, so timeout works differently. The default total timeout is still 60 seconds, but you're able to set differents timeout for different part of the process with the following arguments :

  • total : The whole operation time including connection establishment, request sending and response reading.
  • connect : The time consists connection establishment for a new connection or waiting for a free connection from a pool if pool connection limits are exceeded.
  • sock_connect : A timeout for connecting to a peer for a new connection, not given from a pool.
  • sock_read : The maximum allowed timeout for period between reading a new data portion from a peer.
sandbox = ASandbox("http://www.my-sandbox.com", total=2.5, connect=0.5)

All parameters are floats. None or 0 disables a particular timeout check, see the aiohttp's ClientTimeout reference for defaults and additional details.

To send requests, ASandbox uses a session that must be closed once done with the instance :

sandbox = ASandbox("http://www.my-sandbox.com", total=2.5, connect=0.5)

# do stuff with sandbox

await sandbox.close()

You can also use a context manager to automatically close the session once done :

async with ASandbox("http://www.my-sandbox.com") as sandbox:
    # do stuff with sandbox

Apart from the above, all the methods are the same than Sandbox but are all declared with the async keyword and must be awaited :

async with ASandbox("http://my-sandbox.com") as sandbox:
    usage = await sandbox.usage()
    specs = await sandbox.specifications()
    execution = await sandbox.execute({
        "commands": [
            "echo $((2+2))"
        ]
    })

Exceptions

Since sandbox-api rely on HTTP, any response with a status code greater or equal to 300 will raise a corresponding exception Sanbox{STATUS} the response (a requests.Response object) received is available in the response attribute of the exception.

All of these exceptions inherit SandboxError.

from sandbox_api import SandboxError, Sandbox404

try:
    file = s.download("872b604c-9cce-42e2-8888-9a0311f3a724", "unknown")
except Sandbox404:
    print("'unknown' does not exists in environment '872b604c-9cce-42e2-8888-9a0311f3a724'")
except SandboxError as e:
    print("Sandbox responded with a '%d' status code" % e.response.status_code)

Changelog

1.1.0

  • Sandbox.check() now return the size of the environment / file, 0 if not found. It should not break backward compatibility because of how int are evaluated as boolean.
  • Added an async implementation of the api using the class ASandbox.
  • async implementation depends on aiohttp.
  • Drop support for Python 3.5 & 3.6

1.0.1

  • Update README.md

1.0.0

  • Now compatible with Sandbox 3.0.0

0.0.7

  • Now use Github Action for testing and deployment to PyPI

0.0.6

  • Added method Sandbox451
  • Added python 3.8 tp test matrix

0.0.5

  • Added method check() to Sandbox to check if an environment/file exists on the sandbox.

0.0.4

  • Added function validate() to check a config dict in sandbox_api.utils.

0.0.3

  • Added missing type hints cpu for Sandbox

0.0.2

  • Added type hints for Sandbox's attributes.

0.0.1

  • Initial release

Project details


Download files

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

Files for sandbox-api, version 1.1.0
Filename, size File type Python version Upload date Hashes
Filename, size sandbox-api-1.1.0.tar.gz (12.3 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page