"""
setuptools_extversion

Allows getting distribution version from external sources (e.g.: shell command,
Python function)
"""

import contextlib
import os
import pkg_resources
import subprocess
import sys


# True if we are running on Python 3.
PY3 = sys.version_info[0] == 3

if PY3: # pragma: no cover
    string_types = str,
else:
    string_types = basestring,


VERSION_PROVIDER_KEY = 'extversion'


def git(distribution):
    cmd = command(['git', 'describe', '--tags', '--dirty'])
    return cmd(distribution)


def hg(distribution):
    cmd = command([
        'hg', 'log', '-r .',
        '--template',
        '{latesttag}-{branch}-{latesttagdistance}-m{node|short}'])
    return cmd(distribution)


def read_file(filename, distribution):
    return open(filename).read().strip()


def version_calc(dist, attr, value):
    """
    Handler for parameter to setup(extversion=value)
    """

    if attr == VERSION_PROVIDER_KEY:
        if callable(value):
            extversion = value
        elif hasattr(value, 'get'):
            if value.get('command'):
                extversion = command(value.get('command'), shell=True)
            if value.get('function'):
                extversion = function(
                    value.get('function'),
                    *value.get('args', ()),
                    **value.get('kwargs', {}))
        elif isinstance(value, string_types):
            if ':' in value:
                extversion = function(value)
            elif value.startswith('`'):
                cmd = value[1:-1]
                extversion = command(cmd, shell=True)
        else:
            raise Exception('Unknown type for %s = %r' % (attr, value))

        setup_py_dir = os.path.dirname(sys.argv[0])

        with chdir(setup_py_dir):
            dist.metadata.version = extversion(distribution=dist)


@contextlib.contextmanager
def chdir(new_dir):
    if new_dir:
        old_dir = os.curdir
        os.chdir(new_dir)
    yield
    if new_dir:
        os.chdir(old_dir)


class command(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __call__(self, distribution):
        return subprocess.check_output(*self.args, **self.kwargs).strip()


class function(object):
    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __call__(self, *args, **kwargs):
        if isinstance(self.func, basestring):
            ep = pkg_resources.EntryPoint.parse('x=' + self.func)
            self.func = ep.load(False)
            args = list(self.args + args)
            kwargs = dict(self.kwargs.items() + kwargs.items())
        return self.func(*args, **kwargs)

@@ -1410,84 +1410,8 @@
  if 
-':' in value:%0A                extversion = function(value)%0A            elif 
 valu
@@ -1509,32 +1509,95 @@
 md, shell=True)%0A
+            else:%0A                extversion = function(value)%0A
         else:%0A  
@@ -2236,25 +2236,352 @@
 ass 
-function(object):
+PkgResourcesResolver(object):%0A    parse = pkg_resources.EntryPoint.parse%0A%0A    def maybe_resolve(self, dotted):%0A        if isinstance(dotted, basestring):%0A            entry_point = self.parse('x=' + dotted)%0A            return entry_point.load(False)%0A%0A        return dotted%0A%0A%0Aclass function(object):%0A    default_resolver = PkgResourcesResolver()%0A
 %0A   
@@ -2605,16 +2605,31 @@
 f, func,
+ resolver=None,
  *args, 
@@ -2722,205 +2722,167 @@
 rgs%0A
-%0A    def __call__(self, *args, **kwargs):%0A        if isinstance(self.func, basestring):%0A            ep = pkg_resources.EntryPoint.parse('x=' + self.func)%0A            self.func = ep.load(False)%0A    
+        self.resolver = resolver or self.default_resolver%0A%0A    def __call__(self, *args, **kwargs):%0A        self.func = self.resolver.maybe_resolve(self.func)%0A
     
@@ -2915,20 +2915,16 @@
 + args)%0A
-    
         

Make resolving of functions pluggable