summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2015-08-27 17:44:39 +0200
committerAnthon van der Neut <anthon@mnt.org>2015-08-27 17:44:39 +0200
commit3403025e03dd626d270f8f075fb4862daeff9878 (patch)
tree7bdad65614b4948b1ad478a76c2ca3261e652425
parente8188ad86449c239b6366937fea0578c315c1d5d (diff)
downloadruamel.std.argparse-3403025e03dd626d270f8f075fb4862daeff9878.tar.gz
- added license
- move test to _test - move _action to action - updated setup.py (and pkg_data in __init__.py) - flask8 in tox.ini
-rw-r--r--.hgignore1
-rw-r--r--LICENSE21
-rw-r--r--MANIFEST.in1
-rw-r--r--__init__.py39
-rw-r--r--_test/test_argparse.py (renamed from test/test_argparse.py)7
-rw-r--r--_test/test_program.py (renamed from test/test_program.py)7
-rw-r--r--action/__init__.py (renamed from _action/__init__.py)0
-rw-r--r--action/checksinglestore.py (renamed from _action/checksinglestore.py)0
-rw-r--r--action/count.py (renamed from _action/count.py)5
-rw-r--r--action/splitappend.py (renamed from _action/splitappend.py)0
-rw-r--r--example/checksingleaction.py2
-rw-r--r--example/testcmd.py5
-rw-r--r--setup.py349
-rw-r--r--tox.ini15
14 files changed, 352 insertions, 100 deletions
diff --git a/.hgignore b/.hgignore
index 6007b8b..dd78dbd 100644
--- a/.hgignore
+++ b/.hgignore
@@ -11,3 +11,4 @@ dist
build
*.egg-info
.tox
+ruamel
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f6f753a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+
+ The MIT License (MIT)
+
+ 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.
+
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index f1cb737..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1 +0,0 @@
-include setup.py
diff --git a/__init__.py b/__init__.py
index f9058ce..76928cd 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,16 +1,19 @@
# coding: utf-8
-# Copyright Ruamel bvba 2007-2014
from __future__ import print_function
-# from six
-import sys
-PY3 = sys.version_info[0] == 3
-
-if PY3:
- string_types = str,
-else:
- string_types = basestring,
+# 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'],
+)
# < from ruamel.util.new import _convert_version
@@ -33,13 +36,21 @@ def _convert_version(tup):
# <
-version_info = (0, 5, 2)
+version_info = _package_data['version_info']
__version__ = _convert_version(version_info)
del _convert_version
+import sys
import argparse
-from argparse import ArgumentParser
+from argparse import ArgumentParser # NOQA
+
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ string_types = str,
+else:
+ string_types = basestring,
class SubParsersAction(argparse._SubParsersAction):
@@ -77,9 +88,9 @@ class SubParsersAction(argparse._SubParsersAction):
return parser
-from ._action.checksinglestore import CheckSingleStoreAction
-from ._action.count import CountAction
-from ._action.splitappend import SplitAppendAction
+from .action.checksinglestore import CheckSingleStoreAction # NOQA
+from .action.count import CountAction # NOQA
+from .action.splitappend import SplitAppendAction # NOQA
class SmartFormatter(argparse.HelpFormatter):
diff --git a/test/test_argparse.py b/_test/test_argparse.py
index 9cc818b..72b8d7a 100644
--- a/test/test_argparse.py
+++ b/_test/test_argparse.py
@@ -1,12 +1,9 @@
# coding: utf-8
# Copyright Ruamel bvba 2007-2014
-import pytest
+import pytest # NOQA < so flake doesn't complain about not using
-try:
- from ruamel.std.argparse import argparse, CountAction, SmartFormatter
-except ImportError:
- print("you have to install ruamel.std.argparse to run the tests")
+from ruamel.std.argparse import argparse, SmartFormatter
from textwrap import dedent
diff --git a/test/test_program.py b/_test/test_program.py
index 4487b65..d67b7e0 100644
--- a/test/test_program.py
+++ b/_test/test_program.py
@@ -7,10 +7,7 @@ from __future__ import print_function
import sys
import pytest
-try:
- from ruamel.std.argparse import ProgramBase, option, sub_parser, version
-except ImportError:
- print("you have to install ruamel.std.argparse to run the tests")
+from ruamel.std.argparse import ProgramBase, option, sub_parser, version
class Program(ProgramBase):
@@ -136,7 +133,7 @@ class ParseHelpOutput:
chunk.append(line)
print('chunks', self._chunks)
if not self._chunks:
- print('stderr', err)
+ print('stderr', 'chunks')
def start(self, chunk, s, strip=True):
"""check if a stripped line in the chunk text starts with s"""
diff --git a/_action/__init__.py b/action/__init__.py
index f49013d..f49013d 100644
--- a/_action/__init__.py
+++ b/action/__init__.py
diff --git a/_action/checksinglestore.py b/action/checksinglestore.py
index 3a37792..3a37792 100644
--- a/_action/checksinglestore.py
+++ b/action/checksinglestore.py
diff --git a/_action/count.py b/action/count.py
index 77bf54f..a3d0a36 100644
--- a/_action/count.py
+++ b/action/count.py
@@ -10,13 +10,14 @@ class CountAction(argparse.Action):
"""argparse action for counting up and down
parser = argparse.ArgumentParser()
- parser.add_argument('--verbose', '-v', action=CountAction, const=1, nargs=0)
+ parser.add_argument('--verbose', '-v', action=CountAction, const=1,
+ nargs=0)
parser.add_argument('--quiet', '-q', action=CountAction, dest='verbose',
const=-1, nargs=0)
"""
def __call__(self, parser, namespace, values, option_string=None):
try:
val = getattr(namespace, self.dest) + self.const
- except TypeError: # probably None
+ except TypeError: # probably None
val = self.const
setattr(namespace, self.dest, val)
diff --git a/_action/splitappend.py b/action/splitappend.py
index 82b003b..82b003b 100644
--- a/_action/splitappend.py
+++ b/action/splitappend.py
diff --git a/example/checksingleaction.py b/example/checksingleaction.py
index 054891c..f4d8166 100644
--- a/example/checksingleaction.py
+++ b/example/checksingleaction.py
@@ -7,4 +7,4 @@ parser = argparse.ArgumentParser()
parser.add_argument('--check', '-c', action=CheckSingleStoreAction, const=1,
nargs=0)
-print(parser.parse_args("--check -c".split())) \ No newline at end of file
+print(parser.parse_args("--check -c".split()))
diff --git a/example/testcmd.py b/example/testcmd.py
index 1618820..d5a3079 100644
--- a/example/testcmd.py
+++ b/example/testcmd.py
@@ -1,7 +1,6 @@
-from __future__ import print_function
+# coding: utf-8
-import sys
-import os
+from __future__ import print_function
from ruamel.std.argparse import ProgramBase, option, sub_parser, version, \
SmartFormatter
diff --git a/setup.py b/setup.py
index 3f45fa1..81b3fb8 100644
--- a/setup.py
+++ b/setup.py
@@ -1,41 +1,68 @@
-#! /usr/bin/env python
+# # header
# coding: utf-8
-# Copyright Ruamel bvba 2007-2014
from __future__ import print_function
-import sys
-import os
-from textwrap import dedent
+if __name__ != '__main__':
+ raise NotImplementedError('should never include setup.py')
+
+# # definitions
-name_space = 'ruamel'
-package_name = 'argparse'
-full_package_name = name_space + '.std.' + package_name
+full_package_name = None
+
+
+def _package_data(fn):
+ data = {}
+ with open(fn) as fp:
+ parsing = False
+ for line in fp.readlines():
+ if line.startswith('_package_data'):
+ parsing = True
+ 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(', ')]
+ else:
+ print('Unknown: >>>>> {0!r} {1!r}'.format(k, v))
+ return data
+
+pkg_data = _package_data('__init__.py')
exclude_files = [
'setup.py',
]
-if __name__ == '__main__':
- # put here so setup.py can be imported more easily
- from setuptools import setup, find_packages, Extension
- from setuptools.command import install_lib
-
+# # imports
+import os
+import sys
-# < from ruamel.util.new.setupinc import get_version, _check_convert_version
-def get_version():
- v_i = 'version_info = '
- for line in open('__init__.py'):
- if not line.startswith(v_i):
- continue
- s_e = line[len(v_i):].strip()[1:-1].split(', ')
- elems = [x.strip()[1:-1] if x[0] in '\'"' else int(x) for x in s_e]
- break
- return elems
+from setuptools import setup
+from setuptools.command import install_lib
+# # helper
def _check_convert_version(tup):
- """create a PEP 386 pseudo-format conformant string from tuple 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 "."
nr_digits = 0 # nr of adjacent digits in rest, to verify
@@ -44,7 +71,7 @@ def _check_convert_version(tup):
if isinstance(x, int):
nr_digits += 1
if nr_digits > 2:
- raise ValueError("to many consecutive digits " + ret_val)
+ raise ValueError("too many consecutive digits " + ret_val)
ret_val += next_sep + str(x)
next_sep = '.'
continue
@@ -65,19 +92,13 @@ def _check_convert_version(tup):
return ret_val
-# < from ruamel.util.new.setupinc import version_info, version_str
-version_info = get_version()
+version_info = pkg_data['version_info']
version_str = _check_convert_version(version_info)
-# < from ruamel.util.new.setupinc import MyInstallLib
class MyInstallLib(install_lib.install_lib):
- "create __init__.py on the fly"
- def run(self):
- install_lib.install_lib.run(self)
-
def install(self):
- fpp = full_package_name.split('.') # full package path
+ fpp = pkg_data['full_package_name'].split('.') # full package path
full_exclude_files = [os.path.join(*(fpp + [x]))
for x in exclude_files]
alt_files = []
@@ -92,44 +113,240 @@ class MyInstallLib(install_lib.install_lib):
return alt_files
-# <
+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._split = None
+ self.depth = self.full_package_name.count('.')
+ self.command = None
+ 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 ."')
+ sys.exit(1)
+ for x in sys.argv:
+ if x[0] == '-' or x == 'setup.py':
+ continue
+ self.command = x
+ break
+ @property
+ def split(self):
+ """split the full package name in list of compontents"""
+ if self._split is None:
+ fpn = self.full_package_name.split('.')
+ self._split = []
+ while fpn:
+ 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] == '_':
+ continue
+ x = os.path.join(d, '__init__.py')
+ if os.path.exists(x):
+ self._split.append(self.full_package_name + '.' + d)
+ return self._split
-def main():
- install_requires = [
- "ruamel.base==0.3",
- ]
- if sys.version_info[:2] == (2, 6):
- install_requires.append("argparse")
- packages = [full_package_name] + [(full_package_name + '.' + x) for x
- in find_packages(exclude=['test'])]
- setup(
- name=full_package_name,
- version=version_str,
- description="Enhancements to argparse: extra actions, subparser "
- "aliases, smart formatter, a decorator based wrapper",
- install_requires=install_requires,
- long_description=open('README.rst').read(),
- url='https://bitbucket.org/ruamel/std.' + package_name,
- author='Anthon van der Neut',
- author_email='a.van.der.neut@ruamel.eu',
- license="MIT license",
- package_dir={full_package_name: '.'},
- namespace_packages=[name_space],
- packages=packages,
- cmdclass={'install_lib': MyInstallLib},
- classifiers=[
- 'Development Status :: 4 - Beta',
+ @property
+ def namespace_packages(self):
+ return self.split[:self.depth]
+
+ @property
+ def package_dir(self):
+ return {
+ # don't specify empty dir, clashes with package_data spec
+ self.full_package_name: '.',
+ self.split[0]: self.split[0],
+ }
+
+ 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('.'))
+ 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')
+ 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:
+ from pip.exceptions import InstallationError
+ except ImportError:
+ return
+ # arg is either develop (pip install -e) or install
+ if self.command not in ['install', 'develop']:
+ return
+
+ # if hgi and hgi.base are both in namespace_packages matching
+ # against the top (hgi.) it suffices to find minus-e and non-minus-e
+ # installed packages. As we don't know the order in namespace_packages
+ # do some magic
+ prefix = self.split[0]
+ prefixes = set([prefix, prefix.replace('_', '-')])
+ for p in sys.path:
+ if not p:
+ continue # directory with setup.py
+ if os.path.exists(os.path.join(p, 'setup.py')):
+ continue # some linked in stuff might not be hgi based
+ if not os.path.isdir(p):
+ continue
+ if p.startswith('/tmp/'):
+ continue
+ for fn in os.listdir(p):
+ for pre in prefixes:
+ if fn.startswith(pre):
+ break
+ else:
+ continue
+ full_name = os.path.join(p, fn)
+ # not in prefixes the toplevel is never changed from _ to -
+ if fn == prefix and os.path.isdir(full_name):
+ # directory -> other, non-minus-e, install
+ if self.command == 'develop':
+ raise InstallationError(
+ 'Cannot mix develop (pip install -e),\nwith '
+ 'non-develop installs for package name {0}'.format(
+ fn))
+ elif fn == prefix:
+ raise InstallationError(
+ 'non directory package {0} in {1}'.format(
+ fn, p))
+ for pre in [x + '.' for x in prefixes]:
+ if fn.startswith(pre):
+ break
+ else:
+ continue # hgiabc instead of hgi.
+ if fn.endswith('-link') and self.command == 'install':
+ raise InstallationError(
+ 'Cannot mix non-develop with develop\n(pip install -e)'
+ ' installs for package name {0}'.format(fn))
+
+ def entry_points(self, script_name=None, package_name=None):
+ ep = self._pkg_data.get('entry_points', True)
+ if ep is None:
+ return None
+ if ep is not True:
+ return {'console_scripts': [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),
+ ]}
+
+ @property
+ def url(self):
+ return 'https://bitbucket.org/{0}/{1}'.format(
+ *self.full_package_name.split('.', 1))
+
+ @property
+ def author(self):
+ return self._pkg_data['author']
+
+ @property
+ def author_email(self):
+ return self._pkg_data['author_email']
+
+ @property
+ def license(self):
+ # lic = self._pkg_data.get('license')
+ # if lic is None:
+ # lic_file_name = os.path.join(os.path.dirname(__file__), 'LICENSE')
+ # assert os.path.exists(lic_file_name)
+ return "MIT license"
+
+ @property
+ def description(self):
+ return self._pkg_data['description']
+
+ @property
+ def status(self):
+ # αβ
+ status = self._pkg_data.get('status', 'β')
+ if status == 'α':
+ return (3, 'Alpha')
+ elif status == 'β':
+ return (4, 'Beta')
+ elif 'stable' in status.lower():
+ return (5, 'Production/Stable')
+ raise NotImplementedError
+
+ @property
+ def classifiers(self):
+ return [
+ 'Development Status :: {0} - {1}'.format(*self.status),
'Intended Audience :: Developers',
- 'License :: OSI Approved :: MIT License',
+ 'License :: ' + ('Other/Proprietary License'
+ if self._pkg_data.get('license') else
+ 'OSI Approved :: MIT License'),
'Operating System :: OS Independent',
'Programming Language :: Python',
]
+
+ @property
+ def install_requires(self):
+ return pkg_data.get('install_requires', [])
+
+ @property
+ def data_files(self):
+ df = self._pkg_data.get('data_files', [])
+ if self._pkg_data.get('license') is None:
+ df.append('LICENSE')
+ if not df:
+ return None
+ return [('.', df), ]
+
+ @property
+ def package_data(self):
+ df = self._pkg_data.get('data_files', [])
+ if self._pkg_data.get('license') is None:
+ # include the file
+ df.append('LICENSE')
+ # but don't install it
+ exclude_files.append('LICENSE')
+ if not df:
+ return None
+ return {self.full_package_name: df}
+
+
+# # call setup
+def main():
+ nsp = NameSpacePackager(pkg_data)
+ nsp.check()
+ nsp.create_dirs()
+ kw = dict(
+ name=nsp.full_package_name,
+ namespace_packages=nsp.namespace_packages,
+ version=version_str,
+ packages=nsp.split,
+ url=nsp.url,
+ author=nsp.author,
+ author_email=nsp.author_email,
+ cmdclass={'install_lib': MyInstallLib},
+ package_dir=nsp.package_dir,
+ entry_points=nsp.entry_points(),
+ description=nsp.description,
+ install_requires=nsp.install_requires,
+ license=nsp.license,
+ classifiers=nsp.classifiers,
+ package_data=nsp.package_data,
)
+ if '--version' not in sys.argv:
+ for k in sorted(kw):
+ v = kw[k]
+ print(k, '->', v)
+ with open('README.rst') as fp:
+ kw['long_description'] = fp.read()
+ setup(**kw)
+
-if __name__ == '__main__':
- if len(sys.argv) > 1 and sys.argv[1] == 'sdist':
- assert full_package_name == os.path.abspath(os.path.dirname(
- __file__)).split('site-packages' + os.path.sep)[1].replace(
- os.path.sep, '.')
- main()
+main()
diff --git a/tox.ini b/tox.ini
index e3f59c1..f0d6a5d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,17 @@
[tox]
-envlist = py26,py27,py33,py34
+envlist = pep8,py27,py34,py26,py33
[testenv]
-commands = py.test test
+commands =
+ py.test _test
deps =
pytest
- ruamel.base
+ flake8
+
+[testenv:pep8]
+commands =
+ flake8 {posargs}
+
+[flake8]
+show-source = True
+exclude = .hg,.git,.tox,dist,.cache,__pycache__,ruamel.zip2tar.egg-info