2017-07-12 00:33:43 -06:00

89 lines
2.9 KiB
Python

from __future__ import unicode_literals
from ._utils import escape_chars
from builtins import object
import hashlib
import json
class Node(object):
"""Node base"""
def __init__(self, parents, name, *args, **kwargs):
parent_hashes = [hash(parent) for parent in parents]
if len(parent_hashes) != len(set(parent_hashes)):
raise ValueError('Same node cannot be included as parent multiple times')
self._parents = parents
self._hash = None
self._name = name
self._args = args
self._kwargs = kwargs
def __repr__(self):
formatted_props = ['{}'.format(arg) for arg in self._args]
formatted_props += ['{}={!r}'.format(key, self._kwargs[key]) for key in sorted(self._kwargs)]
return '{}({})'.format(self._name, ','.join(formatted_props))
def __hash__(self):
if self._hash is None:
self._update_hash()
return self._hash
def __eq__(self, other):
return hash(self) == hash(other)
def _update_hash(self):
props = {'args': self._args, 'kwargs': self._kwargs}
props_str = json.dumps(props, sort_keys=True).encode('utf-8')
my_hash = hashlib.md5(props_str).hexdigest()
parent_hashes = [str(hash(parent)) for parent in self._parents]
hashes = parent_hashes + [my_hash]
hashes_str = ','.join(hashes).encode('utf-8')
hash_str = hashlib.md5(hashes_str).hexdigest()
self._hash = int(hash_str, base=16)
class InputNode(Node):
"""InputNode type"""
def __init__(self, name, *args, **kwargs):
super(InputNode, self).__init__(parents=[], name=name, *args, **kwargs)
class FilterNode(Node):
"""FilterNode"""
def _get_filter(self):
args = [escape_chars(x, '\\\'=:') for x in self._args]
kwargs = {}
for k, v in self._kwargs.items():
k = escape_chars(k, '\\\'=:')
v = escape_chars(v, '\\\'=:')
kwargs[k] = v
arg_params = [escape_chars(v, '\\\'=:') for v in args]
kwarg_params = ['{}={}'.format(k, kwargs[k]) for k in sorted(kwargs)]
params = arg_params + kwarg_params
params_text = escape_chars(self._name, '\\\'=:')
if params:
params_text += '={}'.format(':'.join(params))
return escape_chars(params_text, '\\\'[],;')
class OutputNode(Node):
"""OutputNode"""
pass
class GlobalNode(Node):
def __init__(self, parent, name, *args, **kwargs):
if not isinstance(parent, OutputNode):
raise RuntimeError('Global nodes can only be attached after output nodes')
super(GlobalNode, self).__init__([parent], name, *args, **kwargs)
def operator(node_classes={Node}, name=None):
def decorator(func):
func_name = name or func.__name__
[setattr(node_class, func_name, func) for node_class in node_classes]
return func
return decorator