diff options
author | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:24:06 +0300 |
---|---|---|
committer | nkozlovskiy <nmk@ydb.tech> | 2023-09-29 12:41:34 +0300 |
commit | e0e3e1717e3d33762ce61950504f9637a6e669ed (patch) | |
tree | bca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/python/ipython-genutils/py3/ipython_genutils | |
parent | 38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff) | |
download | ydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz |
add ydb deps
Diffstat (limited to 'contrib/python/ipython-genutils/py3/ipython_genutils')
11 files changed, 1730 insertions, 0 deletions
diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/__init__.py b/contrib/python/ipython-genutils/py3/ipython_genutils/__init__.py new file mode 100644 index 0000000000..948f47a695 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/__init__.py @@ -0,0 +1 @@ +from ._version import __version__, version_info diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/_version.py b/contrib/python/ipython-genutils/py3/ipython_genutils/_version.py new file mode 100644 index 0000000000..f660ae1b24 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/_version.py @@ -0,0 +1,2 @@ +version_info = (0, 2, 0) +__version__ = '.'.join(map(str, version_info)) diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/encoding.py b/contrib/python/ipython-genutils/py3/ipython_genutils/encoding.py new file mode 100644 index 0000000000..387a24700c --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/encoding.py @@ -0,0 +1,71 @@ +# coding: utf-8 +""" +Utilities for dealing with text encodings +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import sys +import locale +import warnings + +# to deal with the possibility of sys.std* not being a stream at all +def get_stream_enc(stream, default=None): + """Return the given stream's encoding or a default. + + There are cases where ``sys.std*`` might not actually be a stream, so + check for the encoding attribute prior to returning it, and return + a default if it doesn't exist or evaluates as False. ``default`` + is None if not provided. + """ + if not hasattr(stream, 'encoding') or not stream.encoding: + return default + else: + return stream.encoding + +# Less conservative replacement for sys.getdefaultencoding, that will try +# to match the environment. +# Defined here as central function, so if we find better choices, we +# won't need to make changes all over IPython. +def getdefaultencoding(prefer_stream=True): + """Return IPython's guess for the default encoding for bytes as text. + + If prefer_stream is True (default), asks for stdin.encoding first, + to match the calling Terminal, but that is often None for subprocesses. + + Then fall back on locale.getpreferredencoding(), + which should be a sensible platform default (that respects LANG environment), + and finally to sys.getdefaultencoding() which is the most conservative option, + and usually ASCII on Python 2 or UTF8 on Python 3. + """ + enc = None + if prefer_stream: + enc = get_stream_enc(sys.stdin) + if not enc or enc=='ascii': + try: + # There are reports of getpreferredencoding raising errors + # in some cases, which may well be fixed, but let's be conservative here. + enc = locale.getpreferredencoding() + except Exception: + pass + enc = enc or sys.getdefaultencoding() + # On windows `cp0` can be returned to indicate that there is no code page. + # Since cp0 is an invalid encoding return instead cp1252 which is the + # Western European default. + if enc == 'cp0': + warnings.warn( + "Invalid code page cp0 detected - using cp1252 instead." + "If cp1252 is incorrect please ensure a valid code page " + "is defined for the process.", RuntimeWarning) + return 'cp1252' + return enc + +DEFAULT_ENCODING = getdefaultencoding() diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/importstring.py b/contrib/python/ipython-genutils/py3/ipython_genutils/importstring.py new file mode 100644 index 0000000000..c8e1840eb3 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/importstring.py @@ -0,0 +1,39 @@ +# encoding: utf-8 +""" +A simple utility to import something by its string name. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + + +def import_item(name): + """Import and return ``bar`` given the string ``foo.bar``. + + Calling ``bar = import_item("foo.bar")`` is the functional equivalent of + executing the code ``from foo import bar``. + + Parameters + ---------- + name : string + The fully qualified name of the module/package being imported. + + Returns + ------- + mod : module object + The module that was imported. + """ + + parts = name.rsplit('.', 1) + if len(parts) == 2: + # called with 'foo.bar....' + package, obj = parts + module = __import__(package, fromlist=[obj]) + try: + pak = getattr(module, obj) + except AttributeError: + raise ImportError('No module named %s' % obj) + return pak + else: + # called with un-dotted string + return __import__(parts[0]) diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/ipstruct.py b/contrib/python/ipython-genutils/py3/ipython_genutils/ipstruct.py new file mode 100644 index 0000000000..31511ecdf1 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/ipstruct.py @@ -0,0 +1,376 @@ +# encoding: utf-8 +"""A dict subclass that supports attribute style access. + +Can probably be replaced by types.SimpleNamespace from Python 3.3 +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +__all__ = ['Struct'] + + +class Struct(dict): + """A dict subclass with attribute style access. + + This dict subclass has a a few extra features: + + * Attribute style access. + * Protection of class members (like keys, items) when using attribute + style access. + * The ability to restrict assignment to only existing keys. + * Intelligent merging. + * Overloaded operators. + """ + _allownew = True + def __init__(self, *args, **kw): + """Initialize with a dictionary, another Struct, or data. + + Parameters + ---------- + args : dict, Struct + Initialize with one dict or Struct + kw : dict + Initialize with key, value pairs. + + Examples + -------- + + >>> s = Struct(a=10,b=30) + >>> s.a + 10 + >>> s.b + 30 + >>> s2 = Struct(s,c=30) + >>> sorted(s2.keys()) + ['a', 'b', 'c'] + """ + object.__setattr__(self, '_allownew', True) + dict.__init__(self, *args, **kw) + + def __setitem__(self, key, value): + """Set an item with check for allownew. + + Examples + -------- + + >>> s = Struct() + >>> s['a'] = 10 + >>> s.allow_new_attr(False) + >>> s['a'] = 10 + >>> s['a'] + 10 + >>> try: + ... s['b'] = 20 + ... except KeyError: + ... print('this is not allowed') + ... + this is not allowed + """ + if not self._allownew and key not in self: + raise KeyError( + "can't create new attribute %s when allow_new_attr(False)" % key) + dict.__setitem__(self, key, value) + + def __setattr__(self, key, value): + """Set an attr with protection of class members. + + This calls :meth:`self.__setitem__` but convert :exc:`KeyError` to + :exc:`AttributeError`. + + Examples + -------- + + >>> s = Struct() + >>> s.a = 10 + >>> s.a + 10 + >>> try: + ... s.get = 10 + ... except AttributeError: + ... print("you can't set a class member") + ... + you can't set a class member + """ + # If key is an str it might be a class member or instance var + if isinstance(key, str): + # I can't simply call hasattr here because it calls getattr, which + # calls self.__getattr__, which returns True for keys in + # self._data. But I only want keys in the class and in + # self.__dict__ + if key in self.__dict__ or hasattr(Struct, key): + raise AttributeError( + 'attr %s is a protected member of class Struct.' % key + ) + try: + self.__setitem__(key, value) + except KeyError as e: + raise AttributeError(e) + + def __getattr__(self, key): + """Get an attr by calling :meth:`dict.__getitem__`. + + Like :meth:`__setattr__`, this method converts :exc:`KeyError` to + :exc:`AttributeError`. + + Examples + -------- + + >>> s = Struct(a=10) + >>> s.a + 10 + >>> type(s.get) + <... 'builtin_function_or_method'> + >>> try: + ... s.b + ... except AttributeError: + ... print("I don't have that key") + ... + I don't have that key + """ + try: + result = self[key] + except KeyError: + raise AttributeError(key) + else: + return result + + def __iadd__(self, other): + """s += s2 is a shorthand for s.merge(s2). + + Examples + -------- + + >>> s = Struct(a=10,b=30) + >>> s2 = Struct(a=20,c=40) + >>> s += s2 + >>> sorted(s.keys()) + ['a', 'b', 'c'] + """ + self.merge(other) + return self + + def __add__(self,other): + """s + s2 -> New Struct made from s.merge(s2). + + Examples + -------- + + >>> s1 = Struct(a=10,b=30) + >>> s2 = Struct(a=20,c=40) + >>> s = s1 + s2 + >>> sorted(s.keys()) + ['a', 'b', 'c'] + """ + sout = self.copy() + sout.merge(other) + return sout + + def __sub__(self,other): + """s1 - s2 -> remove keys in s2 from s1. + + Examples + -------- + + >>> s1 = Struct(a=10,b=30) + >>> s2 = Struct(a=40) + >>> s = s1 - s2 + >>> s + {'b': 30} + """ + sout = self.copy() + sout -= other + return sout + + def __isub__(self,other): + """Inplace remove keys from self that are in other. + + Examples + -------- + + >>> s1 = Struct(a=10,b=30) + >>> s2 = Struct(a=40) + >>> s1 -= s2 + >>> s1 + {'b': 30} + """ + for k in other.keys(): + if k in self: + del self[k] + return self + + def __dict_invert(self, data): + """Helper function for merge. + + Takes a dictionary whose values are lists and returns a dict with + the elements of each list as keys and the original keys as values. + """ + outdict = {} + for k,lst in data.items(): + if isinstance(lst, str): + lst = lst.split() + for entry in lst: + outdict[entry] = k + return outdict + + def dict(self): + return self + + def copy(self): + """Return a copy as a Struct. + + Examples + -------- + + >>> s = Struct(a=10,b=30) + >>> s2 = s.copy() + >>> type(s2) is Struct + True + """ + return Struct(dict.copy(self)) + + def hasattr(self, key): + """hasattr function available as a method. + + Implemented like has_key. + + Examples + -------- + + >>> s = Struct(a=10) + >>> s.hasattr('a') + True + >>> s.hasattr('b') + False + >>> s.hasattr('get') + False + """ + return key in self + + def allow_new_attr(self, allow = True): + """Set whether new attributes can be created in this Struct. + + This can be used to catch typos by verifying that the attribute user + tries to change already exists in this Struct. + """ + object.__setattr__(self, '_allownew', allow) + + def merge(self, __loc_data__=None, __conflict_solve=None, **kw): + """Merge two Structs with customizable conflict resolution. + + This is similar to :meth:`update`, but much more flexible. First, a + dict is made from data+key=value pairs. When merging this dict with + the Struct S, the optional dictionary 'conflict' is used to decide + what to do. + + If conflict is not given, the default behavior is to preserve any keys + with their current value (the opposite of the :meth:`update` method's + behavior). + + Parameters + ---------- + __loc_data : dict, Struct + The data to merge into self + __conflict_solve : dict + The conflict policy dict. The keys are binary functions used to + resolve the conflict and the values are lists of strings naming + the keys the conflict resolution function applies to. Instead of + a list of strings a space separated string can be used, like + 'a b c'. + kw : dict + Additional key, value pairs to merge in + + Notes + ----- + + The `__conflict_solve` dict is a dictionary of binary functions which will be used to + solve key conflicts. Here is an example:: + + __conflict_solve = dict( + func1=['a','b','c'], + func2=['d','e'] + ) + + In this case, the function :func:`func1` will be used to resolve + keys 'a', 'b' and 'c' and the function :func:`func2` will be used for + keys 'd' and 'e'. This could also be written as:: + + __conflict_solve = dict(func1='a b c',func2='d e') + + These functions will be called for each key they apply to with the + form:: + + func1(self['a'], other['a']) + + The return value is used as the final merged value. + + As a convenience, merge() provides five (the most commonly needed) + pre-defined policies: preserve, update, add, add_flip and add_s. The + easiest explanation is their implementation:: + + preserve = lambda old,new: old + update = lambda old,new: new + add = lambda old,new: old + new + add_flip = lambda old,new: new + old # note change of order! + add_s = lambda old,new: old + ' ' + new # only for str! + + You can use those four words (as strings) as keys instead + of defining them as functions, and the merge method will substitute + the appropriate functions for you. + + For more complicated conflict resolution policies, you still need to + construct your own functions. + + Examples + -------- + + This show the default policy: + + >>> s = Struct(a=10,b=30) + >>> s2 = Struct(a=20,c=40) + >>> s.merge(s2) + >>> sorted(s.items()) + [('a', 10), ('b', 30), ('c', 40)] + + Now, show how to specify a conflict dict: + + >>> s = Struct(a=10,b=30) + >>> s2 = Struct(a=20,b=40) + >>> conflict = {'update':'a','add':'b'} + >>> s.merge(s2,conflict) + >>> sorted(s.items()) + [('a', 20), ('b', 70)] + """ + + data_dict = dict(__loc_data__,**kw) + + # policies for conflict resolution: two argument functions which return + # the value that will go in the new struct + preserve = lambda old,new: old + update = lambda old,new: new + add = lambda old,new: old + new + add_flip = lambda old,new: new + old # note change of order! + add_s = lambda old,new: old + ' ' + new + + # default policy is to keep current keys when there's a conflict + conflict_solve = dict.fromkeys(self, preserve) + + # the conflict_solve dictionary is given by the user 'inverted': we + # need a name-function mapping, it comes as a function -> names + # dict. Make a local copy (b/c we'll make changes), replace user + # strings for the three builtin policies and invert it. + if __conflict_solve: + inv_conflict_solve_user = __conflict_solve.copy() + for name, func in [('preserve',preserve), ('update',update), + ('add',add), ('add_flip',add_flip), + ('add_s',add_s)]: + if name in inv_conflict_solve_user.keys(): + inv_conflict_solve_user[func] = inv_conflict_solve_user[name] + del inv_conflict_solve_user[name] + conflict_solve.update(self.__dict_invert(inv_conflict_solve_user)) + for key in data_dict: + if key not in self: + self[key] = data_dict[key] + else: + self[key] = conflict_solve[key](self[key],data_dict[key]) + diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/path.py b/contrib/python/ipython-genutils/py3/ipython_genutils/path.py new file mode 100644 index 0000000000..8d3308ed55 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/path.py @@ -0,0 +1,172 @@ +# encoding: utf-8 +""" +Utilities for path handling. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import os +import sys +import errno +import shutil +import random + +from . import py3compat + + +fs_encoding = sys.getfilesystemencoding() + + +def filefind(filename, path_dirs=None): + """Find a file by looking through a sequence of paths. + + This iterates through a sequence of paths looking for a file and returns + the full, absolute path of the first occurence of the file. If no set of + path dirs is given, the filename is tested as is, after running through + :func:`expandvars` and :func:`expanduser`. Thus a simple call:: + + filefind('myfile.txt') + + will find the file in the current working dir, but:: + + filefind('~/myfile.txt') + + Will find the file in the users home directory. This function does not + automatically try any paths, such as the cwd or the user's home directory. + + Parameters + ---------- + filename : str + The filename to look for. + path_dirs : str, None or sequence of str + The sequence of paths to look for the file in. If None, the filename + need to be absolute or be in the cwd. If a string, the string is + put into a sequence and the searched. If a sequence, walk through + each element and join with ``filename``, calling :func:`expandvars` + and :func:`expanduser` before testing for existence. + + Returns + ------- + Raises :exc:`IOError` or returns absolute path to file. + """ + + # If paths are quoted, abspath gets confused, strip them... + filename = filename.strip('"').strip("'") + # If the input is an absolute path, just check it exists + if os.path.isabs(filename) and os.path.isfile(filename): + return filename + + if path_dirs is None: + path_dirs = ("",) + elif isinstance(path_dirs, py3compat.string_types): + path_dirs = (path_dirs,) + + for path in path_dirs: + if path == '.': path = py3compat.getcwd() + testname = expand_path(os.path.join(path, filename)) + if os.path.isfile(testname): + return os.path.abspath(testname) + + raise IOError("File %r does not exist in any of the search paths: %r" % + (filename, path_dirs) ) + + +def expand_path(s): + """Expand $VARS and ~names in a string, like a shell + + :Examples: + + In [2]: os.environ['FOO']='test' + + In [3]: expand_path('variable FOO is $FOO') + Out[3]: 'variable FOO is test' + """ + # This is a pretty subtle hack. When expand user is given a UNC path + # on Windows (\\server\share$\%username%), os.path.expandvars, removes + # the $ to get (\\server\share\%username%). I think it considered $ + # alone an empty var. But, we need the $ to remains there (it indicates + # a hidden share). + if os.name=='nt': + s = s.replace('$\\', 'IPYTHON_TEMP') + s = os.path.expandvars(os.path.expanduser(s)) + if os.name=='nt': + s = s.replace('IPYTHON_TEMP', '$\\') + return s + + +try: + ENOLINK = errno.ENOLINK +except AttributeError: + ENOLINK = 1998 + +def link(src, dst): + """Hard links ``src`` to ``dst``, returning 0 or errno. + + Note that the special errno ``ENOLINK`` will be returned if ``os.link`` isn't + supported by the operating system. + """ + + if not hasattr(os, "link"): + return ENOLINK + link_errno = 0 + try: + os.link(src, dst) + except OSError as e: + link_errno = e.errno + return link_errno + + +def link_or_copy(src, dst): + """Attempts to hardlink ``src`` to ``dst``, copying if the link fails. + + Attempts to maintain the semantics of ``shutil.copy``. + + Because ``os.link`` does not overwrite files, a unique temporary file + will be used if the target already exists, then that file will be moved + into place. + """ + + if os.path.isdir(dst): + dst = os.path.join(dst, os.path.basename(src)) + + link_errno = link(src, dst) + if link_errno == errno.EEXIST: + if os.stat(src).st_ino == os.stat(dst).st_ino: + # dst is already a hard link to the correct file, so we don't need + # to do anything else. If we try to link and rename the file + # anyway, we get duplicate files - see http://bugs.python.org/issue21876 + return + + new_dst = dst + "-temp-%04X" %(random.randint(1, 16**4), ) + try: + link_or_copy(src, new_dst) + except: + try: + os.remove(new_dst) + except OSError: + pass + raise + os.rename(new_dst, dst) + elif link_errno != 0: + # Either link isn't supported, or the filesystem doesn't support + # linking, or 'src' and 'dst' are on different filesystems. + shutil.copy(src, dst) + + +def ensure_dir_exists(path, mode=0o755): + """ensure that a directory exists + + If it doesn't exist, try to create it and protect against a race condition + if another process is doing the same. + + The default permissions are 755, which differ from os.makedirs default of 777. + """ + if not os.path.exists(path): + try: + os.makedirs(path, mode=mode) + except OSError as e: + if e.errno != errno.EEXIST: + raise + elif not os.path.isdir(path): + raise IOError("%r exists but is not a directory" % path) diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/py3compat.py b/contrib/python/ipython-genutils/py3/ipython_genutils/py3compat.py new file mode 100644 index 0000000000..d748753f01 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/py3compat.py @@ -0,0 +1,333 @@ +# coding: utf-8 +"""Compatibility tricks for Python 3. Mainly to do with unicode.""" +import functools +import os +import sys +import re +import shutil +import types + +from .encoding import DEFAULT_ENCODING + +def no_code(x, encoding=None): + return x + +def decode(s, encoding=None): + encoding = encoding or DEFAULT_ENCODING + return s.decode(encoding, "replace") + +def encode(u, encoding=None): + encoding = encoding or DEFAULT_ENCODING + return u.encode(encoding, "replace") + + +def cast_unicode(s, encoding=None): + if isinstance(s, bytes): + return decode(s, encoding) + return s + +def cast_bytes(s, encoding=None): + if not isinstance(s, bytes): + return encode(s, encoding) + return s + +def buffer_to_bytes(buf): + """Cast a buffer or memoryview object to bytes""" + if isinstance(buf, memoryview): + return buf.tobytes() + if not isinstance(buf, bytes): + return bytes(buf) + return buf + +def _modify_str_or_docstring(str_change_func): + @functools.wraps(str_change_func) + def wrapper(func_or_str): + if isinstance(func_or_str, string_types): + func = None + doc = func_or_str + else: + func = func_or_str + doc = func.__doc__ + + doc = str_change_func(doc) + + if func: + func.__doc__ = doc + return func + return doc + return wrapper + +def safe_unicode(e): + """unicode(e) with various fallbacks. Used for exceptions, which may not be + safe to call unicode() on. + """ + try: + return unicode_type(e) + except UnicodeError: + pass + + try: + return str_to_unicode(str(e)) + except UnicodeError: + pass + + try: + return str_to_unicode(repr(e)) + except UnicodeError: + pass + + return u'Unrecoverably corrupt evalue' + +# shutil.which from Python 3.4 +def _shutil_which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + This is a backport of shutil.which from Python 3.4 + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + +import platform +if sys.version_info[0] >= 3 or platform.python_implementation() == 'IronPython': + str_to_unicode = no_code + unicode_to_str = no_code + str_to_bytes = encode + bytes_to_str = decode + cast_bytes_py2 = no_code + cast_unicode_py2 = no_code + buffer_to_bytes_py2 = no_code + + string_types = (str,) + unicode_type = str +else: + str_to_unicode = decode + unicode_to_str = encode + str_to_bytes = no_code + bytes_to_str = no_code + cast_bytes_py2 = cast_bytes + cast_unicode_py2 = cast_unicode + buffer_to_bytes_py2 = buffer_to_bytes + + string_types = (str, unicode) + unicode_type = unicode + +if sys.version_info[0] >= 3: + PY3 = True + + # keep reference to builtin_mod because the kernel overrides that value + # to forward requests to a frontend. + def input(prompt=''): + return builtin_mod.input(prompt) + + builtin_mod_name = "builtins" + import builtins as builtin_mod + + which = shutil.which + + def isidentifier(s, dotted=False): + if dotted: + return all(isidentifier(a) for a in s.split(".")) + return s.isidentifier() + + xrange = range + def iteritems(d): return iter(d.items()) + def itervalues(d): return iter(d.values()) + getcwd = os.getcwd + + MethodType = types.MethodType + + def execfile(fname, glob, loc=None, compiler=None): + loc = loc if (loc is not None) else glob + with open(fname, 'rb') as f: + compiler = compiler or compile + exec(compiler(f.read(), fname, 'exec'), glob, loc) + + # Refactor print statements in doctests. + _print_statement_re = re.compile(r"\bprint (?P<expr>.*)$", re.MULTILINE) + def _print_statement_sub(match): + expr = match.groups('expr') + return "print(%s)" % expr + + @_modify_str_or_docstring + def doctest_refactor_print(doc): + """Refactor 'print x' statements in a doctest to print(x) style. 2to3 + unfortunately doesn't pick up on our doctests. + + Can accept a string or a function, so it can be used as a decorator.""" + return _print_statement_re.sub(_print_statement_sub, doc) + + # Abstract u'abc' syntax: + @_modify_str_or_docstring + def u_format(s): + """"{u}'abc'" --> "'abc'" (Python 3) + + Accepts a string or a function, so it can be used as a decorator.""" + return s.format(u='') + + def get_closure(f): + """Get a function's closure attribute""" + return f.__closure__ + +else: + PY3 = False + + # keep reference to builtin_mod because the kernel overrides that value + # to forward requests to a frontend. + def input(prompt=''): + return builtin_mod.raw_input(prompt) + + builtin_mod_name = "__builtin__" + import __builtin__ as builtin_mod + + import re + _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") + def isidentifier(s, dotted=False): + if dotted: + return all(isidentifier(a) for a in s.split(".")) + return bool(_name_re.match(s)) + + xrange = xrange + def iteritems(d): return d.iteritems() + def itervalues(d): return d.itervalues() + getcwd = os.getcwdu + + def MethodType(func, instance): + return types.MethodType(func, instance, type(instance)) + + def doctest_refactor_print(func_or_str): + return func_or_str + + def get_closure(f): + """Get a function's closure attribute""" + return f.func_closure + + which = _shutil_which + + # Abstract u'abc' syntax: + @_modify_str_or_docstring + def u_format(s): + """"{u}'abc'" --> "u'abc'" (Python 2) + + Accepts a string or a function, so it can be used as a decorator.""" + return s.format(u='u') + + if sys.platform == 'win32': + def execfile(fname, glob=None, loc=None, compiler=None): + loc = loc if (loc is not None) else glob + scripttext = builtin_mod.open(fname).read()+ '\n' + # compile converts unicode filename to str assuming + # ascii. Let's do the conversion before calling compile + if isinstance(fname, unicode): + filename = unicode_to_str(fname) + else: + filename = fname + compiler = compiler or compile + exec(compiler(scripttext, filename, 'exec'), glob, loc) + + else: + def execfile(fname, glob=None, loc=None, compiler=None): + if isinstance(fname, unicode): + filename = fname.encode(sys.getfilesystemencoding()) + else: + filename = fname + where = [ns for ns in [glob, loc] if ns is not None] + if compiler is None: + builtin_mod.execfile(filename, *where) + else: + scripttext = builtin_mod.open(fname).read().rstrip() + '\n' + exec(compiler(scripttext, filename, 'exec'), glob, loc) + + +def annotate(**kwargs): + """Python 3 compatible function annotation for Python 2.""" + if not kwargs: + raise ValueError('annotations must be provided as keyword arguments') + def dec(f): + if hasattr(f, '__annotations__'): + for k, v in kwargs.items(): + f.__annotations__[k] = v + else: + f.__annotations__ = kwargs + return f + return dec + + +# Parts below taken from six: +# Copyright (c) 2010-2013 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + return meta("_NewBase", bases, {}) diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/tempdir.py b/contrib/python/ipython-genutils/py3/ipython_genutils/tempdir.py new file mode 100644 index 0000000000..2865b05bd3 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/tempdir.py @@ -0,0 +1,150 @@ +"""TemporaryDirectory class, copied from Python 3 + +This is copied from the stdlib and will be standard in Python 3.2 and onwards. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +from __future__ import print_function + +import os as _os +import warnings as _warnings +import sys as _sys + +# This code should only be used in Python versions < 3.2, since after that we +# can rely on the stdlib itself. +try: + from tempfile import TemporaryDirectory + +except ImportError: + from tempfile import mkdtemp, template + + class TemporaryDirectory(object): + """Create and return a temporary directory. This has the same + behavior as mkdtemp but can be used as a context manager. For + example: + + with TemporaryDirectory() as tmpdir: + ... + + Upon exiting the context, the directory and everthing contained + in it are removed. + """ + + def __init__(self, suffix="", prefix=template, dir=None): + self.name = mkdtemp(suffix, prefix, dir) + self._closed = False + + def __enter__(self): + return self.name + + def cleanup(self, _warn=False): + if self.name and not self._closed: + try: + self._rmtree(self.name) + except (TypeError, AttributeError) as ex: + # Issue #10188: Emit a warning on stderr + # if the directory could not be cleaned + # up due to missing globals + if "None" not in str(ex): + raise + print("ERROR: {!r} while cleaning up {!r}".format(ex, self,), + file=_sys.stderr) + return + self._closed = True + if _warn: + self._warn("Implicitly cleaning up {!r}".format(self), + Warning) + + def __exit__(self, exc, value, tb): + self.cleanup() + + def __del__(self): + # Issue a ResourceWarning if implicit cleanup needed + self.cleanup(_warn=True) + + + # XXX (ncoghlan): The following code attempts to make + # this class tolerant of the module nulling out process + # that happens during CPython interpreter shutdown + # Alas, it doesn't actually manage it. See issue #10188 + _listdir = staticmethod(_os.listdir) + _path_join = staticmethod(_os.path.join) + _isdir = staticmethod(_os.path.isdir) + _remove = staticmethod(_os.remove) + _rmdir = staticmethod(_os.rmdir) + _os_error = _os.error + _warn = _warnings.warn + + def _rmtree(self, path): + # Essentially a stripped down version of shutil.rmtree. We can't + # use globals because they may be None'ed out at shutdown. + for name in self._listdir(path): + fullname = self._path_join(path, name) + try: + isdir = self._isdir(fullname) + except self._os_error: + isdir = False + if isdir: + self._rmtree(fullname) + else: + try: + self._remove(fullname) + except self._os_error: + pass + try: + self._rmdir(path) + except self._os_error: + pass + +# extra temp-dir-related context managers + +class NamedFileInTemporaryDirectory(object): + + def __init__(self, filename, mode='w+b', bufsize=-1, **kwds): + """ + Open a file named `filename` in a temporary directory. + + This context manager is preferred over `NamedTemporaryFile` in + stdlib `tempfile` when one needs to reopen the file. + + Arguments `mode` and `bufsize` are passed to `open`. + Rest of the arguments are passed to `TemporaryDirectory`. + + """ + self._tmpdir = TemporaryDirectory(**kwds) + path = _os.path.join(self._tmpdir.name, filename) + self.file = open(path, mode, bufsize) + + def cleanup(self): + self.file.close() + self._tmpdir.cleanup() + + __del__ = cleanup + + def __enter__(self): + return self.file + + def __exit__(self, type, value, traceback): + self.cleanup() + + +class TemporaryWorkingDirectory(TemporaryDirectory): + """ + Creates a temporary directory and sets the cwd to that directory. + Automatically reverts to previous cwd upon cleanup. + Usage example: + + with TemporaryWorkingDirectory() as tmpdir: + ... + """ + def __enter__(self): + self.old_wd = _os.getcwd() + _os.chdir(self.name) + return super(TemporaryWorkingDirectory, self).__enter__() + + def __exit__(self, exc, value, tb): + _os.chdir(self.old_wd) + return super(TemporaryWorkingDirectory, self).__exit__(exc, value, tb) + diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/testing/__init__.py b/contrib/python/ipython-genutils/py3/ipython_genutils/testing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/testing/__init__.py diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/testing/decorators.py b/contrib/python/ipython-genutils/py3/ipython_genutils/testing/decorators.py new file mode 100644 index 0000000000..cc81ebe389 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/testing/decorators.py @@ -0,0 +1,342 @@ +# -*- coding: utf-8 -*- +"""Decorators for labeling test objects. + +Decorators that merely return a modified version of the original function +object are straightforward. Decorators that return a new function object need +to use nose.tools.make_decorator(original_function)(decorator) in returning the +decorator, in order to preserve metadata such as function name, setup and +teardown functions and so on - see nose.tools for more information. + +This module provides a set of useful decorators meant to be ready to use in +your own tests. See the bottom of the file for the ready-made ones, and if you +find yourself writing a new one that may be of generic use, add it here. + +Included decorators: + + +Lightweight testing that remains unittest-compatible. + +- An @as_unittest decorator can be used to tag any normal parameter-less + function as a unittest TestCase. Then, both nose and normal unittest will + recognize it as such. This will make it easier to migrate away from Nose if + we ever need/want to while maintaining very lightweight tests. + +NOTE: This file contains IPython-specific decorators. Using the machinery in +IPython.external.decorators, we import either numpy.testing.decorators if numpy is +available, OR use equivalent code in IPython.external._decorators, which +we've copied verbatim from numpy. + +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import sys +import os +import tempfile +import unittest + +# For onlyif_cmd_exists decorator +from ..py3compat import string_types, which + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +# Simple example of the basic idea +def as_unittest(func): + """Decorator to make a simple function into a normal test via unittest.""" + class Tester(unittest.TestCase): + def test(self): + func() + + Tester.__name__ = func.__name__ + + return Tester + +# Utility functions + + + +def make_label_dec(label,ds=None): + """Factory function to create a decorator that applies one or more labels. + + Parameters + ---------- + label : string or sequence + One or more labels that will be applied by the decorator to the functions + it decorates. Labels are attributes of the decorated function with their + value set to True. + + ds : string + An optional docstring for the resulting decorator. If not given, a + default docstring is auto-generated. + + Returns + ------- + A decorator. + + Examples + -------- + + A simple labeling decorator: + + >>> slow = make_label_dec('slow') + >>> slow.__doc__ + "Labels a test as 'slow'." + + And one that uses multiple labels and a custom docstring: + + >>> rare = make_label_dec(['slow','hard'], + ... "Mix labels 'slow' and 'hard' for rare tests.") + >>> rare.__doc__ + "Mix labels 'slow' and 'hard' for rare tests." + + Now, let's test using this one: + >>> @rare + ... def f(): pass + ... + >>> + >>> f.slow + True + >>> f.hard + True + """ + + if isinstance(label, string_types): + labels = [label] + else: + labels = label + + # Validate that the given label(s) are OK for use in setattr() by doing a + # dry run on a dummy function. + tmp = lambda : None + for label in labels: + setattr(tmp,label,True) + + # This is the actual decorator we'll return + def decor(f): + for label in labels: + setattr(f,label,True) + return f + + # Apply the user's docstring, or autogenerate a basic one + if ds is None: + ds = "Labels a test as %r." % label + decor.__doc__ = ds + + return decor + + +# Inspired by numpy's skipif, but uses the full apply_wrapper utility to +# preserve function metadata better and allows the skip condition to be a +# callable. +def skipif(skip_condition, msg=None): + ''' Make function raise SkipTest exception if skip_condition is true + + Parameters + ---------- + + skip_condition : bool or callable + Flag to determine whether to skip test. If the condition is a + callable, it is used at runtime to dynamically make the decision. This + is useful for tests that may require costly imports, to delay the cost + until the test suite is actually executed. + msg : string + Message to give on raising a SkipTest exception. + + Returns + ------- + decorator : function + Decorator, which, when applied to a function, causes SkipTest + to be raised when the skip_condition was True, and the function + to be called normally otherwise. + + Notes + ----- + You will see from the code that we had to further decorate the + decorator with the nose.tools.make_decorator function in order to + transmit function name, and various other metadata. + ''' + + def skip_decorator(f): + # Local import to avoid a hard nose dependency and only incur the + # import time overhead at actual test-time. + import nose + + # Allow for both boolean or callable skip conditions. + if callable(skip_condition): + skip_val = skip_condition + else: + skip_val = lambda : skip_condition + + def get_msg(func,msg=None): + """Skip message with information about function being skipped.""" + if msg is None: out = 'Test skipped due to test condition.' + else: out = msg + return "Skipping test: %s. %s" % (func.__name__,out) + + # We need to define *two* skippers because Python doesn't allow both + # return with value and yield inside the same function. + def skipper_func(*args, **kwargs): + """Skipper for normal test functions.""" + if skip_val(): + raise nose.SkipTest(get_msg(f,msg)) + else: + return f(*args, **kwargs) + + def skipper_gen(*args, **kwargs): + """Skipper for test generators.""" + if skip_val(): + raise nose.SkipTest(get_msg(f,msg)) + else: + for x in f(*args, **kwargs): + yield x + + # Choose the right skipper to use when building the actual generator. + if nose.util.isgenerator(f): + skipper = skipper_gen + else: + skipper = skipper_func + + return nose.tools.make_decorator(f)(skipper) + + return skip_decorator + +# A version with the condition set to true, common case just to attach a message +# to a skip decorator +def skip(msg=None): + """Decorator factory - mark a test function for skipping from test suite. + + Parameters + ---------- + msg : string + Optional message to be added. + + Returns + ------- + decorator : function + Decorator, which, when applied to a function, causes SkipTest + to be raised, with the optional message added. + """ + + return skipif(True,msg) + + +def onlyif(condition, msg): + """The reverse from skipif, see skipif for details.""" + + if callable(condition): + skip_condition = lambda : not condition() + else: + skip_condition = lambda : not condition + + return skipif(skip_condition, msg) + +#----------------------------------------------------------------------------- +# Utility functions for decorators +def module_not_available(module): + """Can module be imported? Returns true if module does NOT import. + + This is used to make a decorator to skip tests that require module to be + available, but delay the 'import numpy' to test execution time. + """ + def condition(): + try: + mod = __import__(module) + return False + except ImportError: + return True + return condition + + +def decorated_dummy(dec, name): + """Return a dummy function decorated with dec, with the given name. + + Examples + -------- + import IPython.testing.decorators as dec + setup = dec.decorated_dummy(dec.skip_if_no_x11, __name__) + """ + dummy = lambda: None + dummy.__name__ = name + return dec(dummy) + +#----------------------------------------------------------------------------- +# Decorators for public use + +# Decorators to skip certain tests on specific platforms. +skip_win32 = skipif(sys.platform == 'win32', + "This test does not run under Windows") +skip_linux = skipif(sys.platform.startswith('linux'), + "This test does not run under Linux") +skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X") + + +# Decorators to skip tests if not on specific platforms. +skip_if_not_win32 = skipif(sys.platform != 'win32', + "This test only runs under Windows") +skip_if_not_linux = skipif(not sys.platform.startswith('linux'), + "This test only runs under Linux") +skip_if_not_osx = skipif(sys.platform != 'darwin', + "This test only runs under OSX") + + +_x11_skip_cond = (sys.platform not in ('darwin', 'win32') and + os.environ.get('DISPLAY', '') == '') +_x11_skip_msg = "Skipped under *nix when X11/XOrg not available" + +skip_if_no_x11 = skipif(_x11_skip_cond, _x11_skip_msg) + +# not a decorator itself, returns a dummy function to be used as setup +def skip_file_no_x11(name): + return decorated_dummy(skip_if_no_x11, name) if _x11_skip_cond else None + +# Other skip decorators + +# generic skip without module +skip_without = lambda mod: skipif(module_not_available(mod), "This test requires %s" % mod) + +skipif_not_numpy = skip_without('numpy') + +skipif_not_matplotlib = skip_without('matplotlib') + +skipif_not_sympy = skip_without('sympy') + +# A null 'decorator', useful to make more readable code that needs to pick +# between different decorators based on OS or other conditions +null_deco = lambda f: f + +# Some tests only run where we can use unicode paths. Note that we can't just +# check os.path.supports_unicode_filenames, which is always False on Linux. +try: + f = tempfile.NamedTemporaryFile(prefix=u"tmp€") +except UnicodeEncodeError: + unicode_paths = False +else: + unicode_paths = True + f.close() + +onlyif_unicode_paths = onlyif(unicode_paths, ("This test is only applicable " + "where we can use unicode in filenames.")) + + +def onlyif_cmds_exist(*commands): + """ + Decorator to skip test when at least one of `commands` is not found. + """ + for cmd in commands: + if not which(cmd): + return skip("This test runs only if command '{0}' " + "is installed".format(cmd)) + return null_deco + +def onlyif_any_cmd_exists(*commands): + """ + Decorator to skip test unless at least one of `commands` is found. + """ + for cmd in commands: + if which(cmd): + return null_deco + return skip("This test runs only if one of the commands {0} " + "is installed".format(commands)) diff --git a/contrib/python/ipython-genutils/py3/ipython_genutils/text.py b/contrib/python/ipython-genutils/py3/ipython_genutils/text.py new file mode 100644 index 0000000000..e196a70ad6 --- /dev/null +++ b/contrib/python/ipython-genutils/py3/ipython_genutils/text.py @@ -0,0 +1,244 @@ +# encoding: utf-8 +""" +Utilities for working with strings and text. + +Inheritance diagram: + +.. inheritance-diagram:: IPython.utils.text + :parts: 3 +""" + +import os +import re +import sys +import textwrap +from string import Formatter + +# datetime.strftime date format for ipython +if sys.platform == 'win32': + date_format = "%B %d, %Y" +else: + date_format = "%B %-d, %Y" + + +def indent(instr,nspaces=4, ntabs=0, flatten=False): + """Indent a string a given number of spaces or tabstops. + + indent(str,nspaces=4,ntabs=0) -> indent str by ntabs+nspaces. + + Parameters + ---------- + + instr : basestring + The string to be indented. + nspaces : int (default: 4) + The number of spaces to be indented. + ntabs : int (default: 0) + The number of tabs to be indented. + flatten : bool (default: False) + Whether to scrub existing indentation. If True, all lines will be + aligned to the same indentation. If False, existing indentation will + be strictly increased. + + Returns + ------- + + str|unicode : string indented by ntabs and nspaces. + + """ + if instr is None: + return + ind = '\t'*ntabs+' '*nspaces + if flatten: + pat = re.compile(r'^\s*', re.MULTILINE) + else: + pat = re.compile(r'^', re.MULTILINE) + outstr = re.sub(pat, ind, instr) + if outstr.endswith(os.linesep+ind): + return outstr[:-len(ind)] + else: + return outstr + + +def dedent(text): + """Equivalent of textwrap.dedent that ignores unindented first line. + + This means it will still dedent strings like: + '''foo + is a bar + ''' + + For use in wrap_paragraphs. + """ + + if text.startswith('\n'): + # text starts with blank line, don't ignore the first line + return textwrap.dedent(text) + + # split first line + splits = text.split('\n',1) + if len(splits) == 1: + # only one line + return textwrap.dedent(text) + + first, rest = splits + # dedent everything but the first line + rest = textwrap.dedent(rest) + return '\n'.join([first, rest]) + + +def wrap_paragraphs(text, ncols=80): + """Wrap multiple paragraphs to fit a specified width. + + This is equivalent to textwrap.wrap, but with support for multiple + paragraphs, as separated by empty lines. + + Returns + ------- + + list of complete paragraphs, wrapped to fill `ncols` columns. + """ + paragraph_re = re.compile(r'\n(\s*\n)+', re.MULTILINE) + text = dedent(text).strip() + paragraphs = paragraph_re.split(text)[::2] # every other entry is space + out_ps = [] + indent_re = re.compile(r'\n\s+', re.MULTILINE) + for p in paragraphs: + # presume indentation that survives dedent is meaningful formatting, + # so don't fill unless text is flush. + if indent_re.search(p) is None: + # wrap paragraph + p = textwrap.fill(p, ncols) + out_ps.append(p) + return out_ps + + +def strip_ansi(source): + """ + Remove ansi escape codes from text. + + Parameters + ---------- + source : str + Source to remove the ansi from + """ + return re.sub(r'\033\[(\d|;)+?m', '', source) + + +#----------------------------------------------------------------------------- +# Utils to columnize a list of string +#----------------------------------------------------------------------------- + +def _chunks(l, n): + """Yield successive n-sized chunks from l.""" + for i in range(0, len(l), n): + yield l[i:i+n] + + +def _find_optimal(rlist , separator_size=2 , displaywidth=80): + """Calculate optimal info to columnize a list of string""" + for nrow in range(1, len(rlist)+1) : + chk = list(map(max,_chunks(rlist, nrow))) + sumlength = sum(chk) + ncols = len(chk) + if sumlength+separator_size*(ncols-1) <= displaywidth : + break; + return {'columns_numbers' : ncols, + 'optimal_separator_width':(displaywidth - sumlength)/(ncols-1) if (ncols -1) else 0, + 'rows_numbers' : nrow, + 'columns_width' : chk + } + + +def _get_or_default(mylist, i, default=None): + """return list item number, or default if don't exist""" + if i >= len(mylist): + return default + else : + return mylist[i] + + +def compute_item_matrix(items, empty=None, *args, **kwargs) : + """Returns a nested list, and info to columnize items + + Parameters + ---------- + + items + list of strings to columize + empty : (default None) + default value to fill list if needed + separator_size : int (default=2) + How much caracters will be used as a separation between each columns. + displaywidth : int (default=80) + The width of the area onto wich the columns should enter + + Returns + ------- + + strings_matrix + + nested list of string, the outer most list contains as many list as + rows, the innermost lists have each as many element as colums. If the + total number of elements in `items` does not equal the product of + rows*columns, the last element of some lists are filled with `None`. + + dict_info + some info to make columnize easier: + + columns_numbers + number of columns + rows_numbers + number of rows + columns_width + list of with of each columns + optimal_separator_width + best separator width between columns + + Examples + -------- + :: + + In [1]: l = ['aaa','b','cc','d','eeeee','f','g','h','i','j','k','l'] + ...: compute_item_matrix(l,displaywidth=12) + Out[1]: + ([['aaa', 'f', 'k'], + ['b', 'g', 'l'], + ['cc', 'h', None], + ['d', 'i', None], + ['eeeee', 'j', None]], + {'columns_numbers': 3, + 'columns_width': [5, 1, 1], + 'optimal_separator_width': 2, + 'rows_numbers': 5}) + """ + info = _find_optimal(list(map(len, items)), *args, **kwargs) + nrow, ncol = info['rows_numbers'], info['columns_numbers'] + return ([[ _get_or_default(items, c*nrow+i, default=empty) for c in range(ncol) ] for i in range(nrow) ], info) + + +def columnize(items, separator=' ', displaywidth=80): + """ Transform a list of strings into a single string with columns. + + Parameters + ---------- + items : sequence of strings + The strings to process. + + separator : str, optional [default is two spaces] + The string that separates columns. + + displaywidth : int, optional [default is 80] + Width of the display in number of characters. + + Returns + ------- + The formatted string. + """ + if not items : + return '\n' + matrix, info = compute_item_matrix(items, separator_size=len(separator), displaywidth=displaywidth) + fmatrix = [filter(None, x) for x in matrix] + sjoin = lambda x : separator.join([ y.ljust(w, ' ') for y, w in zip(x, info['columns_width'])]) + return '\n'.join(map(sjoin, fmatrix))+'\n' + |