"""Utility functions and classes used by nose internally. """
except NameError: try: from sets import Set as set except ImportError: pass
skip_pattern=skip_pattern, indent="|-- ", branch_indent="| ", last_indent="`-- ", last_branch_indent=" "): # TODO: empty directories look like non-directory files return "\n".join(_ls_tree_lines(dir_path, skip_pattern, indent, branch_indent, last_indent, last_branch_indent))
indent, branch_indent, last_indent, last_branch_indent): if dir_path == "": dir_path = os.getcwd()
lines = []
names = os.listdir(dir_path) names.sort() dirs, nondirs = [], [] for name in names: if re.match(skip_pattern, name): continue if os.path.isdir(os.path.join(dir_path, name)): dirs.append(name) else: nondirs.append(name)
# list non-directories first entries = list(itertools.chain([(name, False) for name in nondirs], [(name, True) for name in dirs])) def ls_entry(name, is_dir, ind, branch_ind): if not is_dir: yield ind + name else: path = os.path.join(dir_path, name) if not os.path.islink(path): yield ind + name subtree = _ls_tree_lines(path, skip_pattern, indent, branch_indent, last_indent, last_branch_indent) for x in subtree: yield branch_ind + x for name, is_dir in entries[:-1]: for line in ls_entry(name, is_dir, indent, branch_indent): yield line if entries: name, is_dir = entries[-1] for line in ls_entry(name, is_dir, last_indent, last_branch_indent): yield line
"""Return absolute, normalized path to directory, if it exists; None otherwise. """ if not os.path.isabs(path): path = os.path.normpath(os.path.abspath(os.path.join(os.getcwd(), path))) if path is None or not os.path.isdir(path): return None return path
"""Return absolute, normalized path to file (optionally in directory where), or None if the file can't be found either in where or the current working directory. """ orig = path if where is None: where = os.getcwd() if isinstance(where, list) or isinstance(where, tuple): for maybe_path in where: maybe_abs = absfile(path, maybe_path) if maybe_abs is not None: return maybe_abs return None if not os.path.isabs(path): path = os.path.normpath(os.path.abspath(os.path.join(where, path))) if path is None or not os.path.exists(path): if where != os.getcwd(): # try the cwd instead path = os.path.normpath(os.path.abspath(os.path.join(os.getcwd(), orig))) if path is None or not os.path.exists(path): return None if os.path.isdir(path): # might want an __init__.py from pacakge init = os.path.join(path,'__init__.py') if os.path.isfile(init): return init elif os.path.isfile(path): return path return None
return True
"""A name is file-like if it is a path that exists, or it has a directory part, or it ends in .py, or it isn't a legal python identifier. """ or os.path.dirname(name) or name.endswith('.py') or not ident_re.match(os.path.splitext(name)[0]))
"""Get the line number of a function. First looks for compat_co_firstlineno, then func_code.co_first_lineno. """ except AttributeError: return -1
"""Is obj a class? Inspect's isclass is too liberal and returns True for objects that can't be subclasses of anything. """
# backwards compat (issue #64)
""" Is this path a package directory?
>>> ispackage('nose') True >>> ispackage('unit_tests') False >>> ispackage('nose/plugins') True >>> ispackage('nose/loader.py') False """ # at least the end of the path must be a legal python identifier # and __init__.py[co] must exist os.path.isfile(os.path.join(path, '__init__$py.class')): return True
""" Is this a property?
>>> class Foo: ... def got(self): ... return 2 ... def get(self): ... return 1 ... get = property(get)
>>> isproperty(Foo.got) False >>> isproperty(Foo.get) True """ return type(obj) == property
"""Find the python source file for a package, relative to a particular directory (defaults to current working directory if not given). """ if relativeTo is None: relativeTo = os.getcwd() path = os.path.join(relativeTo, os.sep.join(package.split('.'))) if os.path.exists(path + '/__init__.py'): return path filename = path + '.py' if os.path.exists(filename): return filename return None
""" Find the full dotted package name for a given python source file name. Returns None if the file is not a python source file.
>>> getpackage('foo.py') 'foo' >>> getpackage('biff/baf.py') 'baf' >>> getpackage('nose/util.py') 'nose.util'
Works for directories too.
>>> getpackage('nose') 'nose' >>> getpackage('nose/plugins') 'nose.plugins'
And __init__ files stuck onto directories
>>> getpackage('nose/plugins/__init__.py') 'nose.plugins'
Absolute paths also work.
>>> path = os.path.abspath(os.path.join('nose', 'plugins')) >>> getpackage(path) 'nose.plugins' """ return None mod_parts = [] else: else:
"""Draw a 70-char-wide divider, with label in the middle.
>>> ln('hello there') '---------------------------- hello there -----------------------------' """ label_len = len(label) + 2 chunk = (70 - label_len) // 2 out = '%s %s %s' % ('-' * chunk, label, '-' * chunk) pad = 70 - len(out) if pad > 0: out = out + ('-' * pad) return out
"""Resolve a dotted name to a module and its parts. This is stolen wholesale from unittest.TestLoader.loadTestByName.
>>> resolve_name('nose.util') #doctest: +ELLIPSIS <module 'nose.util' from...> >>> resolve_name('nose.util.resolve_name') #doctest: +ELLIPSIS <function resolve_name at...> """ except ImportError: del parts_copy[-1] if not parts_copy: raise obj = getattr(obj, part)
"""Split a test name into a 3-tuple containing file, module, and callable names, any of which (but not all) may be blank.
Test names are in the form:
file_or_module:callable
Either side of the : may be dotted. To change the splitting behavior, you can alter nose.util.split_test_re. """ # only a file or mod part else: return (None, test, None)
# could be path|mod:callable, or a : in the file path someplace head, tail = os.path.split(test) if not head: # this is a case like 'foo:bar' -- generally a module # name followed by a callable, but also may be a windows # drive letter followed by a path try: file_or_mod, fn = test.split(':') if file_like(fn): # must be a funny path file_or_mod, fn = test, None except ValueError: # more than one : in the test # this is a case like c:\some\path.py:a_test parts = test.split(':') if len(parts[0]) == 1: file_or_mod, fn = ':'.join(parts[:-1]), parts[-1] else: # nonsense like foo:bar:baz raise ValueError("Test name '%s' could not be parsed. Please " "format test names as path:callable or " "module:callable." % (test,)) elif not tail: # this is a case like 'foo:bar/' # : must be part of the file path, so ignore it file_or_mod = test else: if ':' in tail: file_part, fn = tail.split(':') else: file_part = tail file_or_mod = os.sep.join([head, file_part]) if file_or_mod: if file_like(file_or_mod): return (norm(file_or_mod), None, fn) else: return (None, file_or_mod, fn) else: return (None, None, fn)
"""Find the test address for a test, which may be a module, filename, class, method or function. """ return test.address() # type-based polymorphism sucks in general, but I believe is # appropriate here file = getattr(test, '__file__', None) module = getattr(test, '__name__', None) return (src(file), module, call) if t == types.MethodType: cls_adr = test_address(test.__self__.__class__) return (src(cls_adr[0]), cls_adr[1], "%s.%s" % (cls_adr[2], test.__name__)) # handle unittest.TestCase instances if isinstance(test, unittest.TestCase): if (hasattr(test, '_FunctionTestCase__testFunc') # pre 2.7 or hasattr(test, '_testFunc')): # 2.7 # unittest FunctionTestCase try: return test_address(test._FunctionTestCase__testFunc) except AttributeError: return test_address(test._testFunc) # regular unittest.TestCase cls_adr = test_address(test.__class__) # 2.5 compat: __testMethodName changed to _testMethodName try: method_name = test._TestCase__testMethodName except AttributeError: method_name = test._testMethodName return (src(cls_adr[0]), cls_adr[1], "%s.%s" % (cls_adr[2], method_name)) if (hasattr(test, '__class__') and test.__class__.__module__ not in ('__builtin__', 'builtins')): return test_address(test.__class__) raise TypeError("I don't know what %s is (%s)" % (test, t))
"""Given a list of possible method names, try to run them with the provided object. Keep going until something works. Used to run setup/teardown methods for module, package, and function tests. """ if type(obj) == types.ModuleType: # py.test compatibility if isinstance(func, types.FunctionType): args, varargs, varkw, defaults = \ inspect.getargspec(func) else: # Not a function. If it's callable, call it anyway if hasattr(func, '__call__') and not inspect.ismethod(func): func = func.__call__ try: args, varargs, varkw, defaults = \ inspect.getargspec(func) args.pop(0) # pop the self off except TypeError: raise TypeError("Attribute %s of %r is not a python " "function. Only functions or callables" " may be used as fixtures." % (name, obj)) if len(args): log.debug("call fixture %s.%s(%s)", obj, name, obj) return func(obj) log.debug("call fixture %s.%s", obj, name) return func()
"""Find the python source file for a .pyc, .pyo or $py.class file on jython. Returns the filename provided if it is not a python source file. """ return filename return '.'.join((filename[:-9], 'py'))
"""Sort key function factory that puts items that match a regular expression last.
>>> from nose.config import Config >>> from nose.pyversion import sort_list >>> c = Config() >>> regex = c.testMatch >>> entries = ['.', '..', 'a_test', 'src', 'lib', 'test', 'foo.py'] >>> sort_list(entries, regex_last_key(regex)) >>> entries ['.', '..', 'foo.py', 'lib', 'src', 'a_test', 'test'] """
"""Convert a value that may be a list or a (possibly comma-separated) string into a list. The exception: None is returned as None, not [None].
>>> tolist(["one", "two"]) ['one', 'two'] >>> tolist("hello") ['hello'] >>> tolist("separate,values, with, commas, spaces , are ,ok") ['separate', 'values', 'with', 'commas', 'spaces', 'are', 'ok'] """ # might already be a list return val # might be a string except TypeError: # who knows... return list(val)
"""Simple ordered dict implementation, based on:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747 """ self._keys = [] super(odict, self).__init__(*arg, **kw)
super(odict, self).__delitem__(key) self._keys.remove(key)
super(odict, self).__setitem__(key, item) if key not in self._keys: self._keys.append(key)
def __str__(self): return "{%s}" % ', '.join(["%r: %r" % (k, v) for k, v in list(self.items())])
super(odict, self).clear() self._keys = []
d = super(odict, self).copy() d._keys = self._keys[:] return d
return list(zip(self._keys, list(self.values())))
return self._keys[:]
item = super(odict, self).setdefault(key, failobj) if key not in self._keys: self._keys.append(key) return item
super(odict, self).update(dict) for key in list(dict.keys()): if key not in self._keys: self._keys.append(key)
return list(map(self.get, self._keys))
""" Make a function imported from module A appear as if it is located in module B.
>>> from pprint import pprint >>> pprint.__module__ 'pprint' >>> pp = transplant_func(pprint, __name__) >>> pp.__module__ 'nose.util'
The original function is not modified.
>>> pprint.__module__ 'pprint'
Calling the transplanted function calls the original.
>>> pp([1, 2]) [1, 2] >>> pprint([1,2]) [1, 2]
""" from nose.tools import make_decorator if isgenerator(func): def newfunc(*arg, **kw): for v in func(*arg, **kw): yield v else: def newfunc(*arg, **kw): return func(*arg, **kw)
newfunc = make_decorator(func)(newfunc) newfunc.__module__ = module return newfunc
""" Make a class appear to reside in `module`, rather than the module in which it is actually defined.
>>> from nose.failure import Failure >>> Failure.__module__ 'nose.failure' >>> Nf = transplant_class(Failure, __name__) >>> Nf.__module__ 'nose.util' >>> Nf.__name__ 'Failure'
""" class C(cls): pass C.__module__ = module C.__name__ = cls.__name__ C.__qualname__ = cls.__name__ return C
try: return str(val) except UnicodeEncodeError: if isinstance(val, Exception): return ' '.join([safe_str(arg, encoding) for arg in val]) return str(val).encode(encoding)
return False
if __name__ == '__main__': import doctest doctest.testmod() |