Data types

This chapter describes the mapping of data types between Python and Tango.

Tango has more data types than Python which is more dynamic. The input and output values of the commands are translated according to the array below. Note that if PyTango is compiled with numpy support the numpy type will be the used for the input arguments. Also, it is recomended to use numpy arrays of the appropiate type for output arguments as well, as they tend to be much more efficient.

For scalar types (SCALAR)

Tango data type

Python 2.x type

Python 3.x type (New in PyTango 8.0)

DEV_VOID

No data

No data

DEV_BOOLEAN

bool

bool

DEV_SHORT

int

int

DEV_LONG

int

int

DEV_LONG64

  • long (on a 32 bits computer)

  • int (on a 64 bits computer)

int

DEV_FLOAT

float

float

DEV_DOUBLE

float

float

DEV_USHORT

int

int

DEV_ULONG

int

int

DEV_ULONG64

  • long (on a 32 bits computer)

  • int (on a 64 bits computer)

int

DEV_STRING

str

str (decoded with latin-1, aka ISO-8859-1)

DEV_ENCODED (New in PyTango 8.0)

sequence of two elements:

  1. str

  2. bytes (for any value of extract_as)

sequence of two elements:

  1. str (decoded with latin-1, aka ISO-8859-1)

  2. bytes (for any value of extract_as, except String. In this case it is str (decoded with default python encoding utf-8))

DEV_ENUM (New in PyTango 9.0)

Note: direct attribute access via DeviceProxy will return enumerated

type enum.IntEnum. This type uses the package enum34.

Note: direct attribute access via DeviceProxy will return enumerated

type enum.IntEnum. Python < 3.4, uses the package enum34. Python >= 3.4, uses standard package enum.

For array types (SPECTRUM/IMAGE)

Tango data type

ExtractAs

Data type (Python 2.x)

Data type (Python 3.x) (New in PyTango 8.0)

DEVVAR_CHARARRAY

Numpy

numpy.ndarray (dtype= numpy.uint8)

numpy.ndarray (dtype= numpy.uint8)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int>

list <int>

Tuple

tuple <int>

tuple <int>

DEVVAR_SHORTARRAY or (DEV_SHORT + SPECTRUM) or (DEV_SHORT + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint16)

numpy.ndarray (dtype= numpy.uint16)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int>

list <int>

Tuple

tuple <int>

tuple <int>

DEVVAR_LONGARRAY or (DEV_LONG + SPECTRUM) or (DEV_LONG + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint32)

numpy.ndarray (dtype= numpy.uint32)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int>

list <int>

Tuple

tuple <int>

tuple <int>

DEVVAR_LONG64ARRAY or (DEV_LONG64 + SPECTRUM) or (DEV_LONG64 + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint64)

numpy.ndarray (dtype= numpy.uint64)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int (64 bits) / long (32 bits)>

list <int>

Tuple

tuple <int (64 bits) / long (32 bits)>

tuple <int>

DEVVAR_FLOATARRAY or (DEV_FLOAT + SPECTRUM) or (DEV_FLOAT + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.float32)

numpy.ndarray (dtype= numpy.float32)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <float>

list <float>

Tuple

tuple <float>

tuple <float>

DEVVAR_DOUBLEARRAY or (DEV_DOUBLE + SPECTRUM) or (DEV_DOUBLE + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.float64)

numpy.ndarray (dtype= numpy.float64)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <float>

list <float>

Tuple

tuple <float>

tuple <float>

DEVVAR_USHORTARRAY or (DEV_USHORT + SPECTRUM) or (DEV_USHORT + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint16)

numpy.ndarray (dtype= numpy.uint16)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int>

list <int>

Tuple

tuple <int>

tuple <int>

DEVVAR_ULONGARRAY or (DEV_ULONG + SPECTRUM) or (DEV_ULONG + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint32)

numpy.ndarray (dtype= numpy.uint32)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int>

list <int>

Tuple

tuple <int>

tuple <int>

DEVVAR_ULONG64ARRAY or (DEV_ULONG64 + SPECTRUM) or (DEV_ULONG64 + IMAGE)

Numpy

numpy.ndarray (dtype= numpy.uint64)

numpy.ndarray (dtype= numpy.uint64)

Bytes

bytes (which is in fact equal to str)

