Working with Value and Type

Each Value corresponds conceptually with a C/C++ struct in that it consists of zero or more data fields. Values are strongly typed, with each Value having a corresponding Type. Each field has a concrete type, which may in turn be a sub-structure.

Further, each Value holds a bit mask which identifies fields which have “changed”. This bit mask is used during PVA protocol operations to select a subset of fields which will actually be transfered over the network.

Working with Type and Value

Value is initialized in two steps. First a Type describing the data structure is created, then the Value container is built, and optionally initialized.

>>> from p4p import Type, Value
>>> T = Type([('value', 'i')])
>>> V = Value(T, {'value':42})

Here a simple structure is defined with a single field ‘value’ which is a signed 32-bit integer. The created value initializes ‘value’ to 42. This Value can then be accessed with:

>>> V.value
42
>>> V['value']
42
>>> V.get('value', 111)
42
>>> V.get('invalid', 111) # uses default
111

Field values can also be changed

>>> V.value = 43
>>> V['value'] = 43

Change tracking

Each Value maintains a mask marking which of its field have been initialized/changed.

A newly created Value has no fields marked (empty mask).

V = Value(T)
assert V.asSet()==set()

Initial values can be provided at construction.

V = Value(T, {'value': 42})
assert V.changed('value')
assert V.asSet()==set('value')

Assignment of a new value automatically marks a field as changed.

V = Value(T)
assert not V.changed('value')
V.value = 42
assert V.changed('value')

The change mask can be cleared if necessary. eg. when passing the same Value to SharedPV.post() several times.

V = Value(T, {'value': 42})
assert V.changed('value')
V.unmark()
assert not V.changed('value')

Type definitions

The p4p.nt module contains helpers to build common Type definitions.

Structures are strongly typed. The type is specified with a code. Supported codes are given in the table below.

Type specifier codes:

Code

Type

?

bool

s

unicode

b

s8

B

u8

h

s16

H

u16

i

i32

I

u32

l

i64

L

u64

f

f32

d

f64

v

variant

U

union

S

struct

Note

Prefix with ‘a’ for “array of”. For example, ‘ai’ is an array of signed 32-bit integers.

A Type is build with a list of tuples, where each tuple defines a field.

For all type codes except struct ‘S’ and discriminating union ‘U’ only the type code is needed.

T = Type([
   ('value', 's'), # string
   ('other', 'ad'), # array of double floating
])

sub-structures and discriminating union have a nested tuple to fully define the field type.

>>> T = Type([
   ('value', 's'), # string
   ('alarm', ('S', None, [
       ('severity', 'i'),
       ('status', 'i'),
       ('message', 's'),
   ])),
])
>>> V = Value(T, {'alarm':{'severity':0}})
>>> V.alarm.severity
0
>>> V.alarm['severity']
0
>>> V['alarm.severity']
0

Here a sub-structure ‘alarm’ is defined with three fields.

A discriminating union is defined in the same manner.

>>> V = Type([
   ('value', ('u', None, [
       ('ival', 'i'),
       ('sval', 's'),
   ])),
])()
>>> V.value
None
>>> V.value = ('ival', 42) # explicitly select union field name
>>> V.value
42
>>> V.value = ('sval', 'hello')
>>> V.value
u'hello'
>>> V.value = 43   # beware still using 'sval' !!
>>> V.value
u'43'

Assigning variant and union

As the preceding example suggests, the rules for assigning values to variant and union fields can be surprising.

The rules for assigning a variant are as follows:

value type

Action

None

clears current value

Value

Stores a structure

int

signed 32-bit (python 2.x only)

long

signed 64-bit

float

64-bit floating

bytes|unicode

string

ndarray

array of integer or floating

Further, a variant union may be explicitly assigned with a specific scalar/array type using a tuple of a type specifier code and value.

Other types throw an Exception.

>>> V = Type([('x','v')])()
>>> V.x = 4.2 # inferred 64-bit floating
>>> V.x = ('f', 4.2) # explicit 32-bit floating

The rules for assigning a discriminating union are as follows:

value type

Action

None

clears current value

(‘field’, val)

explicitly specify the union field name

val

If a union field has previously been selected, coerce assigned value

val

If no union field previously select, attempt magic selection and coerce.

Other types throw an Exception.

API Reference

class p4p.Value(type[, initial])[source]
Parameters:
  • type (Type) – The Type describing the structure to be instanciated

  • initial (dict) – A dictionary (or any mapping) which gives initial values for fields.

Representation of a data structure of a particular Type.

Can be created using a Type, with an optional dict containing initial values.

A = Value(Type([
    ('value', 'I'),
]), {
    'value': 42,
})

Defines a structure with a single field named ‘value’ with type u32 (unsigned integer width 32-bit).

An example of defining a sub-structure.

A = Value(Type([
    ('value', ('S', None, [
        ('index', 'i'),
    ])),
]), {
    'value': {'index', 5},
    # 'value.index': 5,  # equivalent
})

