Source code for orangecanvas.registry.description

"""
Widget meta description classes
===============================

"""

import sys
import copy

# Exceptions


class DescriptionError(Exception):
    pass


class WidgetSpecificationError(DescriptionError):
    pass


class SignalSpecificationError(DescriptionError):
    pass


class CategorySpecificationError(DescriptionError):
    pass


###############
# Channel flags
###############

# A single signal
Single = 2

# Multiple signal (more then one input on the channel)
Multiple = 4

# Default signal (default or primary input/output)
Default = 8
NonDefault = 16

# Explicit - only connected if specifically requested or the only possibility
Explicit = 32

# Dynamic type output signal
Dynamic = 64


# Input/output signal (channel) description


[docs]class InputSignal(object): """ Description of an input channel. Parameters ---------- name : str Name of the channel. type : str or `type` Type of the accepted signals. handler : str Name of the handler method for the signal. flags : int, optional Channel flags. id : str A unique id of the input signal. doc : str, optional A docstring documenting the channel. replaces : List[str] A list of names this input replaces. """ def __init__(self, name, type, handler, flags=Single + NonDefault, id=None, doc=None, replaces=[]): self.name = name self.type = type self.handler = handler self.id = id self.doc = doc self.replaces = list(replaces) if not (flags & Single or flags & Multiple): flags += Single if not (flags & Default or flags & NonDefault): flags += NonDefault self.single = flags & Single self.default = flags & Default self.explicit = flags & Explicit self.flags = flags def __str__(self): fmt = ("{0.__name__}(name={name!r}, type={type!s}, " "handler={handler}, ...)") return fmt.format(type(self), **self.__dict__) __repr__ = __str__
def input_channel_from_args(args): if isinstance(args, tuple): return InputSignal(*args) elif isinstance(args, dict): return InputSignal(**args) elif isinstance(args, InputSignal): return copy.copy(args) else: raise TypeError("tuple, dict or InputSignal expected " "(got {0!r})".format(type(args)))
[docs]class OutputSignal(object): """ Description of an output channel. Parameters ---------- name : str Name of the channel. type : str or `type` Type of the output signals. flags : int, optional Channel flags. id : str A unique id of the output signal. doc : str, optional A docstring documenting the channel. replaces : List[str] A list of names this output replaces. """ def __init__(self, name, type, flags=Single + NonDefault, id=None, doc=None, replaces=[]): self.name = name self.type = type self.id = id self.doc = doc self.replaces = list(replaces) if not (flags & Single or flags & Multiple): flags += Single if not (flags & Default or flags & NonDefault): flags += NonDefault self.single = flags & Single self.default = flags & Default self.explicit = flags & Explicit self.dynamic = flags & Dynamic self.flags = flags if self.dynamic and not self.single: raise SignalSpecificationError( "Output signal can not be 'Multiple' and 'Dynamic'." ) def __str__(self): fmt = ("{0.__name__}(name={name!r}, type={type!s}, " "...)") return fmt.format(type(self), **self.__dict__) __repr__ = __str__
def output_channel_from_args(args): if isinstance(args, tuple): return OutputSignal(*args) elif isinstance(args, dict): return OutputSignal(**args) elif isinstance(args, OutputSignal): return copy.copy(args) else: raise TypeError("tuple, dict or OutputSignal expected " "(got {0!r})".format(type(args)))
[docs]class WidgetDescription(object): """ Description of a widget. Parameters ---------- name : str A human readable name of the widget. id : str A unique identifier of the widget (in most situations this should be the full module name). category : str, optional A name of the category in which this widget belongs. version : str, optional Version of the widget. By default the widget inherits the project version. description : str, optional A short description of the widget, suitable for a tool tip. long_description : str, optional A longer description of the widget, suitable for a 'what's this?' role. qualified_name : str A qualified name (import name) of the class implementing the widget. package : str, optional A package name where the widget is implemented. project_name : str, optional The distribution name that provides the widget. inputs : list of :class:`InputSignal`, optional A list of input channels provided by the widget. outputs : list of :class:`OutputSignal`, optional A list of output channels provided by the widget. help : str, optional URL or an Resource template of a detailed widget help page. help_ref : str, optional A text reference id that can be used to identify the help page, for instance an intersphinx reference. author : str, optional Author name. author_email : str, optional Author email address. maintainer : str, optional Maintainer name maintainer_email : str, optional Maintainer email address. keywords : list-of-str, optional A list of keyword phrases. priority : int, optional Widget priority (the order of the widgets in a GUI presentation). icon : str, optional A filename of the widget icon (in relation to the package). background : str, optional Widget's background color (in the canvas GUI). replaces : list-of-str, optional A list of ids this widget replaces (optional). """ def __init__(self, name, id, category=None, version=None, description=None, long_description=None, qualified_name=None, package=None, project_name=None, inputs=[], outputs=[], author=None, author_email=None, maintainer=None, maintainer_email=None, help=None, help_ref=None, url=None, keywords=None, priority=sys.maxsize, icon=None, background=None, replaces=None, ): if not qualified_name: # TODO: Should also check that the name is real. raise ValueError("'qualified_name' must be supplied.") self.name = name self.id = id self.category = category self.version = version self.description = description self.long_description = long_description self.qualified_name = qualified_name self.package = package self.project_name = project_name self.inputs = inputs self.outputs = outputs self.help = help self.help_ref = help_ref self.author = author self.author_email = author_email self.maintainer = maintainer self.maintainer_email = maintainer_email self.url = url self.keywords = keywords self.priority = priority self.icon = icon self.background = background self.replaces = replaces def __str__(self): return ("WidgetDescription(name=%(name)r, id=%(id)r), " "category=%(category)r, ...)") % self.__dict__ def __repr__(self): return self.__str__()
[docs] @classmethod def from_module(cls, module): """ Get the widget description from a module. The module is inspected for global variables (upper case versions of `WidgetDescription.__init__` parameters). Parameters ---------- module : `module` or str A module to inspect for widget description. Can be passed as a string (qualified import name). """ if isinstance(module, str): module = __import__(module, fromlist=[""]) module_name = module.__name__.rsplit(".", 1)[-1] if module.__package__: package_name = module.__package__.rsplit(".", 1)[-1] else: package_name = None # Default widget class name unless otherwise specified is the # module name, and default category the package name default_cls_name = module_name default_cat_name = package_name if package_name else "" widget_cls_name = getattr(module, "WIDGET_CLASS", default_cls_name) try: widget_class = getattr(module, widget_cls_name) name = getattr(module, "NAME") except AttributeError: # The module does not have a widget class implementation or the # widget name. raise WidgetSpecificationError qualified_name = "%s.%s" % (module.__name__, widget_cls_name) id = getattr(module, "ID", module_name) inputs = getattr(module, "INPUTS", []) outputs = getattr(module, "OUTPUTS", []) category = getattr(module, "CATEGORY", default_cat_name) version = getattr(module, "VERSION", None) description = getattr(module, "DESCRIPTION", name) long_description = getattr(module, "LONG_DESCRIPTION", None) author = getattr(module, "AUTHOR", None) author_email = getattr(module, "AUTHOR_EMAIL", None) maintainer = getattr(module, "MAINTAINER", None) maintainer_email = getattr(module, "MAINTAINER_EMAIL", None) help = getattr(module, "HELP", None) help_ref = getattr(module, "HELP_REF", None) url = getattr(module, "URL", None) icon = getattr(module, "ICON", None) priority = getattr(module, "PRIORITY", sys.maxsize) keywords = getattr(module, "KEYWORDS", None) background = getattr(module, "BACKGROUND", None) replaces = getattr(module, "REPLACES", None) inputs = list(map(input_channel_from_args, inputs)) outputs = list(map(output_channel_from_args, outputs)) # Convert all signal types into qualified names. # This is to prevent any possible import problems when cached # descriptions are unpickled (the relevant code using this lists # should be able to handle missing types better). for s in inputs + outputs: s.type = "%s.%s" % (s.type.__module__, s.type.__name__) return WidgetDescription( name=name, id=id, category=category, version=version, description=description, long_description=long_description, qualified_name=qualified_name, package=module.__package__, inputs=inputs, outputs=outputs, author=author, author_email=author_email, maintainer=maintainer, maintainer_email=maintainer_email, help=help, help_ref=help_ref, url=url, keywords=keywords, priority=priority, icon=icon, background=background, replaces=replaces)
[docs]class CategoryDescription(object): """ Description of a widget category. Parameters ---------- name : str A human readable name. version : str, optional Version string. description : str, optional A short description of the category, suitable for a tool tip. long_description : str, optional A longer description. qualified_name : str, Qualified name project_name : str A project name providing the category. priority : int Priority (order in the GUI). icon : str An icon filename (a resource name retrievable using `pkg_resources` relative to `qualified_name`). background : str An background color for widgets in this category. hidden : bool Is this category (by default) hidden in the canvas gui. """ def __init__(self, name=None, version=None, description=None, long_description=None, qualified_name=None, package=None, project_name=None, author=None, author_email=None, maintainer=None, maintainer_email=None, url=None, help=None, keywords=None, widgets=None, priority=sys.maxsize, icon=None, background=None, hidden=False ): self.name = name self.version = version self.description = description self.long_description = long_description self.qualified_name = qualified_name self.package = package self.project_name = project_name self.author = author self.author_email = author_email self.maintainer = maintainer self.maintainer_email = maintainer_email self.url = url self.help = help self.keywords = keywords self.widgets = widgets or [] self.priority = priority self.icon = icon self.background = background self.hidden = hidden def __str__(self): return "CategoryDescription(name=%(name)r, ...)" % self.__dict__ def __repr__(self): return self.__str__()
[docs] @classmethod def from_package(cls, package): """ Get the CategoryDescription from a package. Parameters ---------- package : `module` or `str` A package containing the category. """ if isinstance(package, str): package = __import__(package, fromlist=[""]) package_name = package.__name__ qualified_name = package_name default_name = package_name.rsplit(".", 1)[-1] name = getattr(package, "NAME", default_name) description = getattr(package, "DESCRIPTION", None) long_description = getattr(package, "LONG_DESCRIPTION", None) author = getattr(package, "AUTHOR", None) author_email = getattr(package, "AUTHOR_EMAIL", None) maintainer = getattr(package, "MAINTAINER", None) maintainer_email = getattr(package, "MAINTAINER_MAIL", None) url = getattr(package, "URL", None) help = getattr(package, "HELP", None) keywords = getattr(package, "KEYWORDS", None) widgets = getattr(package, "WIDGETS", None) priority = getattr(package, "PRIORITY", sys.maxsize - 1) icon = getattr(package, "ICON", None) background = getattr(package, "BACKGROUND", None) hidden = getattr(package, "HIDDEN", None) if priority == sys.maxsize - 1 and name.lower() == "prototypes": priority = sys.maxsize return CategoryDescription( name=name, qualified_name=qualified_name, description=description, long_description=long_description, help=help, author=author, author_email=author_email, maintainer=maintainer, maintainer_email=maintainer_email, url=url, keywords=keywords, widgets=widgets, priority=priority, icon=icon, background=background, hidden=hidden)