<NME> setup.py
<BEF> #!/usr/bin/python
import os
import sys
try:
    from setuptools import setup
    from setuptools.command.build_py import build_py
    from distutils.core import setup
from distutils.extension import Extension

version = '0.1.44'

try:
    with open('src/version.py', 'rU') as f:
def get_version():
    with open('VERSION', 'rb') as f:
        version = f.read().strip()

    # Append the Git commit id if this is a development version.
    if version.endswith('+'):
        import re
        import subprocess
        version = version[:-1]
        tag = 'v' + version
        desc = subprocess.check_output([
            'git', 'describe', '--dirty', '--long', '--match', tag,
        ])
        match = re.match(r'^v([^-]*)-([0-9]+)-(.*)$', desc)
        assert match is not None
        verpart, revpart, localpart = match.groups()
        assert verpart == version
        # Local part may be g0123abcd or g0123abcd-dirty.  Hyphens are
        # not kosher here, so replace by dots.
        localpart = localpart.replace('-', '.')
        full_version = '%s.post%s+%s' % (verpart, revpart, localpart)
    else:
        full_version = version

    # Strip the local part if there is one, to appease pkg_resources,
    # which handles only PEP 386, not PEP 440.
    if '+' in full_version:
        pkg_version = full_version[:full_version.find('+')]
    else:
        pkg_version = full_version

    # Sanity-check the result.  XXX Consider checking the full PEP 386
    # and PEP 440 regular expressions here?
    assert '-' not in full_version, '%r' % (full_version,)
    assert '-' not in pkg_version, '%r' % (pkg_version,)
    assert '+' not in pkg_version, '%r' % (pkg_version,)

    return pkg_version, full_version

def write_version_py(path):
    try:
        with open(path, 'rb') as f:
            version_old = f.read()
    except IOError:
        version_old = None
    version_new = '__version__ = %r\n' % (full_version,)
    if version_old != version_new:
        print 'writing %s' % (path,)
        with open(path, 'wb') as f:
            f.write(version_new)

pkg_version, full_version = get_version()

try:
    from Cython.Distutils import build_ext
except ImportError:
    import distutils.command.build_ext
    class build_ext(distutils.command.build_ext.build_ext):
        def build_extension(self, extension):
            raise Exception('Cython is unavailable to compile .pyx files.')


try:
    import numpy
except ImportError:
    numpy_includes = []
else:
    numpy_includes = [numpy.get_include()]


# http://stackoverflow.com/a/18992595
ON_LINUX = 'linux' in sys.platform
if ON_LINUX:
    has_ccache = os.system('which ccache >/dev/null 2>/dev/null') == 0
    if has_ccache:
        os.environ['CC'] = 'ccache gcc'

# http://stackoverflow.com/a/13176803
# monkey-patch for parallel compilation
import multiprocessing
import multiprocessing.pool
def parallelCCompile(self, sources, output_dir=None, macros=None,
        include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None,
        depends=None):
    # XXX Kludge to work around distutils bug that passes all
    # CPython's C compiler flags to the C++ compiler, even ones that
    # don't make sense like -Wstrict-prototypes.
    nocxxflags = [
        '-Wstrict-prototypes',
    ]
    for flag in nocxxflags:
        if flag in self.compiler_so:
            self.compiler_so.remove(flag)
    # those lines are copied from distutils.ccompiler.CCompiler directly
    macros, objects, extra_postargs, pp_opts, build = \
            self._setup_compile(output_dir, macros, include_dirs, sources,
                    depends, extra_postargs)
    cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
    # parallel code
    N_cores = multiprocessing.cpu_count()
    def _single_compile(obj):
        try: src, ext = build[obj]
        except KeyError: return
        self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
    # convert to list, imap is evaluated on-demand
    list(multiprocessing.pool.ThreadPool(N_cores).imap(_single_compile,objects))
    return objects
#
import distutils.ccompiler
distutils.ccompiler.CCompiler.compile=parallelCCompile

def generate_sources(dir_files_tuples):
    sources = []
    for dir, files in dir_files_tuples:
        full_files = [
                os.path.join(dir, file)
                for file in files
                ]
        sources.extend(full_files)
    return sources