Defines a structure containing a sub-structure ‘value’ which has a single field ‘index’ which is a signed 32-bit integer.

tolist(name=None) List[Tuple[str, Value]]

Return this Value (or the named sub-field) translated into a list of tuples

todict(name=None, wrapper=None) Mapping[str, Value]

Return this Value (or the named sub-field) translated into a dict

Parameters:
  • name (str) – Sub-field name, or None

  • wrapper (callable) – Passed an iterable of name,value tuples. By default dict eg. could be OrderedDict

tostr(limit: int = 0) str

Return a string representation, optionally truncated to a length limit

Parameters:

limit (int) – If greater than zero, formatting is terminated at limit charactors.

items(key: str = None) Iterable[Value | Any]
Parameters:

key (str) – Sub-field name

getID() str

Return Type id= string

type(fld: str = None) Type

Return the Type of this Value, or the named sub-field.

Parameters:

fld (str) – Sub-field name, or None

has(name: str) bool

Test for sub-field existance

Parameters:

name (str) – Sub-field name

get(key: str, default=None) Value | Any

dict-like access to sub-field

Parameters:
  • key (str) – Sub-field name

  • default – returned if sub-field doesn’t exist

select(name: str, selector: str)

Explicitly select Union member

Parameters:

name (str) – Sub-field name

__getattr__(field)

Access a sub-field. If the sub-field value.

__setattr__(field, value)

Assign sub-field.

__getitem__(field)

Same as __getattr__

__setitem__(field, value)

Same as __setattr__

changed(*fields) bool[source]

Test if one or more named fields have changed.

A field is considered to have changed if it is marked as changed, or if its parent, or any child, field is marked as changed.

changedSet(expand=False, parents=False) set[source]
Parameters:
  • expand (bool) – Whether to expand when entire sub-structures are marked as changed. If True, then sub-structures are expanded and only leaf fields will be included. If False, then a direct translation is made, which may include both leaf and sub-structure fields.

  • parents (bool) – If True, include fake entries for parent sub-structures with leaf fields marked as changed.

Returns:

A set of names of those fields marked as changed.

Return a set containing the names of all changed fields.

A = Value(Type([
    ('x', 'i'),
    ('z', ('S', None, [
        ('a', 'i'),
        ('b', 'i'),
    ])),
]), {
})

A.mark('z')
assert A.changedSet(expand=False) == {'z'}         # only shows fields explicitly marked
assert A.changedSet(expand=True) == {'z.a', 'z.b'} # actually used during network transmission
A.mark('z.a') # redundant
assert A.changedSet(expand=False) == {'z', 'z.a'}
assert A.changedSet(expand=True) == {'z.a', 'z.b'}
A.unmark('z')
assert A.changedSet(expand=False) == {'z.a'}
assert A.changedSet(expand=True) == {'z.a'}
assert A.changedSet(expand=False, parents=True) == {'z', 'z.a'}
assert A.changedSet(expand=True, parents=True) == {'z', 'z.a'}
  • expand=False, parents=False gives a direct mapping of the underlying BitSet as it would (get/monitor), or have been (put/rpc), moved over the network.

  • expand=True, parents=False gives the effective set of leaf fields which will be moved over the network. taking into account the use of whole sub-structure compress/shorthand bits.

  • expand=False, parents=True gives a way of testing if anything changed within a set of interesting fields (cf. set.intersect).

mark(field=None, val=True)

Mark (or unmark) the this field, or the named sub-field.

Parameters:
  • field (str) – Sub-field name

  • val (bool) – To mark, or unmark

unmark()

Unmark Value and all sub-fields.

asSet()

Deprecated alias for asSet()

class p4p.Type(fields, id=None, base=None)[source]
Parameters:
  • fields (list) – A list of tuples describing members of this data structure.

  • id (str) – Type label string.

  • base (Type) – Copy the fields of Type, then amend with the provided fields.

A definition of a data structure consisting of a list of field names and types, as well as an optional type name string (id=””). Field type specifications are either a string eg. “d” (double precision float), or a tuple (“S”, None, [fields…) defining a sub-structure.

T = Type([
    ('value', 'I'),
])

Defines a structure with a single field named ‘value’ with type u32 (unsigned integer width 32-bit).

An example of defining a sub-structure.

T = Type([
    ('value', ('S', None, [
        ('index', 'i'),
    ])),
])

Type specifier codes:

Code

Type

?

bool

s

unicode

b

s8

B

u8

h

s16

H

u16

i

i32

I

u32

l

i64

L

u64

f

f32

d

f64

v

variant

U

union

S

struct

getID()

getId() -> str Return Type id= string

aspy(str=None) list

Return a Type specification list equivalent to the one passed to the constructor.

has(str) bool

Does this Type include the named member field?

__getattr__(field)

Return Type of field. Same as self.aspy(field) for non-structure fields.

keys() Iterable[str]

Return child field names

values()[source]
items()[source]
__iter__()[source]

Relation to C++ API

For those familiar with the PVXS API. A Type wraps a TypeDef. Value wraps a Value.