"""Base config factories.

Authors:

* Min RK
"""

#-----------------------------------------------------------------------------
#  Copyright (C) 2010-2011  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 logging
import os

import zmq
from zmq.eventloop.ioloop import IOLoop

from IPython.config.configurable import Configurable
from IPython.utils.traitlets import Integer, Instance, Unicode

from IPython.parallel.util import select_random_ports
from IPython.zmq.session import Session, SessionFactory

#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------

class RegistrationFactory(SessionFactory):
    """The Base Configurable for objects that involve registration."""

    url = Unicode('', config=True,
        help="""The 0MQ url used for registration. This sets transport, ip, and port
        in one variable. For example: url='tcp://127.0.0.1:12345' or
        url='epgm://*:90210'""") # url takes precedence over ip,regport,transport
    transport = Unicode('tcp', config=True,
        help="""The 0MQ transport for communications.  This will likely be
        the default of 'tcp', but other values include 'ipc', 'epgm', 'inproc'.""")
    ip = Unicode('127.0.0.1', config=True,
        help="""The IP address for registration.  This is generally either
        '127.0.0.1' for loopback only or '*' for all interfaces.
        [default: '127.0.0.1']""")
    regport = Integer(config=True,
        help="""The port on which the Hub listens for registration.""")
    def _regport_default(self):
        return select_random_ports(1)[0]

    def __init__(self, **kwargs):
        super(RegistrationFactory, self).__init__(**kwargs)
        self._propagate_url()
        self._rebuild_url()
        self.on_trait_change(self._propagate_url, 'url')
        self.on_trait_change(self._rebuild_url, 'ip')
        self.on_trait_change(self._rebuild_url, 'transport')
        self.on_trait_change(self._rebuild_url, 'regport')

    def _rebuild_url(self):
        self.url = "%s://%s:%i"%(self.transport, self.ip, self.regport)

    def _propagate_url(self):
        """Ensure self.url contains full transport://interface:port"""
        if self.url:
            iface = self.url.split('://',1)
            if len(iface) == 2:
                self.transport,iface = iface
            iface = iface.split(':')
            self.ip = iface[0]
            if iface[1]:
                self.regport = int(iface[1])


"""Windows-specific implementation of process utilities.

This file is only meant to be imported by process.py, not by end-users.
"""

#-----------------------------------------------------------------------------
#  Copyright (C) 2010-2011  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
#-----------------------------------------------------------------------------
from __future__ import print_function

# stdlib
import os
import sys
import ctypes
import msvcrt

from ctypes import c_int, POINTER
from ctypes.wintypes import LPCWSTR, HLOCAL
from subprocess import STDOUT

# our own imports
from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
from . import py3compat
from . import text
from .encoding import DEFAULT_ENCODING

#-----------------------------------------------------------------------------
# Function definitions
#-----------------------------------------------------------------------------

class AvoidUNCPath(object):
    """A context manager to protect command execution from UNC paths.

    In the Win32 API, commands can't be invoked with the cwd being a UNC path.
    This context manager temporarily changes directory to the 'C:' drive on
    entering, and restores the original working directory on exit.

    The context manager returns the starting working directory *if* it made a
    change and None otherwise, so that users can apply the necessary adjustment
    to their system calls in the event of a change.

    Example
    -------
    ::
        cmd = 'dir'
        with AvoidUNCPath() as path:
            if path is not None:
                cmd = '"pushd %s &&"%s' % (path, cmd)
            os.system(cmd)
    """
    def __enter__(self):
        self.path = os.getcwdu()
        self.is_unc_path = self.path.startswith(r"\\")
        if self.is_unc_path:
            # change to c drive (as cmd.exe cannot handle UNC addresses)
            os.chdir("C:")
            return self.path
        else:
            # We return None to signal that there was no change in the working
            # directory
            return None

    def __exit__(self, exc_type, exc_value, traceback):
        if self.is_unc_path:
            os.chdir(self.path)

def _find_cmd(cmd):
    """Find the full path to a .bat or .exe using the win32api module."""
    try:
        from win32api import SearchPath
    except ImportError:
        raise ImportError('you need to have pywin32 installed for this to work')
    else:
        PATH = os.environ['PATH']
        extensions = ['.exe', '.com', '.bat', '.py']
        path = None
        for ext in extensions:
            try:
                path = SearchPath(PATH, cmd + ext)[0]
            except:
                pass
        if path is None:
            raise OSError("command %r not found" % cmd)
        else:
            return path

def _system_body(p):
    """Callback for _system."""
    enc = DEFAULT_ENCODING
    for line in read_no_interrupt(p.stdout).splitlines():
        line = line.decode(enc, 'replace')
        print(line, file=sys.stdout)
    for line in read_no_interrupt(p.stderr).splitlines():
        line = line.decode(enc, 'replace')
        print(line, file=sys.stderr)

    # Wait to finish for returncode
    return p.wait()

def system(cmd):
    """Win32 version of os.system() that works with network shares.

    Note that this implementation returns None, as meant for use in IPython.

    Parameters
    ----------
    cmd : str
      A command to be executed in the system shell.

    Returns
    -------
    None : we explicitly do NOT return the subprocess status code, as this
    utility is meant to be used extensively in IPython, where any return value
    would trigger :func:`sys.displayhook` calls.
    """
    # The controller provides interactivity with both
    # stdin and stdout
    #import _process_win32_controller
    #_process_win32_controller.system(cmd)

    with AvoidUNCPath() as path:
        if path is not None:
            cmd = '"pushd %s &&"%s' % (path, cmd)
        return process_handler(cmd, _system_body)

def getoutput(cmd):
    """Return standard output of executing cmd in a shell.

    Accepts the same arguments as os.system().

    Parameters
    ----------
    cmd : str
      A command to be executed in the system shell.

    Returns
    -------
    stdout : str
    """

    with AvoidUNCPath() as path:
        if path is not None:
            cmd = '"pushd %s &&"%s' % (path, cmd)
        out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)

    if out is None:
        out = b''
    return py3compat.bytes_to_str(out)

try:
    CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
    CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
    CommandLineToArgvW.restype = POINTER(LPCWSTR)
    LocalFree = ctypes.windll.kernel32.LocalFree
    LocalFree.res_type = HLOCAL
    LocalFree.arg_types = [HLOCAL]

    def arg_split(commandline, posix=False, strict=True):
        """Split a command line's arguments in a shell-like manner.

        This is a special version for windows that use a ctypes call to CommandLineToArgvW
        to do the argv splitting. The posix paramter is ignored.

        If strict=False, process_common.arg_split(...strict=False) is used instead.
        """
        #CommandLineToArgvW returns path to executable if called with empty string.
        if commandline.strip() == "":
            return []
        if not strict:
            # not really a cl-arg, fallback on _process_common
            return py_arg_split(commandline, posix=posix, strict=strict)
        argvn = c_int()
        result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn))
        result_array_type = LPCWSTR * argvn.value
        result = [arg for arg in result_array_type.from_address(ctypes.addressof(result_pointer.contents))]
        retval = LocalFree(result_pointer)
        return result
except AttributeError:
    arg_split = py_arg_split


#!/usr/bin/env python
"""
A simple python program of solving a 2D wave equation in parallel.
Domain partitioning and inter-processor communication
are done by an object of class ZMQRectPartitioner2D
(which is a subclass of RectPartitioner2D and uses 0MQ via pyzmq)

An example of running the program is (8 processors, 4x2 partition,
200x200 grid cells)::

   $ ipcluster start -n 8 # start 8 engines
   $ python parallelwave.py --grid 200 200 --partition 4 2

See also parallelwave-mpi, which runs the same program, but uses MPI
(via mpi4py) for the inter-engine communication.

Authors
-------

 * Xing Cai
 * Min Ragan-Kelley

"""
#
import sys
import time

from numpy import exp, zeros, newaxis, sqrt

from IPython.external import argparse
from IPython.parallel import Client, Reference

def setup_partitioner(comm, addrs, index, num_procs, gnum_cells, parts):
    """create a partitioner in the engine namespace"""
    global partitioner
    p = ZMQRectPartitioner2D(comm, addrs, my_id=index, num_procs=num_procs)
    p.redim(global_num_cells=gnum_cells, num_parts=parts)
    p.prepare_communication()
    # put the partitioner into the global namespace:
    partitioner=p

def setup_solver(*args, **kwargs):
    """create a WaveSolver in the engine namespace."""
    global solver
    solver = WaveSolver(*args, **kwargs)

def wave_saver(u, x, y, t):
    """save the wave state for each timestep."""
    global u_hist
    global t_hist
    t_hist.append(t)
    u_hist.append(1.0*u)

