# -*- coding: ascii -*- # # Copyright 2007 - 2013 # Andr\xe9 Malo or his licensors, as applicable # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ =================== Main setup runner =================== This module provides a wrapper around the distutils core setup. """ __author__ = u"Andr\xe9 Malo" __docformat__ = "restructuredtext en" import ConfigParser as _config_parser from distutils import core as _core import os as _os import posixpath as _posixpath import sys as _sys from _setup import commands as _commands from _setup import data as _data from _setup import ext as _ext from _setup import util as _util from _setup import shell as _shell def check_python_version(impl, version_min, version_max): """ Check python version """ if impl == 'python': version_info = _sys.version_info elif impl == 'pypy': version_info = getattr(_sys, 'pypy_version_info', None) if not version_info: return elif impl == 'jython': if not 'java' in _sys.platform.lower(): return version_info = _sys.version_info else: raise AssertionError("impl not in ('python', 'pypy', 'jython')") pyversion = map(int, version_info[:3]) if version_min: min_required = \ map(int, '.'.join((version_min, '0.0.0')).split('.')[:3]) if pyversion < min_required: raise EnvironmentError("Need at least %s %s (vs. %s)" % ( impl, version_min, '.'.join(map(str, pyversion)) )) if version_max: max_required = map(int, version_max.split('.')) max_required[-1] += 1 if pyversion >= max_required: raise EnvironmentError("Need at max %s %s (vs. %s)" % ( impl, version_max, '.'.join(map(str, pyversion)) )) def find_description(docs): """ Determine the package description from DESCRIPTION :Parameters: `docs` : ``dict`` Docs config section :Return: Tuple of summary, description and license (``('summary', 'description', 'license')``) (all may be ``None``) :Rtype: ``tuple`` """ summary = None filename = docs.get('meta.summary', 'SUMMARY').strip() if filename and _os.path.isfile(filename): fp = open(filename) try: try: summary = fp.read().strip().splitlines()[0].rstrip() except IndexError: summary = '' finally: fp.close() description = None filename = docs.get('meta.description', 'DESCRIPTION').strip() if filename and _os.path.isfile(filename): fp = open(filename) try: description = fp.read().rstrip() finally: fp.close() if summary is None and description: from docutils import core summary = core.publish_parts( source=description, source_path=filename, writer_name='html', )['title'].encode('utf-8') return summary, description def find_classifiers(docs): """ Determine classifiers from CLASSIFIERS :return: List of classifiers (``['classifier', ...]``) :rtype: ``list`` """ filename = docs.get('meta.classifiers', 'CLASSIFIERS').strip() if filename and _os.path.isfile(filename): fp = open(filename) try: content = fp.read() finally: fp.close() content = [item.strip() for item in content.splitlines()] return [item for item in content if item and not item.startswith('#')] return [] def find_provides(docs): """ Determine provides from PROVIDES :return: List of provides (``['provides', ...]``) :rtype: ``list`` """ filename = docs.get('meta.provides', 'PROVIDES').strip() if filename and _os.path.isfile(filename): fp = open(filename) try: content = fp.read() finally: fp.close() content = [item.strip() for item in content.splitlines()] return [item for item in content if item and not item.startswith('#')] return [] def find_license(docs): """ Determine license from LICENSE :return: License text :rtype: ``str`` """ filename = docs.get('meta.license', 'LICENSE').strip() if filename and _os.path.isfile(filename): fp = open(filename) try: return fp.read().rstrip() finally: fp.close() return None def find_packages(manifest): """ Determine packages and subpackages """ packages = {} collect = manifest.get('packages.collect', '').split() lib = manifest.get('packages.lib', '.') try: sep = _os.path.sep except AttributeError: sep = _os.path.join('1', '2')[1:-1] for root in collect: for dirpath, _, filenames in _shell.walk(_os.path.join(lib, root)): if dirpath.find('.svn') >= 0: continue if '__init__.py' in filenames: packages[ _os.path.normpath(dirpath).replace(sep, '.') ] = None packages = packages.keys() packages.sort() return packages def find_data(name, docs): """ Determine data files """ result = [] if docs.get('extra', '').strip(): result.append(_data.Documentation(docs['extra'].split(), prefix='share/doc/%s' % name, )) if docs.get('examples.dir', '').strip(): tpl = ['recursive-include %s *' % docs['examples.dir']] if docs.get('examples.ignore', '').strip(): tpl.extend(["global-exclude %s" % item for item in docs['examples.ignore'].split() ]) strip = int(docs.get('examples.strip', '') or 0) result.append(_data.Documentation.from_templates(*tpl, **{ 'strip': strip, 'prefix': 'share/doc/%s' % name, 'preserve': 1, })) if docs.get('userdoc.dir', '').strip(): tpl = ['recursive-include %s *' % docs['userdoc.dir']] if docs.get('userdoc.ignore', '').strip(): tpl.extend(["global-exclude %s" % item for item in docs['userdoc.ignore'].split() ]) strip = int(docs.get('userdoc.strip', '') or 0) result.append(_data.Documentation.from_templates(*tpl, **{ 'strip': strip, 'prefix': 'share/doc/%s' % name, 'preserve': 1, })) if docs.get('apidoc.dir', '').strip(): tpl = ['recursive-include %s *' % docs['apidoc.dir']] if docs.get('apidoc.ignore', '').strip(): tpl.extend(["global-exclude %s" % item for item in docs['apidoc.ignore'].split() ]) strip = int(docs.get('apidoc.strip', '') or 0) result.append(_data.Documentation.from_templates(*tpl, **{ 'strip': strip, 'prefix': 'share/doc/%s' % name, 'preserve': 1, })) if docs.get('man', '').strip(): result.extend(_data.Manpages.dispatch(docs['man'].split())) return result def make_manifest(manifest, config, docs, kwargs): """ Create file list to pack up """ # pylint: disable = R0912 kwargs = kwargs.copy() kwargs['script_args'] = ['install'] kwargs['packages'] = list(kwargs.get('packages') or ()) + [ '_setup', '_setup.py2', '_setup.py3', ] + list(manifest.get('packages.extra', '').split() or ()) _core._setup_stop_after = "commandline" try: dist = _core.setup(**kwargs) finally: _core._setup_stop_after = None result = ['MANIFEST', 'PKG-INFO', 'setup.py'] + list(config) # TODO: work with default values: for key in ('classifiers', 'description', 'summary', 'provides', 'license'): filename = docs.get('meta.' + key, '').strip() if filename and _os.path.isfile(filename): result.append(filename) cmd = dist.get_command_obj("build_py") cmd.ensure_finalized() #from pprint import pprint; pprint(("build_py", cmd.get_source_files())) for item in cmd.get_source_files(): result.append(_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) )) cmd = dist.get_command_obj("build_ext") cmd.ensure_finalized() #from pprint import pprint; pprint(("build_ext", cmd.get_source_files())) for item in cmd.get_source_files(): result.append(_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) )) for ext in cmd.extensions: if ext.depends: result.extend([_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) ) for item in ext.depends]) cmd = dist.get_command_obj("build_clib") cmd.ensure_finalized() if cmd.libraries: #import pprint; pprint.pprint(("build_clib", cmd.get_source_files())) for item in cmd.get_source_files(): result.append(_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) )) for lib in cmd.libraries: if lib[1].get('depends'): result.extend([_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) ) for item in lib[1]['depends']]) cmd = dist.get_command_obj("build_scripts") cmd.ensure_finalized() #import pprint; pprint.pprint(("build_scripts", cmd.get_source_files())) if cmd.get_source_files(): for item in cmd.get_source_files(): result.append(_posixpath.sep.join( _os.path.normpath(item).split(_os.path.sep) )) cmd = dist.get_command_obj("install_data") cmd.ensure_finalized() #from pprint import pprint; pprint(("install_data", cmd.get_inputs())) try: strings = basestring except NameError: strings = (str, unicode) for item in cmd.get_inputs(): if isinstance(item, strings): result.append(item) else: result.extend(item[1]) for item in manifest.get('dist', '').split(): result.append(item) if _os.path.isdir(item): for filename in _shell.files(item): result.append(filename) result = dict([(item, None) for item in result]).keys() result.sort() return result def run(config=('package.cfg',), ext=None, script_args=None, manifest_only=0): """ Main runner """ if ext is None: ext = [] cfg = _util.SafeConfigParser() cfg.read(config) pkg = dict(cfg.items('package')) python_min = pkg.get('python.min') or None python_max = pkg.get('python.max') or None check_python_version('python', python_min, python_max) pypy_min = pkg.get('pypy.min') or None pypy_max = pkg.get('pypy.max') or None check_python_version('pypy', pypy_min, pypy_max) jython_min = pkg.get('jython.min') or None jython_max = pkg.get('jython.max') or None check_python_version('jython', jython_min, jython_max) manifest = dict(cfg.items('manifest')) try: docs = dict(cfg.items('docs')) except _config_parser.NoSectionError: docs = {} summary, description = find_description(docs) scripts = manifest.get('scripts', '').strip() or None if scripts: scripts = scripts.split() modules = manifest.get('modules', '').strip() or None if modules: modules = modules.split() keywords = docs.get('meta.keywords', '').strip() or None if keywords: keywords = keywords.split() revision = pkg.get('version.revision', '').strip() if revision: revision = "-r%s" % (revision,) kwargs = { 'name': pkg['name'], 'version': "%s%s" % ( pkg['version.number'], ["", "-dev%s" % (revision,)][_util.humanbool( 'version.dev', pkg.get('version.dev', 'false') )], ), 'provides': find_provides(docs), 'description': summary, 'long_description': description, 'classifiers': find_classifiers(docs), 'keywords': keywords, 'author': pkg['author.name'], 'author_email': pkg['author.email'], 'maintainer': pkg.get('maintainer.name'), 'maintainer_email': pkg.get('maintainer.email'), 'url': pkg.get('url.homepage'), 'download_url': pkg.get('url.download'), 'license': find_license(docs), 'package_dir': {'': manifest.get('packages.lib', '.')}, 'packages': find_packages(manifest), 'py_modules': modules, 'ext_modules': ext, 'scripts': scripts, 'script_args': script_args, 'data_files': find_data(pkg['name'], docs), 'cmdclass': { 'build' : _commands.Build, 'build_ext' : _commands.BuildExt, 'install' : _commands.Install, 'install_data': _commands.InstallData, 'install_lib' : _commands.InstallLib, } } for key in ('provides',): if key not in _core.setup_keywords: del kwargs[key] if manifest_only: return make_manifest(manifest, config, docs, kwargs) # monkey-patch crappy manifest writer away. from distutils.command import sdist sdist.sdist.get_file_list = sdist.sdist.read_manifest return _core.setup(**kwargs)