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:
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.
- 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:
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
Relation to C++ API¶
For those familiar with the PVXS
API.
A Type
wraps a TypeDef.
Value
wraps a Value.