summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthon van der Neut <anthon@mnt.org>2015-08-27 16:01:05 +0200
committerAnthon van der Neut <anthon@mnt.org>2015-08-27 16:01:05 +0200
commit93e249469454df3c83e7bd29359d460baf29e9f6 (patch)
treed2478078fe3eb4713deeae6b7c426efbaaf1b813
parenta1facf25cc3a0578afed89e9ea4d11cf9ea46e13 (diff)
downloadruamel.base-93e249469454df3c83e7bd29359d460baf29e9f6.tar.gz
no longer create __init__.py files
-rw-r--r--README.rst3
-rw-r--r--__init__.py12
-rw-r--r--_test/test_base.py12
-rw-r--r--setup.py364
-rw-r--r--test/test_base.py8
-rw-r--r--tox.ini3
6 files changed, 305 insertions, 97 deletions
diff --git a/README.rst b/README.rst
index e300991..c53c31b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,4 @@
ruamel.base
-- provides a single definition point for ``ruamel/__init__.py``,
- ``ruamel/ext/__init__.py`` and
- ``ruamel/std/__init__.py``
- (future) place for elements common to multiple packages
diff --git a/__init__.py b/__init__.py
index bcc1555..e25444e 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,5 +1,15 @@
# coding: utf-8
+from __future__ import print_function
+
+_package_data = dict(
+ full_package_name="ruamel.base",
+ version_info = (1, 0, 0),
+ author='Anthon van der Neut',
+ author_email='a.van.der.neut@ruamel.eu',
+ description="common routines for ruamel packages",
+ entry_points=None,
+)
def _convert_version(tup):
"""create a PEP 386 pseudo-format conformant string from tuple tup"""
@@ -19,7 +29,7 @@ def _convert_version(tup):
return ret_val
-version_info = (0, 3)
+version_info = _package_data['version_info']
__version__ = _convert_version(version_info)
del _convert_version
diff --git a/_test/test_base.py b/_test/test_base.py
new file mode 100644
index 0000000..cd49d07
--- /dev/null
+++ b/_test/test_base.py
@@ -0,0 +1,12 @@
+# coding: utf-8
+
+from __future__ import print_function
+
+import pytest
+
+# default for tox stub is to Fail
+def test_base():
+ import ruamel
+ with pytest.raises(ImportError):
+ import ruamel.std
+ assert True
diff --git a/setup.py b/setup.py
index c491b27..669006e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,33 +1,66 @@
-#! /usr/bin/env python
+## header
# coding: utf-8
from __future__ import print_function
-import sys
-import os
-from textwrap import dedent
+if __name__ != '__main__':
+ raise NotImplementedError('should never include setup.py')
+
+## definitions
+
+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
+ 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
-name_space = 'ruamel'
-package_name = 'base'
-full_package_name = name_space + '.' + package_name
+pkg_data = _package_data('__init__.py')
exclude_files = [
'setup.py',
]
+## imports
+import os
+import sys
-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(', ')
- els = [x.strip()[1:-1] if x[0] in '\'"' else int(x) for x in s_e]
- return els
+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
@@ -57,47 +90,12 @@ def _check_convert_version(tup):
return ret_val
-version_info = get_version()
+version_info = pkg_data['version_info']
version_str = _check_convert_version(version_info)
-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
-
-
class MyInstallLib(install_lib.install_lib):
- "create __init__.py on the fly"
- def run(self):
- install_lib.install_lib.run(self)
- init_txt = dedent('''\
- # coding: utf-8
- # Copyright (c) 2013-2015 Anthon van der Neut, RUAMEL bvba
- "generated __init__.py "
- try:
- __import__('pkg_resources').declare_namespace(__name__)
- except ImportError:
- pass
- ''')
- init_paths = ["ruamel/std", ] # "ruamel/ext"]
- for init_path_s in init_paths:
- init_path = init_path_s.split('/')
- for product_init in [
- os.path.join(
- *([self.install_dir] + init_path[:p+1] + ['__init__.py']))
- for p in range(len(init_path))
- ]:
- if not os.path.exists(product_init):
- d = os.path.dirname(product_init)
- if not os.path.exists(d):
- os.makedirs(d)
- print('creating %s' % product_init)
- with open(product_init, "wt") as fp:
- fp.write(init_txt)
- setup = os.path.join(self.install_dir, 'setup.py')
-
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 = []
@@ -112,40 +110,238 @@ class MyInstallLib(install_lib.install_lib):
return alt_files
-def main():
- install_requires = [
- ]
- # if sys.version_info < (3, 4):
- # install_requires.append("")
- packages = [full_package_name] + [(full_package_name + '.' + x) for x
- in find_packages(exclude=['tests'])]
- setup(
- name=full_package_name,
- version=version_str,
- description="common elements for ruamel packages, "
- "hierarchy structure with __init__.py",
- install_requires=install_requires,
- long_description=open('README.rst').read(),
- url='https://bitbucket.org/ruamel/' + 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',
+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 \
+ not '--single-version-externally-managed' 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
+
+ @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,
)
+ 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/test/test_base.py b/test/test_base.py
deleted file mode 100644
index d2c0125..0000000
--- a/test/test_base.py
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-# default for tox stub is to Fail
-def test_base():
- import ruamel
- import ruamel.std
- # import ruamel.ext
- assert True
diff --git a/tox.ini b/tox.ini
index 4082297..69cd5ab 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,6 +2,7 @@
envlist = py26,py27,py33,py34
[testenv]
-commands = py.test test
+commands =
+ py.test _test
deps =
pytest