'''
Utilities for making objects that work with the `CLICommandWrapper`
'''
from .utils import FCN
DEFAULT_OWM_DIR = '.owm'
[docs]class IVar(object):
'''
A descriptor for instance variables amended to provide some attributes like
default values, value types, etc.
'''
_instance_counter = 0
def __init__(self, default_value=None, doc=None, value_type=str, name=None):
self.name = ('_ivar_' + str(IVar._instance_counter)) if name is None else name
IVar._instance_counter += 1
self.default_value = default_value
self.__doc__ = doc.strip() if doc is not None else doc
self.value_type = value_type
def __repr__(self):
return '{}(name={}, doc={}, value_type={}, default_value={})'.format(
FCN(type(self)), repr(self.name), repr(self.__doc__), repr(self.value_type), repr(self.default_value))
__str__ = __repr__
def __get__(self, target, typ=None):
if target is None:
return self
return getattr(target, self.name, self.default_value)
def __set__(self, target, value):
setattr(target, self.name, value)
[docs] @classmethod
def property(cls, wrapped=None, *args, **kwargs):
'''
Creates a `PropertyIVar` from a method
Typically, this will be used as a decorator for a method
Parameters
----------
wrapped : types.FunctionType or types.MethodType
The function to wrap. optional: if omitted, returns a function that can be
invoked later to create the `PropertyIVar`
'''
if wrapped is not None and \
callable(wrapped) and \
len(args) == 0 and \
len(kwargs) == 0: # we only got the wrapped. Just execute now.
return _property_update(wrapped)
first_arg = wrapped
aargs = list(args)
def wrapper(wrapped):
if first_arg is not None:
args = [first_arg] + list(aargs)
else:
args = aargs
return _property_update(wrapped, *args, **kwargs)
return wrapper
def _property_update(wrapped, *args, **kwargs):
res = PropertyIVar(*args, **kwargs)
res.value_getter = wrapped
wrapped_doc = getattr(wrapped, '__doc__', None)
if wrapped_doc:
res.__doc__ = wrapped_doc
res.__doc__ = '' if res.__doc__ is None else res.__doc__.strip()
return res
[docs]class SubCommand(object):
'''
A descriptor that wraps objects which function as sub-commands to
`~owmeta_core.command.OWM` or to other sub-commands
'''
def __init__(self, cmd):
self.cmd = cmd
cmd_doc = getattr(cmd, '__doc__', '')
self.__doc__ = f'`~{FCN(cmd)}`: {cmd_doc}'
def __repr__(self):
return '{}({})'.format(FCN(type(self)), repr(self.cmd))
__str__ = __repr__
def __get__(self, target, typ=None):
if target is None:
return self
return self.cmd(target)
[docs]class PropertyIVar(IVar):
'''
An `.IVar` that functions similarly to a `property`
Typically a PropertyIVar will be created by using `.IVar.property` as a decorator for
a method like::
class A(object):
@IVar.property('default_value')
def prop(self):
return 'value'
.. automethod:: __get__
'''
def __init__(self, *args, **kwargs):
super(PropertyIVar, self).__init__(*args, **kwargs)
self.value_getter = None
self.value_setter = None
self._setter_called_flag = '_' + self.name + '_is_set'
[docs] def setter(self, fset):
'''
Decorator for the setter that goes along with this property.
See Also
--------
property
'''
res = type(self)(default_value=self.default_value,
doc=self.__doc__,
value_type=self.value_type,
name=self.name)
res.value_getter = self.value_getter
res.value_setter = fset
return res
def __set__(self, target, value):
if self.value_setter is None:
raise AttributeError("can't set attribute")
setattr(target, self._setter_called_flag, True)
self.value_setter(target, value)
[docs] def __get__(self, target, objecttype=None):
''' Executes the provided getter
When the getter is first called, and when a setter is also defined, the setter will be called with the default
value before the getter is called for the first time. _Even if the default_value is not set explicitly, the
setter will still be called with 'None'.
'''
if target is None:
return self
if self.value_setter is not None and \
not hasattr(target, self._setter_called_flag):
setattr(target, self._setter_called_flag, True)
self.value_setter(target, self.default_value)
return self.value_getter(target)
class GeneratorWithData(object):
def __init__(self, generator, header=None, text_format=None, default_columns=None, columns=None):
self._gen = iter(generator)
self.header = header
if columns is tuple:
if not self.header:
raise Exception('Must provide a header if columns is `tuple`')
columns = []
for i, _ in enumerate(self.header):
columns.append((lambda i: lambda x: x[i])(i))
self.columns = columns
else:
self.columns = columns
self.default_columns = default_columns
self.text_format = text_format or None
def __iter__(self):
for m in self._gen:
yield m
def __next__(self):
return next(self._gen)
next = __next__
[docs]class GenericUserError(Exception):
'''
An error which should be reported to the user. Not necessarily an error that is
the user's fault
'''