Source code for ways.base.finder

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

'''The main class/functions used to find actions for Context/Asset objects.'''

# IMPORT STANDARD LIBRARIES
import functools
import itertools
import collections

# IMPORT WAYS LIBRARIES
import ways

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

# TODO : In general, I regret the name of this module. FIXME


[docs]class Find(compat.DirMixIn, object): '''A wrapper around a Context object that provides some basic syntax-sugar. The syntax of using Context objects is clunky. This class is meant to help. See the second and last example, for details. Example: >>> context = Context('/some/context') >>> command = context.get_action('get_assets') >>> command() >>> # ['/some/asset1.tif', '/some/asset2.tif', '/some/asset2.tif'] Example: >>> # If an action is meant to return back an iterable object and the >>> # action that it gets back is None, that can cause immediate problems >>> # >>> context = Context('/some/context') >>> command = context.get_action('get_assets') # Returns None >>> for asset in command(): >>> print(asset) >>> # The above code will TypeError error if get_action returns None Example: >>> # The best you can do is this >>> context = Context('/some/context') >>> command = context.get_action('get_assets') or lambda: [] >>> for asset in command(): >>> print(asset) >>> # The above code will not error but it's pretty verbose compared to >>> # what we're actually trying to accomplish. Example: >>> # Here is (IMO) the best solution >>> context = Context('/some/context') >>> find = finder.Find(context) >>> # Returns [] even if get_assets isn't defined >>> # because get_assets is listed in Finder(context).defaults >>> # >>> for asset in find.get_assets(): >>> print(asset) ''' _default_key_name = 'default' _default_key = (_default_key_name, ) defaults = collections.defaultdict() defaults[_default_key] = dict() def __init__(self, context): '''Create this object and store the given Context. Args: context (:class:`ways.api.Context`): The Context to wrap. ''' super(Find, self).__init__() self.context = context
[docs] @classmethod def add_to_defaults(cls, name, value, hierarchy=None): '''Add default value if an Action name is missing. Args: name (str): The name of the Action to add a default value for. value: The object to add as the default return value for a missing Action. hierarchy (:obj:`tuple[str] or str`, optional): The location to put these default values. If no hierarchy is given, ('default', ) is used, instead. ''' if hierarchy is None: hierarchy = cls._default_key_name hierarchy = common.split_by_comma(hierarchy) cls.defaults[hierarchy][name] = value
[docs] @classmethod def clear(cls): '''Remove all stored default values from this class.''' cls.defaults.clear() cls.defaults[cls._default_key] = dict()
def __getattr__(self, name): '''Try to pass missing calls from Finder to the stored Context's actions. If the action is missing in Context, try to return a default value for the action. If no default is found, just return None like normal. ''' def return_value(arg): '''Just return the vaue of the original function.''' return arg command = self.context.get_action(name) if command is None: try: return functools.partial( return_value, self.defaults[self.context.get_hierarchy()][name]) except KeyError: pass try: return functools.partial(return_value, self.defaults[self._default_key][name]) except KeyError: message = "AttributeError: '{obj}' has no attribute '{attr}'" \ "".format(obj=self.context.__class__.__name__, attr=name) raise AttributeError(message) return functools.partial(command, self.context) def __dir__(self): '''list[str]: Add Action names to the list of return items.''' return sorted( set(itertools.chain( super(Find, self).__dir__(), self.__dict__.keys(), ways.get_action_names(self.context.get_hierarchy()))))