summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2017-06-23 16:05:59 +0200
committerAnthon van der Neut <anthon@mnt.org>2017-06-23 16:05:59 +0200
commit3996b7964ecceae0c8d6adc21ad6cceb676a9e88 (patch)
tree1e458659cd3ecb1c30676f4e2bbfb11125e0823a
parent2c663616c8186ad3fd0d8b19c77c15b75095f170 (diff)
downloadruamel.std.argparse-3996b7964ecceae0c8d6adc21ad6cceb676a9e88.tar.gz
Added tag 0.7.1 for changeset dc56189a99f20.8.0
-rw-r--r--LICENSE12
-rw-r--r--__init__.py56
-rw-r--r--setup.py155
3 files changed, 155 insertions, 68 deletions
diff --git a/LICENSE b/LICENSE
index f6f753a..f2e500f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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:
diff --git a/setup.py b/setup.py
index 7112227..dbb552e 100644
--- a/setup.py
+++ b/setup.py
@@ -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