(to10.3-other)=

# Other changes

(to10.3-attribute-config-cache)=

## Attribute configuration caching

When an attribute is read or written with the high-level API, PyTango needs the
attribute configuration: for reads, to convert a `DevEnum` value into a Python
`IntEnum` with labels; for writes, to convert the Python value into the correct C++
data type.

In 10.3.0 each attribute's configuration is **cached on first access by default**,
and the cached configuration is reused to take the "fast" write path (avoiding an
extra synchronous read of the attribute configuration on every write). Each
`DeviceProxy` instance has its own cache.

This is a behaviour change. If the attribute configuration on the server changes
while a `DeviceProxy` is alive (e.g. the server is restarted after a change, or a
dynamic attribute is removed and reinstalled), the cached values are not updated
automatically, which can lead to unexpected behaviour. To stay safe, PyTango
automatically invalidates the cache and re-fetches the configuration if a
`TypeError` is encountered during a write, so in the worst case a genuine type
mismatch only costs one extra IO.

Three new {class}`~tango.DeviceProxy` methods let you control the cache:

- {meth}`~tango.DeviceProxy.set_attribute_config_cache` — enable (`True`) or
  disable (`False`) caching.
- {meth}`~tango.DeviceProxy.is_attribute_config_cache_enabled` — query the current
  setting.
- {meth}`~tango.DeviceProxy.invalidate_attribute_config_cache` — invalidate the
  cache once while keeping caching enabled.

If you expect attribute configurations to mutate during the lifetime of a
`DeviceProxy`, disable caching. PyTango will then do one additional IO to the server
per read/write to fetch the configuration. See
[attribute configuration caching](#attribute-configuration-caching) for more details.

(to10.3-device-properties)=

## Stricter string-to-boolean device properties

Boolean device property values are now parsed strictly. The accepted spellings
(case-insensitive) are:

- True: `y`, `yes`, `t`, `true`, `on`, `1`
- False: `n`, `no`, `f`, `false`, `off`, `0`

Anything else now raises an error. Previously, any string other than `"true"` was
silently coerced to `False`, so typos such as `"ture"` or `"yse"` masked
configuration errors:

```python
# Before 10.3.0: "ture" silently became False
# From 10.3.0:   "ture" raises an error
```

This affects `DevBoolean` and `DevVarBooleanArray` device properties. Make sure your
database property values use one of the accepted spellings.

Errors raised while parsing any device property value are now also more actionable:
they report the device name, property name, raw value, and target type, for example:

```text
Failed to convert property 'MyProp' of device 'sys/mydev/1' (value=['1, 2']) to type DevVarLongArray: invalid literal for int() with base 10: '1, 2'
```

The internal `PropUtil.stringArray2values` and `PropUtil.values2string` methods were
removed as part of this change. `PropUtil` is internal API, so this should not affect
normal use.

(to10.3-other-additions)=

## Other additions

These are additive and require no migration, but may let you simplify your code:

- {class}`~tango.utils.PyTangoThread` wraps {class}`threading.Thread` so the task
  runs inside an {class}`~tango.EnsureOmniThread` context for the whole lifetime of
  the thread. This removes the need for scattered manual `EnsureOmniThread` calls:

  ```python
  import tango

  # Instead of wrapping the body of your thread in `with EnsureOmniThread(): ...`
  thread = tango.utils.PyTangoThread(target=my_task)
  thread.start()
  ```

  Both supplying a `target` and subclassing/overriding `run()` are supported. See the
  [multiprocessing how-to](#multiprocessing).

- {class}`~tango.Util` now has a `cleanup` method, and `tango_loop` calls it on
  shutdown. This makes it possible to restart a Tango server within the same process,
  which was not possible before.

- Equality operators (`==` / `!=`) are now exposed for {class}`~tango.CommandInfo`,
  {class}`~tango.AttributeInfo`, {class}`~tango.AttributeInfoEx`,
  {class}`~tango.AttributeEventInfo`, and {class}`~tango.AttributeAlarmInfo`, matching
  the operators already present in cppTango.
