Skip to content

Commit

Permalink
Documentation fixes (#914)
Browse files Browse the repository at this point in the history
  • Loading branch information
darbyjohnston authored Mar 22, 2021
1 parent 458db39 commit 93f5e36
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 60 deletions.
60 changes: 30 additions & 30 deletions docs/cxx/cxx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ already defines properties ``name`` and ``metadata``: ::
class Marker : public SerializableObjectWithMetadata {
public:
struct Schema {
static auto constexpr name = "Marker";
static std::string constexpr name = "Marker";
static int constexpr version = 1;
};

Expand All @@ -72,7 +72,7 @@ already defines properties ``name`` and ``metadata``: ::
void set_color(std::string const& color);

protected:
virtual ~Marker;
virtual ~Marker();

virtual bool read_from(Reader&);
virtual void write_to(Writer&) const;
Expand All @@ -93,7 +93,7 @@ binding code will allow users to define their own schemas in Python exactly as
they do today, with no changes required.

The ``Schema`` structure exists so that this type can be added to the OTIO type
registry with a simple call ::
registry with a simple call: ::
TypeRegistry::instance()::register_type<Marker>();

Expand Down Expand Up @@ -127,16 +127,16 @@ When an error is encountered in reading, ``read_from`` should set the error
on the ``Reader`` instance and return ``false``: ::

bool Marker::read_from(Reader& reader) {
if (!reader.read(“color”, &_color)) {
return false;
}
if (_color == “invalid_value”) {
reader.error( ErrorStatus(ErrorStatus::JSON_PARSE_ERROR,
“invalid_value not allowed for color”));
return false;
if (!reader.read(“color”, &_color)) {
return false;
}
if (_color == “invalid_value”) {
reader.error( ErrorStatus(ErrorStatus::JSON_PARSE_ERROR,
“invalid_value not allowed for color”));
return false;
}
return reader.read(“marked_range”, &_marked_range) &&
Parent::read_from(reader);
return reader.read(“marked_range”, &_marked_range) &&
Parent::read_from(reader);
}

This is a contrived example but it describes the basic mechanics. Adjust the
Expand All @@ -154,7 +154,7 @@ details above as appropriate for your case.

.. Note::
Also note that the order of properties within a JSON file for data
that is essentially an ``std::map<>`` (see ``AnyDictionary`` below)
that is essentially a ``std::map<>`` (see ``AnyDictionary`` below)
is always alphebetical by key. This ensures deterministic JSON file
writing which is important for comparison and testing.

Expand Down Expand Up @@ -228,7 +228,7 @@ will be as simple as always, from an untyped language like Python, while in
C++/Swift, the typical and necessary querying and casting would need to be
written.

As we saw above, declaring and and handling read/writing for "atomic" property
As we saw above, declaring and handling reading/writing for "atomic" property
types (e.g. ``std::string``, ``TimeRange``) is straightforward and requires
little effort. Additionally, reading/writing support is automatically provided
for the (recursively defined) types ``std::vector<P>``, ``std::list<P>`` and
Expand All @@ -254,15 +254,15 @@ automatically: ::
};

In this case, the derived schema could choose to store extra media references.
The reading/writing code would simply call ::
The reading/writing code would simply call: ::

reader.read("extra_references", &_extra_references)

and ::
To read the property, and: ::

writer.write("extra_references", _extra_references)

to read/write the property.
To write the property.

.. Note::
The comment "don't actually do this" will be explained in the next section;
Expand Down Expand Up @@ -300,7 +300,7 @@ speaking, even need to be a DAG: ::
clip1->metadata()["other_clip"] = clip2;
clip2->metadata()["other_clip"] = clip1;

will work just fine: writing/reading or simply cloning ``clip1`` would yield a
This will work just fine: writing/reading or simply cloning ``clip1`` would yield a
new ``clip1`` that pointed to a new ``clip2`` and vice versa.

.. Note::
Expand Down Expand Up @@ -335,16 +335,16 @@ route, coupled with the ``pybind`` binding system (or even with the older
experience in Python. (And we would expect similar difficulties in Swift.)

Consider the following requirements from the perspective of an OTIO user in a
Python framework. In Python, a user types ::
Python framework. In Python, a user types: ::

clip = otio.schema.Clip()

Behind the scenes, in C++, an actual ``Clip`` instance has been created. From
the user's perspective, they "own" this clip, and if they immediately type ::
the user's perspective, they "own" this clip, and if they immediately type: ::

del clip

then they would expect the Python clip object to be destroyed (and the actual
Then they would expect the Python clip object to be destroyed (and the actual
C++ ``Clip`` instance to be deleted). Anything less than this is a memory
leak.

Expand All @@ -369,12 +369,12 @@ in Swift.) Ensuring a system that does not leak memory, and that also keeps
both objects alive as long as either side (C++ or the bridged language) is,
simply put, challenging.

With all that as a preamble, here is our proposed solution for C++.
With all that as a preamble, here is our proposed solution for C++:

- A new instance of a schema object is created by a call to ``new``. - All
schema objects have protected destructors. Given a raw pointer to a schema
object, client code may not directly invoke the ``delete`` operator, but may
write ::
write: ::

Clip* c = new Clip();
...
Expand Down Expand Up @@ -485,7 +485,7 @@ To illustrate the above point in a less contrived fashion, consider this incorre
c->remove_child(index);

#if DEBUG
std::cout << "Debug: removed item named" << item->name();
std::cout << "Debug: removed item named " << item->name();
#endif
}

Expand All @@ -499,7 +499,7 @@ A correct version of this code would be: ::
c->remove_child(index);

#if DEBUG
std::cout << "Debug: removed item named" << item.value->name();
std::cout << "Debug: removed item named " << item.value->name();
#endif
}

Expand All @@ -513,7 +513,7 @@ A correct version of this code would be: ::
auto& children = c->children();
for (size_t i = 0; i < children.size(); ++i) {
if (children[i].value->name() == name) {
SerializableObject::Retainer<Item> r_item(kids[i]);
SerializableObject::Retainer<Item> r_item(children[i]);
c->remove_child(i);
return r_item.take_value();
}
Expand Down Expand Up @@ -681,7 +681,7 @@ What makes this complicated is the following set of rules/constraints:
is passed to C++ in some way, then X will exist only as long as P does.

How can we satisfy all these contraints, while ensuring we don't create retain
cycles (which might be fixable with Python garbage collection, but might also
cycles (which might be fixable with Python garbage collection, but also
might not)? Here is the solution we came up with; if you have an alternate
suggestion, we would be happy to hear it.

Expand All @@ -696,7 +696,7 @@ Our scheme works as follows:
``Retainer<>`` object in addition to the one in P, a "keep-alive" reference to P is created
and held by X. This ensures that P won’t be destroyed even if the Python interpreter appears
to lose all references to P, because we've hidden one away. (Remember, the C++ object X could
always be passed back to Python, and we can’t/don’t want to regenerate a new P corresponding to X)
always be passed back to Python, and we can’t/don’t want to regenerate a new P corresponding to X.)

- However, when X's C++ count reference count drops back to one, then we know that P is now
the only reason we are keeping X alive. At this point, the keep-alive reference to P is destroyed.
Expand All @@ -707,10 +707,10 @@ Our scheme works as follows:
The tricky part here is the interaction of watching the reference count of C++
objects oscillate from 1 to greater than one, and vice versa. (There is no way
of watching the Python reference count change, and even if we could, the
performance constraints this would be entail would be likely untenable).
performance constraints this would be entail would be likely untenable.)