bytes

ByteArray

bytearray

bytearray

String

str

str (decoded with latin-1, aka ISO-8859-1)

List

list <int (64 bits) / long (32 bits)>

list <int>

Tuple

tuple <int (64 bits) / long (32 bits)>

tuple <int>

DEVVAR_STRINGARRAY or (DEV_STRING + SPECTRUM) or (DEV_STRING + IMAGE)

sequence<str>

sequence<str> (decoded with latin-1, aka ISO-8859-1)

DEV_LONGSTRINGARRAY

sequence of two elements:

  1. numpy.ndarray (dtype= numpy.int32) or sequence<int>

  2. sequence<str>

sequence of two elements:

  1. numpy.ndarray (dtype= numpy.int32) or sequence<int>

  2. sequence<str> (decoded with latin-1, aka ISO-8859-1)

DEV_DOUBLESTRINGARRAY

sequence of two elements:

  1. numpy.ndarray (dtype= numpy.float64) or sequence<int>

  2. sequence<str>

sequence of two elements:

  1. numpy.ndarray (dtype= numpy.float64) or sequence<int>

  2. sequence<str> (decoded with latin-1, aka ISO-8859-1)

For SPECTRUM and IMAGES the actual sequence object used depends on the context where the tango data is used, and the availability of numpy.

  1. for properties the sequence is always a list. Example:

    >>> import tango
    >>> db = tango.Database()
    >>> s = db.get_property(["TangoSynchrotrons"])
    >>> print type(s)
    <type 'list'>
    
  2. for attribute/command values

Pipe data types

Pipes require different data types. You can think of them as a structured type.

A pipe transports data which is called a blob. A blob consists of name and a list of fields. Each field is called data element. Each data element consists of a name and a value. Data element names must be unique in the same blob.

The value can be of any of the SCALAR or SPECTRUM tango data types (except DevEnum).

Additionally, the value can be a blob itself.

In PyTango, a blob is represented by a sequence of two elements:

  • blob name (str)

  • data is either:

    • sequence (list, tuple, or other) of data elements where each element is a dict with the following keys:

      • name (mandatory): (str) data element name

      • value (mandatory): data (compatible with any of the SCALAR or SPECTRUM data types except DevEnum). If value is to be a sub-blob then it should be sequence of [blob name, sequence of data elements] (see above)

      • dtype (optional, mandatory if a DevEncoded is required): see Data type equivalence. If dtype key is not given, PyTango will try to find the proper tango type by inspecting the value.

    • a dict where key is the data element name and value is the data element value (compact version)

When using the compact dictionary version note that the order of the data elements is lost. If the order is important for you, consider using collections.OrderedDict instead (if you have python >=2.7. If not you can use ordereddict backport module available on pypi). Also, in compact mode it is not possible to enforce a specific type. As a consequence, DevEncoded is not supported in compact mode.

The description sounds more complicated that it actually is. Here are some practical examples of what you can return in a server as a read request from a pipe:

import numpy as np

# plain (one level) blob showing different tango data types
# (explicity and implicit):

PIPE0 = \
('BlobCase0',
 ({'name': 'DE1', 'value': 123,},                                # converts to DevLong64
  {'name': 'DE2', 'value': np.int32(456),},                      # converts to DevLong
  {'name': 'DE3', 'value': 789, 'dtype': 'int32'},               # converts to DevLong
  {'name': 'DE4', 'value': np.uint32(123)},                      # converts to DevULong
  {'name': 'DE5', 'value': range(5), 'dtype': ('uint16',)},      # converts to DevVarUShortArray
  {'name': 'DE6', 'value': [1.11, 2.22], 'dtype': ('float64',)}, # converts to DevVarDoubleArray
  {'name': 'DE7', 'value': numpy.zeros((100,))},                 # converts to DevVarDoubleArray
  {'name': 'DE8', 'value': True},                                # converts to DevBoolean
 )
)


# similar as above but in compact version (implicit data type conversion):

PIPE1 = \
('BlobCase1', dict(DE1=123, DE2=np.int32(456), DE3=np.int32(789),
                   DE4=np.uint32(123), DE5=np.arange(5, dtype='uint16'),
                   DE6=[1.11, 2.22], DE7=numpy.zeros((100,)),
                   DE8=True)
)

