diff options
author | Anthon van der Neut <anthon@mnt.org> | 2017-06-23 16:05:59 +0200 |
---|---|---|
committer | Anthon van der Neut <anthon@mnt.org> | 2017-06-23 16:05:59 +0200 |
commit | 3996b7964ecceae0c8d6adc21ad6cceb676a9e88 (patch) | |
tree | 1e458659cd3ecb1c30676f4e2bbfb11125e0823a | |
parent | 2c663616c8186ad3fd0d8b19c77c15b75095f170 (diff) | |
download | ruamel.std.argparse-3996b7964ecceae0c8d6adc21ad6cceb676a9e88.tar.gz |
Added tag 0.7.1 for changeset dc56189a99f20.8.0
-rw-r--r-- | LICENSE | 12 | ||||
-rw-r--r-- | __init__.py | 56 | ||||
-rw-r--r-- | setup.py | 155 |
3 files changed, 155 insertions, 68 deletions
@@ -1,21 +1,21 @@ - The MIT License (MIT) + Copyright (c) 2007-2017 Anthon van der Neut, Ruamel bvba + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. diff --git a/__init__.py b/__init__.py index 099145f..2d475a2 100644 --- a/__init__.py +++ b/__init__.py @@ -2,52 +2,29 @@ from __future__ import print_function -# install_requires of ruamel.base is not really required but the old -# ruamel.base installed __init__.py, and thus a new version should -# be installed at some point _package_data = dict( - full_package_name="ruamel.std.argparse", - version_info=(0, 7, 1), + full_package_name='ruamel.std.argparse', + version_info=(0, 8, 0), + __version__='0.8.0', author='Anthon van der Neut', author_email='a.van.der.neut@ruamel.eu', - description="Enhancements to argparse: extra actions, subparser aliases, " - "smart formatter, a decorator based wrapper", + description='Enhancements to argparse: extra actions, subparser aliases, smart formatter, a decorator based wrapper', # NOQA entry_points=None, - install_requires=dict( - any=[] - ), + since=2007, + keywords='argparse enhanced', + install_requires=[], universal=True, ) - -# < from ruamel.util.new import _convert_version -def _convert_version(tup): - """create a PEP 386 pseudo-format conformant string from tuple tup""" - ret_val = str(tup[0]) # first is always digit - next_sep = "." # separator for next extension, can be "" or "." - for x in tup[1:]: - if isinstance(x, int): - ret_val += next_sep + str(x) - next_sep = '.' - continue - first_letter = x[0].lower() - next_sep = '' - if first_letter in 'abcr': - ret_val += 'rc' if first_letter == 'r' else first_letter - elif first_letter in 'pd': - ret_val += '.post' if first_letter == 'p' else '.dev' - return ret_val - - -# < version_info = _package_data['version_info'] -__version__ = _convert_version(version_info) - -del _convert_version +__version__ = _package_data['__version__'] import sys # NOQA +import os # NOQA import argparse # NOQA from argparse import ArgumentParser # NOQA +import glob # NOQA +from importlib import import_module # NOQA PY3 = sys.version_info[0] == 3 @@ -176,6 +153,7 @@ class ProgramBase(object): """ self._verbose = kw.pop('verbose', 0) aliases = kw.pop('aliases', 0) + self._full_package_name = kw.pop('full_package_name', None) self._parser = argparse.ArgumentParser(*args, **kw) if aliases and sys.version_info < (3,): self._parser.register('action', 'parsers', SubParsersAction) # NOQA @@ -184,6 +162,16 @@ class ProgramBase(object): self._sub_parsers = None methods_with_sub_parsers = [] # list to process, multilevel all_methods_with_sub_parsers = [] + file_names = glob.glob('*/__plug_in__.py') + if self._full_package_name: + for file_name in file_names: + dn, fn = file_name.split(os.sep) + fn = fn.replace('.py', '') + module = import_module( + '.' + dn + '.' + fn, + package=self._full_package_name, + ) + sp = module.load(self) # NOQA def add_subparsers(method_name_list, parser, level=0): if not method_name_list: @@ -15,6 +15,8 @@ from ast import parse # NOQA from setuptools import setup, Extension, Distribution # NOQA from setuptools.command import install_lib # NOQA +from setuptools.command.sdist import sdist as _sdist # NOQA +from wheel.bdist_wheel import bdist_wheel as _bdist_wheel # NOQA if __name__ != '__main__': raise NotImplementedError('should never include setup.py') @@ -231,6 +233,28 @@ class MyInstallLib(install_lib.install_lib): return alt_files +class MySdist(_sdist): + def initialize_options(self): + _sdist.initialize_options(self) + # because of unicode_literals + self.formats = [b'bztar'] if sys.version_info < (3, ) else ['bztar'] + dist_base = os.environ.get('PYDISTBASE') + fpn = getattr(getattr(self, 'nsp', self), 'full_package_name', None) + if fpn and dist_base: + print('setting distdir {}/{}'.format(dist_base, fpn)) + self.dist_dir = os.path.join(dist_base, fpn) + + +class MyBdistWheel(_bdist_wheel): + def initialize_options(self): + _bdist_wheel.initialize_options(self) + dist_base = os.environ.get('PYDISTBASE') + fpn = getattr(getattr(self, 'nsp', self), 'full_package_name', None) + if fpn and dist_base: + print('setting distdir {}/{}'.format(dist_base, fpn)) + self.dist_dir = os.path.join(dist_base, fpn) + + class InMemoryZipFile(object): def __init__(self, file_name=None): try: @@ -308,14 +332,16 @@ class NameSpacePackager(object): self.depth = self.full_package_name.count('.') self.nested = self._pkg_data.get('nested', False) self.command = None + self.python_version() self._pkg = [None, None] # required and pre-installable packages if sys.argv[0] == 'setup.py' and sys.argv[1] == 'install' and \ '--single-version-externally-managed' not in sys.argv: if os.environ.get('READTHEDOCS', None) == 'True': os.system('pip install .') sys.exit(0) - print('error: you have to install with "pip install ."') - sys.exit(1) + if not os.environ.get('RUAMEL_NO_PIP_INSTALL_CHECK', False): + print('error: you have to install with "pip install ."') + sys.exit(1) # If you only support an extension module on Linux, Windows thinks it # is pure. That way you would get pure python .whl files that take # precedence for downloading on Linux over source with compilable C @@ -351,14 +377,11 @@ class NameSpacePackager(object): if not os.path.isdir(d) or d == self._split[0] or d[0] in '._': continue # prevent sub-packages in namespace from being included - x = os.path.join(d, 'setup.py') - if os.path.exists(x): - if not os.path.exists(os.path.join(d, 'tox.ini')): - print('\n>>>>> found "{0}" without tox.ini <<<<<\n' - ''.format(x)) - continue x = os.path.join(d, '__init__.py') if os.path.exists(x): + pd = _package_data(x) + if pd.get('nested', False): + continue self._split.append(self.full_package_name + '.' + d) if sys.version_info < (3, ): self._split = [(y.encode('utf-8') if isinstance(y, unicode) else y) @@ -403,6 +426,23 @@ class NameSpacePackager(object): fp.write('import pkg_resources\n' 'pkg_resources.declare_namespace(__name__)\n') + def python_version(self): + supported = self._pkg_data.get('supported') + if supported is None: + return + if len(supported) == 1: + minimum = supported[0] + else: + for x in supported: + if x[0] == sys.version_info[0]: + minimum = x + break + else: + return + if sys.version_info < minimum: + print('minimum python version(s): ' + str(supported)) + sys.exit(1) + def check(self): try: from pip.exceptions import InstallationError @@ -568,6 +608,8 @@ class NameSpacePackager(object): to be installed explicitly as they are not on PyPI install_requires should be dict, with keys 'any', 'py27' etc or a list (which is as if only 'any' was defined + + ToDo: update with: pep508 conditional dependencies """ if self._pkg[0] is None: self._pkg[0] = [] @@ -606,6 +648,19 @@ class NameSpacePackager(object): return self._pkg @property + def extras_require(self): + """dict of conditions -> extra packages informaton required for installation + as of setuptools 33 doing `package ; python_version<=2.7' in install_requires + still doesn't work + + https://www.python.org/dev/peps/pep-0508/ + https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies + https://hynek.me/articles/conditional-python-dependencies/ + """ + ep = self._pkg_data.get('extras_require') + return ep + + @property def data_files(self): df = self._pkg_data.get('data_files', []) if self.has_mit_lic(): @@ -628,8 +683,16 @@ class NameSpacePackager(object): @property def ext_modules(self): - """check if the C module can be build by trying to compile a small - program against the libyaml development library""" + """check if all modules specified in the value for 'ext_modules' can be build + that value (if not None) is a list of dicts with 'name', 'src', 'lib' + Optional 'test' can be used to make sure trying to compile will work on the host + + creates and return the external modules as Extensions, unless that + is not necessary at all for the action (like --version) + + test existence of compiler by using export CC=nonexistent; export CXX=nonexistent + """ + if hasattr(self, '_ext_modules'): return self._ext_modules if '--version' in sys.argv: @@ -638,12 +701,32 @@ class NameSpacePackager(object): return None if sys.platform == "win32" and not self._pkg_data.get('win32bin'): return None + if sys.platform == "win32": + if os.getenv("RUAMEL_FORCE_EXT_BUILD") is None: + return None try: plat = sys.argv.index('--plat-name') if 'win' in sys.argv[plat + 1]: return None except ValueError: pass + self._ext_modules = [] + no_test_compile = False + if '--restructuredtext' in sys.argv: + no_test_compile = True + elif 'sdist' in sys.argv: + no_test_compile = True + if no_test_compile: + for target in self._pkg_data.get('ext_modules', []): + ext = Extension( + self.pn(target['name']), + sources=[self.pn(x) for x in target['src']], + libraries=[self.pn(x) for x in target.get('lib')], + ) + self._ext_modules.append(ext) + return self._ext_modules + + print('sys.argv', sys.argv) import tempfile import shutil from textwrap import dedent @@ -652,16 +735,13 @@ class NameSpacePackager(object): import distutils.ccompiler from distutils.errors import CompileError, LinkError - self._ext_modules = [] for target in self._pkg_data.get('ext_modules', []): # list of dicts - test_code = target.get('test') - libraries = [self.pn(x) for x in target.get('lib')] ext = Extension( self.pn(target['name']), sources=[self.pn(x) for x in target['src']], - libraries=libraries, + libraries=[self.pn(x) for x in target.get('lib')], ) - if not test_code: + if 'test' not in target: # no test just hope it works self._ext_modules.append(ext) continue # write a temporary .c file to compile @@ -669,24 +749,30 @@ class NameSpacePackager(object): try: tmp_dir = tempfile.mkdtemp(prefix='tmp_ruamel_') bin_file_name = 'test' + self.pn(target['name']) + print('test compiling', bin_file_name) file_name = os.path.join(tmp_dir, bin_file_name + '.c') - with open(file_name, 'w') as fp: + with open(file_name, 'w') as fp: # write source fp.write(c_code) - # and try to compile it compiler = distutils.ccompiler.new_compiler() assert isinstance(compiler, distutils.ccompiler.CCompiler) + # do any platform specific initialisations distutils.sysconfig.customize_compiler(compiler) - + # make sure you can reach header files because compile does change dir + compiler.add_include_dir(os.getcwd()) + if sys.version_info < (3, ): + tmp_dir = tmp_dir.encode('utf-8') + # used to be a different directory, not necessary + compile_out_dir = tmp_dir try: compiler.link_executable( compiler.compile( [file_name], - output_dir='/', # as file_name has absolute prefix + output_dir=compile_out_dir, ), bin_file_name, output_dir=tmp_dir, - libraries=libraries, + libraries=ext.libraries, ) except CompileError: print('compile error:', file_name) @@ -696,7 +782,7 @@ class NameSpacePackager(object): continue self._ext_modules.append(ext) except Exception as e: # NOQA - # print('Exception:', e) + print('Exception:', e) pass finally: shutil.rmtree(tmp_dir) @@ -733,12 +819,16 @@ def main(): if dump_kw in sys.argv: import wheel import distutils - print('python: ', sys.version) - print('distutils:', distutils.__version__) - print('wheel: ', wheel.__version__) + import setuptools + print('python: ', sys.version) + print('setuptools:', setuptools.__version__) + print('distutils: ', distutils.__version__) + print('wheel: ', wheel.__version__) nsp = NameSpacePackager(pkg_data) nsp.check() nsp.create_dirs() + MySdist.nsp = nsp + MyBdistWheel.nsp = nsp kw = dict( name=nsp.full_package_name, namespace_packages=nsp.namespace_packages, @@ -747,25 +837,34 @@ def main(): url=nsp.url, author=nsp.author, author_email=nsp.author_email, - cmdclass={'install_lib': MyInstallLib}, + cmdclass=dict( + install_lib=MyInstallLib, + sdist=MySdist, + bdist_wheel=MyBdistWheel, + ), package_dir=nsp.package_dir, entry_points=nsp.entry_points(), description=nsp.description, install_requires=nsp.install_requires, + extras_require=nsp.extras_require, # available since setuptools 18.0 / 2015-06 license=nsp.license, classifiers=nsp.classifiers, keywords=nsp.keywords, package_data=nsp.package_data, ext_modules=nsp.ext_modules, ) + if '--version' not in sys.argv and ('--verbose' in sys.argv or dump_kw in sys.argv): for k in sorted(kw): v = kw[k] print(' "{0}": "{1}",'.format(k, v)) if dump_kw in sys.argv: sys.argv.remove(dump_kw) - with open('README.rst') as fp: - kw['long_description'] = fp.read() + try: + with open('README.rst') as fp: + kw['long_description'] = fp.read() + except: + pass if nsp.wheel(kw, setup): return for x in ['-c', 'egg_info', '--egg-base', 'pip-egg-info']: @@ -791,7 +890,7 @@ def main(): try_dir = os.path.dirname(try_dir) setup(**kw) if nsp.nested and sys.argv[:2] == ['-c', 'bdist_wheel']: - d = sys.argv[sys.argv.index('-d')+1] + d = sys.argv[sys.argv.index('-d') + 1] for x in os.listdir(d): if x.endswith('.whl'): # remove .pth file from the wheel |