Essentially, we are monitoring changes in whether or not there is a single
unique ``Retainer<>`` instance pointing to a given C++ objects, or multiple
unique ``Retainer<>`` instance pointing to a given C++ object, or multiple
such retainers. We’ve verified with some extremely processor intensive
multi-threading/multi-core tests that our coding of the mutation of the C++
reference count, coupled with creating/destroying the Python keep-alive
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorials/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ for each_seq in tl.tracks:
print each_item.media_reference
```

or, in the case of any nested composition, like this:
Or, in the case of a nested composition, like this:

```python
import opentimelineio as otio
Expand All @@ -72,7 +72,7 @@ A clip may set its timing information (which is used to compute its `duration()`
- `source_range`
The range of media that is cut into the sequence, in the space of the available range (if it is set). In other words, it further truncates the available_range.

A clip must have at least one set or else its duration is not computable.
A clip must have at least one set or else its duration is not computable:

```python
cl.duration()
Expand Down
1 change: 1 addition & 0 deletions docs/tutorials/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Now you fetch the latest changes from Pixar's OpenTimelineIO repo like this:

```bash
git fetch upstream
git merge upstream/master
```

All the development should happen against the `master` branch. We recommend you create a new branch for each feature or fix that you'd like to make and give it a descriptive name so that you can remember it later. You can checkout a new branch and create it simultaneously like this:
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/otio-timeline-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ media file contains media that starts at 4 seconds and 4 frames (timecode
In many cases you might not know the available_range because the media is
missing, or points to a file path or URL which might be expensive to query.
If that’s the case, then the available_range of a media_reference will be
None.
`None`.

Above the media references, we see that each `Clip` has a source_range, which
specifies a trimmed segment of media. In cases where we only want a portion
of the available media, the source_range will start at a higher start_time,
and/or have a shorter duration. The colored segments of "Media-001", "Media-002"
and "Media-003" show the portion that each clip’s source_range references.

In the case of "Clip-004", the source_range is None, so it exposes its entire
In the case of "Clip-004", the source_range is `None`, so it exposes its entire
media reference. In the OTIO API, you can query the trimmed_range() of a
clip to get the range of media used regardless of whether it has a
source_range, available_range or both - but it needs at least one of the
Expand Down Expand Up @@ -150,7 +150,7 @@ If a source_range is specified, then only a trimmed segment of the inner
available_range of the nested composition is computed and included in
the outer composition.

"Nested Stack" contains two tracks, with some clips, gaps, a track-level
"Nested Stack" contains two tracks, with some clips, gaps, and a track-level
source_range on the lower track. This illustrates how the content of
"Nested Stack" is composed upwards into "Track-001" so that a trimmed portion
of "Clip-005" and "Clip-003" appear in the flattened composition.
Expand Down
13 changes: 6 additions & 7 deletions docs/tutorials/time-ranges.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ The `source_range` is specified in the Clip time frame.

Note that `source_range` may be `None` indicating that the Clip should use
the full `available_range()` whatever that may be. If both `source_range`
and `available_range()` are None, then the Clip is invalid. You need at
and `available_range()` are `None`, then the Clip is invalid. You need at
least one of those.

Usually this will be a shorter segment than the `available_range()` but this
Expand Down Expand Up @@ -82,16 +82,15 @@ code when you are asking for a different duration.

#### `Clip.range_in_parent()`

The answer to this depends on what type of the Clip's parent. In the
The answer to this depends on what type is the Clip's parent. In the
typical case, the Clip is inside a Track, so the `Clip.range_in_parent()`
will give you the range within that Track where this Clip is visible.
Each clip within the Track will have a start time that is directly after
the previous clip's end. So, given a Track with clipA and clipB in it,
this is always true:

The `range_in_parent()` is specified in the parent time frame.

`clipA.range_in_parent().end_time_exclusive() == clipB.range_in_parent().start_time`
- The `range_in_parent()` is specified in the parent time frame.
- `clipA.range_in_parent().end_time_exclusive() == clipB.range_in_parent().start_time`

If the parent is a Stack, then `range_in_parent()` is less interesting. The
start time will always have `.value == 0` and the duration is the Clip's
Expand All @@ -103,11 +102,11 @@ Track around.
#### `Clip.trimmed_range_in_parent()`

This is the same as `Clip.range_in_parent()` but trimmed to the *parent*
`source_range`. In most cases the parent has a `source_range` of None, so
`source_range`. In most cases the parent has a `source_range` of `None`, so
there is no trimming, but in cases where the parent is trimmed, you may
want to ask where a Clip is relative to the trimmed parent. In cases where
the Clip is completely trimmed by the parent, the
`Clip.trimmed_range_in_parent()` will be None.
`Clip.trimmed_range_in_parent()` will be `None`.

The `trimmed_range_in_parent()` is specified in the parent time frame.

Expand Down
17 changes: 9 additions & 8 deletions docs/tutorials/write-a-hookscript.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
OpenTimelineIO Hook Scripts are plugins that run at predefined points during the execution of various OTIO functions, for example after an adapter has read a file into memory but before the media linker has run.

To write a new hook script, you create a python source file that defines a
a function named ``hook_function`` with signature:
function named ``hook_function`` with signature:
``hook_function :: otio.schema.Timeline, Dict => otio.schema.Timeline``

The first argument is the timeline to process, and the second one is a dictionary of arguments that can be passed to it by the adapter or media linker. Only one hook function can be defined per python file.

For example:
```python

def hook_function(tl, argument_map=None):
for cl in tl.each_clip():
cl.metadata['example_hook_function_was_here'] = True
Expand Down Expand Up @@ -47,13 +46,15 @@ To create a new OTIO hook script, you need to create a file myhooks.py. Then add

The ``hook_scripts`` section will register the plugin with the system, and the ``hooks`` section will attach the scripts to hooks.

Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment variable (which is "`:`" separated). You may also define media linkers and adapters via the same manifest.
Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment variable (which is separated with `:` for POSIX or `;` for Windows). You may also define media linkers and adapters via the same manifest.

## Running a Hook Script

If you would like to call a hook script from a plugin, the hooks need not be one of the ones that otio pre-defines. You can have a plugin adapter or media linker, for example, that defines its own hooks and calls your own custom studio specific hook scripts. To run a hook script from your custom code, you can call:
If you would like to call a hook script from a plugin, the hooks need not be one of the ones that OTIO pre-defines. You can have a plugin adapter or media linker, for example, that defines its own hooks and calls your own custom studio specific hook scripts. To run a hook script from your custom code, you can call:

``otio.hooks.run("some_hook", some_timeline, optional_argument_dict)``
```python
otio.hooks.run("some_hook", some_timeline, optional_argument_dict)
```

This will call the ``some_hook`` hook script and pass in ``some_timeline`` and ``optional_argument_dict``.

Expand All @@ -76,7 +77,7 @@ print hook_list # ['c','b','a']

Now c will run, then b, then a.

To delete a function the list:
To delete a function in the list:
```python
del hook_list[1]
```
Expand All @@ -85,7 +86,7 @@ del hook_list[1]

### Replacing part of a path for drive mapping

An example use-case would be to create a pre-write adapter hook that checks the argument map for style being identified as nucoda and then preforms a path replacement on the reference url
An example use-case would be to create a pre-write adapter hook that checks the argument map for a style being identified as nucoda and then preforms a path replacement on the reference url:

```python
def hook_function(in_timeline,argument_map=None):
Expand All @@ -99,7 +100,7 @@ def hook_function(in_timeline,argument_map=None):

### Add an incremental copy of otio file to backup folder

Example of a post adapter write hook that creates a timestamped copy of newly written file in a hidden "incremental" folder
Example of a post adapter write hook that creates a timestamped copy of newly written file in a hidden "incremental" folder:

```python
import os
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/write-a-media-linker.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ To create a new OTIO Adapter, you need to create a file mymedialinker.py. Then a
]
}
Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment variable (which is "`:`" separated).
Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment variable (which is separated with `:` for POSIX or `;` for Windows).

Finally, to specify this linker as the default media linker, set `OTIO_DEFAULT_MEDIA_LINKER` to the name of the media linker:

Expand Down
5 changes: 3 additions & 2 deletions docs/tutorials/write-a-schemadef.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ but your schema class could have any number of parameters as needed to
contain the data fields you want to have in your class.

One or more class definitions like this one can be included in a plugin
source file, which must then be added to the plugin manifest as shown below:
source file, which must then be added to the plugin manifest as shown below.


## Registering Your SchemaDef Plugin
Expand All @@ -85,7 +85,8 @@ Then you must add it to a plugin manifest:

The same plugin manifest may also include adapters and media linkers, if desired.

Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment variable (which is "`:`" separated).
Then you need to add this manifest to your `$OTIO_PLUGIN_MANIFEST_PATH` environment
variable (which is separated with `:` for POSIX or `;` for Windows).

## Using the New Schema in Your Code

Expand Down
Loading

0 comments on commit 93f5e36

Please sign in to comment.