Coverage for tw2/core/util.py : 72%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" Utility functions, used internally. """
3import copy
4import re
5import functools
6import six.moves
8try:
9 # py2
10 import thread
11except ImportError:
12 # py3
13 import _thread as thread
15import webob
17_thread_local = {}
20def thread_local():
21 threadid = thread.get_ident()
22 try:
23 return _thread_local[threadid]
24 except KeyError:
25 rl_data = {}
26 _thread_local[threadid] = rl_data
27 return rl_data
30class class_or_instance(object):
31 def __init__(self, fn):
32 self._function = fn
33 self._wrapper = functools.wraps(fn)
35 def __get__(self, ins, cls):
36 return self._wrapper(functools.partial(self._function, ins, cls))
39def name2label(name):
40 """
41 Convert a column name to a Human Readable name.
42 1) Strip _id from the end
43 2) Convert _ to spaces
44 3) Convert CamelCase to Camel Case
45 4) Upcase first character of Each Word
46 """
47 if name.endswith('_id'):
48 name = name[:-3]
49 return ' '.join([s.capitalize() for s in
50 re.findall(r'([A-Z][a-z0-9]+|[a-z0-9]+|[A-Z0-9]+)', name)])
53class MultipleReplacer(object):
54 """Performs several regexp substitutions on a string with a single pass.
56 ``dct`` is a dictionary keyed by a regular expression string and with a
57 callable as value that will get called to produce a substituted value.
59 The callable takes the matched text as first argument and may take any
60 number of positional and keyword arguments. These arguments are any extra
61 args passed when calling the instance.
63 For performance, a single regular expression is built.
65 Example::
67 >>> string = "aaaabbbcc"
68 >>> replacer = MultipleReplacer({
69 ... 'a+':lambda g, context: g + context['after_a'],
70 ... 'b+':lambda g, context: g + context['after_b'],
71 ... 'c+':lambda g, context: context['before_c'] + g,
72 ... })
73 >>> replacer("aaaabbbcc", dict(
74 ... after_a = "1",
75 ... after_b = "2",
76 ... before_c = "3"
77 ... ))
78 'aaaa1bbb23cc'
79 """
80 def __init__(self, dct, options=0):
81 self._raw_regexp = r"|".join("(%s)" % k for k in dct.keys())
82 self._substitutors = dct.values()
83 self._regexp = re.compile(self._raw_regexp, options)
85 def __repr__(self):
86 return "<%s at %d (%r)>" % (self.__class__.__name__, id(self),
87 self._raw_regexp)
89 def _substitutor(self, *args, **kw):
90 def substitutor(match):
91 tuples = six.moves.zip(self._substitutors, match.groups())
92 for substitutor, group in tuples:
93 if group is not None:
94 return substitutor(group, *args, **kw)
95 return substitutor
97 def __call__(self, string, *args, **kw):
98 return self._regexp.sub(self._substitutor(*args, **kw), string)
101def abort(req, status):
102 return webob.Response(request=req, status=status, content_type="text/html")
105_memoization_flush_callbacks = []
108class memoize(object):
109 def __init__(self, f):
110 global _memoization_flush_callbacks
111 self.f = f
112 self.mem = {}
113 _memoization_flush_callbacks.append(self._flush)
115 def _flush(self):
116 self.mem = {}
118 def __call__(self, *args, **kwargs):
119 if (args, str(kwargs)) in self.mem:
120 return self.mem[args, str(kwargs)]
121 else:
122 tmp = self.f(*args, **kwargs)
123 self.mem[args, str(kwargs)] = tmp
124 return tmp
127def flush_memoization():
128 for cb in _memoization_flush_callbacks:
129 cb()
132def clone_object(obj, **values):
133 if obj is None:
134 obj = type('_TemporaryObject', (object,), {})()
135 else:
136 obj = copy.copy(obj)
138 for k,v in values.items():
139 setattr(obj, k, v)
141 return obj
144# relpath support for python-2.5
145# Taken from https://github.com/nipy/nipype/issues/62
146# Related to https://github.com/toscawidgets/tw2.core/issues/30
147try:
148 from os.path import relpath
149except ImportError:
150 import os
151 import os.path as op
153 def relpath(path, start=None):
154 """Return a relative version of a path"""
155 if start is None:
156 start = os.curdir
157 if not path:
158 raise ValueError("no path specified")
159 start_list = op.abspath(start).split(op.sep)
160 path_list = op.abspath(path).split(op.sep)
162 if start_list[0].lower() != path_list[0].lower():
163 unc_path, rest = op.splitunc(path)
164 unc_start, rest = op.splitunc(start)
165 if bool(unc_path) ^ bool(unc_start):
166 raise ValueError(
167 "Cannot mix UNC and non-UNC paths (%s and%s)" %
168 (path, start))
169 else:
170 raise ValueError(
171 "path is on drive %s, start on drive %s"
172 % (path_list[0], start_list[0]))
174 # Work out how much of the filepath is shared by start and path.
175 for i in range(min(len(start_list), len(path_list))):
176 if start_list[i].lower() != path_list[i].lower():
177 break
178 else:
179 i += 1
181 rel_list = [op.pardir] * (len(start_list) - i) + path_list[i:]
182 if not rel_list:
183 return os.curdir
184 return op.join(*rel_list)