# make sure cwd is correct
this_file = os.path.abspath(__file__)
this_dir = os.path.split(this_file)[0]
os.chdir(this_dir)


# locations
pyx_src_dir = 'src/cython_code'
cpp_src_dir = 'cpp_code/src'
include_dirs = ['cpp_code/include/CrossCat'] \
    + numpy_includes \
    + []


# specify sources
ContinuousComponentModel_pyx_sources = ['ContinuousComponentModel.pyx']
ContinuousComponentModel_cpp_sources = [
    'ComponentModel.cpp',
    'ContinuousComponentModel.cpp',
    'RandomNumberGenerator.cpp',
    'numerics.cpp',
    'utils.cpp',
    'weakprng.cpp',
]
ContinuousComponentModel_sources = generate_sources([
    (pyx_src_dir, ContinuousComponentModel_pyx_sources),
    (cpp_src_dir, ContinuousComponentModel_cpp_sources),
])
#
MultinomialComponentModel_pyx_sources = ['MultinomialComponentModel.pyx']
MultinomialComponentModel_cpp_sources = [
    'ComponentModel.cpp',
    'MultinomialComponentModel.cpp',
    'RandomNumberGenerator.cpp',
    'numerics.cpp',
    'utils.cpp',
    'weakprng.cpp',
]
MultinomialComponentModel_sources = generate_sources([
    (pyx_src_dir, MultinomialComponentModel_pyx_sources),
    (cpp_src_dir, MultinomialComponentModel_cpp_sources),
])
#
CyclicComponentModel_pyx_sources = ['CyclicComponentModel.pyx']
CyclicComponentModel_cpp_sources = [
    'ComponentModel.cpp',
    'CyclicComponentModel.cpp',
    'RandomNumberGenerator.cpp',
    'numerics.cpp',
    'utils.cpp',
    'weakprng.cpp',
]
CyclicComponentModel_sources = generate_sources([
    (pyx_src_dir, CyclicComponentModel_pyx_sources),
    (cpp_src_dir, CyclicComponentModel_cpp_sources),
])
#
State_pyx_sources = ['State.pyx']
State_cpp_sources = [
    'Cluster.cpp',
    'ComponentModel.cpp',
    'ContinuousComponentModel.cpp',
    'CyclicComponentModel.cpp',
    'DateTime.cpp',
    'MultinomialComponentModel.cpp',
    'RandomNumberGenerator.cpp',
    'State.cpp',
    'View.cpp',
    'numerics.cpp',
    'utils.cpp',
    'weakprng.cpp',
]
State_sources = generate_sources([
    (pyx_src_dir, State_pyx_sources),
    (cpp_src_dir, State_cpp_sources),
])


# create exts
ContinuousComponentModel_ext = Extension(
    'crosscat.cython_code.ContinuousComponentModel',
    extra_compile_args = [],
    sources=ContinuousComponentModel_sources,
    include_dirs=include_dirs,
    language='c++',
)
MultinomialComponentModel_ext = Extension(
    'crosscat.cython_code.MultinomialComponentModel',
    extra_compile_args = [],
    sources=MultinomialComponentModel_sources,
    include_dirs=include_dirs,
    language='c++',
)
CyclicComponentModel_ext = Extension(
    'crosscat.cython_code.CyclicComponentModel',
    extra_compile_args = [],
    sources=CyclicComponentModel_sources,
    include_dirs=include_dirs,
    language='c++',
)
State_ext = Extension(
    'crosscat.cython_code.State',
    extra_compile_args = [],
    sources=State_sources,
    include_dirs=include_dirs,
    language='c++',
)
#
ext_modules = [
    CyclicComponentModel_ext,
    ContinuousComponentModel_ext,
    MultinomialComponentModel_ext,
    State_ext,
]

packages = [
    'crosscat',
    'crosscat.utils',
    'crosscat.cython_code',
    'crosscat.tests',
    'crosscat.tests.component_model_extensions',
    'crosscat.tests.unit_tests',
]


