Source code for ways.base.plugin

#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''A module that holds Plugin classes and objects that combine into a Context.'''

# IMPORT STANDARD LIBRARIES
import uuid

# IMPORT THIRD-PARTY LIBRARIES
import six

# IMPORT WAYS LIBRARIES
import ways

# IMPORT LOCAL LIBRARIES
from ..core import check
from ..helper import common


[docs]class PluginRegistry(type): '''A metaclass that adds new Plugin objects to a cache.''' def __new__(mcs, clsname, bases, attrs): '''Add the created object to the HistoryCache.''' new_class = super(PluginRegistry, mcs).__new__( mcs, clsname, bases, attrs) # TODO : We still need to not be using 'Plugin' ... # If we explicitly state not to register a plugin, don't register it # If add_to_registry isn't defined for this Plugin, # assume that we should register it # try: if new_class.__name__ == 'Plugin' or not new_class.add_to_registry: return new_class except AttributeError: return new_class assignment = get_assignment(new_class) ways.add_plugin(new_class(), assignment) return new_class
# pylint: disable=too-few-public-methods
[docs]@six.add_metaclass(PluginRegistry) class Plugin(object): '''An add-on that is later retrieved by Context to gather its data.''' add_to_registry = True _data = dict() @property def data(self): '''dict[str]: The display properties (like {'color': 'red'}).''' return self._data @data.setter def data(self, value): '''Set the data on this instance with whatever value is. Args: value (dict[str]): The new values for this instance. ''' self._data = value
[docs]class DataPlugin(Plugin): '''An add-on that was made from a serialized file (JSON/YAML/etc). This class behaves exactly like a regular Plugin object and is stored in the same space as Plugin objects. DataPlugin does not add itself to the cache automatically. It is the responsibility of some other class/function to register it to Ways. We do this so that we can have better control over the DataPlugin's args and its assignment before it hits the cache. ''' add_to_registry = False def __init__(self, name, sources, info, assignment): '''Create the object and set its sources. Args: sources (list[str]): The location(s) that defined this Plugin. info (dict[str]): The information that came from a JSON or YAML file which sets the base settings of the object. assignment (str): The grouping location where this Plugin will go. This assignment must be the same as the Context that this Plugin is meant for. Raises: ValueError: If there are missing keys in data that this class needs. ''' missing_required_keys = set(self._required_keys()) - set(info.keys()) if missing_required_keys: raise ValueError('Info: "{info}" is missing keys, "{keys}".' ''.format(info=info, keys=missing_required_keys)) # Give this plugin a UUID if none was given, for us # Data is assumed to be a core.classes.dict_class.ReadOnlyDict so we # try to unlock it, here. If it's not a custom dict, just let it pass # try: is_settable = info.settable info.settable = True except AttributeError: pass info.setdefault('uuid', str(uuid.uuid4())) try: info.settable = is_settable except AttributeError: pass self.name = name self._info = info self.sources = tuple(sources) self._data = self._info.get('data', dict()) self.assignment = assignment super(DataPlugin, self).__init__() @classmethod def _required_keys(cls): '''tuple[str]: Keys that must be set in our Plugin.''' return ('hierarchy', )
[docs] def is_path(self): '''If the mapping is a filepath or None if unsure. Returns: bool or NoneType: If the mapping is a path to a file/folder on disk. ''' try: return self._info['path'] except KeyError: return None
[docs] def get_assignment(self): '''str: Where this Plugin lives in Ways, along with its hierarchy.''' return self.assignment
[docs] def get_groups(self): '''Get the groups that this Plugin evaluates onto. Note: The term 'groups' is not the same as the assignment of a Plugin. They are two different things. Returns: tuple[str]: The groups. ''' value = check.force_itertype(self._info.get('groups', ('*', )), itertype=tuple) is_empty = not [val for val in value if val.strip()] if is_empty: value = ('*', ) return value
[docs] def get_hierarchy(self): '''tuple[str] or str: The location that this Plugin exists within.''' return self._info['hierarchy']
[docs] def get_mapping(self): '''str: The physical location of this Plugin (on the filesystem).''' try: return self._info['mapping'] except KeyError: return ''
[docs] def get_mapping_details(self): '''dict[str]: Information about the mapping, if needed.''' return self._info.get('mapping_details', dict())
[docs] def get_max_folder(self): '''str: The furthest location up that this plugin can navigate to.''' return self._info.get('max_folder', '')
[docs] def get_platforms(self): '''set[str]: The platforms that this Plugin is allowed to run on.''' platforms = ways.get_known_platfoms() return set(self._info.get('platforms', platforms))
[docs] def get_uses(self): '''tuple[str]: The Context hierarchies this instance depends on.''' return self._info.get('uses', tuple())
[docs] def get_uuid(self): '''str: A unique ID for this plugin.''' return self._info.get('uuid', '')
def __repr__(self): '''str: The information needed to reproduce this instance.''' return '{cls_}(sources={sources!r}, data={data})'.format( cls_=self.__class__.__name__, sources=self.sources, data=dict(self._info)) def __str__(self): '''str: A more concise print-out of this instance.''' return '{cls_}(hierarchy={hierarchy}, sources={sources!r})'.format( cls_=self.__class__.__name__, hierarchy=self.get_hierarchy(), sources=self.sources)
[docs]def get_assignment(obj): '''str: Get an object's assignment or fallback to ways.DEFAULT_ASSIGNMENT.''' try: return obj.get_assignment() except AttributeError: return common.DEFAULT_ASSIGNMENT