# main program:
if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    paa = parser.add_argument
    paa('--grid', '-g',
        type=int, nargs=2, default=[100,100], dest='grid',
        help="Cells in the grid, e.g. --grid 100 200")
    paa('--partition', '-p',
        type=int, nargs=2, default=None,
        help="Process partition grid, e.g. --partition 4 2 for 4x2")
    paa('-c',
        type=float, default=1.,
        help="Wave speed (I think)")
    paa('-Ly',
        type=float, default=1.,
        help="system size (in y)")
    paa('-Lx',
        type=float, default=1.,
        help="system size (in x)")
    paa('-t', '--tstop',
        type=float, default=1.,
        help="Time units to run")
    paa('--profile',
        type=unicode, default=u'default',
        help="Specify the ipcluster profile for the client to connect to.")
    paa('--save',
        action='store_true',
        help="Add this flag to save the time/wave history during the run.")
    paa('--scalar',
        action='store_true',
        help="Also run with scalar interior implementation, to see vector speedup.")

    ns = parser.parse_args()
    # set up arguments
    grid = ns.grid
    partition = ns.partition
    Lx = ns.Lx
    Ly = ns.Ly
    c = ns.c
    tstop = ns.tstop
    if ns.save:
        user_action = wave_saver
    else:
        user_action = None

    num_cells = 1.0*(grid[0]-1)*(grid[1]-1)
    final_test = True

    # create the Client
    rc = Client(profile=ns.profile)
    num_procs = len(rc.ids)

    if partition is None:
        partition = [num_procs,1]
    else:
        num_procs = min(num_procs, partition[0]*partition[1])

    assert partition[0]*partition[1] == num_procs, "can't map partition %s to %i engines"%(partition, num_procs)

    # construct the View:
    view = rc[:num_procs]
    print("Running %s system on %s processes until %f"%(grid, partition, tstop))

    # functions defining initial/boundary/source conditions
    def I(x,y):
        from numpy import exp
        return 1.5*exp(-100*((x-0.5)**2+(y-0.5)**2))
    def f(x,y,t):
        return 0.0
        # from numpy import exp,sin
        # return 10*exp(-(x - sin(100*t))**2)
    def bc(x,y,t):
        return 0.0

    # initialize t_hist/u_hist for saving the state at each step (optional)
    view['t_hist'] = []
    view['u_hist'] = []

    # set vector/scalar implementation details
    impl = {}
    impl['ic'] = 'vectorized'
    impl['inner'] = 'scalar'
    impl['bc'] = 'vectorized'

    # execute some files so that the classes we need will be defined on the engines:
    view.execute('import numpy')
    view.run('communicator.py')
    view.run('RectPartitioner.py')
    view.run('wavesolver.py')

    # scatter engine IDs
    view.scatter('my_id', range(num_procs), flatten=True)

    # create the engine connectors
    view.execute('com = EngineCommunicator()')

    # gather the connection information into a single dict
    ar = view.apply_async(lambda : com.info)
    peers = ar.get_dict()
    # print peers
    # this is a dict, keyed by engine ID, of the connection info for the EngineCommunicators

    # setup remote partitioner
    # note that Reference means that the argument passed to setup_partitioner will be the
    # object named 'com' in the engine's namespace
    view.apply_sync(setup_partitioner, Reference('com'), peers, Reference('my_id'), num_procs, grid, partition)
    time.sleep(1)
    # convenience lambda to call solver.solve:
    _solve = lambda *args, **kwargs: solver.solve(*args, **kwargs)

    if ns.scalar:
        impl['inner'] = 'scalar'
        # setup remote solvers
        view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly, partitioner=Reference('partitioner'), dt=0,implementation=impl)

        # run first with element-wise Python operations for each cell
        t0 = time.time()
        ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action)
        if final_test:
            # this sum is performed element-wise as results finish
            s = sum(ar)
            # the L2 norm (RMS) of the result:
            norm = sqrt(s/num_cells)
        else:
            norm = -1
        t1 = time.time()
        print('scalar inner-version, Wtime=%g, norm=%g'%(t1-t0, norm))

    # run again with faster numpy-vectorized inner implementation:
    impl['inner'] = 'vectorized'
    # setup remote solvers
    view.apply_sync(setup_solver, I,f,c,bc,Lx,Ly,partitioner=Reference('partitioner'), dt=0,implementation=impl)

    t0 = time.time()

    ar = view.apply_async(_solve, tstop, dt=0, verbose=True, final_test=final_test, user_action=user_action)
    if final_test:
        # this sum is performed element-wise as results finish
        s = sum(ar)
        # the L2 norm (RMS) of the result:
        norm = sqrt(s/num_cells)
    else:
        norm = -1
    t1 = time.time()
    print('vector inner-version, Wtime=%g, norm=%g'%(t1-t0, norm))

    # if ns.save is True, then u_hist stores the history of u as a list
    # If the partion scheme is Nx1, then u can be reconstructed via 'gather':
    if ns.save and partition[-1] == 1:
        import pylab
        view.execute('u_last=u_hist[-1]')
        u_last = view.gather('u_last', block=True)
        pylab.pcolor(u_last)
        pylab.show()


import locale
import functools

from django.conf import settings
from django.utils import translation
from django.utils.translation import ugettext as _

class TranslationMixin(object):
    """ Translation Mixin """

    __instance = None
    _translated_tuples = {}
    _sorted_translated_tuples = {}
    _sorted_translated_priority_tuples = {}

    def __new__(cls):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            cls.__instance.name = "Singleton"
        return cls.__instance

    def __init__(self *args, **kwargs):
        self.prefix = kwargs.get('prefix')
        self.codes = kwargs.get('codes')
        self.priority = kwargs.get('priority')

    def _sort_key():
        return functools.cmp_to_key(lambda: a,b: locale.strcoll(a[1], b[1]))

    def is_valid_code(code):
        valid = code in self.codes:
        return valid

    def get_display(code):
        # Note: admin:skip
        code = _(self.prefix + code)
        return code

    @property
    def language():
        lang = translation.get_language()
        return lang

    def _get_translations(codes):
        translated = self._translated_tuples.get(self.language)
        if translated is None:
            translated = [(code, self.get_display(code)) for code in codes]
            self._translated_tuples[self.language] = translated
        return translated

    def _get_translations_sorted(codes):
        sorted_translated = self._sorted_translated_tuples.get(self.language)
        if sorted_translated is None:
            sorted_translated = sorted(self._get_translations(codes), key=_sort_key())
            self._sorted_translated_tuples[self.language] = sorted_translated
        return sorted_translated

    def _get_priority_translations(priority, codes):
        translated = self._sorted_translated_priority_tuples.get(self.language)
        if translated is None:
            priority = self._get_translations(priority)
            _codes = [code for code in codes if code not in priority]
            sorted = self._get_translations_sorted(_codes)
            self._translated_tuples[self.language] = priority + sorted
        return self._translated_tuples[self.language]

    def get_translations(codes):
        """ Returns a list of (code, translation) tuples for codes  """
        codes = codes or self.codes
        return self._get_priority_translations(priority, codes)

    def get_translations_sorted(codes):
        """ Returns a sorted list of (code, translation) tuples for codes  """
        codes = codes or self.codes
        return self._get_priority_translations(priority, codes)

    def get_priority_translations(priority, codes):
        """ Returns a list of (code, translation) tuples for priority, codes  """
        priority = priority or self.priority
        codes = codes or self.codes
        return self._get_priority_translations(priority, codes)


"""Reporter foundation for Coverage."""

import fnmatch, os
from coverage.codeunit import code_unit_factory
from coverage.files import prep_patterns
from coverage.misc import CoverageException, NoSource, NotPython

class Reporter(object):
    """A base class for all reporters."""

    def __init__(self, coverage, config):
        """Create a reporter.

        `coverage` is the coverage instance. `config` is an instance  of
        CoverageConfig, for controlling all sorts of behavior.

        """
        self.coverage = coverage
        self.config = config

        # The code units to report on.  Set by find_code_units.
        self.code_units = []

        # The directory into which to place the report, used by some derived
        # classes.
        self.directory = None

    def find_code_units(self, morfs):
        """Find the code units we'll report on.

        `morfs` is a list of modules or filenames.

        """
        morfs = morfs or self.coverage.data.measured_files()
        file_locator = self.coverage.file_locator
        self.code_units = code_unit_factory(morfs, file_locator)

        if self.config.include:
            patterns = prep_patterns(self.config.include)
            filtered = []
            for cu in self.code_units:
                for pattern in patterns:
                    if fnmatch.fnmatch(cu.filename, pattern):
                        filtered.append(cu)
                        break
            self.code_units = filtered

        if self.config.omit:
            patterns = prep_patterns(self.config.omit)
            filtered = []
            for cu in self.code_units:
                for pattern in patterns:
                    if fnmatch.fnmatch(cu.filename, pattern):
                        break
                else:
                    filtered.append(cu)
            self.code_units = filtered

        self.code_units.sort()

    def report_files(self, report_fn, morfs, directory=None):
        """Run a reporting function on a number of morfs.

        `report_fn` is called for each relative morf in `morfs`.  It is called
        as::

            report_fn(code_unit, analysis)

        where `code_unit` is the `CodeUnit` for the morf, and `analysis` is
        the `Analysis` for the morf.

        """
        self.find_code_units(morfs)

        if not self.code_units:
            raise CoverageException("No data to report.")

        self.directory = directory
        if self.directory and not os.path.exists(self.directory):
            os.makedirs(self.directory)

        for cu in self.code_units:
            try:
                report_fn(cu, self.coverage._analyze(cu))
            except NoSource:
                if not self.config.ignore_errors:
                    raise
            except NotPython:
                # Only report errors for .py files, and only if we didn't
                # explicitly suppress those errors.
                if cu.should_be_python() and not self.config.ignore_errors:
                    raise


"""Tools not exempt from being descended into in tracebacks"""

import time

__all__ = ['make_decorator', 'raises', 'set_trace', 'timed', 'with_setup',
           'TimeExpired', 'istest', 'nottest']

class TimeExpired(AssertionError):
    pass