# fall back long description if the README is missing
long_description = 'CrossCat is a domain-general, Bayesian method for analyzing ' +\
    'high-dimensional data tables. CrossCat estimates the full joint distribution ' +\
    'over the variables in the table from the data, via approximate inference in ' +\
    'a hierarchical, nonparametric Bayesian model, and provides efficient samplers ' +\
    'for every conditional distribution. CrossCat combines strengths of ' +\
    'nonparametric mixture modeling and Bayesian network structure learning: it ' +\
    'can model any joint distribution given enough data by positing latent ' +\
    'variables, but also discovers independencies between the observable variables.'

if os.path.exists('README.rst'):
    long_description = open('README.rst').read()

class local_build_py(build_py):
    def run(self):
        write_version_py(version_py)
        build_py.run(self)

# Make sure the VERSION file in the sdist is exactly specified, even
# if it is a development version, so that we do not need to run git to
# discover it -- which won't work because there's no .git directory in
# the sdist.
class local_sdist(sdist):
    def make_release_tree(self, base_dir, files):
        import os
        sdist.make_release_tree(self, base_dir, files)
        version_file = os.path.join(base_dir, 'VERSION')
        print('updating %s' % (version_file,))
        # Write to temporary file first and rename over permanent not
        # just to avoid atomicity issues (not likely an issue since if
        # interrupted the whole sdist directory is only partially
        # written) but because the upstream sdist may have made a hard
        # link, so overwriting in place will edit the source tree.
        with open(version_file + '.tmp', 'wb') as f:
            f.write('%s\n' % (pkg_version,))
        os.rename(version_file + '.tmp', version_file)

# XXX These should be attributes of `setup', but helpful distutils
# doesn't pass them through when it doesn't know about them a priori.
version_py = 'src/version.py'

setup(
    name='crosscat',
    version=pkg_version,
    author='MIT Probabilistic Computing Project',
    author_email='bayesdb@mit.edu',
    license='Apache License, Version 2.0',
    description='A domain-general, Bayesian method for analyzing high-dimensional data tables',
    url='https://github.com/probcomp/crosscat',
    long_description=long_description,
    packages=packages,
    package_dir={
        'crosscat': 'src',
        'crosscat.cython_code': 'src/cython_code',
        'crosscat.tests': 'src/tests',
        'crosscat.tests.component_model_extensions':
            'src/tests/component_model_extensions',
        'crosscat.tests.unit_tests': 'src/tests/unit_tests',
        'crosscat.utils': 'src/utils',
    },
    tests_require=[
        'pytest',
    ],
    ext_modules=ext_modules,
    cmdclass={
        'build_ext': build_ext,
        'build_py': local_build_py,
        'sdist': local_sdist,
    },
    zip_safe=False,
)

<MSG> Bump version to 0.1.45 for previous merge.

<DFF> @@ -7,7 +7,7 @@ except ImportError:
     from distutils.core import setup
 from distutils.extension import Extension
 
-version = '0.1.44'
+version = '0.1.45'
 
 try:
     with open('src/version.py', 'rU') as f:

