Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
utilities.py
Go to the documentation of this file.
1 """Copyright (c) 2005-2016, University of Oxford.
2 All rights reserved.
3 
4 University of Oxford means the Chancellor, Masters and Scholars of the
5 University of Oxford, having an administrative office at Wellington
6 Square, Oxford OX1 2JD, UK.
7 
8 This file is part of Chaste.
9 
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12  * Redistributions of source code must retain the above copyright notice,
13  this list of conditions and the following disclaimer.
14  * Redistributions in binary form must reproduce the above copyright notice,
15  this list of conditions and the following disclaimer in the documentation
16  and/or other materials provided with the distribution.
17  * Neither the name of the University of Oxford nor the names of its
18  contributors may be used to endorse or promote products derived from this
19  software without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """
32 
33 """
34 Various helpful utility functions/classes used by PyCml.
35 """
36 
37 import logging
38 import sys
39 from xml.dom import Node # For nodeType values
40 
41 import amara
42 
43 from enum import Enum
44 
45 __all__ = ['OnlyWarningsFilter', 'OnlyDebugFilter', 'OnlyTheseSourcesFilter', 'NotifyHandler',
46  'DEBUG', 'LOG',
47  'Colourable', 'DFS', 'Sentinel', 'unitary_iterator',
48  'amara_parse', 'element_path', 'element_path_cmp', 'element_xpath', 'brief_xml',
49  'call_if', 'max_i', 'prid', 'add_dicts',
50  'open_output_stream', 'close_output_stream']
51 
52 ################################################################################
53 # #
54 # Logging #
55 # #
56 ################################################################################
57 
58 class OnlyWarningsFilter(logging.Filter):
59  """A filter that only passes warning messages."""
60  def filter(self, rec):
61  return (logging.WARNING <= rec.levelno < logging.ERROR)
62 
63 
64 class OnlyDebugFilter(logging.Filter):
65  """A filter that only passes debug messages."""
66  def filter(self, rec):
67  return (logging.DEBUG <= rec.levelno < logging.INFO)
68 
69 
70 class OnlyTheseSourcesFilter(logging.Filter):
71  """A filter that only emits messages from the given sources."""
72  def __init__(self, sources):
73  logging.Filter.__init__(self)
74  self.__sources = sources
75  def filter(self, rec):
76  return rec.name in self.__sources
77 
78 
79 class NotifyHandler(logging.Handler):
80  """
81  A logging handler that just notes if any messages are logged.
82  """
83  def __init__(self, level=logging.NOTSET):
84  logging.Handler.__init__(self, level=level)
85  self.reset()
86 
87  def emit(self, record):
88  self.messages = True
89 
90  def reset(self):
91  """Reset the handler, as if no messages have occurred."""
92  self.messages = False
93 
94 
95 def DEBUG(facility, *args):
96  """Log a debug message to facility.
97 
98  Arguments are treated as for the print statement.
99  """
100  logger = logging.getLogger(facility)
101  if logger.isEnabledFor(logging.DEBUG):
102  logger.debug(' '.join(map(str, args)))
103 
104 
105 def LOG(facility, level, *args):
106  """Log a message to facility with the given level.
107 
108  Arguments are treated as for the print statement.
109  """
110  logger = logging.getLogger(facility)
111  if logger.isEnabledFor(level):
112  logger.log(level, ' '.join(map(str, args)))
113 
114 ################################################################################
115 # #
116 # Miscellaneous classes #
117 # #
118 ################################################################################
119 
120 # Useful constants for depth-first search
121 DFS = Enum('White', 'Gray', 'Black')
122 
123 class Colourable(object):
124  """
125  A mixin class for objects that have a colour attribute, and so support
126  a depth-first search.
127  """
128  def __init__(self, *args, **kwargs):
129  super(Colourable, self).__init__(*args, **kwargs)
130  self.clear_colour()
131 
132  def set_colour(self, colour):
133  self._cml_colour = colour
134 
135  def get_colour(self):
136  return self._cml_colour
137 
138  def clear_colour(self):
139  self._cml_colour = DFS.White
140 
141 
142 class Sentinel(object):
143  """A simple true/false store that can be used as a default parameter value in recursive calls.
144 
145  This provides a slightly nicer looking alternative to code such as:
146 
147  def f(done=[False]):
148  if not done[0]:
149  # do some stuff
150  if xyz: done[0] = True
151  f(done)
152 
153  The list can be replaced with a Sentinel instance.
154  """
155  def __init__(self, tf=False):
156  """Create a new, unset sentinel."""
157  self._tf = tf
158 
159  def __nonzero__(self):
160  """Test whether the sentinel has been set."""
161  return self._tf
162 
163  def set(self, tf=True):
164  """Set the sentinel."""
165  self._tf = tf
166 
167 
168 class unitary_iterator(object):
169  """An iterator over a single item."""
170  def __init__(self, start):
171  self.curr = start
172  return
173 
174  def __iter__(self):
175  return self
176 
177  def next(self):
178  if not self.curr:
179  raise StopIteration()
180  result = self.curr
181  self.curr = None
182  return result
183 
184 
185 ################################################################################
186 # #
187 # XML-related functions #
188 # #
189 ################################################################################
190 
191 def amara_parse(source, uri=None, rules=None, binderobj=None,
192  prefixes=None):
193  """Convenience function for parsing XML.
194 
195  Works just as amara.parse, except that if source is '-' then
196  it reads from standard input.
197  """
198  if source == '-':
199  return amara.parse(sys.stdin, uri=uri, rules=rules,
200  binderobj=binderobj, prefixes=prefixes)
201  else:
202  return amara.parse(source, uri=uri, rules=rules,
203  binderobj=binderobj, prefixes=prefixes)
204 
205 
206 def element_path(elt):
207  """Find the path from the root element to this element."""
208  if hasattr(elt, 'xml_parent'):
209  idx = 0
210  for child in elt.xml_parent.xml_children:
211  if getattr(child, 'nodeType', None) == Node.ELEMENT_NODE:
212  idx += 1
213  if child is elt:
214  break
215  return element_path(elt.xml_parent) + [idx]
216  else:
217  return []
218 
219 
220 def element_path_cmp(e1, e2):
221  """Compare 2 elements by comparing their paths from the root element."""
222  return cmp(element_path(e1), element_path(e2))
223 
224 
225 def element_xpath(elt):
226  """Return an xpath expression that will select this element."""
227  indices = element_path(elt)
228  xpath = u'/*[' + u']/*['.join(map(str, indices)) + u']'
229  return xpath
230 
231 
232 def brief_xml(elt):
233  """Print a more concise version of elt.xml() that omits all attributes."""
234  s = ''
235  if getattr(elt, 'nodeType', None) == Node.ELEMENT_NODE:
236  children = getattr(elt, 'xml_children', [])
237  if children:
238  s += '<' + elt.localName + '>'
239  for child in children:
240  s += brief_xml(child)
241  s += '</' + elt.localName + '>'
242  else:
243  s += '<' + elt.localName + '/>'
244  else:
245  s += str(elt)
246  return s
247 
248 
249 ################################################################################
250 # #
251 # Miscellaneous functions #
252 # #
253 ################################################################################
254 
255 def call_if(tf, callable, *args, **kwargs):
256  """Call the given callable with the given arguments iff tf is True."""
257  if tf:
258  return callable(*args, **kwargs)
259 
260 
261 def max_i(it):
262  """Find the maximum entry of an iterable, and return it with its index.
263 
264  Returns (i, m) where i is the index of m, the maximum entry of `it`.
265  """
266  idx, m = None, None
267  for i, val in enumerate(it):
268  if m is None or val > m:
269  m, idx = val, i
270  return idx, m
271 
272 
273 def prid(obj, show_cls=False):
274  """Get the id of an object as a hex string, optionally with its class/type."""
275  if obj is None:
276  s = 'None'
277  else:
278  s = hex(id(obj))
279  if show_cls:
280  s += str(type(obj))
281  return s
282 
283 
284 def add_dicts(r, *ds):
285  """Add multiple dictionaries together.
286 
287  Updates the first input dictionary by adding values from
288  subsequent inputs. Produces a dictionary with keys taken from the
289  input dictionaries, and values being the sum of the corresponding
290  values in all inputs.
291  Assumes values are numeric.
292  """
293  for d in ds:
294  for k, v in d.iteritems():
295  r[k] = r.get(k, 0) + v
296 
297 
299  """Open fname for output.
300 
301  fname should be a local filename, or '-' for standard output.
302  Additionally, the names 'stdout' and 'stderr' have the usual
303  special meanings.
304  """
305  if fname == '-' or fname == 'stdout':
306  stream = sys.stdout
307  elif fname == 'stderr':
308  stream = sys.stderr
309  else:
310  stream = open(fname, 'w')
311  return stream
312 
313 
315  """
316  Close the given output stream, unless it's one of the standard streams
317  (i.e. sys.stdout or sys.stderr).
318  Note that closing a stream multiple times is safe.
319  """
320  if not stream is sys.stdout and not stream is sys.stderr:
321  stream.close()
def call_if
# Miscellaneous functions # # ...
Definition: utilities.py:255