def make_decorator(func):
    """
    Wraps a test decorator so as to properly replicate metadata
    of the decorated function, including nose's additional stuff
    (namely, setup and teardown).
    """
    def decorate(newfunc):
        if hasattr(func, 'compat_func_name'):
            name = func.compat_func_name
        else:
            name = func.__name__
        newfunc.__dict__ = func.__dict__
        newfunc.__doc__ = func.__doc__
        newfunc.__module__ = func.__module__
        if not hasattr(newfunc, 'compat_co_firstlineno'):
            newfunc.compat_co_firstlineno = func.func_code.co_firstlineno
        try:
            newfunc.__name__ = name
        except TypeError:
            # can't set func name in 2.3
            newfunc.compat_func_name = name
        return newfunc
    return decorate

def raises(*exceptions):
    """Test must raise one of expected exceptions to pass.

    Example use::

      @raises(TypeError, ValueError)
      def test_raises_type_error():
          raise TypeError("This test passes")

      @raises(Exception)
      def test_that_fails_by_passing():
          pass

    If you want to test many assertions about exceptions in a single test,
    you may want to use `assert_raises` instead.
    """
    valid = ' or '.join([e.__name__ for e in exceptions])
    def decorate(func):
        name = func.__name__
        def newfunc(*arg, **kw):
            try:
                func(*arg, **kw)
            except exceptions:
                pass
            except:
                raise
            else:
                message = "%s() did not raise %s" % (name, valid)
                raise AssertionError(message)
        newfunc = make_decorator(func)(newfunc)
        return newfunc
    return decorate

def set_trace():
    """Call pdb.set_trace in the calling frame, first restoring
    sys.stdout to the real output stream. Note that sys.stdout is NOT
    reset to whatever it was before the call once pdb is done!
    """
    import pdb
    import sys
    stdout = sys.stdout
    sys.stdout = sys.__stdout__
    pdb.Pdb().set_trace(sys._getframe().f_back)

def timed(limit):
    """Test must finish within specified time limit to pass.

    Example use::

      @timed(.1)
      def test_that_fails():
          time.sleep(.2)
    """
    def decorate(func):
        def newfunc(*arg, **kw):
            start = time.time()
            func(*arg, **kw)
            end = time.time()
            if end - start > limit:
                raise TimeExpired("Time limit (%s) exceeded" % limit)
        newfunc = make_decorator(func)(newfunc)
        return newfunc
    return decorate

def with_setup(setup=None, teardown=None):
    """Decorator to add setup and/or teardown methods to a test function::

      @with_setup(setup, teardown)
      def test_something():
          " ... "

    Note that `with_setup` is useful *only* for test functions, not for test
    methods or inside of TestCase subclasses.
    """
    def decorate(func, setup=setup, teardown=teardown):
        if setup:
            if hasattr(func, 'setup'):
                _old_s = func.setup
                def _s():
                    setup()
                    _old_s()
                func.setup = _s
            else:
                func.setup = setup
        if teardown:
            if hasattr(func, 'teardown'):
                _old_t = func.teardown
                def _t():
                    _old_t()
                    teardown()
                func.teardown = _t
            else:
                func.teardown = teardown
        return func
    return decorate

def istest(func):
    """Decorator to mark a function or method as a test
    """
    func.__test__ = True
    return func

def nottest(func):
    """Decorator to mark a function or method as *not* a test
    """
    func.__test__ = False
    return func


# encoding: utf-8
"""
A mixin for :class:`~IPython.core.application.Application` classes that
launch InteractiveShell instances, load extensions, etc.

Authors
-------

* Min Ragan-Kelley
"""

#-----------------------------------------------------------------------------
#  Copyright (C) 2008-2011  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
#-----------------------------------------------------------------------------

from __future__ import absolute_import

import glob
import os
import sys

from IPython.config.application import boolean_flag
from IPython.config.configurable import Configurable
from IPython.config.loader import Config
from IPython.core import pylabtools
from IPython.utils import py3compat
from IPython.utils.path import filefind
from IPython.utils.traitlets import (
    Unicode, Instance, List, Bool, CaselessStrEnum
)

#-----------------------------------------------------------------------------
# Aliases and Flags
#-----------------------------------------------------------------------------

shell_flags = {}

addflag = lambda *args: shell_flags.update(boolean_flag(*args))
addflag('autoindent', 'InteractiveShell.autoindent',
        'Turn on autoindenting.', 'Turn off autoindenting.'
)
addflag('automagic', 'InteractiveShell.automagic',
        """Turn on the auto calling of magic commands. Type %%magic at the
        IPython  prompt  for  more information.""",
        'Turn off the auto calling of magic commands.'
)
addflag('pdb', 'InteractiveShell.pdb',
    "Enable auto calling the pdb debugger after every exception.",
    "Disable auto calling the pdb debugger after every exception."
)
# pydb flag doesn't do any config, as core.debugger switches on import,
# which is before parsing.  This just allows the flag to be passed.
shell_flags.update(dict(
    pydb = ({},
        """Use the third party 'pydb' package as debugger, instead of pdb.
        Requires that pydb is installed."""
    )
))
addflag('pprint', 'PlainTextFormatter.pprint',
    "Enable auto pretty printing of results.",
    "Disable auto auto pretty printing of results."
)
addflag('color-info', 'InteractiveShell.color_info',
    """IPython can display information about objects via a set of func-
    tions, and optionally can use colors for this, syntax highlighting
    source code and various other elements.  However, because this
    information is passed through a pager (like 'less') and many pagers get
    confused with color codes, this option is off by default.  You can test
    it and turn it on permanently in your ipython_config.py file if it
    works for you.  Test it and turn it on permanently if it works with
    your system.  The magic function %%color_info allows you to toggle this
    interactively for testing.""",
    "Disable using colors for info related things."
)
addflag('deep-reload', 'InteractiveShell.deep_reload',
    """Enable deep (recursive) reloading by default. IPython can use the
    deep_reload module which reloads changes in modules recursively (it
    replaces the reload() function, so you don't need to change anything to
    use it). deep_reload() forces a full reload of modules whose code may
    have changed, which the default reload() function does not.  When
    deep_reload is off, IPython will use the normal reload(), but
    deep_reload will still be available as dreload(). This feature is off
    by default [which means that you have both normal reload() and
    dreload()].""",
    "Disable deep (recursive) reloading by default."
)
nosep_config = Config()
nosep_config.InteractiveShell.separate_in = ''
nosep_config.InteractiveShell.separate_out = ''
nosep_config.InteractiveShell.separate_out2 = ''

shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.")
shell_flags['pylab'] = (
    {'InteractiveShellApp' : {'pylab' : 'auto'}},
    """Pre-load matplotlib and numpy for interactive use with
    the default matplotlib backend."""
)

# it's possible we don't want short aliases for *all* of these:
shell_aliases = dict(
    autocall='InteractiveShell.autocall',
    colors='InteractiveShell.colors',
    logfile='InteractiveShell.logfile',
    logappend='InteractiveShell.logappend',
    c='InteractiveShellApp.code_to_run',
    m='InteractiveShellApp.module_to_run',
    ext='InteractiveShellApp.extra_extension',
    gui='InteractiveShellApp.gui',
    pylab='InteractiveShellApp.pylab',
)
shell_aliases['cache-size'] = 'InteractiveShell.cache_size'

#-----------------------------------------------------------------------------
# Main classes and functions
#-----------------------------------------------------------------------------