{'commit': "diff --git a/setup.py b/setup.py\nindex 6c51af37..59742c2c 100644\n--- a/setup.py\n+++ b/setup.py\n@@ -7,7 +7,7 @@ except ImportError:\n     from distutils.core import setup\n from distutils.extension import Extension\n \n-version = '0.1.44'\n+version = '0.1.45'\n \n try:\n     with open('src/version.py', 'rU') as f:\n", 'commit_subject': 'Bump version to 0.1.45 for previous merge.', 'repo_name': 'probcomp/crosscat', 'committer': {'name': 'Taylor R Campbell', 'email': '87d78e45e8242b44fa86c5fe294c2103d5389e6d@csail.mit.edu', 'time_sec': '1448050698', 'tz_offset': '0', 'date': {'seconds': '1448050698'}}, 'commit_tree': 'cfe0462c3ab157c4cdbb9de613b99b8974f879eb', 'commit_message': 'Bump version to 0.1.45 for previous merge.\n', 'language_name': 'Shell', 'license': 'apache-2.0', 'hunks': "@@ -7,7 +7,7 @@ except ImportError:\n     from distutils.core import setup\n from distutils.extension import Extension\n \n-version = '0.1.44'\n+version = '0.1.45'\n \n try:\n     with open('src/version.py', 'rU') as f:\n", 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/setup.py', 'tgt_file': 'b/setup.py', 'file_extension': '.py', 'before_file': "#!/usr/bin/python\nimport os\nimport sys\ntry:\n    from setuptools import setup\n    from setuptools.command.build_py import build_py\n    from distutils.core import setup\nfrom distutils.extension import Extension\n\nversion = '0.1.44'\n\ntry:\n    with open('src/version.py', 'rU') as f:\ndef get_version():\n    with open('VERSION', 'rb') as f:\n        version = f.read().strip()\n\n    # Append the Git commit id if this is a development version.\n    if version.endswith('+'):\n        import re\n        import subprocess\n        version = version[:-1]\n        tag = 'v' + version\n        desc = subprocess.check_output([\n            'git', 'describe', '--dirty', '--long', '--match', tag,\n        ])\n        match = re.match(r'^v([^-]*)-([0-9]+)-(.*)$', desc)\n        assert match is not None\n        verpart, revpart, localpart = match.groups()\n        assert verpart == version\n        # Local part may be g0123abcd or g0123abcd-dirty.  Hyphens are\n        # not kosher here, so replace by dots.\n        localpart = localpart.replace('-', '.')\n        full_version = '%s.post%s+%s' % (verpart, revpart, localpart)\n    else:\n        full_version = version\n\n    # Strip the local part if there is one, to appease pkg_resources,\n    # which handles only PEP 386, not PEP 440.\n    if '+' in full_version:\n        pkg_version = full_version[:full_version.find('+')]\n    else:\n        pkg_version = full_version\n\n    # Sanity-check the result.  XXX Consider checking the full PEP 386\n    # and PEP 440 regular expressions here?\n    assert '-' not in full_version, '%r' % (full_version,)\n    assert '-' not in pkg_version, '%r' % (pkg_version,)\n    assert '+' not in pkg_version, '%r' % (pkg_version,)\n\n    return pkg_version, full_version\n\ndef write_version_py(path):\n    try:\n        with open(path, 'rb') as f:\n            version_old = f.read()\n    except IOError:\n        version_old = None\n    version_new = '__version__ = %r\\n' % (full_version,)\n    if version_old != version_new:\n        print 'writing %s' % (path,)\n        with open(path, 'wb') as f:\n            f.write(version_new)\n\npkg_version, full_version = get_version()\n\ntry:\n    from Cython.Distutils import build_ext\nexcept ImportError:\n    import distutils.command.build_ext\n    class build_ext(distutils.command.build_ext.build_ext):\n        def build_extension(self, extension):\n            raise Exception('Cython is unavailable to compile .pyx files.')\n\n\ntry:\n    import numpy\nexcept ImportError:\n    numpy_includes = []\nelse:\n    numpy_includes = [numpy.get_include()]\n\n\n# http://stackoverflow.com/a/18992595\nON_LINUX = 'linux' in sys.platform\nif ON_LINUX:\n    has_ccache = os.system('which ccache >/dev/null 2>/dev/null') == 0\n    if has_ccache:\n        os.environ['CC'] = 'ccache gcc'\n\n# http://stackoverflow.com/a/13176803\n# monkey-patch for parallel compilation\nimport multiprocessing\nimport multiprocessing.pool\ndef parallelCCompile(self, sources, output_dir=None, macros=None,\n        include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None,\n        depends=None):\n    # XXX Kludge to work around distutils bug that passes all\n    # CPython's C compiler flags to the C++ compiler, even ones that\n    # don't make sense like -Wstrict-prototypes.\n    nocxxflags = [\n        '-Wstrict-prototypes',\n    ]\n    for flag in nocxxflags:\n        if flag in self.compiler_so:\n            self.compiler_so.remove(flag)\n    # those lines are copied from distutils.ccompiler.CCompiler directly\n    macros, objects, extra_postargs, pp_opts, build = \\\n            self._setup_compile(output_dir, macros, include_dirs, sources,\n                    depends, extra_postargs)\n    cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)\n    # parallel code\n    N_cores = multiprocessing.cpu_count()\n    def _single_compile(obj):\n        try: src, ext = build[obj]\n        except KeyError: return\n        self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)\n    # convert to list, imap is evaluated on-demand\n    list(multiprocessing.pool.ThreadPool(N_cores).imap(_single_compile,objects))\n    return objects\n#\nimport distutils.ccompiler\ndistutils.ccompiler.CCompiler.compile=parallelCCompile\n\ndef generate_sources(dir_files_tuples):\n    sources = []\n    for dir, files in dir_files_tuples:\n        full_files = [\n                os.path.join(dir, file)\n                for file in files\n                ]\n        sources.extend(full_files)\n    return sources\n\n\n# make sure cwd is correct\nthis_file = os.path.abspath(__file__)\nthis_dir = os.path.split(this_file)[0]\nos.chdir(this_dir)\n\n\n# locations\npyx_src_dir = 'src/cython_code'\ncpp_src_dir = 'cpp_code/src'\ninclude_dirs = ['cpp_code/include/CrossCat'] \\\n    + numpy_includes \\\n    + []\n\n\n# specify sources\nContinuousComponentModel_pyx_sources = ['ContinuousComponentModel.pyx']\nContinuousComponentModel_cpp_sources = [\n    'ComponentModel.cpp',\n    'ContinuousComponentModel.cpp',\n    'RandomNumberGenerator.cpp',\n    'numerics.cpp',\n    'utils.cpp',\n    'weakprng.cpp',\n]\nContinuousComponentModel_sources = generate_sources([\n    (pyx_src_dir, ContinuousComponentModel_pyx_sources),\n    (cpp_src_dir, ContinuousComponentModel_cpp_sources),\n])\n#\nMultinomialComponentModel_pyx_sources = ['MultinomialComponentModel.pyx']\nMultinomialComponentModel_cpp_sources = [\n    'ComponentModel.cpp',\n    'MultinomialComponentModel.cpp',\n    'RandomNumberGenerator.cpp',\n    'numerics.cpp',\n    'utils.cpp',\n    'weakprng.cpp',\n]\nMultinomialComponentModel_sources = generate_sources([\n    (pyx_src_dir, MultinomialComponentModel_pyx_sources),\n    (cpp_src_dir, MultinomialComponentModel_cpp_sources),\n])\n#\nCyclicComponentModel_pyx_sources = ['CyclicComponentModel.pyx']\nCyclicComponentModel_cpp_sources = [\n    'ComponentModel.cpp',\n    'CyclicComponentModel.cpp',\n    'RandomNumberGenerator.cpp',\n    'numerics.cpp',\n    'utils.cpp',\n    'weakprng.cpp',\n]\nCyclicComponentModel_sources = generate_sources([\n    (pyx_src_dir, CyclicComponentModel_pyx_sources),\n    (cpp_src_dir, CyclicComponentModel_cpp_sources),\n])\n#\nState_pyx_sources = ['State.pyx']\nState_cpp_sources = [\n    'Cluster.cpp',\n    'ComponentModel.cpp',\n    'ContinuousComponentModel.cpp',\n    'CyclicComponentModel.cpp',\n    'DateTime.cpp',\n    'MultinomialComponentModel.cpp',\n    'RandomNumberGenerator.cpp',\n    'State.cpp',\n    'View.cpp',\n    'numerics.cpp',\n    'utils.cpp',\n    'weakprng.cpp',\n]\nState_sources = generate_sources([\n    (pyx_src_dir, State_pyx_sources),\n    (cpp_src_dir, State_cpp_sources),\n])\n\n\n# create exts\nContinuousComponentModel_ext = Extension(\n    'crosscat.cython_code.ContinuousComponentModel',\n    extra_compile_args = [],\n    sources=ContinuousComponentModel_sources,\n    include_dirs=include_dirs,\n    language='c++',\n)\nMultinomialComponentModel_ext = Extension(\n    'crosscat.cython_code.MultinomialComponentModel',\n    extra_compile_args = [],\n    sources=MultinomialComponentModel_sources,\n    include_dirs=include_dirs,\n    language='c++',\n)\nCyclicComponentModel_ext = Extension(\n    'crosscat.cython_code.CyclicComponentModel',\n    extra_compile_args = [],\n    sources=CyclicComponentModel_sources,\n    include_dirs=include_dirs,\n    language='c++',\n)\nState_ext = Extension(\n    'crosscat.cython_code.State',\n    extra_compile_args = [],\n    sources=State_sources,\n    include_dirs=include_dirs,\n    language='c++',\n)\n#\next_modules = [\n    CyclicComponentModel_ext,\n    ContinuousComponentModel_ext,\n    MultinomialComponentModel_ext,\n    State_ext,\n]\n\npackages = [\n    'crosscat',\n    'crosscat.utils',\n    'crosscat.cython_code',\n    'crosscat.tests',\n    'crosscat.tests.component_model_extensions',\n    'crosscat.tests.unit_tests',\n]\n\n\n# fall back long description if the README is missing\nlong_description = 'CrossCat is a domain-general, Bayesian method for analyzing ' +\\\n    'high-dimensional data tables. CrossCat estimates the full joint distribution ' +\\\n    'over the variables in the table from the data, via approximate inference in ' +\\\n    'a hierarchical, nonparametric Bayesian model, and provides efficient samplers ' +\\\n    'for every conditional distribution. CrossCat combines strengths of ' +\\\n    'nonparametric mixture modeling and Bayesian network structure learning: it ' +\\\n    'can model any joint distribution given enough data by positing latent ' +\\\n    'variables, but also discovers independencies between the observable variables.'\n\nif os.path.exists('README.rst'):\n    long_description = open('README.rst').read()\n\nclass local_build_py(build_py):\n    def run(self):\n        write_version_py(version_py)\n        build_py.run(self)\n\n# Make sure the VERSION file in the sdist is exactly specified, even\n# if it is a development version, so that we do not need to run git to\n# discover it -- which won't work because there's no .git directory in\n# the sdist.\nclass local_sdist(sdist):\n    def make_release_tree(self, base_dir, files):\n        import os\n        sdist.make_release_tree(self, base_dir, files)\n        version_file = os.path.join(base_dir, 'VERSION')\n        print('updating %s' % (version_file,))\n        # Write to temporary file first and rename over permanent not\n        # just to avoid atomicity issues (not likely an issue since if\n        # interrupted the whole sdist directory is only partially\n        # written) but because the upstream sdist may have made a hard\n        # link, so overwriting in place will edit the source tree.\n        with open(version_file + '.tmp', 'wb') as f:\n            f.write('%s\\n' % (pkg_version,))\n        os.rename(version_file + '.tmp', version_file)\n\n# XXX These should be attributes of `setup', but helpful distutils\n# doesn't pass them through when it doesn't know about them a priori.\nversion_py = 'src/version.py'\n\nsetup(\n    name='crosscat',\n    version=pkg_version,\n    author='MIT Probabilistic Computing Project',\n    author_email='bayesdb@mit.edu',\n    license='Apache License, Version 2.0',\n    description='A domain-general, Bayesian method for analyzing high-dimensional data tables',\n    url='https://github.com/probcomp/crosscat',\n    long_description=long_description,\n    packages=packages,\n    package_dir={\n        'crosscat': 'src',\n        'crosscat.cython_code': 'src/cython_code',\n        'crosscat.tests': 'src/tests',\n        'crosscat.tests.component_model_extensions':\n            'src/tests/component_model_extensions',\n        'crosscat.tests.unit_tests': 'src/tests/unit_tests',\n        'crosscat.utils': 'src/utils',\n    },\n    tests_require=[\n        'pytest',\n    ],\n    ext_modules=ext_modules,\n    cmdclass={\n        'build_ext': build_ext,\n        'build_py': local_build_py,\n        'sdist': local_sdist,\n    },\n    zip_safe=False,\n)\n", 'message': 'Bump version to 0.1.45 for previous merge.\n', 'author_name': 'Taylor R Campbell', 'lang': 'py'}