Add support for entry points
Change-Id: I11fd40645479a815b29afaac0b12d45f6f7e6c21
This commit is contained in:
parent
98a0fb2405
commit
02bcffc174
42
README.rst
42
README.rst
@ -245,10 +245,44 @@ How to extend
|
||||
|
||||
Given the ever-widening OpenStack ecosystem, OSPurge can't support every
|
||||
OpenStack services. We intend to support in-tree, only the 'core' services.
|
||||
Fortunately, OSPurge is easily extensible. All you have to do is add a new
|
||||
Python module in the ``resources`` package and define one or more Python
|
||||
class(es) that subclass ``ospurge.resources.base.ServiceResource``. Your module
|
||||
will automatically be loaded and your methods called. Have a look at the
|
||||
Fortunately, OSPurge is easily extensible. There are 2 methods and you can
|
||||
chose the one you prefer:
|
||||
|
||||
1: Add a new Python module in the ``resources`` package and define one or more
|
||||
Python class(es) that subclass ``ospurge.resources.base.ServiceResource``.
|
||||
Your module will automatically be loaded and your methods called.
|
||||
|
||||
2: Create your standalone python modules and in your module's setup.py or
|
||||
setup.cfg file add an entry point to ``ospurge_resources`` pointing to the
|
||||
python module in which you subclass ``ospurge.resources.base.ServiceResource``.
|
||||
|
||||
setup.py example::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='my_ospurge_extension',
|
||||
entry_points={
|
||||
'ospurge_resources': [
|
||||
'foo = my_module.submodule_with_subclass',
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
setup.cfg example::
|
||||
|
||||
[entry_points]
|
||||
ospurge_resources =
|
||||
foo = my_module.submodule_with_subclass
|
||||
|
||||
Once your module installed, it will automatically be loaded and your methods
|
||||
called.
|
||||
|
||||
More examples on entry points:
|
||||
https://amir.rachum.com/blog/2017/07/28/python-entry-points/
|
||||
|
||||
Have a look
|
||||
at the
|
||||
``main.main`` and ``main.runner`` functions to fully understand the mechanism.
|
||||
|
||||
Note: We won't accept any patch that broaden what OSPurge supports, beyond
|
||||
|
28
ospurge/tests/resources/entry_points.py
Normal file
28
ospurge/tests/resources/entry_points.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from ospurge.resources.base import ServiceResource
|
||||
|
||||
|
||||
class Foo(ServiceResource):
|
||||
ORDER = 15
|
||||
|
||||
def list(self):
|
||||
return []
|
||||
|
||||
def delete(self, resource):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def to_str(resource):
|
||||
return "Foo"
|
@ -10,9 +10,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import logging
|
||||
import os
|
||||
import types
|
||||
import typing
|
||||
import unittest
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import six
|
||||
|
||||
import shade
|
||||
|
||||
from ospurge.resources.base import ServiceResource
|
||||
@ -20,6 +26,21 @@ from ospurge.tests import mock
|
||||
from ospurge import utils
|
||||
|
||||
|
||||
def register_test_entry_point():
|
||||
test_resource_file = os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__), 'resources/entry_points.py'
|
||||
)
|
||||
)
|
||||
distribution = pkg_resources.Distribution.from_filename(test_resource_file)
|
||||
entry_point = pkg_resources.EntryPoint(
|
||||
'foo', 'ospurge.tests.resources.entry_points', dist=distribution
|
||||
)
|
||||
distribution._ep_map = {utils.ENTRY_POINTS_NAME: {'foo': entry_point}}
|
||||
pkg_resources.working_set.add(distribution, 'foo')
|
||||
return entry_point
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
def test_replace_project_info_in_config(self):
|
||||
config = {
|
||||
@ -43,6 +64,25 @@ class TestUtils(unittest.TestCase):
|
||||
}
|
||||
})
|
||||
|
||||
def test_load_ospurge_resource_modules(self):
|
||||
modules = utils.load_ospurge_resource_modules()
|
||||
self.assertIsInstance(modules, typing.Dict)
|
||||
for name, module in six.iteritems(modules):
|
||||
# assertIsInstance(name, typing.AnyStr) fails with:
|
||||
# TypeError: Type variables cannot be used with isinstance().
|
||||
self.assertIsInstance(name, six.string_types)
|
||||
self.assertIsInstance(module, types.ModuleType)
|
||||
|
||||
def test_load_entry_points_modules(self):
|
||||
register_test_entry_point()
|
||||
modules = utils.load_entry_points_modules()
|
||||
self.assertIsInstance(modules, typing.Dict)
|
||||
for name, module in six.iteritems(modules):
|
||||
# assertIsInstance(name, typing.AnyStr) fails with:
|
||||
# TypeError: Type variables cannot be used with isinstance().
|
||||
self.assertIsInstance(name, six.string_types)
|
||||
self.assertIsInstance(module, types.ModuleType)
|
||||
|
||||
def test_get_all_resource_classes(self):
|
||||
classes = utils.get_resource_classes()
|
||||
self.assertIsInstance(classes, typing.List)
|
||||
|
@ -17,24 +17,45 @@ import os
|
||||
import pkgutil
|
||||
import re
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from ospurge.resources import base
|
||||
|
||||
|
||||
def get_resource_classes(resources=None):
|
||||
"""
|
||||
Import all the modules in the `resources` package and return all the
|
||||
subclasses of the `ServiceResource` ABC that match the `resources` arg.
|
||||
ENTRY_POINTS_NAME = 'ospurge_resources'
|
||||
|
||||
This way we can easily extend OSPurge by just adding a new file in the
|
||||
`resources` dir.
|
||||
"""
|
||||
|
||||
def load_ospurge_resource_modules():
|
||||
"""Import all the modules in the `resources` package."""
|
||||
modules = {}
|
||||
iter_modules = pkgutil.iter_modules(
|
||||
[os.path.join(os.path.dirname(__file__), 'resources')],
|
||||
prefix='ospurge.resources.'
|
||||
)
|
||||
for (_, name, ispkg) in iter_modules:
|
||||
if not ispkg:
|
||||
importlib.import_module(name)
|
||||
modules[name] = importlib.import_module(name)
|
||||
return modules
|
||||
|
||||
|
||||
def load_entry_points_modules(name=ENTRY_POINTS_NAME):
|
||||
"""Import all modules in the `name` entry point."""
|
||||
entry_points = {}
|
||||
for entry_point in pkg_resources.iter_entry_points(name):
|
||||
entry_points[entry_point.name] = entry_point.load()
|
||||
return entry_points
|
||||
|
||||
|
||||
def get_resource_classes(resources=None):
|
||||
"""
|
||||
Load all ospurge resource and entry point modules and return all the
|
||||
subclasses of the `ServiceResource` ABC that match the `resources` arg.
|
||||
|
||||
This way we can easily extend OSPurge by just adding a new file in the
|
||||
`resources` dir or a package with `ENTRY_POINTS_NAME` entry point.
|
||||
"""
|
||||
load_ospurge_resource_modules()
|
||||
load_entry_points_modules()
|
||||
|
||||
all_classes = base.ServiceResource.__subclasses__()
|
||||
|
||||
|
@ -17,6 +17,15 @@ from typing import Iterable
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import TypeVar
|
||||
from typing import AnyStr
|
||||
|
||||
|
||||
def load_ospurge_resource_modules() -> Dict:
|
||||
...
|
||||
|
||||
|
||||
def load_entry_points_modules(name: AnyStr) -> Dict:
|
||||
...
|
||||
|
||||
|
||||
def get_resource_classes(resources: Optional[Iterable[str]]=None) -> List:
|
||||
|
Loading…
Reference in New Issue
Block a user