"""Pillar extensions support. Each Pillar extension should create a subclass of PillarExtension, which can then be registered to the application at app creation time: from pillar_server import PillarServer from attract_server import AttractExtension app = PillarServer('.') app.load_extension(AttractExtension(), url_prefix='/attract') app.process_extensions() # Always process extensions after the last one is loaded. if __name__ == '__main__': app.run('::0', 5000) """ import abc import inspect import pathlib import typing import flask import pillarsdk class PillarExtension(object, metaclass=abc.ABCMeta): # Set to True when your extension implements the project_settings() method. has_project_settings = False # Set to True when your extension implements the context_processor() method. has_context_processor = False # List of Celery task modules introduced by this extension. celery_task_modules: typing.List[str] = [] # Set of user roles used/introduced by this extension. user_roles: typing.Set[str] = set() user_roles_indexable: typing.Set[str] = set() # User capabilities introduced by this extension. The final set of # capabilities is the union of all app-level and extension-level caps. user_caps: typing.Mapping[str, typing.FrozenSet] = {} @property @abc.abstractmethod def name(self): """The name of this extension. The name determines the path at which Eve exposes the extension's resources (/{extension name}/{resource name}), as well as the MongoDB collection in which those resources are stored ({extensions name}.{resource name}). :rtype: unicode """ @property def icon(self) -> str: """Returns the icon HTML class, for use like i.pi-{{ext.icon}} Defaults to the extension name. """ return self.name @abc.abstractmethod def flask_config(self): """Returns extension-specific defaults for the Flask configuration. Use this to set sensible default values for configuration settings introduced by the extension. :rtype: dict """ @abc.abstractmethod def blueprints(self): """Returns the list of top-level blueprints for the extension. These blueprints will be mounted at the url prefix given to app.load_extension(). :rtype: list of flask.Blueprint objects. """ @abc.abstractmethod def eve_settings(self): """Returns extensions to the Eve settings. Currently only the DOMAIN key is used to insert new resources into Eve's configuration. :rtype: dict """ @property def template_path(self): """Returns the path where templates for this extension are stored. Note that this path is not connected to any blueprint, so it is up to the extension to provide extension-unique subdirectories. """ return None @property def static_path(self): """Returns the path where static files are stored. Registers an endpoint named 'static_', to use like: `url_for('static_attract', filename='js/somefile.js')` May return None, in which case the extension will not be able to serve static files. """ return None @property def translations_path(self) -> typing.Union[pathlib.Path, None]: """Returns the path where the translations for this extension are stored. This is top folder that contains a "translations" sub-folder May return None, in which case English will always be used for this extension. """ class_filename = pathlib.Path(inspect.getfile(self.__class__)) # Pillar extensions instantiate the PillarExtension from a sub-folder in # the main project (e.g. //blender_cloud/blender_cloud/__init__.py), but # the translations folders is in the main project folder. translations_path = class_filename.parents[1] / 'translations' return translations_path if translations_path.is_dir() else None def setup_app(self, app): """Called during app startup, after all extensions have loaded.""" def sidebar_links(self, project: pillarsdk.Project) -> str: """Returns the sidebar link(s) for the given projects. :returns: HTML as a string for the sidebar. """ return '' def project_settings(self, project: pillarsdk.Project, **template_args: dict) -> flask.Response: """Renders the project settings page for this extension. Set YourExtension.has_project_settings = True and Pillar will call this function. :param project: the project for which to render the settings. :param template_args: additional template arguments. :returns: a Flask HTTP response """ def context_processor(self) -> dict: """Returns a dictionary that gets injected into the Flask Jinja2 namespace. Set has_context_processor to True when your extension implements this method. """ return {}