Skip to content

Commit

Permalink
Allow overriding builtin plugins via entrypoint method. (#1389)
Browse files Browse the repository at this point in the history
* Load entrypoint plugins before builtin and contrib in order to allow overriding builtin plugins.

Signed-off-by: Mark Reid <[email protected]>
  • Loading branch information
markreidvfx authored Sep 12, 2022
1 parent 424967a commit dcf3033
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 30 deletions.
60 changes: 30 additions & 30 deletions src/py-opentimelineio/opentimelineio/plugins/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,10 @@ def load_manifest():
The order of loading (and precedence) is:
1. manifests specfied via the ``OTIO_PLUGIN_MANIFEST_PATH`` variable
2. builtin plugin manifest
3. contrib plugin manifest
4. ``setuptools.pkg_resources`` based plugin manifests
1. Manifests specified via the ``OTIO_PLUGIN_MANIFEST_PATH`` variable
2. Entrypoint based plugin manifests
3. Builtin plugin manifest
4. Contrib plugin manifest
"""

result = Manifest()
Expand All @@ -232,32 +232,6 @@ def load_manifest():

result.extend(manifest_from_file(json_path))

# the builtin plugin manifest
builtin_manifest_path = os.path.join(
os.path.dirname(os.path.dirname(inspect.getsourcefile(core))),
"adapters",
"builtin_adapters.plugin_manifest.json"
)
if os.path.abspath(builtin_manifest_path) not in result.source_files:
plugin_manifest = manifest_from_file(builtin_manifest_path)
result.extend(plugin_manifest)

# the contrib plugin manifest (located in the opentimelineio_contrib package)
try:
import opentimelineio_contrib as otio_c

contrib_manifest_path = os.path.join(
os.path.dirname(inspect.getsourcefile(otio_c)),
"adapters",
"contrib_adapters.plugin_manifest.json"
)
if os.path.abspath(contrib_manifest_path) not in result.source_files:
contrib_manifest = manifest_from_file(contrib_manifest_path)
result.extend(contrib_manifest)

except ImportError:
pass

# setuptools.pkg_resources based plugins
if pkg_resources:
for plugin in pkg_resources.iter_entry_points(
Expand Down Expand Up @@ -324,6 +298,32 @@ def load_manifest():
# available?
pass

# the builtin plugin manifest
builtin_manifest_path = os.path.join(
os.path.dirname(os.path.dirname(inspect.getsourcefile(core))),
"adapters",
"builtin_adapters.plugin_manifest.json"
)
if os.path.abspath(builtin_manifest_path) not in result.source_files:
plugin_manifest = manifest_from_file(builtin_manifest_path)
result.extend(plugin_manifest)

# the contrib plugin manifest (located in the opentimelineio_contrib package)
try:
import opentimelineio_contrib as otio_c

contrib_manifest_path = os.path.join(
os.path.dirname(inspect.getsourcefile(otio_c)),
"adapters",
"contrib_adapters.plugin_manifest.json"
)
if os.path.abspath(contrib_manifest_path) not in result.source_files:
contrib_manifest = manifest_from_file(contrib_manifest_path)
result.extend(contrib_manifest)

except ImportError:
pass

# force the schemadefs to load and add to schemadef module namespace
for s in result.schemadefs:
s.module()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Metadata-Version: 1.0
Name: otio-override-adapter
Version: 1.0.0
Summary: Dummy Adapter used for testing.
Home-page: http://opentimeline.io
Author: Contributors to the OpenTimelineIO project
Author-email: [email protected]
License: Modified Apache 2.0 License
Description-Content-Type: UNKNOWN
Description: UNKNOWN
Platform: any
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[opentimelineio.plugins]
mock_plugin = otio_override_adapter

Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"OTIO_SCHEMA" : "PluginManifest.1",
"adapters": [
{
"OTIO_SCHEMA" : "Adapter.1",
"name" : "cmx_3600",
"execution_scope" : "in process",
"filepath" : "adapter.py",
"suffixes" : ["edl"]
}
]
}
35 changes: 35 additions & 0 deletions tests/test_plugin_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ def setUp(self):
"plugin_manifest.json"
)

self.override_adapter_manifest_path = os.path.join(
mock_module_path,
"otio_override_adapter",
"plugin_manifest.json"
)

# Create a WorkingSet as if the module were installed
entries = [mock_module_path] + pkg_resources.working_set.entries

Expand All @@ -70,6 +76,9 @@ def tearDown(self):
if 'otio_mockplugin' in sys.modules:
del sys.modules['otio_mockplugin']

if 'otio_override_adapter' in sys.modules:
del sys.modules['otio_override_adapter']

def test_detect_plugin(self):
"""This manifest uses the plugin_manifest function"""

Expand All @@ -91,6 +100,28 @@ def test_detect_plugin(self):
for linker in man.media_linkers:
self.assertIsInstance(linker, otio.media_linker.MediaLinker)

def test_overrride_adapter(self):
# Test that entrypoint plugins load before builtin and contrib
man = otio.plugins.manifest.load_manifest()

# The override_adapter creates another cmx_3600 adapter
adapters = [adapter for adapter in man.adapters
if adapter.name == "cmx_3600"]

# More then one cmx_3600 adapter should exist.
self.assertTrue(len(adapters) > 1)

# Override adapter should be the first adapter found
manifest = adapters[0].plugin_info_map().get('from manifest', None)
self.assertEqual(manifest, os.path.abspath(self.override_adapter_manifest_path))

self.assertTrue(
any(
True for p in man.source_files
if self.override_adapter_manifest_path in p
)
)

def test_pkg_resources_disabled(self):
os.environ["OTIO_DISABLE_PKG_RESOURCE_PLUGINS"] = "1"
import_reload(otio.plugins.manifest)
Expand All @@ -100,6 +131,10 @@ def test_pkg_resources_disabled(self):
with self.assertRaises(AssertionError):
self.test_detect_plugin()

# override adapter should not be loaded either
with self.assertRaises(AssertionError):
self.test_overrride_adapter()

# remove the environment variable and reload again for usage in the
# other tests
del os.environ["OTIO_DISABLE_PKG_RESOURCE_PLUGINS"]
Expand Down

0 comments on commit dcf3033

Please sign in to comment.