pax_global_header00006660000000000000000000000064130615564310014516gustar00rootroot0000000000000052 comment=bbb7fd6405bc0101c5ee6b25500825ac9a3f1727 ipython_genutils-0.2.0/000077500000000000000000000000001306155643100151215ustar00rootroot00000000000000ipython_genutils-0.2.0/.gitignore000066400000000000000000000004741306155643100171160ustar00rootroot00000000000000MANIFEST build dist _build docs/man/*.gz docs/source/api/generated docs/source/config/options docs/source/interactive/magics-generated.txt docs/gh-pages IPython/html/notebook/static/mathjax IPython/html/static/style/*.map *.py[co] __pycache__ *.egg-info *~ *.bak .ipynb_checkpoints .tox .DS_Store \#*# .#* .coverage ipython_genutils-0.2.0/.mailmap000066400000000000000000000250671306155643100165540ustar00rootroot00000000000000A. J. Holyoake ajholyoake Aaron Culich Aaron Culich Aron Ahmadia ahmadia Benjamin Ragan-Kelley Benjamin Ragan-Kelley Min RK Benjamin Ragan-Kelley MinRK Barry Wark Barry Wark Ben Edwards Ben Edwards Bradley M. Froehle Bradley M. Froehle Bradley M. Froehle Bradley Froehle Brandon Parsons Brandon Parsons Brian E. Granger Brian Granger Brian E. Granger Brian Granger <> Brian E. Granger bgranger <> Brian E. Granger bgranger Christoph Gohlke cgohlke Cyrille Rossant rossant Damián Avila damianavila Damián Avila damianavila Damon Allen damontallen Darren Dale darren.dale <> Darren Dale Darren Dale <> Dav Clark Dav Clark <> Dav Clark Dav Clark David Hirschfeld dhirschfeld David P. Sanders David P. Sanders David Warde-Farley David Warde-Farley <> Doug Blank Doug Blank Eugene Van den Bulke Eugene Van den Bulke Evan Patterson Evan Patterson Evan Patterson Evan Patterson Evan Patterson epatters Evan Patterson epatters Ernie French Ernie French Ernie French ernie french Ernie French ernop Fernando Perez Fernando Perez Fernando Perez Fernando Perez fperez <> Fernando Perez fptest <> Fernando Perez fptest1 <> Fernando Perez Fernando Perez Fernando Perez Fernando Perez <> Fernando Perez Fernando Perez Frank Murphy Frank Murphy Gabriel Becker gmbecker Gael Varoquaux gael.varoquaux <> Gael Varoquaux gvaroquaux Gael Varoquaux Gael Varoquaux <> Ingolf Becker watercrossing Jake Vanderplas Jake Vanderplas Jakob Gager jakobgager Jakob Gager jakobgager Jakob Gager jakobgager Jason Grout Jason Grout Jason Gors jason gors Jason Gors jgors Jens Hedegaard Nielsen Jens Hedegaard Nielsen Jens Hedegaard Nielsen Jens H Nielsen Jens Hedegaard Nielsen Jens H. Nielsen Jez Ng Jez Ng Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic Jonathan Frederic jon Jonathan Frederic U-Jon-PC\Jon Jonathan March Jonathan March Jonathan March jdmarch Jörgen Stenarson Jörgen Stenarson Jörgen Stenarson Jorgen Stenarson Jörgen Stenarson Jorgen Stenarson <> Jörgen Stenarson jstenar Jörgen Stenarson jstenar <> Jörgen Stenarson Jörgen Stenarson Juergen Hasch juhasch Juergen Hasch juhasch Julia Evans Julia Evans Kester Tong KesterTong Kyle Kelley Kyle Kelley Kyle Kelley rgbkrk Laurent Dufréchou Laurent Dufréchou Laurent Dufréchou laurent dufrechou <> Laurent Dufréchou laurent.dufrechou <> Laurent Dufréchou Laurent Dufrechou <> Laurent Dufréchou laurent.dufrechou@gmail.com <> Laurent Dufréchou ldufrechou Lorena Pantano Lorena Luis Pedro Coelho Luis Pedro Coelho Marc Molla marcmolla Martín Gaitán Martín Gaitán Matthias Bussonnier Matthias BUSSONNIER Matthias Bussonnier Bussonnier Matthias Matthias Bussonnier Matthias BUSSONNIER Matthias Bussonnier Matthias Bussonnier Michael Droettboom Michael Droettboom Nicholas Bollweg Nicholas Bollweg (Nick) Nicolas Rougier Nikolay Koldunov Nikolay Koldunov Omar Andrés Zapata Mesa Omar Andres Zapata Mesa Omar Andrés Zapata Mesa Omar Andres Zapata Mesa Pankaj Pandey Pankaj Pandey Pascal Schetelat pascal-schetelat Paul Ivanov Paul Ivanov Pauli Virtanen Pauli Virtanen <> Pauli Virtanen Pauli Virtanen Pierre Gerold Pierre Gerold Pietro Berkes Pietro Berkes Piti Ongmongkolkul piti118 Prabhu Ramachandran Prabhu Ramachandran <> Puneeth Chaganti Puneeth Chaganti Robert Kern rkern <> Robert Kern Robert Kern Robert Kern Robert Kern Robert Kern Robert Kern <> Robert Marchman Robert Marchman Satrajit Ghosh Satrajit Ghosh Satrajit Ghosh Satrajit Ghosh Scott Sanderson Scott Sanderson smithj1 smithj1 smithj1 smithj1 Steven Johnson stevenJohnson Steven Silvester blink1073 S. Weber s8weber Stefan van der Walt Stefan van der Walt Silvia Vinyes Silvia Silvia Vinyes silviav12 Sylvain Corlay Sylvain Corlay sylvain.corlay Ted Drain TD22057 Théophile Studer Théophile Studer Thomas Kluyver Thomas Thomas Spura Thomas Spura Timo Paulssen timo vds vds2212 vds vds Ville M. Vainio Ville M. Vainio ville Ville M. Vainio ville Ville M. Vainio vivainio <> Ville M. Vainio Ville M. Vainio Ville M. Vainio Ville M. Vainio Walter Doerwald walter.doerwald <> Walter Doerwald Walter Doerwald <> W. Trevor King W. Trevor King Yoval P. y-p ipython_genutils-0.2.0/.travis.yml000066400000000000000000000004641306155643100172360ustar00rootroot00000000000000language: python python: - "nightly" - 3.4 - 3.3 - 2.7 sudo: false env: install: - pip install -e . coveralls script: - nosetests --with-coverage --cover-package=ipython_genutils ipython_genutils after_success: - coveralls matrix: allow_failures: - python: "nightly" ipython_genutils-0.2.0/CONTRIBUTING.md000066400000000000000000000001741306155643100173540ustar00rootroot00000000000000# Contributing We follow the [IPython Contributing Guide](https://github.com/ipython/ipython/blob/master/CONTRIBUTING.md). ipython_genutils-0.2.0/COPYING.md000066400000000000000000000054241306155643100165600ustar00rootroot00000000000000# Licensing terms This project is licensed under the terms of the Modified BSD License (also known as New or Revised or 3-Clause BSD), as follows: - Copyright (c) 2001-, IPython Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the IPython Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## About the IPython Development Team The IPython Development Team is the set of all contributors to the IPython project. This includes all of the IPython subprojects. The core team that coordinates development on GitHub can be found here: https://github.com/jupyter/. ## Our Copyright Policy IPython uses a shared copyright model. Each contributor maintains copyright over their contributions to IPython. But, it is important to note that these contributions are typically only changes to the repositories. Thus, the IPython source code, in its entirety is not the copyright of any single person or institution. Instead, it is the collective copyright of the entire IPython Development Team. If individual contributors want to maintain a record of what changes/contributions they have specific copyright on, they should indicate their copyright in the commit message of the change, when they commit the change to one of the IPython repositories. With this in mind, the following banner should be used in any source code file to indicate the copyright and license terms: # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. ipython_genutils-0.2.0/MANIFEST.in000066400000000000000000000003311306155643100166540ustar00rootroot00000000000000include COPYING.md include CONTRIBUTING.md include README.md # Patterns to exclude from any directory global-exclude *~ global-exclude *.pyc global-exclude *.pyo global-exclude .git global-exclude .ipynb_checkpoints ipython_genutils-0.2.0/README.md000066400000000000000000000005701306155643100164020ustar00rootroot00000000000000# IPython vestigial utilities This package shouldn't exist. It contains some common utilities shared by Jupyter and IPython projects during The Big Split™. As soon as possible, those packages will remove their dependency on this, and this repo will go away. No functionality should be added to this repository, and no packages outside IPython/Jupyter should depend on it. ipython_genutils-0.2.0/ipython_genutils/000077500000000000000000000000001306155643100205255ustar00rootroot00000000000000ipython_genutils-0.2.0/ipython_genutils/__init__.py000066400000000000000000000000601306155643100226320ustar00rootroot00000000000000from ._version import __version__, version_info ipython_genutils-0.2.0/ipython_genutils/_version.py000066400000000000000000000001101306155643100227130ustar00rootroot00000000000000version_info = (0, 2, 0) __version__ = '.'.join(map(str, version_info)) ipython_genutils-0.2.0/ipython_genutils/encoding.py000066400000000000000000000054651306155643100226770ustar00rootroot00000000000000# 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() ipython_genutils-0.2.0/ipython_genutils/importstring.py000066400000000000000000000020131306155643100236340ustar00rootroot00000000000000# 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]) ipython_genutils-0.2.0/ipython_genutils/ipstruct.py000066400000000000000000000257721306155643100227710ustar00rootroot00000000000000# 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]) ipython_genutils-0.2.0/ipython_genutils/path.py000066400000000000000000000124121306155643100220330ustar00rootroot00000000000000# 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) ipython_genutils-0.2.0/ipython_genutils/py3compat.py000066400000000000000000000250721306155643100230240ustar00rootroot00000000000000# 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.*)$", 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, {}) ipython_genutils-0.2.0/ipython_genutils/tempdir.py000066400000000000000000000115011306155643100225410ustar00rootroot00000000000000"""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) ipython_genutils-0.2.0/ipython_genutils/testing/000077500000000000000000000000001306155643100222025ustar00rootroot00000000000000ipython_genutils-0.2.0/ipython_genutils/testing/__init__.py000066400000000000000000000000001306155643100243010ustar00rootroot00000000000000ipython_genutils-0.2.0/ipython_genutils/testing/decorators.py000066400000000000000000000252411306155643100247250ustar00rootroot00000000000000# -*- 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)) ipython_genutils-0.2.0/ipython_genutils/tests/000077500000000000000000000000001306155643100216675ustar00rootroot00000000000000ipython_genutils-0.2.0/ipython_genutils/tests/__init__.py000066400000000000000000000000001306155643100237660ustar00rootroot00000000000000ipython_genutils-0.2.0/ipython_genutils/tests/test_importstring.py000066400000000000000000000012161306155643100260410ustar00rootroot00000000000000"""Tests for IPython.utils.importstring.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import nose.tools as nt from ..importstring import import_item def test_import_plain(): "Test simple imports" import os os2 = import_item('os') nt.assert_true(os is os2) def test_import_nested(): "Test nested imports from the stdlib" from os import path path2 = import_item('os.path') nt.assert_true(path is path2) def test_import_raises(): "Test that failing imports raise the right exception" nt.assert_raises(ImportError, import_item, 'IPython.foobar') ipython_genutils-0.2.0/ipython_genutils/tests/test_path.py000066400000000000000000000063561306155643100242460ustar00rootroot00000000000000# encoding: utf-8 """Tests for genutils.path""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import os import sys import tempfile import nose.tools as nt from ..testing.decorators import skip_if_not_win32, skip_win32 from .. import path from .. import py3compat from ..tempdir import TemporaryDirectory def test_filefind(): f = tempfile.NamedTemporaryFile() t = path.filefind(f.name, '.') def test_ensure_dir_exists(): with TemporaryDirectory() as td: d = os.path.join(td, u'∂ir') path.ensure_dir_exists(d) # create it assert os.path.isdir(d) path.ensure_dir_exists(d) # no-op f = os.path.join(td, u'ƒile') open(f, 'w').close() # touch with nt.assert_raises(IOError): path.ensure_dir_exists(f) class TestLinkOrCopy(object): def setUp(self): self.tempdir = TemporaryDirectory() self.src = self.dst("src") with open(self.src, "w") as f: f.write("Hello, world!") def tearDown(self): self.tempdir.cleanup() def dst(self, *args): return os.path.join(self.tempdir.name, *args) def assert_inode_not_equal(self, a, b): nt.assert_not_equals(os.stat(a).st_ino, os.stat(b).st_ino, "%r and %r do reference the same indoes" %(a, b)) def assert_inode_equal(self, a, b): nt.assert_equals(os.stat(a).st_ino, os.stat(b).st_ino, "%r and %r do not reference the same indoes" %(a, b)) def assert_content_equal(self, a, b): with open(a) as a_f: with open(b) as b_f: nt.assert_equals(a_f.read(), b_f.read()) @skip_win32 def test_link_successful(self): dst = self.dst("target") path.link_or_copy(self.src, dst) self.assert_inode_equal(self.src, dst) @skip_win32 def test_link_into_dir(self): dst = self.dst("some_dir") os.mkdir(dst) path.link_or_copy(self.src, dst) expected_dst = self.dst("some_dir", os.path.basename(self.src)) self.assert_inode_equal(self.src, expected_dst) @skip_win32 def test_target_exists(self): dst = self.dst("target") open(dst, "w").close() path.link_or_copy(self.src, dst) self.assert_inode_equal(self.src, dst) @skip_win32 def test_no_link(self): real_link = os.link try: del os.link dst = self.dst("target") path.link_or_copy(self.src, dst) self.assert_content_equal(self.src, dst) self.assert_inode_not_equal(self.src, dst) finally: os.link = real_link @skip_if_not_win32 def test_windows(self): dst = self.dst("target") path.link_or_copy(self.src, dst) self.assert_content_equal(self.src, dst) def test_link_twice(self): # Linking the same file twice shouldn't leave duplicates around. # See https://github.com/ipython/ipython/issues/6450 dst = self.dst('target') path.link_or_copy(self.src, dst) path.link_or_copy(self.src, dst) self.assert_inode_equal(self.src, dst) nt.assert_equal(sorted(os.listdir(self.tempdir.name)), ['src', 'target']) ipython_genutils-0.2.0/ipython_genutils/tests/test_tempdir.py000066400000000000000000000012341306155643100247440ustar00rootroot00000000000000 import os from ..tempdir import NamedFileInTemporaryDirectory from ..tempdir import TemporaryWorkingDirectory def test_named_file_in_temporary_directory(): with NamedFileInTemporaryDirectory('filename') as file: name = file.name assert not file.closed assert os.path.exists(name) file.write(b'test') assert file.closed assert not os.path.exists(name) def test_temporary_working_directory(): with TemporaryWorkingDirectory() as dir: assert os.path.exists(dir) assert os.path.realpath(os.curdir) == os.path.realpath(dir) assert not os.path.exists(dir) assert os.path.abspath(os.curdir) != dir ipython_genutils-0.2.0/ipython_genutils/tests/test_text.py000066400000000000000000000037731306155643100242760ustar00rootroot00000000000000# encoding: utf-8 """Tests for IPython.utils.text""" from __future__ import print_function # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import os import math import random import sys import nose.tools as nt from .. import text def test_columnize(): """Basic columnize tests.""" size = 5 items = [l*size for l in 'abc'] out = text.columnize(items, displaywidth=80) nt.assert_equal(out, 'aaaaa bbbbb ccccc\n') out = text.columnize(items, displaywidth=12) nt.assert_equal(out, 'aaaaa ccccc\nbbbbb\n') out = text.columnize(items, displaywidth=10) nt.assert_equal(out, 'aaaaa\nbbbbb\nccccc\n') def test_columnize_random(): """Test with random input to hopfully catch edge case """ for nitems in [random.randint(2,70) for i in range(2,20)]: displaywidth = random.randint(20,200) rand_len = [random.randint(2,displaywidth) for i in range(nitems)] items = ['x'*l for l in rand_len] out = text.columnize(items, displaywidth=displaywidth) longer_line = max([len(x) for x in out.split('\n')]) longer_element = max(rand_len) if longer_line > displaywidth: print("Columnize displayed something lager than displaywidth : %s " % longer_line) print("longer element : %s " % longer_element) print("displaywidth : %s " % displaywidth) print("number of element : %s " % nitems) print("size of each element :\n %s" % rand_len) assert False def test_columnize_medium(): """Test with inputs than shouldn't be wider tahn 80 """ size = 40 items = [l*size for l in 'abc'] out = text.columnize(items, displaywidth=80) nt.assert_equal(out, '\n'.join(items+[''])) def test_columnize_long(): """Test columnize with inputs longer than the display window""" size = 11 items = [l*size for l in 'abc'] out = text.columnize(items, displaywidth=size-1) nt.assert_equal(out, '\n'.join(items+[''])) ipython_genutils-0.2.0/ipython_genutils/text.py000066400000000000000000000154211306155643100220660ustar00rootroot00000000000000# 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' ipython_genutils-0.2.0/setup.cfg000066400000000000000000000000321306155643100167350ustar00rootroot00000000000000[bdist_wheel] universal=1 ipython_genutils-0.2.0/setup.py000066400000000000000000000051661306155643100166430ustar00rootroot00000000000000#!/usr/bin/env python # coding: utf-8 # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. from __future__ import print_function # the name of the project name = 'ipython_genutils' #----------------------------------------------------------------------------- # Minimal Python version sanity check #----------------------------------------------------------------------------- import sys v = sys.version_info if v[:2] < (2,7) or (v[0] >= 3 and v[:2] < (3,3)): error = "ERROR: %s requires Python version 2.7 or 3.3 or above." % name print(error, file=sys.stderr) sys.exit(1) PY3 = (sys.version_info[0] >= 3) #----------------------------------------------------------------------------- # get on with it #----------------------------------------------------------------------------- import os from glob import glob from distutils.core import setup pjoin = os.path.join here = os.path.abspath(os.path.dirname(__file__)) pkg_root = pjoin(here, name) packages = [] for d, _, _ in os.walk(pjoin(here, name)): if os.path.exists(pjoin(d, '__init__.py')): packages.append(d[len(here)+1:].replace(os.path.sep, '.')) version_ns = {} with open(pjoin(here, name, '_version.py')) as f: exec(f.read(), {}, version_ns) setup_args = dict( name = name, version = version_ns['__version__'], scripts = glob(pjoin('scripts', '*')), packages = packages, description = "Vestigial utilities from IPython", long_description= "Pretend this doesn't exist. Nobody should use it.", author = 'IPython Development Team', author_email = 'ipython-dev@scipy.org', url = 'http://ipython.org', license = 'BSD', platforms = "Linux, Mac OS X, Windows", keywords = ['Interactive', 'Interpreter', 'Shell', 'Web'], classifiers = [ 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', ], ) if 'develop' in sys.argv or any(a.startswith('bdist') for a in sys.argv): import setuptools setuptools_args = {} install_requires = setuptools_args['install_requires'] = [ ] extras_require = setuptools_args['extras_require'] = { } if 'setuptools' in sys.modules: setup_args.update(setuptools_args) if __name__ == '__main__': setup(**setup_args)