forked from OpenAssetIO/OpenAssetIO-MediaCreation
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Docs] Add DR002 for versioning in codegen
Part of OpenAssetIO#88. Consolidate the discussion, provoked by iterations of the design proposal, into a decision record. Signed-off-by: David Feltell <david.feltell@foundry.com>
- Loading branch information
Showing
1 changed file
with
151 additions
and
0 deletions.
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
decisions/DR002-Versioning-traits-and-specifications-codegen.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# DR025 Versioning Traits and Specification - generated view classes | ||
|
||
- **Status:** Proposed | ||
- **Impact:** High | ||
- **Driver:** @feltech | ||
- **Approver:** @felltech @elliotcmorris @themissingcow | ||
- **Outcome:** | ||
|
||
|
||
## Background | ||
|
||
The medium of data exchange between a host and a manager is a logically | ||
opaque data blob, i.e. a `TraitsData` object. In order to extract information | ||
from this object, Trait and/or Specification view classes must be | ||
used[^1]. These classes wrap a `TraitsData` instance, and provide a | ||
suite of accessor and mutator methods that are relevant to the target | ||
trait. The classes are generated from a YAML schema (e.g. see | ||
[traits.yaml](../traits.yaml)). | ||
|
||
Hosts and managers may use different versions of the schema, and hence | ||
different versions of the view classes, and yet still wish to work | ||
together. | ||
|
||
This decision record follows on from a previous decision (OpenAssetIO | ||
[DR023](https://github.com/OpenAssetIO/OpenAssetIO/blob/main/doc/decisions/DR023-Versioning-traits-and-specifications-method.md)) | ||
that communicating a trait's version should be done by bundling the | ||
version number with the data blob that is communicated across the API, | ||
i.e. within `TraitsData`, most likely by appending the version number to | ||
the unique trait ID. | ||
|
||
With this previous decision in mind, we then need to decide on how the | ||
trait versions are represented in the high level interface, i.e. in | ||
the definition and usage of Trait/Specification view classes. | ||
|
||
A motivating example should make this problem clear. | ||
|
||
[^1]: In reality, a `TraitsData` is a simple dictionary-like structure, | ||
and the `TraitsData` type has a low-level interface for interacting with | ||
it, but usage of this is discouraged. | ||
|
||
### Motivating example | ||
|
||
An example usage of the current form of these generated classes might | ||
be: | ||
|
||
```python | ||
url = LocatableContentTrait(trait_data).getLocation() | ||
``` | ||
|
||
Imagine that we want to rename the LocatableContent trait's `"location"` | ||
property to a more descriptive `"url"` property, hence changing the | ||
generated view class's method from `getLocation` to `getUrl`. | ||
|
||
Given that hosts and managers are developed independently, we may end up | ||
with a situation where one side is setting `"location"` (using | ||
`setLocation`) in the data, handing it over to the other side, who then | ||
attempts to read `"url"` (using `getUrl`). I.e. we have a version | ||
mismatch. | ||
|
||
There is therefore a conflict at the data layer (i.e. field names differ | ||
for the same semantic information). With C++, the data layer is where | ||
the conflict ends. The Trait/Specification view classes are private | ||
utility classes whose symbols should not be exported, so there will be | ||
no source or binary incompatibility. | ||
|
||
However, with Python there is no such concept of a private, build-time | ||
only, class. The manager plugin and host application must use the same | ||
`openassetio-mediacreation` distribution package in the Python | ||
environment (not considering, for the moment, custom vendoring). So one | ||
side or the other will hit an `AttributeError` exception when trying to | ||
use a method from the version they developed against, rather than the | ||
version installed into the environment. | ||
|
||
### Assumptions | ||
|
||
We need a way for host and manager plugin authors to work with multiple | ||
trait versions. | ||
|
||
* Hosts and manager plugins must have access to a Trait/Specification | ||
view class for each version, such that they can detect that a | ||
particular version of a trait is imbued in the data, and extract | ||
property values specific to that version. | ||
* Trait unique IDs will be suffixed with a version number whenever the | ||
trait ID is used. This means two Trait view classes for the same | ||
trait, but for different versions, will not be able to detect the | ||
presence of a trait of the other version in trait data. Utility | ||
functions to do this may be added in the future, but it is out of scope | ||
for now. | ||
* If a Specification view class is used to construct a trait set/data, | ||
that data will _not_ have the Specification version encoded in the | ||
data directly (only implicitly through the versioned IDs of the | ||
composite traits). | ||
|
||
## Relevant data | ||
|
||
[OpenTimelineIO schema | ||
versioning](https://opentimelineio.readthedocs.io/en/latest/tutorials/otio-file-format-specification.html#example) | ||
is perhaps the closest analog. The version of the schema is appended to | ||
the schema ID whenever it appears within a OTIO JSON document. | ||
|
||
The options presented were arrived at by sketching a proposal in [a Pull | ||
Request](https://github.com/OpenAssetIO/OpenAssetIO-MediaCreation/pull/90), | ||
soliciting feedback, and iterating. The final form of that PR reflects | ||
the chosen option. | ||
|
||
## Options considered | ||
|
||
### Option 1 - Per schema versioning | ||
|
||
For example | ||
```python | ||
from openassetio_mediacreation.v1.traits import LocatableContent as LocatableContent_v1 | ||
from openassetio_mediacreation.v2.traits import LocatableContent as LocatableContent_v2 | ||
from openassetio_mediacreation.v2.specification import ImageSpecification | ||
``` | ||
|
||
#### Pros | ||
|
||
- Tantalising possibility to use [Python namespace | ||
packages](https://packaging.python.org/en/latest/guides/packaging-namespace-packages) | ||
to allow different schema versions to be installed independently | ||
side-by-side. | ||
- The schema version a Specification comes from instantly tells you the | ||
schema version of the consitituent traits. | ||
|
||
#### Cons | ||
|
||
- A source-incompatible breaking change without significant | ||
special-casing. | ||
|
||
### Option 2 - Per Trait/Specification versioning | ||
|
||
For example | ||
```python | ||
from openassetio_mediacreation.traits import LocatableContent_v1 | ||
from openassetio_mediacreation.traits import LocatableContent_v2 | ||
from openassetio_mediacreation.specification import ImageSpecification_v2 | ||
``` | ||
|
||
#### Pros | ||
|
||
- Fairly trivial to say "`_v0`" is equivalent to "" (blank), then e.g. | ||
`import LocatableContent` continues to work as before, and this option | ||
is fully source compatible. I.e. not a breaking change. | ||
|
||
#### Cons | ||
|
||
- No indication of the version of the constituent traits from the | ||
version of a Schema | ||
|
||
## Outcome |