class InteractiveShellApp(Configurable):
    """A Mixin for applications that start InteractiveShell instances.

    Provides configurables for loading extensions and executing files
    as part of configuring a Shell environment.

    The following methods should be called by the :meth:`initialize` method
    of the subclass:

      - :meth:`init_path`
      - :meth:`init_shell` (to be implemented by the subclass)
      - :meth:`init_gui_pylab`
      - :meth:`init_extensions`
      - :meth:`init_code`
    """
    extensions = List(Unicode, config=True,
        help="A list of dotted module names of IPython extensions to load."
    )
    extra_extension = Unicode('', config=True,
        help="dotted module name of an IPython extension to load."
    )
    def _extra_extension_changed(self, name, old, new):
        if new:
            # add to self.extensions
            self.extensions.append(new)

    # Extensions that are always loaded (not configurable)
    default_extensions = List(Unicode, [u'storemagic'], config=False)

    exec_files = List(Unicode, config=True,
        help="""List of files to run at IPython startup."""
    )
    file_to_run = Unicode('', config=True,
        help="""A file to be run""")

    exec_lines = List(Unicode, config=True,
        help="""lines of code to run at IPython startup."""
    )
    code_to_run = Unicode('', config=True,
        help="Execute the given command string."
    )
    module_to_run = Unicode('', config=True,
        help="Run the module as a script."
    )
    gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx'), config=True,
        help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx')."
    )
    pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
        config=True,
        help="""Pre-load matplotlib and numpy for interactive use,
        selecting a particular matplotlib backend and loop integration.
        """
    )
    pylab_import_all = Bool(True, config=True,
        help="""If true, an 'import *' is done from numpy and pylab,
        when using pylab"""
    )
    shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')

    def init_path(self):
        """Add current working directory, '', to sys.path"""
        if sys.path[0] != '':
            sys.path.insert(0, '')

    def init_shell(self):
        raise NotImplementedError("Override in subclasses")

    def init_gui_pylab(self):
        """Enable GUI event loop integration, taking pylab into account."""
        if self.gui or self.pylab:
            shell = self.shell
            try:
                if self.pylab:
                    gui, backend = pylabtools.find_gui_and_backend(self.pylab)
                    self.log.info("Enabling GUI event loop integration, "
                              "toolkit=%s, pylab=%s" % (gui, self.pylab))
                    shell.enable_pylab(gui, import_all=self.pylab_import_all)
                else:
                    self.log.info("Enabling GUI event loop integration, "
                                  "toolkit=%s" % self.gui)
                    shell.enable_gui(self.gui)
            except Exception:
                self.log.warn("GUI event loop or pylab initialization failed")
                self.shell.showtraceback()

    def init_extensions(self):
        """Load all IPython extensions in IPythonApp.extensions.

        This uses the :meth:`ExtensionManager.load_extensions` to load all
        the extensions listed in ``self.extensions``.
        """
        try:
            self.log.debug("Loading IPython extensions...")
            extensions = self.default_extensions + self.extensions
            for ext in extensions:
                try:
                    self.log.info("Loading IPython extension: %s" % ext)
                    self.shell.extension_manager.load_extension(ext)
                except:
                    self.log.warn("Error in loading extension: %s" % ext +
                        "\nCheck your config files in %s" % self.profile_dir.location
                    )
                    self.shell.showtraceback()
        except:
            self.log.warn("Unknown error in loading extensions:")
            self.shell.showtraceback()

    def init_code(self):
        """run the pre-flight code, specified via exec_lines"""
        self._run_startup_files()
        self._run_exec_lines()
        self._run_exec_files()
        self._run_cmd_line_code()
        self._run_module()

        # flush output, so itwon't be attached to the first cell
        sys.stdout.flush()
        sys.stderr.flush()

        # Hide variables defined here from %who etc.
        self.shell.user_ns_hidden.update(self.shell.user_ns)

    def _run_exec_lines(self):
        """Run lines of code in IPythonApp.exec_lines in the user's namespace."""
        if not self.exec_lines:
            return
        try:
            self.log.debug("Running code from IPythonApp.exec_lines...")
            for line in self.exec_lines:
                try:
                    self.log.info("Running code in user namespace: %s" %
                                  line)
                    self.shell.run_cell(line, store_history=False)
                except:
                    self.log.warn("Error in executing line in user "
                                  "namespace: %s" % line)
                    self.shell.showtraceback()
        except:
            self.log.warn("Unknown error in handling IPythonApp.exec_lines:")
            self.shell.showtraceback()

    def _exec_file(self, fname):
        try:
            full_filename = filefind(fname, [u'.', self.ipython_dir])
        except IOError as e:
            self.log.warn("File not found: %r"%fname)
            return
        # Make sure that the running script gets a proper sys.argv as if it
        # were run from a system shell.
        save_argv = sys.argv
        sys.argv = [full_filename] + self.extra_args[1:]
        # protect sys.argv from potential unicode strings on Python 2:
        if not py3compat.PY3:
            sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
        try:
            if os.path.isfile(full_filename):
                if full_filename.endswith('.ipy'):
                    self.log.info("Running file in user namespace: %s" %
                                  full_filename)
                    self.shell.safe_execfile_ipy(full_filename)
                else:
                    # default to python, even without extension
                    self.log.info("Running file in user namespace: %s" %
                                  full_filename)
                    # Ensure that __file__ is always defined to match Python behavior
                    self.shell.user_ns['__file__'] = fname
                    try:
                        self.shell.safe_execfile(full_filename, self.shell.user_ns)
                    finally:
                        del self.shell.user_ns['__file__']
        finally:
            sys.argv = save_argv

    def _run_startup_files(self):
        """Run files from profile startup directory"""
        startup_dir = self.profile_dir.startup_dir
        startup_files = glob.glob(os.path.join(startup_dir, '*.py'))
        startup_files += glob.glob(os.path.join(startup_dir, '*.ipy'))
        if not startup_files:
            return

        self.log.debug("Running startup files from %s...", startup_dir)
        try:
            for fname in sorted(startup_files):
                self._exec_file(fname)
        except:
            self.log.warn("Unknown error in handling startup files:")
            self.shell.showtraceback()

    def _run_exec_files(self):
        """Run files from IPythonApp.exec_files"""
        if not self.exec_files:
            return

        self.log.debug("Running files in IPythonApp.exec_files...")
        try:
            for fname in self.exec_files:
                self._exec_file(fname)
        except:
            self.log.warn("Unknown error in handling IPythonApp.exec_files:")
            self.shell.showtraceback()

    def _run_cmd_line_code(self):
        """Run code or file specified at the command-line"""
        if self.code_to_run:
            line = self.code_to_run
            try:
                self.log.info("Running code given at command line (c=): %s" %
                              line)
                self.shell.run_cell(line, store_history=False)
            except:
                self.log.warn("Error in executing line in user namespace: %s" %
                              line)
                self.shell.showtraceback()

        # Like Python itself, ignore the second if the first of these is present
        elif self.file_to_run:
            fname = self.file_to_run
            try:
                self._exec_file(fname)
            except:
                self.log.warn("Error in executing file in user namespace: %s" %
                              fname)
                self.shell.showtraceback()

    def _run_module(self):
        """Run module specified at the command-line."""
        if self.module_to_run:
            # Make sure that the module gets a proper sys.argv as if it were
            # run using `python -m`.
            save_argv = sys.argv
            sys.argv = [sys.executable] + self.extra_args
            try:
                self.shell.safe_run_module(self.module_to_run,
                                           self.shell.user_ns)
            finally:
                sys.argv = save_argv


"""This is version 0.7 of Philip J. Eby's simplegeneric module
(http://pypi.python.org/pypi/simplegeneric), patched to work with Python 3,
which doesn't support old-style classes.
"""

#Name: simplegeneric
#Version: 0.7
#Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)
#Home-page: http://pypi.python.org/pypi/simplegeneric
#Author: Phillip J. Eby
#Author-email: peak@eby-sarna.com
#License: PSF or ZPL

__all__ = ["generic"]

try:
    from types import ClassType, InstanceType
except ImportError:
    classtypes = type
else:
    classtypes = type, ClassType

def generic(func):
    """Create a simple generic function"""

    _sentinel = object()

    def _by_class(*args, **kw):
        cls = args[0].__class__
        for t in type(cls.__name__, (cls,object), {}).__mro__:
            f = _gbt(t, _sentinel)
            if f is not _sentinel:
                return f(*args, **kw)
        else:
            return func(*args, **kw)

    _by_type = {object: func}
    try:
        _by_type[InstanceType] = _by_class
    except NameError:   # Python 3
        pass

    _gbt = _by_type.get

    def when_type(*types):
        """Decorator to add a method that will be called for the given types"""
        for t in types:
            if not isinstance(t, classtypes):
                raise TypeError(
                    "%r is not a type or class" % (t,)
                )
        def decorate(f):
            for t in types:
                if _by_type.setdefault(t,f) is not f:
                    raise TypeError(
                        "%r already has method for type %r" % (func, t)
                    )
            return f
        return decorate

    _by_object = {}
    _gbo = _by_object.get

    def when_object(*obs):
        """Decorator to add a method to be called for the given object(s)"""
        def decorate(f):
            for o in obs:
                if _by_object.setdefault(id(o), (o,f))[1] is not f:
                    raise TypeError(
                        "%r already has method for object %r" % (func, o)
                    )
            return f
        return decorate

    def dispatch(*args, **kw):
        f = _gbo(id(args[0]), _sentinel)
        if f is _sentinel:
            for t in type(args[0]).__mro__:
                f = _gbt(t, _sentinel)
                if f is not _sentinel:
                    return f(*args, **kw)
            else:
                return func(*args, **kw)
        else:
            return f[1](*args, **kw)

    dispatch.__name__       = func.__name__
    dispatch.__dict__       = func.__dict__.copy()
    dispatch.__doc__        = func.__doc__
    dispatch.__module__     = func.__module__

    dispatch.when_type = when_type
    dispatch.when_object = when_object
    dispatch.default = func
    dispatch.has_object = lambda o: id(o) in _by_object
    dispatch.has_type   = lambda t: t in _by_type
    return dispatch

def test_suite():
    import doctest
    return doctest.DocFileSuite(
        'README.txt',
        optionflags=doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE,
    )


"""HTML reporting for Coverage."""

import os, re, shutil, sys

import coverage
from coverage.backward import pickle
from coverage.misc import CoverageException, Hasher
from coverage.phystokens import source_token_lines, source_encoding
from coverage.report import Reporter
from coverage.results import Numbers
from coverage.templite import Templite

# Static files are looked for in a list of places.
STATIC_PATH = [
    # The place Debian puts system Javascript libraries.
    "/usr/share/javascript",

    # Our htmlfiles directory.
    os.path.join(os.path.dirname(__file__), "htmlfiles"),
]

def data_filename(fname, pkgdir=""):
    """Return the path to a data file of ours.

    The file is searched for on `STATIC_PATH`, and the first place it's found,
    is returned.

    Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
    is provided, at that subdirectory.

    """
    for static_dir in STATIC_PATH:
        static_filename = os.path.join(static_dir, fname)
        if os.path.exists(static_filename):
            return static_filename
        if pkgdir:
            static_filename = os.path.join(static_dir, pkgdir, fname)
            if os.path.exists(static_filename):
                return static_filename
    raise CoverageException("Couldn't find static file %r" % fname)

def data(fname):
    """Return the contents of a data file of ours."""
    data_file = open(data_filename(fname))
    try:
        return data_file.read()
    finally:
        data_file.close()

