Document patterns for maintaining consistency between traits
- Dominant language
- Python
- Stars
- 462
- Forks
- 90
- PR merge metrics
- No merged PRs in 30d
Description
There's a pattern that's turned up a good many times in various different projects over the years, where it may make sense to document the issues and possible solutions. It looks something like this:
- We have a Traits-based model with two or more related pieces of state, represented as concrete traits. For example:
```python
class SearchInterval(HasTraits):
low = Float(0.0)
high = Float(1.0)
```
- We want both `low` and `high` to be observable.
- We also want both `low` and `high` to be readable attributes (IOW, it's not enough for them just to be events)
- We want the model to maintain the invariant that `obj.low < obj.high` for any particular instance `obj` of `SearchInterval`
In particular, we want the invariant `obj.low < obj.high` to be true at any _observable_ time: e.g., if we have listeners attached to either `low` or `high`, then at the point where those listeners execute, the invariant will hold. The issues occur at the point where we want to change both of `low` and `high` "simultaneously" - e.g., go from an interval of `(0, 1)` to `(2, 3)`. In this case, if we set `low` first, then a listener reacting to the change in `low` will see a `low` value of `2` and a `high` value of `1`, violating the constraints.
For this *particular* example, we can be careful about the order in which we change `low` and `high`, and that may be good enough. But in general, that sort of trickery isn't always going to be possible.
Note: this is not about concurrency and multithreading - that's a separate issue. We're assuming here that everything is behaving as though single-threaded. In particular, that means that we can generally assume that any listener is effectively atomic, in the sense that the model can't change while the listener code is executing.
Other, similar cases
- we might have two `Dict` traits where model consistency depends on the set of keys for both dictionaries being the same.
- for an `Enum` trait with a dynamic value set (that's "set" as in "collection"), we'd like to maintain the invariant that the current value is always a member of the set
- concrete example from Traits Futures: https://github.com/enthought/traits-futures/issues/60. Here we have traits `state` and `done`, where `done` is a function of `state`, but an observer for the `done` trait should never see a `state` value that's inconsistent either with `future.done` or with the `new` value for done that it received in the `observe` event.
There are a range of possible solutions here, with different solutions applicable in different circumstances; the goal of this issue is to document some of those solutions and their trade-offs.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.