# similar as above but order matters so we use an ordered dict:

import collections

data = collections.OrderedDict()
data['DE1'] = 123
data['DE2'] = np.int32(456)
data['DE3'] = np.int32(789)
data['DE4'] = np.uint32(123)
data['DE5'] = np.arange(5, dtype='uint16')
data['DE6'] = [1.11, 2.22]
data['DE7'] = numpy.zeros((100,))
data['DE8'] = True

PIPE2 = 'BlobCase2', data

# another plain blob showing string, string array and encoded data types:

PIPE3 = \
('BlobCase3',
 ({'name': 'stringDE',  'value': 'Hello'},
  {'name': 'VectorStringDE', 'value': ('bonjour', 'le', 'monde')},
  {'name': 'DevEncodedDE', 'value': ('json', '"isn\'t it?"'), 'dtype': 'bytes'},
 )
)

# blob with sub-blob which in turn has a sub-blob

PIPE4 = \
('BlobCase4',
 ({'name': '1DE', 'value': ('Inner', ({'name': '1_1DE', 'value': 'Grenoble'},
                                      {'name': '1_2DE', 'value': ('InnerInner',
                                                                  ({'name': '1_1_1DE', 'value': np.int32(111)},
                                                                   {'name': '1_1_2DE', 'value': [3.33]}))
                                     })
  )},
  {'name': '2DE', 'value': (3,4,5,6), 'dtype': ('int32',) },
 )
)

DevEnum pythonic usage

When using regular tango DeviceProxy and AttributeProxy DevEnum is treated just like in cpp tango (see enumerated attributes for more info). However, since PyTango >= 9.2.5 there is a more pythonic way of using DevEnum data types if you use the high level API, both in server and client side.

In server side you can use python enum.IntEnum class to deal with DevEnum attributes:

import time
from enum import IntEnum
from tango import AttrWriteType
from tango.server import Device, attribute, command


class Noon(IntEnum):
    AM = 0  # DevEnum's must start at 0
    PM = 1  # and increment by 1


class DisplayType(IntEnum):
    ANALOG = 0  # DevEnum's must start at 0
    DIGITAL = 1  # and increment by 1


class Clock(Device):

    display_type = DisplayType(0)

    @attribute(dtype=float)
    def time(self):
        return time.time()

    gmtime = attribute(dtype=(int,), max_dim_x=9)

    def read_gmtime(self):
        return time.gmtime()

    @attribute(dtype=Noon)
    def noon(self):
        time_struct = time.gmtime(time.time())
        return Noon.AM if time_struct.tm_hour < 12 else Noon.PM

    display = attribute(dtype=DisplayType, access=AttrWriteType.READ_WRITE)

    def read_display(self):
        return self.display_type

    def write_display(self, display_type):
        self.display_type = display_type

    @command(dtype_in=float, dtype_out=str)
    def ctime(self, seconds):
        """
        Convert a time in seconds since the Epoch to a string in local time.
        This is equivalent to asctime(localtime(seconds)). When the time tuple
        is not present, current time as returned by localtime() is used.
        """
        return time.ctime(seconds)

    @command(dtype_in=(int,), dtype_out=float)
    def mktime(self, tupl):
        return time.mktime(tupl)


if __name__ == "__main__":
    Clock.run_server()

On the client side you can also use a pythonic approach for using DevEnum attributes:

import sys
import PyTango

if len(sys.argv) != 2:
    print("must provide one and only one clock device name")
    sys.exit(1)

clock = PyTango.DeviceProxy(sys.argv[1])
t = clock.time
gmt = clock.gmtime
noon = clock.noon
display = clock.display
print(t)
print(gmt)
print(noon, noon.name, noon.value)
if noon == noon.AM:
    print('Good morning!')
print(clock.ctime(t))
print(clock.mktime(gmt))
print(display, display.name, display.value)
clock.display = display.ANALOG
clock.display = 'DIGITAL'  # you can use a valid string to set the value
print(clock.display, clock.display.name, clock.display.value)
display_type = type(display)  # or even create your own IntEnum type
analog = display_type(0)
clock.display = analog
print(clock.display, clock.display.name, clock.display.value)
clock.display = clock.display.DIGITAL
print(clock.display, clock.display.name, clock.display.value)