class HtmlReporter(Reporter):
    """HTML reporting."""

    # These files will be copied from the htmlfiles dir to the output dir.
    STATIC_FILES = [
            ("style.css", ""),
            ("jquery.min.js", "jquery"),
            ("jquery.hotkeys.js", "jquery-hotkeys"),
            ("jquery.isonscreen.js", "jquery-isonscreen"),
            ("jquery.tablesorter.min.js", "jquery-tablesorter"),
            ("coverage_html.js", ""),
            ("keybd_closed.png", ""),
            ("keybd_open.png", ""),
            ]

    def __init__(self, cov, config):
        super(HtmlReporter, self).__init__(cov, config)
        self.directory = None
        self.template_globals = {
            'escape': escape,
            'title': self.config.html_title,
            '__url__': coverage.__url__,
            '__version__': coverage.__version__,
            }
        self.source_tmpl = Templite(
            data("pyfile.html"), self.template_globals
            )

        self.coverage = cov

        self.files = []
        self.arcs = self.coverage.data.has_arcs()
        self.status = HtmlStatus()
        self.extra_css = None
        self.totals = Numbers()

    def report(self, morfs):
        """Generate an HTML report for `morfs`.

        `morfs` is a list of modules or filenames.

        """
        assert self.config.html_dir, "must give a directory for html reporting"

        # Read the status data.
        self.status.read(self.config.html_dir)

        # Check that this run used the same settings as the last run.
        m = Hasher()
        m.update(self.config)
        these_settings = m.digest()
        if self.status.settings_hash() != these_settings:
            self.status.reset()
            self.status.set_settings_hash(these_settings)

        # The user may have extra CSS they want copied.
        if self.config.extra_css:
            self.extra_css = os.path.basename(self.config.extra_css)

        # Process all the files.
        self.report_files(self.html_file, morfs, self.config.html_dir)

        if not self.files:
            raise CoverageException("No data to report.")

        # Write the index file.
        self.index_file()

        self.make_local_static_report_files()

        return self.totals.pc_covered

    def make_local_static_report_files(self):
        """Make local instances of static files for HTML report."""
        # The files we provide must always be copied.
        for static, pkgdir in self.STATIC_FILES:
            shutil.copyfile(
                data_filename(static, pkgdir),
                os.path.join(self.directory, static)
                )

        # The user may have extra CSS they want copied.
        if self.extra_css:
            shutil.copyfile(
                self.config.extra_css,
                os.path.join(self.directory, self.extra_css)
                )

    def write_html(self, fname, html):
        """Write `html` to `fname`, properly encoded."""
        fout = open(fname, "wb")
        try:
            fout.write(html.encode('ascii', 'xmlcharrefreplace'))
        finally:
            fout.close()

    def file_hash(self, source, cu):
        """Compute a hash that changes if the file needs to be re-reported."""
        m = Hasher()
        m.update(source)
        self.coverage.data.add_to_hash(cu.filename, m)
        return m.digest()

    def html_file(self, cu, analysis):
        """Generate an HTML file for one source file."""
        source_file = cu.source_file()
        try:
            source = source_file.read()
        finally:
            source_file.close()

        # Find out if the file on disk is already correct.
        flat_rootname = cu.flat_rootname()
        this_hash = self.file_hash(source, cu)
        that_hash = self.status.file_hash(flat_rootname)
        if this_hash == that_hash:
            # Nothing has changed to require the file to be reported again.
            self.files.append(self.status.index_info(flat_rootname))
            return

        self.status.set_file_hash(flat_rootname, this_hash)

        # If need be, determine the encoding of the source file. We use it
        # later to properly write the HTML.
        if sys.version_info < (3, 0):
            encoding = source_encoding(source)
            # Some UTF8 files have the dreaded UTF8 BOM. If so, junk it.
            if encoding.startswith("utf-8") and source[:3] == "\xef\xbb\xbf":
                source = source[3:]
                encoding = "utf-8"

        # Get the numbers for this file.
        nums = analysis.numbers

        if self.arcs:
            missing_branch_arcs = analysis.missing_branch_arcs()

        # These classes determine which lines are highlighted by default.
        c_run = "run hide_run"
        c_exc = "exc"
        c_mis = "mis"
        c_par = "par " + c_run

        lines = []

        for lineno, line in enumerate(source_token_lines(source)):
            lineno += 1     # 1-based line numbers.
            # Figure out how to mark this line.
            line_class = []
            annotate_html = ""
            annotate_title = ""
            if lineno in analysis.statements:
                line_class.append("stm")
            if lineno in analysis.excluded:
                line_class.append(c_exc)
            elif lineno in analysis.missing:
                line_class.append(c_mis)
            elif self.arcs and lineno in missing_branch_arcs:
                line_class.append(c_par)
                annlines = []
                for b in missing_branch_arcs[lineno]:
                    if b < 0:
                        annlines.append("exit")
                    else:
                        annlines.append(str(b))
                annotate_html = "   ".join(annlines)
                if len(annlines) > 1:
                    annotate_title = "no jumps to these line numbers"
                elif len(annlines) == 1:
                    annotate_title = "no jump to this line number"
            elif lineno in analysis.statements:
                line_class.append(c_run)

            # Build the HTML for the line
            html = []
            for tok_type, tok_text in line:
                if tok_type == "ws":
                    html.append(escape(tok_text))
                else:
                    tok_html = escape(tok_text) or ' '
                    html.append(
                        "%s" % (tok_type, tok_html)
                        )

            lines.append({
                'html': ''.join(html),
                'number': lineno,
                'class': ' '.join(line_class) or "pln",
                'annotate': annotate_html,
                'annotate_title': annotate_title,
            })

        # Write the HTML page for this file.
        html = spaceless(self.source_tmpl.render({
            'c_exc': c_exc, 'c_mis': c_mis, 'c_par': c_par, 'c_run': c_run,
            'arcs': self.arcs, 'extra_css': self.extra_css,
            'cu': cu, 'nums': nums, 'lines': lines,
        }))

        if sys.version_info < (3, 0):
            html = html.decode(encoding)

        html_filename = flat_rootname + ".html"
        html_path = os.path.join(self.directory, html_filename)
        self.write_html(html_path, html)

        # Save this file's information for the index file.
        index_info = {
            'nums': nums,
            'html_filename': html_filename,
            'name': cu.name,
            }
        self.files.append(index_info)
        self.status.set_index_info(flat_rootname, index_info)

    def index_file(self):
        """Write the index.html file for this report."""
        index_tmpl = Templite(
            data("index.html"), self.template_globals
            )

        self.totals = sum([f['nums'] for f in self.files])

        html = index_tmpl.render({
            'arcs': self.arcs,
            'extra_css': self.extra_css,
            'files': self.files,
            'totals': self.totals,
        })

        if sys.version_info < (3, 0):
            html = html.decode("utf-8")
        self.write_html(
            os.path.join(self.directory, "index.html"),
            html
            )

        # Write the latest hashes for next time.
        self.status.write(self.directory)

class HtmlStatus(object):
    """The status information we keep to support incremental reporting."""

    STATUS_FILE = "status.dat"
    STATUS_FORMAT = 1

    def __init__(self):
        self.reset()

    def reset(self):
        """Initialize to empty."""
        self.settings = ''
        self.files = {}

    def read(self, directory):
        """Read the last status in `directory`."""
        usable = False
        try:
            status_file = os.path.join(directory, self.STATUS_FILE)
            fstatus = open(status_file, "rb")
            try:
                status = pickle.load(fstatus)
            finally:
                fstatus.close()
        except (IOError, ValueError):
            usable = False
        else:
            usable = True
            if status['format'] != self.STATUS_FORMAT:
                usable = False
            elif status['version'] != coverage.__version__:
                usable = False

        if usable:
            self.files = status['files']
            self.settings = status['settings']
        else:
            self.reset()

    def write(self, directory):
        """Write the current status to `directory`."""
        status_file = os.path.join(directory, self.STATUS_FILE)
        status = {
            'format': self.STATUS_FORMAT,
            'version': coverage.__version__,
            'settings': self.settings,
            'files': self.files,
            }
        fout = open(status_file, "wb")
        try:
            pickle.dump(status, fout)
        finally:
            fout.close()

    def settings_hash(self):
        """Get the hash of the coverage.py settings."""
        return self.settings

    def set_settings_hash(self, settings):
        """Set the hash of the coverage.py settings."""
        self.settings = settings

    def file_hash(self, fname):
        """Get the hash of `fname`'s contents."""
        return self.files.get(fname, {}).get('hash', '')

    def set_file_hash(self, fname, val):
        """Set the hash of `fname`'s contents."""
        self.files.setdefault(fname, {})['hash'] = val

    def index_info(self, fname):
        """Get the information for index.html for `fname`."""
        return self.files.get(fname, {}).get('index', {})

    def set_index_info(self, fname, info):
        """Set the information for index.html for `fname`."""
        self.files.setdefault(fname, {})['index'] = info

# Helpers for templates and generating HTML

