Skip to content

Commit

Permalink
removed sugarbowl dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
roll committed Jan 27, 2015
1 parent b3dc070 commit c815eac
Show file tree
Hide file tree
Showing 28 changed files with 795 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ python:
# Block: install
install:
- pip install coveralls
- pip install sugarbowl clyde nose coverage
- pip install clyde nose coverage

# Block: script
script:
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ mario
sphinx
sphinx-settings
sphinx-rtd-theme
sugarbowl
clyde
nose
coverage
6 changes: 6 additions & 0 deletions run/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
from .cache import cachedproperty
from .function import Function
from .impobj import import_object
from .load import load
from .merge import merge_dicts
from .pack import pack
from .parse import parse
from .settings import Settings
from .stylize import stylize
87 changes: 87 additions & 0 deletions run/helpers/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
class cachedproperty:
"""
Decorator to implement cached property.
To cache property value just use cachedproperty decorator
in class definition instead of regular property decorator::
class Container:
@cachedproperty
def attribute(self):
return 'value'
@attribute.setter
def attribute(self, cache, name, value):
cache[name] = value
@attribute.deleter
def attribute(self, cache, name):
del cache[name]
It works for private attributes (__*) and not tested for threads.
"""

# Public

def __init__(self, getter=None, setter=None, deleter=None):
self.getter(getter)
self.setter(setter)
self.deleter(deleter)

def __get__(self, obj, cls):
if obj is None:
# To get property from class object
return self
if self.__getter is None:
raise AttributeError('Can\'t get attribute')
name = self.__get_name(obj)
cache = self.__get_cache(obj)
if name not in cache:
cache[self.__name] = self.__getter(obj)
return cache[self.__name]

def __set__(self, obj, value):
if self.__setter is None:
raise AttributeError('Can\'t set attribute')
name = self.__get_name(obj)
cache = self.__get_cache(obj)
self.__setter(obj, cache, name, value)

def __delete__(self, obj):
if self.__deleter is None:
raise AttributeError('Can\'t delete attribute')
name = self.__get_name(obj)
cache = self.__get_cache(obj)
self.__deleter(obj, cache, name)

def getter(self, getter):
self.__getter = getter
self.__doc__ = None
if getter is not None:
self.__doc__ = getter.__doc__
return self

def setter(self, setter):
self.__setter = setter
return self

def deleter(self, deleter):
self.__deleter = deleter
return self

# Private

__name = None
__cache = '_cachedproperty'

def __get_name(self, obj):
if self.__name is None:
for cls in type(obj).mro():
for name, value in vars(cls).items():
if self is value:
self.__name = name
return self.__name

def __get_cache(self, obj):
return obj.__dict__.setdefault(self.__cache, {})
100 changes: 100 additions & 0 deletions run/helpers/function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from abc import ABCMeta, abstractmethod


class Metaclass(ABCMeta):

# Public

def __call__(self, *args, **kwargs):
function = object.__new__(self)
protocol = function.protocol
if callable(function.protocol):
protocol = function.protocol(*args, **kwargs)
if protocol == self.CLASS:
function.__init__(*args, **kwargs)
return function.__call__()
elif protocol == self.FUNCTION:
function.__init__()
return function.__call__(*args, **kwargs)
elif protocol == self.DECORATOR:
function.__init__(*args, **kwargs)
return function
else:
raise ValueError(
'Unsupported protocol "{protocol}"'.
format(protocol=protocol))

def __instancecheck__(self, instance):
result = issubclass(instance, self)
if not result:
result = super().__instancecheck__(instance)
return result


class Function(metaclass=Metaclass):
"""Abstract class-based function representation.
Designed to implement functions/decorators as classes
to work with state and inheritance. In this model function will
have state only for invocation. Main use case is function like
parse where there is not global state to create Parser class.
Also supports decorator protocol to create decorators with parameters.
User have to implement this abstract class. Examples below will
make it much more clearer.
Raises
------
ValueError
If protocol is unsupported.
Examples
--------
Invocation protocols:
- Function.CLASS (default)
>>> class echo(Function):
... protocol = Function.CLASS
... def __init__(self, value):
... self.value = value
... def __call__(self):
... return self.value
>>> echo('Hello World')
'Hello World!'
- Function.FUNCTION
>>> class echo(Function):
... protocol = Function.FUNCTION
... def __call__(self, value):
... return value
>>> echo('Hello World!')
'Hello World!'
- Function.DECORATOR
>>> class echo(Function):
... protocol = Function.DECORATOR
... def __init__(self, value):
... self.value = value
... def __call__(self, processor):
... return processor(self.value)
>>> hello('Hello World!')(str.upper)
'HELLO WORLD!'
"""

# Public

CLASS = 'class'
FUNCTION = 'function'
DECORATOR = 'decorator'

protocol = CLASS
"""Invocation protocol.
"""

@abstractmethod
def __call__(self, *args, **kwargs):
"""Abstract method to implement.
"""
pass # pragma: no cover
53 changes: 53 additions & 0 deletions run/helpers/impobj.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import importlib


def import_object(name, *, package=None):
"""Import an object.
Parameters
----------
name: str/object
Object name in "[module.]module.]attr" form.
package: str
Argument is required when performing a relative import.
It specifies the package to use as the anchor point from which
to resolve the relative import to an absolute import.
Returns
-------
object
Imported object.
Raises
------
ValueError
If name is not in a proper form.
ImportError
If there is an error in module importing.
AttributeError
If module doesn't have the given attribute.
Examples
--------
If name not is a string function returns name without changes.
It usefull when client may give you pointer to some objects in
two forms like string or already imported object::
>>> obj = import_object('importlib.import_module')
>>> obj is import_object(obj)
True
>>> obj
<function importlib.import_module>
"""
if isinstance(name, str):
try:
module, name = name.rsplit('.', 1)
except ValueError:
raise ValueError('Name is in a bad form.') from None
if not module:
module = '.'
imported_module = importlib.import_module(module, package=package)
imported_object = getattr(imported_module, name)
else:
imported_object = name
return imported_object
54 changes: 54 additions & 0 deletions run/helpers/merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from copy import copy
from .function import Function


class merge_dicts(Function):
"""Merge dicts with optional conflict resolving and return a new one.
Parameters
----------
dict1: dict
Left dict to merge.
dict2: dict
Right dict to merge.
resolvers: dict
Conflict resolvers. Key is a type, value is a merge_dicts subclass
or callable with [(value1, value2) -> value] signature.
To make function recursive pass {dict: merge_dicts}.
Resolvers parameter will be passed resursively to
any merge_dicts subclass resolver.
Returns
-------
dict
Merged dict.
Examples
--------
Here is the code::
>>> from operators import add
>>> dict1 = {'a': 1, 'b': 2}
>>> dict2 = {'a': 1, 'c': 3}
>>> merge_dicts(dict1, dict2, resolvers={int: add})
{'a': 2, 'b': 2, 'c', 3}
"""

protocol = 'function'

def __call__(self, dict1, dict2, *, resolvers={}):
result = copy(dict1)
for key in dict2:
value = dict2[key]
if key in dict1:
resolver1 = resolvers.get(type(dict1[key]), None)
resolver2 = resolvers.get(type(dict2[key]), None)
if resolver1 is resolver2 is not None:
if (not isinstance(resolver1, type) or
not issubclass(resolver1, merge_dicts)):
value = resolver1(dict1[key], dict2[key])
else:
value = resolver1(
dict1[key], dict2[key], resolvers=resolvers)
result[key] = value
return result
Loading

0 comments on commit c815eac

Please sign in to comment.