dsdb: reset schema->{classes,attributes}_to_remove_size to 0
[Samba/gebeck_regimport.git] / lib / testtools / testtools / helpers.py
blob2595c1d6295eeac7adbb22782c147692c5fcdb75
1 # Copyright (c) 2010-2012 testtools developers. See LICENSE for details.
3 __all__ = [
4 'safe_hasattr',
5 'try_import',
6 'try_imports',
9 import sys
12 def try_import(name, alternative=None, error_callback=None):
13 """Attempt to import ``name``. If it fails, return ``alternative``.
15 When supporting multiple versions of Python or optional dependencies, it
16 is useful to be able to try to import a module.
18 :param name: The name of the object to import, e.g. ``os.path`` or
19 ``os.path.join``.
20 :param alternative: The value to return if no module can be imported.
21 Defaults to None.
22 :param error_callback: If non-None, a callable that is passed the ImportError
23 when the module cannot be loaded.
24 """
25 module_segments = name.split('.')
26 last_error = None
27 while module_segments:
28 module_name = '.'.join(module_segments)
29 try:
30 module = __import__(module_name)
31 except ImportError:
32 last_error = sys.exc_info()[1]
33 module_segments.pop()
34 continue
35 else:
36 break
37 else:
38 if last_error is not None and error_callback is not None:
39 error_callback(last_error)
40 return alternative
41 nonexistent = object()
42 for segment in name.split('.')[1:]:
43 module = getattr(module, segment, nonexistent)
44 if module is nonexistent:
45 if last_error is not None and error_callback is not None:
46 error_callback(last_error)
47 return alternative
48 return module
51 _RAISE_EXCEPTION = object()
52 def try_imports(module_names, alternative=_RAISE_EXCEPTION, error_callback=None):
53 """Attempt to import modules.
55 Tries to import the first module in ``module_names``. If it can be
56 imported, we return it. If not, we go on to the second module and try
57 that. The process continues until we run out of modules to try. If none
58 of the modules can be imported, either raise an exception or return the
59 provided ``alternative`` value.
61 :param module_names: A sequence of module names to try to import.
62 :param alternative: The value to return if no module can be imported.
63 If unspecified, we raise an ImportError.
64 :param error_callback: If None, called with the ImportError for *each*
65 module that fails to load.
66 :raises ImportError: If none of the modules can be imported and no
67 alternative value was specified.
68 """
69 module_names = list(module_names)
70 for module_name in module_names:
71 module = try_import(module_name, error_callback=error_callback)
72 if module:
73 return module
74 if alternative is _RAISE_EXCEPTION:
75 raise ImportError(
76 "Could not import any of: %s" % ', '.join(module_names))
77 return alternative
80 def safe_hasattr(obj, attr, _marker=object()):
81 """Does 'obj' have an attribute 'attr'?
83 Use this rather than built-in hasattr, as the built-in swallows exceptions
84 in some versions of Python and behaves unpredictably with respect to
85 properties.
86 """
87 return getattr(obj, attr, _marker) is not _marker
90 def map_values(function, dictionary):
91 """Map ``function`` across the values of ``dictionary``.
93 :return: A dict with the same keys as ``dictionary``, where the value
94 of each key ``k`` is ``function(dictionary[k])``.
95 """
96 return dict((k, function(dictionary[k])) for k in dictionary)
99 def filter_values(function, dictionary):
100 """Filter ``dictionary`` by its values using ``function``."""
101 return dict((k, v) for k, v in dictionary.items() if function(v))
104 def dict_subtract(a, b):
105 """Return the part of ``a`` that's not in ``b``."""
106 return dict((k, a[k]) for k in set(a) - set(b))
109 def list_subtract(a, b):
110 """Return a list ``a`` without the elements of ``b``.
112 If a particular value is in ``a`` twice and ``b`` once then the returned
113 list then that value will appear once in the returned list.
115 a_only = list(a)
116 for x in b:
117 if x in a_only:
118 a_only.remove(x)
119 return a_only