def escape(t):
    """HTML-escape the text in `t`."""
    return (t
            # Convert HTML special chars into HTML entities.
            .replace("&", "&").replace("<", "<").replace(">", ">")
            .replace("'", "'").replace('"', """)
            # Convert runs of spaces: "......" -> " . . ."
            .replace("  ", "  ")
            # To deal with odd-length runs, convert the final pair of spaces
            # so that "....." -> " .  ."
            .replace("  ", "  ")
        )

def spaceless(html):
    """Squeeze out some annoying extra space from an HTML string.

    Nicely-formatted templates mean lots of extra space in the result.
    Get rid of some.

    """
    html = re.sub(r">\s+
\n<p ", html)
    return html


# encoding: utf-8
"""Utilities for working with data structures like lists, dicts and tuples.
"""

#-----------------------------------------------------------------------------
#  Copyright (C) 2008-2011  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 types

#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------

def uniq_stable(elems):
    """uniq_stable(elems) -> list

    Return from an iterable, a list of all the unique elements in the input,
    but maintaining the order in which they first appear.

    A naive solution to this problem which just makes a dictionary with the
    elements as keys fails to respect the stability condition, since
    dictionaries are unsorted by nature.

    Note: All elements in the input must be valid dictionary keys for this
    routine to work, as it internally uses a dictionary for efficiency
    reasons."""

    unique = []
    unique_dict = {}
    for nn in elems:
        if nn not in unique_dict:
            unique.append(nn)
            unique_dict[nn] = None
    return unique

def sort_compare(lst1, lst2, inplace=1):
    """Sort and compare two lists.

    By default it does it in place, thus modifying the lists. Use inplace = 0
    to avoid that (at the cost of temporary copy creation)."""
    if not inplace:
        lst1 = lst1[:]
        lst2 = lst2[:]
    lst1.sort(); lst2.sort()
    return lst1 == lst2

def list2dict(lst):
    """Takes a list of (key,value) pairs and turns it into a dict."""

    dic = {}
    for k,v in lst: dic[k] = v
    return dic

def list2dict2(lst, default=''):
    """Takes a list and turns it into a dict.
    Much slower than list2dict, but more versatile. This version can take
    lists with sublists of arbitrary length (including sclars)."""

    dic = {}
    for elem in lst:
        if type(elem) in (types.ListType,types.TupleType):
            size = len(elem)
            if  size == 0:
                pass
            elif size == 1:
                dic[elem] = default
            else:
                k,v = elem[0], elem[1:]
                if len(v) == 1: v = v[0]
                dic[k] = v
        else:
            dic[elem] = default
    return dic

def flatten(seq):
    """Flatten a list of lists (NOT recursive, only works for 2d lists)."""

    return [x for subseq in seq for x in subseq]

def get_slice(seq, start=0, stop=None, step=1):
    """Get a slice of a sequence with variable step. Specify start,stop,step."""
    if stop == None:
        stop = len(seq)
    item = lambda i: seq[i]
    return map(item,xrange(start,stop,step))

def chop(seq, size):
    """Chop a sequence into chunks of the given size."""
    chunk = lambda i: seq[i:i+size]
    return map(chunk,xrange(0,len(seq),size))


#!/usr/bin/env python
"""Check the MANIFEST.in file in a Python source package for completeness.

This script works by building a source distribution archive (by running
setup.py sdist), then checking the file list in the archive against the
file list in version control (Subversion, Git, Mercurial, Bazaar are
supported).

Since the first check can fail to catch missing MANIFEST.in entries when
you've got the right setuptools version control system support plugins
installed, the script copies all the versioned files into a temporary
directory and building the source distribution again.  This also avoids issues
with stale egg-info/SOURCES.txt files that may cause files not mentioned in
MANIFEST.in to be included nevertheless.
"""
from __future__ import print_function

import argparse
import fnmatch
import locale
import os
import re
import shutil
import subprocess
import sys
import tarfile
import tempfile
import zipfile
from contextlib import contextmanager, closing
from xml.etree import cElementTree as ET

try:
    import ConfigParser
except ImportError:
    # Python 3.x
    import configparser as ConfigParser

__version__ = '0.24'
__author__ = 'Marius Gedminas <marius@gedmin.as>'
__licence__ = 'MIT'
__url__ = 'https://github.com/mgedmin/check-manifest'

class Failure(Exception):
    """An expected failure (as opposed to a bug in this script)."""

#
# User interface
#

VERBOSE = False

_to_be_continued = False
def _check_tbc():
    global _to_be_continued
    if _to_be_continued:
        print()
        _to_be_continued = False

def info(message):
    _check_tbc()
    print(message)

def info_begin(message):
    if not VERBOSE:
        return
    _check_tbc()
    sys.stdout.write(message)
    sys.stdout.flush()
    global _to_be_continued
    _to_be_continued = True

def info_continue(message):
    if not VERBOSE:
        return
    sys.stdout.write(message)
    sys.stdout.flush()
    global _to_be_continued
    _to_be_continued = True

def info_end(message):
    if not VERBOSE:
        return
    print(message)
    global _to_be_continued
    _to_be_continued = False

def error(message):
    _check_tbc()
    print(message, file=sys.stderr)

def warning(message):
    _check_tbc()
    print(message, file=sys.stderr)

def format_list(list_of_strings):
    return "\n".join("  " + s for s in list_of_strings)

def format_missing(missing_from_a, missing_from_b, name_a, name_b):
    res = []
    if missing_from_a:
        res.append("missing from %s:\n%s"
                   % (name_a, format_list(sorted(missing_from_a))))
    if missing_from_b:
        res.append("missing from %s:\n%s"
                   % (name_b, format_list(sorted(missing_from_b))))
    return '\n'.join(res)

#
# Filesystem/OS utilities
#

class CommandFailed(Failure):
    def __init__(self, command, status, output):
        Failure.__init__(self, "%s failed (status %s):\n%s" % (
                               command, status, output))

def run(command, encoding=None, decode=True):
    """Run a command [cmd, arg1, arg2, ...].

    Returns the output (stdout + stderr).

    Raises CommandFailed in cases of error.
    """
    if not encoding:
        encoding = locale.getpreferredencoding()
    try:
        pipe = subprocess.Popen(command, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
    except OSError as e:
        raise Failure("could not run %s: %s" % (command, e))
    output = pipe.communicate()[0]
    if decode:
        output = output.decode(encoding)
    status = pipe.wait()
    if status != 0:
        raise CommandFailed(command, status, output)
    return output

@contextmanager
def cd(directory):
    """Change the current working directory, temporarily.

    Use as a context manager: with cd(d): ...
    """
    old_dir = os.getcwd()
    try:
        os.chdir(directory)
        yield
    finally:
        os.chdir(old_dir)

@contextmanager
def mkdtemp(hint=''):
    """Create a temporary directory, then clean it up.

    Use as a context manager: with mkdtemp('-purpose'): ...
    """
    dirname = tempfile.mkdtemp(prefix='check-manifest-', suffix=hint)
    try:
        yield dirname
    finally:
        shutil.rmtree(dirname)

def copy_files(filelist, destdir):
    """Copy a list of files to destdir, preserving directory structure.

    File names should be relative to the current working directory.
    """
    for filename in filelist:
        destfile = os.path.join(destdir, filename)
        # filename should not be absolute, but let's double-check
        assert destfile.startswith(destdir + os.path.sep)
        destfiledir = os.path.dirname(destfile)
        if not os.path.isdir(destfiledir):
            os.makedirs(destfiledir)
        if os.path.isdir(filename):
            os.mkdir(destfile)
        else:
            shutil.copy2(filename, destfile)

def get_one_file_in(dirname):
    """Return the pathname of the one file in a directory.

    Raises if the directory has no files or more than one file.
    """
    files = os.listdir(dirname)
    if len(files) > 1:
        raise Failure('More than one file exists in %s:\n%s' %
                      (dirname, '\n'.join(sorted(files))))
    elif not files:
        raise Failure('No files found in %s' % dirname)
    return os.path.join(dirname, files[0])

def unicodify(filename):
    """Make sure filename is Unicode.

    Because the tarfile module on Python 2 doesn't return Unicode.
    """
    if isinstance(filename, bytes):
        return filename.decode(locale.getpreferredencoding())
    else:
        return filename

def get_archive_file_list(archive_filename):
    """Return the list of files in an archive.

    Supports .tar.gz and .zip.
    """
    if archive_filename.endswith('.zip'):
        with closing(zipfile.ZipFile(archive_filename)) as zf:
            return add_directories(zf.namelist())
    elif archive_filename.endswith(('.tar.gz', '.tar.bz2', '.tar')):
        with closing(tarfile.open(archive_filename)) as tf:
            return add_directories(list(map(unicodify, tf.getnames())))
    else:
        ext = os.path.splitext(archive_filename)[-1]
        raise Failure('Unrecognized archive type: %s' % ext)

def strip_toplevel_name(filelist):
    """Strip toplevel name from a file list.

        >>> strip_toplevel_name(['a', 'a/b', 'a/c', 'a/c/d'])
        ['b', 'c', 'c/d']

        >>> strip_toplevel_name(['a/b', 'a/c', 'a/c/d'])
        ['b', 'c', 'c/d']

    """
    if not filelist:
        return filelist
    prefix = filelist[0]
    if '/' in prefix:
        prefix = prefix.partition('/')[0] + '/'
        names = filelist
    else:
        prefix = prefix + '/'
        names = filelist[1:]
    for name in names:
        if not name.startswith(prefix):
            raise Failure("File doesn't have the common prefix (%s): %s"
                          % (name, prefix))
    return [name[len(prefix):] for name in names]

class VCS(object):

    @classmethod
    def detect(cls, location):
        return os.path.isdir(os.path.join(location, cls.metadata_name))

class Git(VCS):
    metadata_name = '.git'

    @staticmethod
    def get_versioned_files():
        """List all files versioned by git in the current directory."""
        # Git for Windows uses UTF-8 instead of the locale encoding.
        # Regular Git on sane POSIX systems uses the locale encoding
        encoding = 'UTF-8' if sys.platform == 'win32' else None
        output = run(['git', 'ls-files', '-z'], encoding=encoding)
        return add_directories(output.split('\0')[:-1])

class Mercurial(VCS):
    metadata_name = '.hg'

    @staticmethod
    def get_versioned_files():
        """List all files under Mercurial control in the current directory."""
        output = run(['hg', 'status', '-ncamd', '.'])
        return add_directories(output.splitlines())

class Bazaar(VCS):
    metadata_name = '.bzr'

    @staticmethod
    def get_versioned_files():
        """List all files versioned in Bazaar in the current directory."""
        output = run(['bzr', 'ls', '-VR'])
        return output.splitlines()

class Subversion(VCS):
    metadata_name = '.svn'

    @classmethod
    def get_versioned_files(cls):
        """List all files under SVN control in the current directory."""
        output = run(['svn', 'st', '-vq', '--xml'], decode=False)
        tree = ET.XML(output)
        return sorted(entry.get('path') for entry in tree.findall('.//entry')
                      if cls.is_interesting(entry))

    @staticmethod
    def is_interesting(entry):
        """Is this entry interesting?

        ``entry`` is an XML node representing one entry of the svn status
        XML output.  It looks like this::

            <entry path="unchanged.txt">
              <wc-status item="normal" revision="1" props="none">
                <commit revision="1">
                  <author>mg</author>
                  <date>2015-02-06T07:52:38.163516Z</date>
                </commit>
              </wc-status>
            </entry>

            <entry path="added-but-not-committed.txt">
              <wc-status item="added" revision="-1" props="none"></wc-status>
            </entry>

            <entry path="ext">
              <wc-status item="external" props="none"></wc-status>
            </entry>

            <entry path="unknown.txt">
              <wc-status props="none" item="unversioned"></wc-status>
            </entry>

        """
        if entry.get('path') == '.':
            return False
        status = entry.find('wc-status')
        if status is None:
            warning('svn status --xml parse error: <entry path="%s"> without'
                    ' <wc-status>' % entry.get('path'))
            return False
        # For SVN externals we get two entries: one mentioning the
        # existence of the external, and one about the status of the external.
        if status.get('item') in ('unversioned', 'external'):
            return False
        return True

def detect_vcs():
    """Detect the version control system used for the current directory."""
    location = os.path.abspath('.')
    while True:
        for vcs in Git, Mercurial, Bazaar, Subversion:
            if vcs.detect(location):
                return vcs
        parent = os.path.dirname(location)
        if parent == location:
            raise Failure("Couldn't find version control data"
                          " (git/hg/bzr/svn supported)")
        location = parent

def get_vcs_files():
    """List all files under version control in the current directory."""
    vcs = detect_vcs()
    return normalize_names(vcs.get_versioned_files())

def normalize_names(names):
    """Some VCS print directory names with trailing slashes.  Strip them.

    Easiest is to normalize the path.
    """
    return [os.path.normpath(name) for name in names]

def add_directories(names):
    """Git/Mercurial/zip files omit directories, let's add them back."""
    res = list(names)
    seen = set(names)
    for name in names:
        while True:
            name = os.path.dirname(name)
            if not name or name in seen:
                break
            res.append(name)
            seen.add(name)
    return sorted(res)

#
# Packaging logic
#

# it's fine if any of these are missing in the VCS or in the sdist
IGNORE = [
    'PKG-INFO',     # always generated
    '*.egg-info',   # always generated
    '*.egg-info/*', # always generated
    'setup.cfg',    # always generated, sometimes also kept in source control
    # it's not a problem if the sdist is lacking these files:
    '.hgtags', '.hgignore', '.gitignore', '.bzrignore',
    '.travis.yml',
    # it's convenient to ship compiled .mo files in sdists, but they shouldn't
    # be checked in
    '*.mo',
]

IGNORE_REGEXPS = [
    # Regular expressions for filename to ignore.  This is useful for
    # filename patterns where the '*' part must not search in
    # directories.
]

WARN_ABOUT_FILES_IN_VCS = [
    # generated files should not be committed into the VCS
    'PKG-INFO',
    '*.egg-info',
    '*.mo',
    '*.py[co]',
    '*.so',
    '*.pyd',
    '*~',
    '.*.sw[po]',
    '.#*',
]

_sep = r'\\' if os.path.sep == '\\' else os.path.sep

SUGGESTIONS = [(re.compile(pattern.replace('/', _sep)), suggestion) for pattern, suggestion in [
    # regexp -> suggestion
    ('^([^/]+[.](cfg|ini))$',       r'include \1'),
    ('^([.]travis[.]yml)$',         r'include \1'),
    ('^([.]coveragerc)$',           r'include \1'),
    ('^([A-Z]+)$',                  r'include \1'),
    ('^(Makefile)$',                r'include \1'),
    ('^[^/]+[.](txt|rst|py)$',      r'include *.\1'),
    ('^([a-zA-Z_][a-zA-Z_0-9]*)/'
     '.*[.](py|zcml|pt|mako|xml|html|txt|rst|css|png|jpg|dot|po|pot|mo|ui|desktop|bat)$',
                                    r'recursive-include \1 *.\2'),
    ('^([a-zA-Z_][a-zA-Z_0-9]*)(?:/.*)?/(Makefile)$',
                                    r'recursive-include \1 \2'),
    # catch-all rules that actually cover some of the above; somewhat
    # experimental: I fear false positives
    ('^([a-zA-Z_0-9]+)$',           r'include \1'),
    ('^[^/]+[.]([a-zA-Z_0-9]+)$',   r'include *.\1'),
    ('^([a-zA-Z_][a-zA-Z_0-9]*)/.*[.]([a-zA-Z_0-9]+)$',
                                    r'recursive-include \1 *.\2'),
]]

def read_config():
    """Read configuration from setup.cfg."""
    # XXX modifies global state, which is kind of evil
    config = ConfigParser.ConfigParser()
    config.read(['setup.cfg'])
    if not config.has_section('check-manifest'):
        return
    if (config.has_option('check-manifest', 'ignore-default-rules')
            and config.getboolean('check-manifest', 'ignore-default-rules')):
        del IGNORE[:]
    if config.has_option('check-manifest', 'ignore'):
        patterns = [p.strip() for p in config.get('check-manifest',
                                                  'ignore').splitlines()]
        IGNORE.extend(p for p in patterns if p)

def read_manifest():
    """Read existing configuration from MANIFEST.in.

    We use that to ignore anything the MANIFEST.in ignores.
    """
    # XXX modifies global state, which is kind of evil
    if not os.path.isfile('MANIFEST.in'):
        return
    with open('MANIFEST.in') as manifest:
        contents = manifest.read()
    ignore, ignore_regexps = _get_ignore_from_manifest(contents)
    IGNORE.extend(ignore)
    IGNORE_REGEXPS.extend(ignore_regexps)

def _glob_to_regexp(pat):
    """Compile a glob pattern into a regexp.

    We need to do this because fnmatch allows * to match /, which we
    don't want.  E.g. an MANIFEST.in exclude of 'dirname/*css' should
    match 'dirname/foo.css' but not 'dirname/subdir/bar.css'.
    """
    pat = fnmatch.translate(pat)
    # Note that distutils in Python 2.6 has a buggy glob_to_re in
    # distutils.filelist -- it converts '*.cfg' to '[^/]*cfg' instead
    # of '[^\\]*cfg' on Windows.
    sep = r'\\\\' if os.path.sep == '\\' else os.path.sep
    return re.sub(r'((?<!\\)(\\\\)*)\.', r'\1[^%s]' % sep, pat)

def _get_ignore_from_manifest(contents):
    """Gather the various ignore patterns from MANIFEST.in.

    'contents' should be a string, which may contain newlines.

    Returns a list of standard ignore patterns and a list of regular
    expressions to ignore.
    """
    ignore = []
    ignore_regexps = []
    for line in contents.splitlines():
        try:
            cmd, rest = line.split(None, 1)
        except ValueError:
            # no whitespace, so not interesting
            continue
        for part in rest.split():
            # distutils enforces these warnings on Windows only
            if part.startswith('/'):
                warning("ERROR: Leading slashes are not allowed in MANIFEST.in on Windows: %s" % part)
            if part.endswith('/'):
                warning("ERROR: Trailing slashes are not allowed in MANIFEST.in on Windows: %s" % part)
        if cmd == 'exclude':
            # An exclude of 'dirname/*css' can match 'dirname/foo.css'
            # but not 'dirname/subdir/bar.css'.  We need a regular
            # expression for that, since fnmatch doesn't pay attention to
            # directory separators.
            for pat in rest.split():
                if '*' in pat or '?' in pat or '[!' in pat:
                    ignore_regexps.append(_glob_to_regexp(pat))
                else:
                    # No need for special handling.
                    ignore.append(pat)
        elif cmd == 'global-exclude':
            ignore.extend(rest.split())
        elif cmd == 'recursive-exclude':
            try:
                dirname, patterns = rest.split(None, 1)
            except ValueError:
                # Wrong MANIFEST.in line.
                warning("You have a wrong line in MANIFEST.in: %r\n"
                        "'recursive-exclude' expects <dir> <pattern1> "
                        "<pattern2> ..." % line)
                continue
            # Strip path separator for clarity.
            dirname = dirname.rstrip(os.path.sep)
            for pattern in patterns.split():
                if pattern.startswith('*'):
                    ignore.append(dirname + os.path.sep + pattern)
                else:
                    # 'recursive-exclude plone metadata.xml' should
                    # exclude plone/metadata.xml and
                    # plone/*/metadata.xml, where * can be any number
                    # of sub directories.  We could use a regexp, but
                    # two ignores seems easier.
                    ignore.append(dirname + os.path.sep + pattern)
                    ignore.append(dirname + os.path.sep + '*' + os.path.sep +
                                  pattern)
        elif cmd == 'prune':
            # rest is considered to be a directory name.  It should
            # not contain a path separator, as it actually has no
            # effect in that case, but that could differ per python
            # version.  We strip it here to avoid double separators.
            # XXX: mg: I'm not 100% sure the above is correct, AFAICS
            # all pythons from 2.6 complain if the path has a leading or
            # trailing slash -- on Windows, that is.
            rest = rest.rstrip('/\\')
            ignore.append(rest)
            ignore.append(rest + os.path.sep + '*')
    return ignore, ignore_regexps

def file_matches(filename, patterns):
    """Does this filename match any of the patterns?"""
    return any(fnmatch.fnmatch(filename, pat) for pat in patterns)

def file_matches_regexps(filename, patterns):
    """Does this filename match any of the regular expressions?"""
    return any(re.match(pat, filename) for pat in patterns)

def strip_sdist_extras(filelist):
    """Strip generated files that are only present in source distributions.

    We also strip files that are ignored for other reasons, like
    command line arguments, setup.cfg rules or MANIFEST.in rules.
    """
    return [name for name in filelist
            if not file_matches(name, IGNORE)
            and not file_matches_regexps(name, IGNORE_REGEXPS)]

def find_bad_ideas(filelist):
    """Find files matching WARN_ABOUT_FILES_IN_VCS patterns."""
    return [name for name in filelist
            if file_matches(name, WARN_ABOUT_FILES_IN_VCS)]

def find_suggestions(filelist):
    """Suggest MANIFEST.in patterns for missing files."""
    suggestions = set()
    unknowns = []
    for filename in filelist:
        if os.path.isdir(filename):
            # it's impossible to add empty directories via MANIFEST.in anyway,
            # and non-empty directories will be added automatically when we
            # specify patterns for files inside them
            continue
        for pattern, suggestion in SUGGESTIONS:
            m = pattern.match(filename)
            if m is not None:
                suggestions.add(pattern.sub(suggestion, filename))
                break
        else:
            unknowns.append(filename)
    return sorted(suggestions), unknowns

def is_package(source_tree='.'):
    """Is the directory the root of a Python package?

    Note: the term "package" here refers to a collection of files
    with a setup.py, not to a directory with an __init__.py.
    """
    return os.path.exists(os.path.join(source_tree, 'setup.py'))

def check_manifest(source_tree='.', create=False, update=False,
                   python=sys.executable):
    """Compare a generated source distribution with list of files in a VCS.

    Returns True if the manifest is fine.
    """
    all_ok = True
    python = os.path.abspath(python)  # in case it was relative
    with cd(source_tree):
        if not is_package():
            raise Failure('This is not a Python project (no setup.py).')
        read_config()
        read_manifest()
        info_begin("listing source files under version control")
        all_source_files = sorted(get_vcs_files())
        source_files = strip_sdist_extras(all_source_files)
        info_continue(": %d files and directories" % len(source_files))
        info_begin("building an sdist")
        with mkdtemp('-sdist') as tempdir:
            run([python, 'setup.py', 'sdist', '-d', tempdir])
            sdist_filename = get_one_file_in(tempdir)
            info_continue(": %s" % os.path.basename(sdist_filename))
            sdist_files = sorted(normalize_names(strip_sdist_extras(
                strip_toplevel_name(get_archive_file_list(sdist_filename)))))
            info_continue(": %d files and directories" % len(sdist_files))
        existing_source_files = list(filter(os.path.exists, all_source_files))
        missing_source_files = sorted(set(all_source_files) - set(existing_source_files))
        if missing_source_files:
            warning("some files listed as being under source control are missing:\n%s"
                    % format_list(missing_source_files))
        info_begin("copying source files to a temporary directory")
        with mkdtemp('-sources') as tempsourcedir:
            copy_files(existing_source_files, tempsourcedir)
            if os.path.exists('MANIFEST.in') and 'MANIFEST.in' not in source_files:
                # See https://github.com/mgedmin/check-manifest/issues/7
                # if do this, we will emit a warning about MANIFEST.in not
                # being in source control, if we don't do this, the user
                # gets confused about their new manifest rules being
                # ignored.
                copy_files(['MANIFEST.in'], tempsourcedir)
            if 'setup.py' not in source_files:
                # See https://github.com/mgedmin/check-manifest/issues/46
                # if do this, we will emit a warning about setup.py not
                # being in source control, if we don't do this, the user
                # gets a scary error
                copy_files(['setup.py'], tempsourcedir)
            info_begin("building a clean sdist")
            with cd(tempsourcedir):
                with mkdtemp('-sdist') as tempdir:
                    run([python, 'setup.py', 'sdist', '-d', tempdir])
                    sdist_filename = get_one_file_in(tempdir)
                    info_continue(": %s" % os.path.basename(sdist_filename))
                    clean_sdist_files = sorted(normalize_names(strip_sdist_extras(
                        strip_toplevel_name(get_archive_file_list(sdist_filename)))))
                    info_continue(": %d files and directories" % len(clean_sdist_files))
        missing_from_manifest = set(source_files) - set(clean_sdist_files)
        missing_from_VCS = set(sdist_files + clean_sdist_files) - set(source_files)
        if not missing_from_manifest and not missing_from_VCS:
            info("lists of files in version control and sdist match")
        else:
            error("lists of files in version control and sdist do not match!\n%s"
                  % format_missing(missing_from_VCS, missing_from_manifest,
                                   "VCS", "sdist"))
            suggestions, unknowns = find_suggestions(missing_from_manifest)
            user_asked_for_help = update or (create and not
                                                os.path.exists('MANIFEST.in'))
            if 'MANIFEST.in' not in existing_source_files:
                if suggestions and not user_asked_for_help:
                    info("no MANIFEST.in found; you can run 'check-manifest -c' to create one")
                else:
                    info("no MANIFEST.in found")
            if suggestions:
                info("suggested MANIFEST.in rules:\n%s"
                     % format_list(suggestions))
                if user_asked_for_help:
                    existed = os.path.exists('MANIFEST.in')
                    with open('MANIFEST.in', 'a') as f:
                        if not existed:
                            info("creating MANIFEST.in")
                        else:
                            info("updating MANIFEST.in")
                            f.write('\n# added by check_manifest.py\n')
                        f.write('\n'.join(suggestions) + '\n')
                    if unknowns:
                        info("don't know how to come up with rules matching\n%s"
                             % format_list(unknowns))
            elif user_asked_for_help:
                info("don't know how to come up with rules"
                     " matching any of the files, sorry!")
            all_ok = False
        bad_ideas = find_bad_ideas(all_source_files)
        if bad_ideas:
            warning("you have %s in source control!\nthat's a bad idea:"
                    " auto-generated files should not be versioned"
                    % bad_ideas[0])
            if len(bad_ideas) > 1:
                warning("this also applies to the following:\n%s"
                        % format_list(bad_ideas[1:]))
            all_ok = False
    return all_ok

#
# Main script
#

def main():
    parser = argparse.ArgumentParser(
        description="Check a Python MANIFEST.in file for completeness",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('source_tree', default='.', nargs='?',
        help='location for the source tree')
    parser.add_argument('--version', action='version',
                        version='%(prog)s version ' + __version__)
    parser.add_argument('-v', '--verbose', action='store_true',
        help='more verbose output')
    parser.add_argument('-c', '--create', action='store_true',
        help='create a MANIFEST.in if missing')
    parser.add_argument('-u', '--update', action='store_true',
        help='append suggestions to MANIFEST.in (implies --create)')
    parser.add_argument('-p', '--python', default=sys.executable,
        help='use this Python interpreter for running setup.py sdist')
    parser.add_argument('--ignore', metavar='patterns', default=None,
                        help='ignore files/directories matching these'
                             ' comma-separated patterns')
    args = parser.parse_args()

    if args.ignore:
        IGNORE.extend(args.ignore.split(','))

    if args.verbose:
        global VERBOSE
        VERBOSE = True

    try:
        if not check_manifest(args.source_tree, create=args.create,
                              update=args.update, python=args.python):
            sys.exit(1)
    except Failure as e:
        error(str(e))
        sys.exit(2)

#
# zest.releaser integration
#

def zest_releaser_check(data):
    """Check the completeness of MANIFEST.in before the release.

    This is an entry point for zest.releaser.  See the documentation at
    http://zestreleaser.readthedocs.org/en/latest/entrypoints.html
    """
    from zest.releaser.utils import ask
    source_tree = data['workingdir']
    if not is_package(source_tree):
        # You can use zest.releaser on things that are not Python packages.
        # It's pointless to run check-manifest in those circumstances.
        # See https://github.com/mgedmin/check-manifest/issues/9 for details.
        return
    if not ask("Do you want to run check-manifest?"):
        return
    try:
        if not check_manifest(source_tree):
            if not ask("MANIFEST.in is not in order. "
                       " Do you want to continue despite that?", default=False):
                sys.exit(1)
    except Failure as e:
        error(str(e))
        if not ask("Something bad happened. "
                   " Do you want to continue despite that?", default=False):
            sys.exit(2)

if __name__ == '__main__':
    main()