summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2015-08-28 17:30:13 +0200
committerAnthon van der Neut <anthon@mnt.org>2015-08-28 17:30:13 +0200
commit0a90fcd5c6708f5891f4fa781d4652949d328525 (patch)
tree8a679ab0d8b16be5d584787383ade4e8609cd103
parent17d25b0424aaac011ef4bb5fd343bd71419e3d12 (diff)
downloadruamel.std.argparse-0a90fcd5c6708f5891f4fa781d4652949d328525.tar.gz
json __init__.py
-rw-r--r--__init__.py25
-rw-r--r--setup.py213
2 files changed, 188 insertions, 50 deletions
diff --git a/__init__.py b/__init__.py
index 9479b85..d9504cf 100644
--- a/__init__.py
+++ b/__init__.py
@@ -5,16 +5,21 @@ 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, 6, 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", # NOQA
- entry_points=None,
- install_requires=['ruamel.base>=1.0.0'],
- universal=True,
-)
+
+null = None
+_package_data = { # JSON
+ "full_package_name": "ruamel.std.argparse",
+ "version_info": [0, 6, 1],
+ "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", # NOQA
+ "entry_points": null,
+ "universal": 1,
+ "install_requires": {
+ "any": ["ruamel.base>=1.0.0"],
+ "py26": ["argparse"] # tox needs this so it is difficult to test
+ }
+} # JSON
# < from ruamel.util.new import _convert_version
diff --git a/setup.py b/setup.py
index 6a2fc44..15518a7 100644
--- a/setup.py
+++ b/setup.py
@@ -11,39 +11,71 @@ if __name__ != '__main__':
full_package_name = None
+# parses python ( "= dict( )" ) or json ( "= { # JSON" )
def _package_data(fn):
data = {}
with open(fn) as fp:
parsing = False
+ lines = []
for line in fp.readlines():
if line.startswith('_package_data'):
- parsing = True
+ if 'dict(' in line:
+ parsing = 'python'
+ elif '# JSON' in line:
+ parsing = 'json'
+ lines.append('{\n')
+ else:
+ raise NotImplementedError
continue
if not parsing:
continue
- if line.startswith(')'):
- break
- if '# NOQA' in line:
- line = line.split('# NOQA', 1)[0].rstrip()
- k, v = [x.strip() for x in line.split('=', 1)]
- if v[-1] == ',':
- v = v[:-1]
- if v[0] in '\'"' and v[0] == v[-1]:
- data[k] = v[1:-1]
- elif v == 'None':
- data[k] = None
- elif v == 'True':
- data[k] = True
- elif v == 'False':
- data[k] = False
- elif v[0] == '(' and v[-1] == ')':
- data[k] = tuple([x.strip()[1:-1] if x[0] in '\'"' else int(x)
- for x in v[1:-1].split(', ')])
- elif v[0] == '[' and v[-1] == ']':
- data[k] = [x.strip()[1:-1] if x[0] in '\'"' else int(x)
- for x in v[1:-1].split(', ')]
+ if parsing == 'json':
+ x = line.rsplit("# ", 1)
+ if len(x) > 1:
+ if x[1].startswith('JSON'):
+ lines.append(x[0]+'\n')
+ import json
+ try:
+ data = json.loads(''.join(lines))
+ except ValueError:
+ w = len(str(len(lines)))
+ for i, line in enumerate(lines):
+ print('{0:{1}}: {2}'.format(
+ i+1, w, line), end='')
+ raise
+ break
+ elif not x[0].strip():
+ continue # empty line can have any comment
+ elif '"' not in x[1] and "'" not in x[1]:
+ # can't deal with quotes might be # in string
+ line = x[0] + '\n'
+ lines.append(line)
+ elif parsing == 'python':
+ if line.startswith(')'):
+ break
+ if '# NOQA' in line:
+ line = line.split('# NOQA', 1)[0].rstrip()
+ k, v = [x.strip() for x in line.split('=', 1)]
+ if v[-1] == ',':
+ v = v[:-1]
+ if v[0] in '\'"' and v[0] == v[-1]:
+ data[k] = v[1:-1]
+ elif v == 'None':
+ data[k] = None
+ elif v == 'True':
+ data[k] = True
+ elif v == 'False':
+ data[k] = False
+ elif v[0] == '(' and v[-1] == ')':
+ data[k] = tuple([x.strip()[1:-1] if x[0] in '\'"' else
+ int(x) for x in v[1:-1].split(', ')])
+ elif v[0] == '[' and v[-1] == ']':
+ data[k] = [x.strip()[1:-1] if x[0] in '\'"' else int(x)
+ for x in v[1:-1].split(', ')]
+ else:
+ print('Unknown: >>>>> {0!r} {1!r}'.format(k, v))
else:
- print('Unknown: >>>>> {0!r} {1!r}'.format(k, v))
+ raise NotImplementedError
return data
pkg_data = _package_data('__init__.py')
@@ -55,8 +87,9 @@ exclude_files = [
# # imports
import os
import sys
+import platform
-from setuptools import setup
+from setuptools import setup, Extension, Distribution # NOQA
from setuptools.command import install_lib
@@ -117,7 +150,7 @@ class NameSpacePackager(object):
def __init__(self, pkg_data):
assert isinstance(pkg_data, dict)
self._pkg_data = pkg_data
- self.full_package_name = self._pkg_data['full_package_name']
+ self.full_package_name = self.pn(self._pkg_data['full_package_name'])
self._split = None
self.depth = self.full_package_name.count('.')
self.command = None
@@ -131,6 +164,11 @@ class NameSpacePackager(object):
self.command = x
break
+ def pn(self, s):
+ if sys.version_info < (3, ) and isinstance(s, unicode):
+ return s.encode('utf-8')
+ return s
+
@property
def split(self):
"""split the full package name in list of compontents"""
@@ -143,6 +181,13 @@ class NameSpacePackager(object):
for d in os.listdir('.'):
if not os.path.isdir(d) or d == self._split[0] or d[0] == '_':
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):
self._split.append(self.full_package_name + '.' + d)
@@ -169,12 +214,14 @@ class NameSpacePackager(object):
with open(os.path.join(d, '__init__.py'), 'w') as fp:
fp.write('import pkg_resources\n'
'pkg_resources.declare_namespace(__name__)\n')
- os.symlink(
- # a.b gives a/b -> ..
- # a.b.c gives a/b/c -> ../..
- os.path.join(*['..'] * self.depth),
- os.path.join(*self.split[self.depth].split('.'))
- )
+ # not necessary if not using find_packages, recursive links
+ # are horrible anyway, slowing down pip
+ # os.symlink(
+ # # a.b gives a/b -> ..
+ # # a.b.c gives a/b/c -> ../..
+ # os.path.join(*['..'] * self.depth),
+ # os.path.join(*self.split[self.depth].split('.'))
+ # )
def check(self):
try:
@@ -272,12 +319,12 @@ class NameSpacePackager(object):
@property
def status(self):
# αβ
- status = self._pkg_data.get('status', 'β')
- if status == 'α':
+ status = self._pkg_data.get('status', u'β')
+ if status == u'α':
return (3, 'Alpha')
- elif status == 'β':
+ elif status == u'β':
return (4, 'Beta')
- elif 'stable' in status.lower():
+ elif u'stable' in status.lower():
return (5, 'Production/Stable')
raise NotImplementedError
@@ -287,15 +334,26 @@ class NameSpacePackager(object):
'Development Status :: {0} - {1}'.format(*self.status),
'Intended Audience :: Developers',
'License :: ' + ('Other/Proprietary License'
- if self._pkg_data.get('license') else
+ if self.pn(self._pkg_data.get('license')) else
'OSI Approved :: MIT License'),
'Operating System :: OS Independent',
'Programming Language :: Python',
- ]
+ ] + [self.pn(x) for x in self._pkg_data.get('classifiers', [])]
@property
def install_requires(self):
- return pkg_data.get('install_requires', [])
+ ir = self._pkg_data.get('install_requires', [])
+ if isinstance(ir, list):
+ return ir
+ # 'any' for all builds, 'py27' etc for specifics versions
+ res = ir.get('any', [])
+ implementation = platform.python_implementation()
+ if implementation == 'CPython':
+ pyver = 'py{0}{1}'.format(*sys.version_info)
+ elif implementation == 'PyPy':
+ pyver = 'pypy' if sys.version_info < (3, ) else 'pypy3'
+ res.extend(ir.get(pyver, []))
+ return res
@property
def data_files(self):
@@ -315,9 +373,83 @@ class NameSpacePackager(object):
# but don't install it
exclude_files.append('LICENSE')
if not df:
- return None
+ return {}
return {self.full_package_name: df}
+ @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"""
+ if hasattr(self, '_ext_modules'):
+ return self._ext_modules
+ if '--version' in sys.argv:
+ return None
+ # if sys.platform == "win32":
+ # return None
+ import tempfile
+ import shutil
+ from textwrap import dedent
+
+ import distutils.sysconfig
+ 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,
+ )
+ if not test_code:
+ self._ext_modules.append(ext)
+ continue
+ # write a temporary .c file to compile
+ c_code = dedent(target['test'])
+ try:
+ tmp_dir = tempfile.mkdtemp(prefix='tmp_ruamel_')
+ bin_file_name = 'test' + self.pn(target['name'])
+ file_name = os.path.join(tmp_dir, bin_file_name + '.c')
+ with open(file_name, 'w') as fp:
+ fp.write(c_code)
+
+ # and try to compile it
+ compiler = distutils.ccompiler.new_compiler()
+ assert isinstance(compiler, distutils.ccompiler.CCompiler)
+ distutils.sysconfig.customize_compiler(compiler)
+
+ try:
+ compiler.link_executable(
+ compiler.compile(
+ [file_name],
+ output_dir='/', # as file_name has absolute prefix
+ ),
+ bin_file_name,
+ output_dir=tmp_dir,
+ libraries=libraries,
+ )
+ except CompileError:
+ print('compile error:', file_name)
+ continue
+ except LinkError:
+ print('libyaml link error', file_name)
+ continue
+ self._ext_modules.append(ext)
+ except Exception as e: # NOQA
+ # print('Exception:', e)
+ pass
+ finally:
+ shutil.rmtree(tmp_dir)
+ return self._ext_modules
+
+ # 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
+ # if '--universal' in sys.argv:
+ # Distribution.is_pure = lambda *args: True
+ # Distribution.is_pure = lambda *args: False
def wheel(self, kw, setup):
"""temporary add setup.cfg if creating a wheel to include LICENSE file
https://bitbucket.org/pypa/wheel/issues/47
@@ -361,8 +493,9 @@ def main():
license=nsp.license,
classifiers=nsp.classifiers,
package_data=nsp.package_data,
+ ext_modules=nsp.ext_modules,
)
- if '--version' not in sys.argv:
+ if '--version' not in sys.argv or '--verbose' in sys.argv:
for k in sorted(kw):
v = kw[k]
print(k, '->', v)