summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2017-01-13 09:36:12 +0100
committerAnthon van der Neut <anthon@mnt.org>2017-01-13 09:36:12 +0100
commit1658aa32f7332c3757ff30e59d650e30a97f52ab (patch)
tree65c8cd331834c9c87f6ba748b053ca23ba2b15a0
parent0a90fcd5c6708f5891f4fa781d4652949d328525 (diff)
downloadruamel.std.argparse-1658aa32f7332c3757ff30e59d650e30a97f52ab.tar.gz
update setup, remove transition dependency on ruamel.yaml0.7.1
-rw-r--r--.hgignore2
-rw-r--r--Makefile3
-rw-r--r--__init__.py38
-rw-r--r--setup.py514
-rw-r--r--tox.ini5
5 files changed, 432 insertions, 130 deletions
diff --git a/.hgignore b/.hgignore
index dd78dbd..350f799 100644
--- a/.hgignore
+++ b/.hgignore
@@ -12,3 +12,5 @@ build
*.egg-info
.tox
ruamel
+.ruamel
+.cache
diff --git a/Makefile b/Makefile
index 594e80a..12eb858 100644
--- a/Makefile
+++ b/Makefile
@@ -15,3 +15,6 @@ updatereadme:
layout: pdf
cp README.pdf /data0/tmp/pdf
+
+owl: sdist
+ make owl_copy owl_devpi
diff --git a/__init__.py b/__init__.py
index d9504cf..099145f 100644
--- a/__init__.py
+++ b/__init__.py
@@ -5,21 +5,19 @@ 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
-
-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
+_package_data = dict(
+ full_package_name="ruamel.std.argparse",
+ version_info=(0, 7, 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",
+ entry_points=None,
+ install_requires=dict(
+ any=[]
+ ),
+ universal=True,
+)
# < from ruamel.util.new import _convert_version
@@ -47,8 +45,8 @@ __version__ = _convert_version(version_info)
del _convert_version
-import sys
-import argparse
+import sys # NOQA
+import argparse # NOQA
from argparse import ArgumentParser # NOQA
PY3 = sys.version_info[0] == 3
@@ -58,6 +56,10 @@ if PY3:
else:
string_types = basestring,
+store_true = 'store_true'
+store_false = 'store_false'
+append = 'append'
+
class SubParsersAction(argparse._SubParsersAction):
"""support aliases, based on differences of 3.3 and 2.7
@@ -176,7 +178,7 @@ class ProgramBase(object):
aliases = kw.pop('aliases', 0)
self._parser = argparse.ArgumentParser(*args, **kw)
if aliases and sys.version_info < (3,):
- self._parser.register('action', 'parsers', SubParsersAction)
+ self._parser.register('action', 'parsers', SubParsersAction) # NOQA
self._program_base_initialising = True
cls = self
self._sub_parsers = None
diff --git a/setup.py b/setup.py
index 15518a7..7112227 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,20 @@
# # header
# coding: utf-8
-from __future__ import print_function
+from __future__ import print_function, absolute_import, division, unicode_literals
+
+# # __init__.py parser
+
+import sys
+import os
+import datetime
+sys.path = [path for path in sys.path if path not in [os.getcwd(), '']]
+import platform # NOQA
+from _ast import * # NOQA
+from ast import parse # NOQA
+
+from setuptools import setup, Extension, Distribution # NOQA
+from setuptools.command import install_lib # NOQA
if __name__ != '__main__':
raise NotImplementedError('should never include setup.py')
@@ -10,88 +23,157 @@ if __name__ != '__main__':
full_package_name = None
+if __name__ != '__main__':
+ raise NotImplementedError('should never include setup.py')
-# parses python ( "= dict( )" ) or json ( "= { # JSON" )
+if sys.version_info < (3, ):
+ string_type = basestring
+else:
+ string_type = str
+
+
+if sys.version_info < (3, 4):
+ class Bytes():
+ pass
+
+ class NameConstant:
+ pass
+
+if sys.version_info < (3, ):
+ open_kw = dict()
+else:
+ open_kw = dict(encoding='utf-8')
+
+
+if sys.version_info < (2, 7) or platform.python_implementation() == 'Jython':
+ class Set():
+ pass
+
+
+def literal_eval(node_or_string):
+ """
+ Safely evaluate an expression node or a string containing a Python
+ expression. The string or node provided may only consist of the following
+ Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
+ sets, booleans, and None.
+ """
+ _safe_names = {'None': None, 'True': True, 'False': False}
+ if isinstance(node_or_string, string_type):
+ node_or_string = parse(node_or_string, mode='eval')
+ if isinstance(node_or_string, Expression):
+ node_or_string = node_or_string.body
+ else:
+ raise TypeError("only string or AST nodes supported")
+
+ def _convert(node):
+ if isinstance(node, (Str, Bytes)):
+ return node.s
+ elif isinstance(node, Num):
+ return node.n
+ elif isinstance(node, Tuple):
+ return tuple(map(_convert, node.elts))
+ elif isinstance(node, List):
+ return list(map(_convert, node.elts))
+ elif isinstance(node, Set):
+ return set(map(_convert, node.elts))
+ elif isinstance(node, Dict):
+ return dict((_convert(k), _convert(v)) for k, v
+ in zip(node.keys, node.values))
+ elif isinstance(node, NameConstant):
+ return node.value
+ elif sys.version_info < (3, 4) and isinstance(node, Name):
+ if node.id in _safe_names:
+ return _safe_names[node.id]
+ elif isinstance(node, UnaryOp) and \
+ isinstance(node.op, (UAdd, USub)) and \
+ isinstance(node.operand, (Num, UnaryOp, BinOp)): # NOQA
+ operand = _convert(node.operand)
+ if isinstance(node.op, UAdd):
+ return + operand
+ else:
+ return - operand
+ elif isinstance(node, BinOp) and \
+ isinstance(node.op, (Add, Sub)) and \
+ isinstance(node.right, (Num, UnaryOp, BinOp)) and \
+ isinstance(node.left, (Num, UnaryOp, BinOp)): # NOQA
+ left = _convert(node.left)
+ right = _convert(node.right)
+ if isinstance(node.op, Add):
+ return left + right
+ else:
+ return left - right
+ elif isinstance(node, Call):
+ func_id = getattr(node.func, 'id', None)
+ if func_id == 'dict':
+ return dict((k.arg, _convert(k.value)) for k in node.keywords)
+ elif func_id == 'set':
+ return set(_convert(node.args[0]))
+ elif func_id == 'date':
+ return datetime.date(*[_convert(k) for k in node.args])
+ elif func_id == 'datetime':
+ return datetime.datetime(*[_convert(k) for k in node.args])
+ err = SyntaxError('malformed node or string: ' + repr(node))
+ err.filename = '<string>'
+ err.lineno = node.lineno
+ err.offset = node.col_offset
+ err.text = repr(node)
+ err.node = node
+ raise err
+ return _convert(node_or_string)
+
+
+# parses python ( "= dict( )" ) or ( "= {" )
def _package_data(fn):
data = {}
- with open(fn) as fp:
+ with open(fn, **open_kw) as fp:
parsing = False
lines = []
for line in fp.readlines():
- if line.startswith('_package_data'):
+ if sys.version_info < (3,):
+ line = line.decode('utf-8')
+ if line.startswith(u'_package_data'):
if 'dict(' in line:
parsing = 'python'
- elif '# JSON' in line:
- parsing = 'json'
- lines.append('{\n')
+ lines.append(u'dict(\n')
+ elif line.endswith(u'= {\n'):
+ parsing = 'python'
+ lines.append(u'{\n')
else:
raise NotImplementedError
continue
if not parsing:
continue
- 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(')'):
+ if parsing == 'python':
+ if line.startswith(u')') or line.startswith(u'}'):
+ lines.append(line)
+ try:
+ data = literal_eval(u''.join(lines))
+ except SyntaxError as e:
+ context = 2
+ from_line = e.lineno - (context + 1)
+ to_line = e.lineno + (context - 1)
+ w = len(str(to_line))
+ for index, line in enumerate(lines):
+ if from_line <= index <= to_line:
+ print(u"{0:{1}}: {2}".format(index, w, line).encode('utf-8'),
+ end=u'')
+ if index == e.lineno - 1:
+ print(u"{0:{1}} {2}^--- {3}".format(
+ u' ', w, u' ' * e.offset, e.node))
+ raise
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))
+ lines.append(line)
else:
raise NotImplementedError
return data
-pkg_data = _package_data('__init__.py')
+# make sure you can run "python ../some/dir/setup.py install"
+pkg_data = _package_data(__file__.replace('setup.py', '__init__.py'))
exclude_files = [
'setup.py',
]
-# # imports
-import os
-import sys
-import platform
-
-from setuptools import setup, Extension, Distribution # NOQA
-from setuptools.command import install_lib
-
# # helper
def _check_convert_version(tup):
@@ -104,7 +186,7 @@ def _check_convert_version(tup):
if isinstance(x, int):
nr_digits += 1
if nr_digits > 2:
- raise ValueError("too many consecutive digits " + ret_val)
+ raise ValueError("too many consecutive digits after " + ret_val)
ret_val += next_sep + str(x)
next_sep = '.'
continue
@@ -113,7 +195,7 @@ def _check_convert_version(tup):
if first_letter in 'abcr':
if post_dev:
raise ValueError("release level specified after "
- "post/dev:" + x)
+ "post/dev: " + x)
nr_digits = 0
ret_val += 'rc' if first_letter == 'r' else first_letter
elif first_letter in 'pd':
@@ -122,6 +204,9 @@ def _check_convert_version(tup):
ret_val += '.post' if first_letter == 'p' else '.dev'
else:
raise ValueError('First letter of "' + x + '" not recognised')
+ # .dev and .post need a number otherwise setuptools normalizes and complains
+ if nr_digits == 1 and post_dev:
+ ret_val += '0'
return ret_val
@@ -146,6 +231,74 @@ class MyInstallLib(install_lib.install_lib):
return alt_files
+class InMemoryZipFile(object):
+ def __init__(self, file_name=None):
+ try:
+ from cStringIO import StringIO
+ except ImportError:
+ from io import BytesIO as StringIO
+ import zipfile
+ self.zip_file = zipfile
+ # Create the in-memory file-like object
+ self._file_name = file_name
+ self.in_memory_data = StringIO()
+ # Create the in-memory zipfile
+ self.in_memory_zip = self.zip_file.ZipFile(
+ self.in_memory_data, "w", self.zip_file.ZIP_DEFLATED, False)
+ self.in_memory_zip.debug = 3
+
+ def append(self, filename_in_zip, file_contents):
+ '''Appends a file with name filename_in_zip and contents of
+ file_contents to the in-memory zip.'''
+ self.in_memory_zip.writestr(filename_in_zip, file_contents)
+ return self # so you can daisy-chain
+
+ def write_to_file(self, filename):
+ '''Writes the in-memory zip to a file.'''
+ # Mark the files as having been created on Windows so that
+ # Unix permissions are not inferred as 0000
+ for zfile in self.in_memory_zip.filelist:
+ zfile.create_system = 0
+ self.in_memory_zip.close()
+ with open(filename, 'wb') as f:
+ f.write(self.in_memory_data.getvalue())
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if self._file_name is None:
+ return
+ self.write_to_file(self._file_name)
+
+ def delete_from_zip_file(self, pattern=None, file_names=None):
+ """
+ zip_file can be a string or a zipfile.ZipFile object, the latter will be closed
+ any name in file_names is deleted, all file_names provided have to be in the ZIP
+ archive or else an IOError is raised
+ """
+ if pattern and isinstance(pattern, string_type):
+ import re
+ pattern = re.compile(pattern)
+ if file_names:
+ if not isinstance(file_names, list):
+ file_names = [file_names]
+ else:
+ file_names = []
+ with self.zip_file.ZipFile(self._file_name) as zf:
+ for l in zf.infolist():
+ if l.filename in file_names:
+ file_names.remove(l.filename)
+ continue
+ if pattern and pattern.match(l.filename):
+ continue
+ self.append(l.filename, zf.read(l))
+ if file_names:
+ raise IOError('[Errno 2] No such file{}: {}'.format(
+ '' if len(file_names) == 1 else 's',
+ ', '.join([repr(f) for f in file_names])))
+
+
class NameSpacePackager(object):
def __init__(self, pkg_data):
assert isinstance(pkg_data, dict)
@@ -153,11 +306,23 @@ class NameSpacePackager(object):
self.full_package_name = self.pn(self._pkg_data['full_package_name'])
self._split = None
self.depth = self.full_package_name.count('.')
+ self.nested = self._pkg_data.get('nested', False)
self.command = None
+ 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:
- print('error: have to install with "pip install ."')
+ 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 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 self._pkg_data.get('universal'):
+ Distribution.is_pure = lambda *args: True
+ else:
+ Distribution.is_pure = lambda *args: False
for x in sys.argv:
if x[0] == '-' or x == 'setup.py':
continue
@@ -171,7 +336,11 @@ class NameSpacePackager(object):
@property
def split(self):
- """split the full package name in list of compontents"""
+ """split the full package name in list of compontents traditionally
+ done by setuptools.find_packages. This routine skips any directories
+ with __init__.py that start with "_" or ".", or contain a
+ setup.py/tox.ini (indicating a subpackage)
+ """
if self._split is None:
fpn = self.full_package_name.split('.')
self._split = []
@@ -179,7 +348,7 @@ class NameSpacePackager(object):
self._split.insert(0, '.'.join(fpn))
fpn = fpn[:-1]
for d in os.listdir('.'):
- if not os.path.isdir(d) or d == self._split[0] or d[0] == '_':
+ 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')
@@ -191,37 +360,48 @@ class NameSpacePackager(object):
x = os.path.join(d, '__init__.py')
if os.path.exists(x):
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)
+ for y in self._split]
return self._split
@property
def namespace_packages(self):
return self.split[:self.depth]
+ def namespace_directories(self, depth=None):
+ """return list of directories where the namespace should be created /
+ can be found
+ """
+ res = []
+ for index, d in enumerate(self.split[:depth]):
+ # toplevel gets a dot
+ if index > 0:
+ d = os.path.join(*d.split('.'))
+ res.append('.' + d)
+ return res
+
@property
def package_dir(self):
- return {
+ d = {
# don't specify empty dir, clashes with package_data spec
self.full_package_name: '.',
- self.split[0]: self.split[0],
}
+ if len(self.split) > 1: # only if package namespace
+ d[self.split[0]] = self.namespace_directories(1)[0]
+ return d
def create_dirs(self):
"""create the directories necessary for namespace packaging"""
- if not os.path.exists(self.split[0]):
- for d in self.split[:self.depth]:
- d = os.path.join(*d.split('.'))
+ directories = self.namespace_directories(self.depth)
+ if not directories:
+ return
+ if not os.path.exists(directories[0]):
+ for d in directories:
os.mkdir(d)
with open(os.path.join(d, '__init__.py'), 'w') as fp:
fp.write('import pkg_resources\n'
'pkg_resources.declare_namespace(__name__)\n')
- # 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:
@@ -277,23 +457,48 @@ class NameSpacePackager(object):
' installs for package name {0}'.format(fn))
def entry_points(self, script_name=None, package_name=None):
+ """normally called without explicit script_name and package name
+ the default console_scripts entry depends on the existence of __main__.py:
+ if that file exists then the function main() in there is used, otherwise
+ the in __init__.py.
+
+ the _package_data entry_points key/value pair can be explicitly specified
+ including a "=" character. If the entry is True or 1 the
+ scriptname is the last part of the full package path (split on '.')
+ if the ep entry is a simple string without "=", that is assumed to be
+ the name of the script.
+ """
+ def pckg_entry_point(name):
+ return '{0}{1}:main'.format(
+ name,
+ '.__main__' if os.path.exists('__main__.py') else '',
+ )
+
ep = self._pkg_data.get('entry_points', True)
if ep is None:
return None
- if ep is not True:
- return {'console_scripts': [ep]}
+ if ep not in [True, 1]:
+ if '=' in ep:
+ # full specification of the entry point like
+ # entry_points=['yaml = ruamel.yaml.cmd:main'],
+ return {'console_scripts': [ep]}
+ # assume that it is just the script name
+ script_name = ep
if package_name is None:
package_name = self.full_package_name
if not script_name:
script_name = package_name.split('.')[-1]
return {'console_scripts': [
- '{0} = {1}:main'.format(script_name, package_name),
+ '{0} = {1}'.format(script_name, pckg_entry_point(package_name)),
]}
@property
def url(self):
- return 'https://bitbucket.org/{0}/{1}'.format(
- *self.full_package_name.split('.', 1))
+ if self.full_package_name.startswith('ruamel.'):
+ sp = self.full_package_name.split('.', 1)
+ else:
+ sp = ['ruamel', self.full_package_name]
+ return 'https://bitbucket.org/{0}/{1}'.format(*sp)
@property
def author(self):
@@ -305,12 +510,16 @@ class NameSpacePackager(object):
@property
def license(self):
+ """return the license field from _package_data, None means MIT"""
lic = self._pkg_data.get('license')
if lic is None:
# lic_fn = os.path.join(os.path.dirname(__file__), 'LICENSE')
# assert os.path.exists(lic_fn)
return "MIT license"
- return license
+ return lic
+
+ def has_mit_lic(self):
+ return 'MIT' in self.license
@property
def description(self):
@@ -319,10 +528,10 @@ class NameSpacePackager(object):
@property
def status(self):
# αβ
- status = self._pkg_data.get('status', u'β')
- if status == u'α':
+ status = self._pkg_data.get('status', u'β').lower()
+ if status in [u'α', u'alpha']:
return (3, 'Alpha')
- elif status == u'β':
+ elif status in [u'β', u'beta']:
return (4, 'Beta')
elif u'stable' in status.lower():
return (5, 'Production/Stable')
@@ -333,32 +542,73 @@ class NameSpacePackager(object):
return [
'Development Status :: {0} - {1}'.format(*self.status),
'Intended Audience :: Developers',
- 'License :: ' + ('Other/Proprietary License'
- if self.pn(self._pkg_data.get('license')) else
- 'OSI Approved :: MIT License'),
+ 'License :: ' + ('OSI Approved :: MIT' if self.has_mit_lic()
+ else 'Other/Proprietary') + ' License',
'Operating System :: OS Independent',
'Programming Language :: Python',
] + [self.pn(x) for x in self._pkg_data.get('classifiers', [])]
@property
+ def keywords(self):
+ return self.pn(self._pkg_data.get('keywords'))
+
+ @property
def install_requires(self):
- ir = self._pkg_data.get('install_requires', [])
+ """list of packages required for installation"""
+ return self._analyse_packages[0]
+
+ @property
+ def install_pre(self):
+ """list of packages required for installation"""
+ return self._analyse_packages[1]
+
+ @property
+ def _analyse_packages(self):
+ """gather from configuration, names starting with * need
+ 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
+ """
+ if self._pkg[0] is None:
+ self._pkg[0] = []
+ self._pkg[1] = []
+
+ ir = self._pkg_data.get('install_requires')
+ if ir is None:
+ return self._pkg # these will be both empty at this point
if isinstance(ir, list):
- return ir
+ self._pkg[0] = ir
+ return self._pkg
# 'any' for all builds, 'py27' etc for specifics versions
- res = ir.get('any', [])
+ packages = ir.get('any', [])
+ if isinstance(packages, string_type):
+ packages = packages.split() # assume white space separated string
+ if self.nested:
+ # parent dir is also a package, make sure it is installed (need its .pth file)
+ parent_pkg = self.full_package_name.rsplit('.', 1)[0]
+ if parent_pkg not in packages:
+ packages.append(parent_pkg)
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
+ elif implementation == 'Jython':
+ pyver = 'jython'
+ packages.extend(ir.get(pyver, []))
+ for p in packages:
+ # package name starting with * means use local source tree, non-published
+ # to PyPi or maybe not latest version on PyPI -> pre-install
+ if p[0] == '*':
+ p = p[1:]
+ self._pkg[1].append(p)
+ self._pkg[0].append(p)
+ return self._pkg
@property
def data_files(self):
df = self._pkg_data.get('data_files', [])
- if self._pkg_data.get('license') is None:
+ if self.has_mit_lic():
df.append('LICENSE')
if not df:
return None
@@ -367,7 +617,7 @@ class NameSpacePackager(object):
@property
def package_data(self):
df = self._pkg_data.get('data_files', [])
- if self._pkg_data.get('license') is None:
+ if self.has_mit_lic():
# include the file
df.append('LICENSE')
# but don't install it
@@ -384,8 +634,16 @@ class NameSpacePackager(object):
return self._ext_modules
if '--version' in sys.argv:
return None
- # if sys.platform == "win32":
- # return None
+ if platform.python_implementation() == 'Jython':
+ return None
+ if sys.platform == "win32" and not self._pkg_data.get('win32bin'):
+ return None
+ try:
+ plat = sys.argv.index('--plat-name')
+ if 'win' in sys.argv[plat + 1]:
+ return None
+ except ValueError:
+ pass
import tempfile
import shutil
from textwrap import dedent
@@ -444,23 +702,20 @@ class NameSpacePackager(object):
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
"""
- if 'bdist_wheel' not in sys.argv or not os.path.exists('LICENSE'):
+ if 'bdist_wheel' not in sys.argv:
return
file_name = 'setup.cfg'
if os.path.exists(file_name): # add it if not in there?
return
with open(file_name, 'w') as fp:
- fp.write('[metadata]\nlicense-file = LICENSE\n')
+ if os.path.exists('LICENSE'):
+ fp.write('[metadata]\nlicense-file = LICENSE\n')
+ else:
+ print("\n\n>>>>>> LICENSE file not found <<<<<\n\n")
if self._pkg_data.get('universal'):
fp.write('[bdist_wheel]\nuniversal = 1\n')
try:
@@ -474,6 +729,13 @@ class NameSpacePackager(object):
# # call setup
def main():
+ dump_kw = '--dump-kw'
+ if dump_kw in sys.argv:
+ import wheel
+ import distutils
+ print('python: ', sys.version)
+ print('distutils:', distutils.__version__)
+ print('wheel: ', wheel.__version__)
nsp = NameSpacePackager(pkg_data)
nsp.check()
nsp.create_dirs()
@@ -492,18 +754,50 @@ def main():
install_requires=nsp.install_requires,
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 or '--verbose' in sys.argv:
+ 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(k, '->', v)
+ 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()
if nsp.wheel(kw, setup):
return
+ for x in ['-c', 'egg_info', '--egg-base', 'pip-egg-info']:
+ if x not in sys.argv:
+ break
+ else:
+ # we're doing a tox setup install any starred package by searching up the source tree
+ # until you match your/package/name for your.package.name
+ for p in nsp.install_pre:
+ import subprocess
+ # search other source
+ setup_path = os.path.join(*p.split('.') + ['setup.py'])
+ try_dir = os.path.dirname(sys.executable)
+ while len(try_dir) > 1:
+ full_path_setup_py = os.path.join(try_dir, setup_path)
+ if os.path.exists(full_path_setup_py):
+ pip = sys.executable.replace('python', 'pip')
+ cmd = [pip, 'install', os.path.dirname(full_path_setup_py)]
+ # with open('/var/tmp/notice', 'a') as fp:
+ # print('installing', cmd, file=fp)
+ subprocess.check_output(cmd)
+ break
+ 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]
+ for x in os.listdir(d):
+ if x.endswith('.whl'):
+ # remove .pth file from the wheel
+ full_name = os.path.join(d, x)
+ with InMemoryZipFile(full_name) as imz:
+ imz.delete_from_zip_file(nsp.full_package_name + '.*.pth')
+ break
main()
diff --git a/tox.ini b/tox.ini
index f0d6a5d..e1ba3bc 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,12 +1,12 @@
[tox]
-envlist = pep8,py27,py34,py26,py33
+envlist = pep8,py35,py27,py34,pypy
[testenv]
commands =
py.test _test
deps =
pytest
- flake8
+ flake8==2.5.5
[testenv:pep8]
commands =
@@ -14,4 +14,5 @@ commands =
[flake8]
show-source = True
+max-line-length = 95
exclude = .hg,.git,.tox,dist,.cache,__pycache__,ruamel.zip2tar.egg-info