Hello community, here is the log from the commit of package python-zetup for openSUSE:Factory checked in at 2019-03-14 15:00:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-zetup (Old) and /work/SRC/openSUSE:Factory/.python-zetup.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-zetup" Thu Mar 14 15:00:09 2019 rev:3 rq:684699 version:0.2.45 Changes: -------- --- /work/SRC/openSUSE:Factory/python-zetup/python-zetup.changes 2018-08-31 10:46:23.159343211 +0200 +++ /work/SRC/openSUSE:Factory/.python-zetup.new.28833/python-zetup.changes 2019-03-14 15:01:07.555698179 +0100 @@ -1,0 +2,8 @@ +Wed Mar 13 13:47:55 UTC 2019 - Tomáš Chvátal <tchva...@suse.com> + +- Update to 0.2.45: + * Cleanup whitespace/formatting/docstrings + * Print install hints + * Fix various py3 compats + +------------------------------------------------------------------- Old: ---- zetup-0.2.43.tar.gz New: ---- zetup-0.2.45.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-zetup.spec ++++++ --- /var/tmp/diff_new_pack.HkPm0H/_old 2019-03-14 15:01:09.507697794 +0100 +++ /var/tmp/diff_new_pack.HkPm0H/_new 2019-03-14 15:01:09.511697794 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-zetup # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,31 +12,32 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-zetup -Version: 0.2.43 +Version: 0.2.45 Release: 0 Summary: Project setups tools License: LGPL-3.0-only Group: Development/Languages/Python URL: https://github.com/zimmermanncode/zetup Source: https://files.pythonhosted.org/packages/source/z/zetup/zetup-%{version}.tar.gz -BuildRequires: %{python_module jupyter_nbconvert >= 5.2} -BuildRequires: %{python_module path.py >= 10.3} -BuildRequires: %{python_module pytest} -BuildRequires: %{python_module setuptools_scm >= 2.0} +BuildRequires: %{python_module jupyter_nbconvert >= 5.4} +BuildRequires: %{python_module path.py >= 11.1} +BuildRequires: %{python_module pytest >= 3.8} +BuildRequires: %{python_module setuptools_scm >= 3.1} BuildRequires: %{python_module setuptools} +BuildRequires: dos2unix BuildRequires: fdupes BuildRequires: python-rpm-macros Requires(post): update-alternatives Requires(postun): update-alternatives Recommends: python-jinjatools >= 0.1.7 -Recommends: python-jupyter_nbconvert >= 5.2 -Recommends: python-path.py >= 10.3 +Recommends: python-jupyter_nbconvert >= 5.4 +Recommends: python-path.py >= 11.1 Recommends: python-pytest BuildArch: noarch %python_subpackages @@ -46,8 +47,8 @@ %prep %setup -q -n zetup-%{version} -sed -i 's/\r$//' README.rst -sed -i 's/\r$//' zetup/script.py +dos2unix README.rst +dos2unix zetup/script.py %build %python_build ++++++ zetup-0.2.43.tar.gz -> zetup-0.2.45.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/PKG-INFO new/zetup-0.2.45/PKG-INFO --- old/zetup-0.2.43/PKG-INFO 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/PKG-INFO 2018-09-17 21:35:10.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: zetup -Version: 0.2.43 +Version: 0.2.45 Summary: Zimmermann's Extensible Tools for Unified Project_setups Home-page: https://github.com/zimmermanncode/zetup Author: Stefan Zimmermann diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/VERSION new/zetup-0.2.45/VERSION --- old/zetup-0.2.43/VERSION 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/VERSION 2018-09-17 21:35:09.000000000 +0200 @@ -1 +1 @@ -0.2.43 \ No newline at end of file +0.2.45 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/requirements.commands.txt new/zetup-0.2.45/requirements.commands.txt --- old/zetup-0.2.43/requirements.commands.txt 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/requirements.commands.txt 2018-09-17 21:33:58.000000000 +0200 @@ -1,2 +1,2 @@ -path.py >= 10.3 #import path +path.py >= 11.1 #import path jinjatools >= 0.1.7 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/requirements.notebook.txt new/zetup-0.2.45/requirements.notebook.txt --- old/zetup-0.2.43/requirements.notebook.txt 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/requirements.notebook.txt 2018-09-17 21:33:58.000000000 +0200 @@ -1,2 +1,2 @@ -path.py >= 10.3 #import path -nbconvert >= 5.2 +path.py >= 11.1 #import path +nbconvert >= 5.4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/requirements.pytest.txt new/zetup-0.2.45/requirements.pytest.txt --- old/zetup-0.2.43/requirements.pytest.txt 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/requirements.pytest.txt 2018-09-17 21:33:58.000000000 +0200 @@ -1 +1 @@ -pytest +pytest >= 3.8 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/requirements.txt new/zetup-0.2.45/requirements.txt --- old/zetup-0.2.43/requirements.txt 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/requirements.txt 2018-09-17 21:33:58.000000000 +0200 @@ -1 +1 @@ -setuptools_scm >= 2.0 +setuptools_scm >= 3.1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/tox.ini new/zetup-0.2.45/tox.ini --- old/zetup-0.2.43/tox.ini 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/tox.ini 2018-09-17 21:33:58.000000000 +0200 @@ -1,5 +1,5 @@ [tox] -envlist = py27,py33,py34,py35,py36,pypy +envlist = py27,py34,py35,py36,pypy [testenv] deps = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/__init__.py new/zetup-0.2.45/zetup/__init__.py --- old/zetup-0.2.43/zetup/__init__.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/__init__.py 2018-09-17 21:33:58.000000000 +0200 @@ -41,6 +41,7 @@ from .annotate import annotate from .modules import package, toplevel, extra_toplevel from .classpackage import classpackage +from .pip import ZetupPipError, pip # import notebook subpackage for defining extra_toplevel below from . import notebook diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/__main__.py new/zetup-0.2.45/zetup/__main__.py --- old/zetup-0.2.43/zetup/__main__.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/__main__.py 2018-09-17 21:33:58.000000000 +0200 @@ -25,5 +25,5 @@ import zetup.script - -zetup.script.run() +if __name__ == '__main__': + zetup.script.run() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/commands/del_.py new/zetup-0.2.45/zetup/commands/del_.py --- old/zetup-0.2.43/zetup/commands/del_.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/commands/del_.py 2018-09-17 21:33:58.000000000 +0200 @@ -27,30 +27,30 @@ import os import pkg_resources -import pip from path import Path from zetup.zetup import Zetup from zetup.commands.command import command from zetup.conda import conda +from zetup.pip import pip -__all__ = ['del_'] +__all__ = ('del_', ) @Zetup.command(name='del') @command(name='del') def del_(zfg, args=None): - """Delete project from python environment. - """ + """Delete project from python environment.""" try: # check for conda conda_info = conda.info() except OSError: # ==> no conda pass else: # are we in a conda environment? - if any(Path(conda_info[key]).samefile(sys.prefix) - for key in ['root_prefix', 'default_prefix'] + if any( + Path(conda_info[key]).samefile(sys.prefix) + for key in ['root_prefix', 'default_prefix'] # and is project installed via conda? ) and conda.list('--no-pip', '--full-name', zfg.NAME): # then also remove it via conda @@ -65,7 +65,7 @@ dist = pkg_resources.WorkingSet().by_key[zfg.NAME] except KeyError: # ==> nothing left to uninstall break - status = pip.main(['uninstall', zfg.NAME, '--yes']) + status = pip(['uninstall', zfg.NAME, '--yes'], raise_=False) if status: # ==> error return status root = Path(dist.location) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/commands/dev.py new/zetup-0.2.45/zetup/commands/dev.py --- old/zetup-0.2.43/zetup/commands/dev.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/commands/dev.py 2018-09-17 21:33:58.000000000 +0200 @@ -26,6 +26,7 @@ import pip from zetup.zetup import Zetup +from zetup.pip import pip from zetup.commands.del_ import del_ @@ -34,12 +35,11 @@ @Zetup.command(depends=['setup.py']) def dev(zfg, args=None): - """Install project in develop mode. - """ + """Install project in develop mode.""" # first remove any current project installation del_(zfg) # then (re)install project in develop mode (and return pip status code) source = str(zfg.ZETUP_DIR) if zfg.EXTRAS: source += '[all]' - return pip.main(['install', '--editable', source]) + return pip(['install', '--editable', source], raise_=False) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/object.py new/zetup-0.2.45/zetup/object.py --- old/zetup-0.2.43/zetup/object.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/object.py 2018-09-17 21:33:58.000000000 +0200 @@ -1,71 +1,107 @@ -# zetup.py +# ZETUP | Zimmermann's Extensible Tools for Unified Projects # -# Zimmermann's Python package setup. +# Copyright (C) 2014-2018 Stefan Zimmermann <u...@zimmermann.co> # -# Copyright (C) 2014-2015 Stefan Zimmermann <zimmermann.c...@gmail.com> -# -# zetup.py is free software: you can redistribute it and/or modify +# ZETUP is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# zetup.py is distributed in the hope that it will be useful, +# ZETUP is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License -# along with zetup.py. If not, see <http://www.gnu.org/licenses/>. +# along with ZETUP. If not, see <http://www.gnu.org/licenses/>. -"""zetup.object +""" +Define basic :class:`zetup.object` and its :class:`zetup.meta`. -Basic ``object``-derived class and ``type``-derived ``meta`` class -with some added features. +For extending builtin ``object`` and ``type`` with useful extra features -.. moduleauthor:: Stefan Zimmermann <zimmermann.c...@gmail.com> +.. moduleauthor:: Stefan Zimmermann <u...@zimmermann.co> """ +from __future__ import absolute_import + from itertools import chain -__all__ = ['object', 'meta'] +__all__ = ('object', 'meta') class meta(type): - """Basic metaclass derived from builtin ``type``, - which adds a unified basic ``__dir__`` method for Python 2 and 3, - which always returns all member names from metaclass and class level. + """ + Basic metaclass that extends builtin ``type`` with useful extra features. + + Adds a unified basic ``__dir__`` method for Python 2 and 3, which always + returns all member names from metaclass and class level + + Adds :meth:`.metamethod` and :meth:`.method` decorators for adding new + members to classes and their metaclasses outside of the class definition + scopes """ if hasattr(type, '__dir__'): # PY3 def __dir__(cls): """Get all member names from class and metaclass level. """ - return list(set(chain(type.__dir__(cls), dir(type(cls))))) + return sorted(set(chain(type.__dir__(cls), dir(type(cls))))) else: # PY2 def __dir__(cls): """Get all member names from class and metaclass level. """ - return list(set(chain( - dir(type(cls)), *(c.__dict__ for c in cls.mro())))) + return sorted(set(chain( + dir(type(cls)), + *(c.__dict__ for c in cls.mro())))) @classmethod def metamember(mcs, obj): - """Decorator for adding `obj` as a member - to the metaclass of this class :: + """ + Decorator for adding `obj` as a member to the metaclass of this class. + + >>> import six # if you want compatibility with Python 2 and 3 + >>> import zetup - class Meta(zetup.meta): - ... + This is of course only useful when you have a custom metaclass: - class Class(zetup.object, metaclass=Meta): - ... + >>> class Meta(zetup.meta): + ... pass - @Class.metamember - def method(cls, ...): - ... + Now you can add new members to your custom metaclass outside of the + class definition scope: + + >>> @Meta.metamember + ... def method(cls, arg): + ... pass >>> Meta.method - <function Meta.method> + <... Meta.method...> + + This also works from a new class based on this metaclass. In Python 3 + syntax you create such a class like:: + + class Class(zetup.object, metaclass=Meta): + pass + + But if you want to be compatible with Python 2 and 3: + + >>> class Class(six.with_metaclass(Meta, zetup.object)): + ... pass + + >>> @Class.metamember + ... def another_method(cls, arg): + ... pass + + >>> Meta.another_method + <... Meta.another_method...> + + >>> Class.another_method + <bound method Meta.another_method of ...Class...> + + And the formerly defined ``@Meta.metamember`` is of course also there: + >>> Class.method - <bound method Meta.method of Class> + <bound method Meta.method of ...Class...> """ if isinstance(obj, property): name = obj.fget.__name__ @@ -82,17 +118,26 @@ return obj def member(cls, obj): - """Decorator for adding `obj` as a member to this class :: + """ + Decorator for adding `obj` as a member to this class. - class Class(zetup.object): - ... + >>> import zetup - @Class.member - def method(self, ...): - ... + >>> class Class(zetup.object): + ... pass + + Now you can add new members to your class outside of the class + definition scope: + + >>> @Class.member + ... def method(self, arg): + ... pass >>> Class.method - <function Class.method> + <... Class.method...> + + >>> Class().method + <bound method Class.method of ...Class...> """ if isinstance(obj, property): name = obj.fget.__name__ @@ -109,18 +154,28 @@ return obj -# PY2/3 compatible way to create class `object` with metaclass `meta` +# PY2/3 compatible way to create class `object` with metaclass `meta`... + clsattrs = { '__doc__': - """Basic class derived from builtin ``object``, - which adds a basic ``__dir__`` method for Python 2. + """ + Basic class that extends builtin ``object`` with useful extra features. + + Adds a basic ``__dir__`` method for Python 2 + + Has :class:`zetup.meta` as metaclass and therefore provides + :meth:`zetup.meta.metamethod` and :meth:`zetup.meta.method` decorators for + adding new members to classes and their metaclasses outside of the class + definition scopes """} + if not hasattr(object, '__dir__'): + def __dir__(self): - """Get all member names from instance and class level. - """ - return list(set(chain( - self.__dict__, *(c.__dict__ for c in type(self).mro())))) + """Get all member names from instance and class level.""" + return sorted(set(chain( + self.__dict__, + *(c.__dict__ for c in type(self).mro())))) clsattrs['__dir__'] = __dir__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/pip/__init__.py new/zetup-0.2.45/zetup/pip/__init__.py --- old/zetup-0.2.43/zetup/pip/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/zetup-0.2.45/zetup/pip/__init__.py 2018-09-17 21:33:58.000000000 +0200 @@ -0,0 +1,53 @@ +"""Define :func:`zetup.pip` command runner and :exc:`zetup.ZetupPipError`.""" + +import logging + +from zetup.requires import requires + +from .errors import ZetupPipError + +__all__ = ('ZetupPipError', 'pip') + + +@requires("pip >= 10.0") +def pip(command, raise_=True): + """ + Run a pip `command`. + + :param command: The list of pip arguments + + >>> import zetup + + >>> zetup.pip(['list', '-v']) + Package...Version...Location...Installer + -------...-------...--------...--------- + ... + 0 + + :return: + ``0`` on success or, if called with ``raise_=False``, also any + non-zero pip exit code + + >>> zetup.pip(['invalid'], raise_=False) + 1 + + :raises zetup.ZetupPipError: + on non-zero pip exit codes, if not called with ``raise_=False`` + + >>> zetup.pip(['invalid']) + Traceback (most recent call last): + ... + ZetupPipError: pip command ['invalid'] failed with exit code 1 + """ + from pip._internal import configuration, main + + # HACK: Avoid unnecessary debug messages (which also destroy doctests) + configuration.logger.level = logging.INFO + try: + exit_code = main(command) + except SystemExit as exc: + exit_code = exc.code + if raise_ and exit_code != 0: + raise ZetupPipError(command, exit_code) + + return exit_code diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/pip/errors.py new/zetup-0.2.45/zetup/pip/errors.py --- old/zetup-0.2.43/zetup/pip/errors.py 1970-01-01 01:00:00.000000000 +0100 +++ new/zetup-0.2.45/zetup/pip/errors.py 2018-09-17 21:33:58.000000000 +0200 @@ -0,0 +1,32 @@ +"""Define exceptions for failing ``zetup.pip`` calls.""" + +from zetup.error import ZetupError + + +class ZetupPipError(ZetupError): + """ + ``zetup.pip([command...])`` failed. + + ``.command`` argument list and ``.exit_code`` can be retrieved from the + exception object: + + >>> import zetup + + >>> try: + ... zetup.pip(['invalid']) + ... except zetup.ZetupPipError as exc: + ... exc.command, exc.exit_code + (['invalid'], 1) + """ + + def __init__(self, command, exit_code): + """ + Compose error message from `command` list and `exit_code`. + + Also store the parameters as exception object attributes + """ + ZetupError.__init__( + self, "pip command {!r} failed with exit code {:d}".format( + command, exit_code)) + self.command = command + self.exit_code = exit_code diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/pip/test_errors.py new/zetup-0.2.45/zetup/pip/test_errors.py --- old/zetup-0.2.43/zetup/pip/test_errors.py 1970-01-01 01:00:00.000000000 +0100 +++ new/zetup-0.2.45/zetup/pip/test_errors.py 2018-09-17 21:33:58.000000000 +0200 @@ -0,0 +1,27 @@ +"""Test :mod:`zetup.pip.errors`.""" + +import zetup +from zetup.pip.errors import ZetupPipError + + +class TestZetupPipError: + """Test :exc:`zetup.pip.errors.ZetupPipError`.""" + + def test_toplevel_export(self): + """Should be properly provided by :mod:`zetup` API.""" + assert zetup.ZetupPipError is ZetupPipError + + def test_superclass(self): + """Should be derived from :exc:`zetup.ZetupError`.""" + assert issubclass(ZetupPipError, zetup.ZetupError) + + def test_attributes(self): + """Should store ``.command`` and ``.exit_code``.""" + exc = ZetupPipError(['invalid'], 1) + assert exc.command == ['invalid'] + assert exc.exit_code is 1 + + def test_message(self): + """Should provide correct exception message.""" + exc = ZetupPipError(['invalid'], 1) + assert str(exc) == "pip command ['invalid'] failed with exit code 1" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/requires.py new/zetup-0.2.45/zetup/requires.py --- old/zetup-0.2.43/zetup/requires.py 2018-04-18 17:15:38.000000000 +0200 +++ new/zetup-0.2.45/zetup/requires.py 2018-09-17 21:33:58.000000000 +0200 @@ -17,19 +17,37 @@ # You should have received a copy of the GNU Lesser General Public License # along with zetup.py. If not, see <http://www.gnu.org/licenses/>. -__all__ = ['Requirements', 'DistributionNotFound', 'VersionConflict'] - +import re import sys +from textwrap import dedent + +import pkg_resources +from pkg_resources import ( + parse_requirements, Requirement, get_distribution) + +__all__ = ( + 'DistributionNotFound', + 'Requirements', + 'VersionConflict', + 'requires', +) + + if sys.version_info[0] == 3: unicode = str -import re -import pkg_resources -from pkg_resources import \ - parse_requirements, Requirement, get_distribution + +def howto_install_requirement(req): + """Create a message explaining how to install `req`""" + return dedent(""" + You can install the needed requirement with the following shell command: + + pip install "{}" + """.format(req)).strip() class DistributionNotFound(pkg_resources.DistributionNotFound): + def __init__(self, req, requirer, reason=None): super(DistributionNotFound, self).__init__(req, [requirer]) self.requirer = requirer @@ -39,38 +57,43 @@ text = "%s needs %s" % (self.requirer, self.req) if self.reason: text += " (%s)" % self.reason - return text + return "{}\n\n{}".format(text, howto_install_requirement(self.req)) class VersionConflict(pkg_resources.VersionConflict): + def __init__(self, req, found_version, requirer, reason=None): super(VersionConflict, self).__init__( - '%s-%s' % (req.key, found_version), req) + '%s-%s' % (req.key, found_version), req) self.requirer = requirer self.reason = reason def __str__(self): text = "%s needs %s but found %s" % ( - self.requirer, self.req, self.dist) + self.requirer, self.req, self.dist) if self.reason: text += " (%s)" % self.reason - return text + return "{}\n\n{}".format(text, howto_install_requirement(self.req)) class Requirements(object): - """Package requirements manager. - """ + """Package requirements manager.""" + @staticmethod def _parse(text): - """ Generate parsed requirements from `text`, - which should contain newline separated requirement specs. + """ + Generate parsed requirements from `text`. + + :param text: + Newline-separated requirement specs + + Additionally looks for ``#import name`` comments after requirement + lines (the actual root module name of the required package to use for + runtime dependency checks) and stores them as .impname attrs on the + Requirement instances. - - Additionally looks for "#import name" comments - after requirement lines (the actual root module name - of the required package to use for runtime dependency checks) - and stores them as .impname attrs on the Requirement instances. - - Supports #py.. tags at the beginning of lines, - specifying a python version the requirement applies to. + Supports ``#py..`` tags at the beginning of lines, specifying a python + version the requirement applies to. """ for line in text.split('\n'): line = line.strip() @@ -96,16 +119,17 @@ yield req def __init__(self, reqs, zfg=None): - """Store a list of :class:`pkg_resources.Requirement` instances - from the given requirement specs - and additionally store them newline separated - in the :class:`str` base. - - :param reqs: Either a single string of requirement specs - or a sequence of strings and/or - :class:`pkg_resources.Requirement` instances. - :param zfg: Optional zetup config object - the requirements are related to. + """ + Generate a list of :class:`pkg_resources.Requirement` instances. + + And additionally store all requirement specs as text with newline + separators in the ``str`` base + + :param reqs: + Either a single string of requirement specs or a sequence of + strings and/or :class:`pkg_resources.Requirement` instances + :param zfg: + Optional zetup config object the requirements are related to """ if isinstance(reqs, Requirements): txt = reqs.txt @@ -130,57 +154,80 @@ self.zfg = zfg def __eq__(self, other): - return isinstance(other, Requirements) \ - and self._list == other._list + return isinstance(other, Requirements) and ( + self._list == other._list) def __str__(self): return '\n'.join(map(str, self)) - def check(self, raise_=True): - """Check that all requirements are available (importable) - and their versions match (using modules' __version__ attributes). - - - Fallback to pkg_resources.get_distribution().version - if no __version__ or is None. - - :param raise_: Raise DistributionNotFound if ImportError - or VersionConflict if version doesn't match? - If False just return False in that case. + def check(self, requirer=None, raise_=True): + """ + Check that all requirements are satisfied. + + They must be importable and their versions (from their top-level + package modules' ``.__version__`` attributes) must match the required + specs + + Falls back to ``pkg_resources.get_distribution().version`` if + ``.__version__`` is missing or ``None`` + + :param requirer: + Optional identification string of the requirer + :param raise_: + If set to ``False`` then also just return ``False`` on unsatisfied + requirements instead of raising exceptions + + :raises zetup.DistributionNotFound: + If a requirement can't be imported + :raises zetup.VersionConflict: + If a required version doesn't match """ - requirer = self.zfg and '%s-%s' % ( - self.zfg.NAME, self.zfg.VERSION or '(none)') + if requirer is None: + requirer = self.zfg and '%s-%s' % ( + self.zfg.NAME, self.zfg.VERSION or '(none)') for req in self: try: mod = __import__(req.impname) except ImportError as e: if raise_: - raise DistributionNotFound(req, requirer, - reason="%s: %s" % (type(e).__name__, e)) + raise DistributionNotFound( + req, requirer, + reason="%s: %s" % (type(e).__name__, e)) + return False - if not req.specs: # No version constraints + + if not req.specs: # No version constraints continue + try: version = mod.__version__ - if version is None: + if version is None or version == 'unknown': # treat the same as if .__version__ not exists # (handle in following except block) # ==> same exception, just different msg raise AttributeError( - "module's '__version__' attribute is None") + "module's '__version__' attribute is {!r}" + .format(version)) + except AttributeError as e_no__version__attr: - try: # try to get version from distribution + try: # try to get version from distribution dist = get_distribution(req.key) except pkg_resources.DistributionNotFound as e: if raise_: - raise VersionConflict(req, None, requirer, - reason="%s: %s. %s: %s" % ( - e_no__version__attr, mod, type(e).__name__, e)) + raise VersionConflict( + req, None, requirer, + reason="%s: %s. %s: %s" % ( + e_no__version__attr, mod, type(e).__name__, e)) + return False + version = dist.version if version not in req: if raise_: raise VersionConflict(req, version, requirer) + return False + return True @property @@ -192,25 +239,28 @@ return iter(self._list) def __getitem__(self, name): - """Get a requirement by its distribution name. - """ + """Get a requirement by its distribution name.""" for req in self._list: if name in [req.key, req.unsafe_name]: return req + raise KeyError(name) def __delitem__(self, name): - """Delete a requirement by its distribution name. - """ + """Delete a requirement by its distribution name.""" for req in list(self._list): if name in [req.key, req.unsafe_name]: return self._list.remove(req) + raise KeyError(name) def __add__(self, reqs): - """Return a new :class:`Requirements` instance - with additional `reqs` from string - or another :class:`Requirements` instance. + """ + Create a new :class:`Requirements` instance. + + :param reqs: + Additional requirements as string or another :class:`Requirements` + instance """ if isinstance(reqs, Requirements): reqs = reqs.txt @@ -223,8 +273,44 @@ @property def py(self): return '%s("""\n%s\n""", zfg=zfg)' % ( - type(self).__name__, self.txt) ## '\n'.join( - ## '%s #import %s' % (req, req.impname) for req in self)) + type(self).__name__, self.txt) # '\n'.join( + # '%s #import %s' % (req, req.impname) for req in self)) def __repr__(self): return str(self) + + +def requires(reqs): + """ + Create a function decorator for defining extra requirements. + + :param reqs: + Requirement specs compatible with :class:`zetup.Requirements` + + >>> @requires("not-installed") + ... def func(): + ... pass + + The actual requirements check happens only on calling the function: + + >>> func() # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + DistributionNotFound: ...func() needs not-installed ... + <BLANKLINE> + You can install the needed requirement with the following shell command: + <BLANKLINE> + pip install "not-installed" + """ + reqs = Requirements(reqs) + + def deco(func): + + def caller(*args, **kwargs): + reqs.check(requirer='{}.{}()'.format( + func.__module__, func.__name__)) + return func(*args, **kwargs) + + return caller + + return deco diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup/zetup_config.py new/zetup-0.2.45/zetup/zetup_config.py --- old/zetup-0.2.43/zetup/zetup_config.py 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/zetup/zetup_config.py 2018-09-17 21:35:09.000000000 +0200 @@ -110,6 +110,18 @@ ) , +Package('zetup.pip', + sources=[ + '__init__.py', + 'errors.py', + 'test_errors.py' + ], + subpackages=[ + + ], + ) +, + Package('zetup.process', sources=[ '__init__.py', @@ -131,23 +143,23 @@ EXTRAS = Extras([ ('commands', Requirements(""" -path.py >= 10.3 #import path +path.py >= 11.1 #import path jinjatools >= 0.1.7 """)), ('notebook', Requirements(""" -path.py >= 10.3 #import path -nbconvert >= 5.2 +path.py >= 11.1 #import path +nbconvert >= 5.4 """)), ('pytest', Requirements(""" -pytest +pytest >= 3.8 """)) ], zfg=zfg) REQUIRES = Requirements(""" -setuptools_scm >= 2.0 +setuptools_scm >= 3.1 """, zfg=zfg) @@ -155,7 +167,7 @@ SETUP_HOOKS = [] -VERSION = Version('0.2.43') +VERSION = Version('0.2.45') KEEP_MADE = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup.egg-info/PKG-INFO new/zetup-0.2.45/zetup.egg-info/PKG-INFO --- old/zetup-0.2.43/zetup.egg-info/PKG-INFO 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/zetup.egg-info/PKG-INFO 2018-09-17 21:35:09.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: zetup -Version: 0.2.43 +Version: 0.2.45 Summary: Zimmermann's Extensible Tools for Unified Project_setups Home-page: https://github.com/zimmermanncode/zetup Author: Stefan Zimmermann diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup.egg-info/SOURCES.txt new/zetup-0.2.45/zetup.egg-info/SOURCES.txt --- old/zetup-0.2.43/zetup.egg-info/SOURCES.txt 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/zetup.egg-info/SOURCES.txt 2018-09-17 21:35:10.000000000 +0200 @@ -62,5 +62,8 @@ zetup/doc/__init__.py zetup/notebook/__init__.py zetup/notebook/jinja.py +zetup/pip/__init__.py +zetup/pip/errors.py +zetup/pip/test_errors.py zetup/process/__init__.py zetup/process/scons.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/zetup-0.2.43/zetup.egg-info/requires.txt new/zetup-0.2.45/zetup.egg-info/requires.txt --- old/zetup-0.2.43/zetup.egg-info/requires.txt 2018-04-18 17:16:50.000000000 +0200 +++ new/zetup-0.2.45/zetup.egg-info/requires.txt 2018-09-17 21:35:09.000000000 +0200 @@ -1,19 +1,19 @@ -setuptools_scm>=2.0 +setuptools_scm>=3.1 [all] -path.py>=10.3 +path.py>=11.1 jinjatools>=0.1.7 -path.py>=10.3 -nbconvert>=5.2 -pytest +path.py>=11.1 +nbconvert>=5.4 +pytest>=3.8 [commands] -path.py>=10.3 +path.py>=11.1 jinjatools>=0.1.7 [notebook] -path.py>=10.3 -nbconvert>=5.2 +path.py>=11.1 +nbconvert>=5.4 [pytest] -pytest +pytest>=3.8