Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pytools for openSUSE:Factory checked in at 2021-01-25 18:23:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytools (Old) and /work/SRC/openSUSE:Factory/.python-pytools.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytools" Mon Jan 25 18:23:38 2021 rev:12 rq:866179 version:2021.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytools/python-pytools.changes 2020-12-28 00:28:45.789781331 +0100 +++ /work/SRC/openSUSE:Factory/.python-pytools.new.28504/python-pytools.changes 2021-01-25 18:23:55.632455402 +0100 @@ -1,0 +2,6 @@ +Sat Jan 23 01:10:24 UTC 2021 - Dirk M??ller <[email protected]> + +- update to 2021.1: + * pytools.tag.Taggable added + +------------------------------------------------------------------- Old: ---- pytools-2020.4.4.tar.gz New: ---- pytools-2021.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytools.spec ++++++ --- /var/tmp/diff_new_pack.qIFgQt/_old 2021-01-25 18:23:56.436456571 +0100 +++ /var/tmp/diff_new_pack.qIFgQt/_new 2021-01-25 18:23:56.440456576 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pytools # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-pytools -Version: 2020.4.4 +Version: 2021.1 Release: 0 Summary: A collection of tools for Python License: MIT @@ -32,10 +32,10 @@ BuildRequires: %{python_module numpy >= 1.6.0} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools} -BuildRequires: ( python3-dataclasses >= 0.7 if python3-base <= 3.6 ) -BuildRequires: ( python36-dataclasses >= 0.7 if python36-base ) BuildRequires: fdupes BuildRequires: python-rpm-macros +BuildRequires: ( python3-dataclasses >= 0.7 if python3-base <= 3.6 ) +BuildRequires: ( python36-dataclasses >= 0.7 if python36-base ) Requires: python-appdirs >= 1.4.0 Requires: python-decorator >= 3.2.0 Requires: python-numpy >= 1.6.0 ++++++ pytools-2020.4.4.tar.gz -> pytools-2021.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/PKG-INFO new/pytools-2021.1/PKG-INFO --- old/pytools-2020.4.4/PKG-INFO 2020-12-13 22:58:32.223667100 +0100 +++ new/pytools-2021.1/PKG-INFO 2021-01-09 20:56:50.842988300 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: pytools -Version: 2020.4.4 +Version: 2021.1 Summary: A collection of tools for Python Home-page: http://pypi.python.org/pypi/pytools Author: Andreas Kloeckner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/doc/conf.py new/pytools-2021.1/doc/conf.py --- old/pytools-2020.4.4/doc/conf.py 2020-12-07 21:36:06.000000000 +0100 +++ new/pytools-2021.1/doc/conf.py 2021-01-04 20:55:35.000000000 +0100 @@ -1,36 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# pytools documentation build configuration file, created by -# sphinx-quickstart on Wed Jun 14 16:28:43 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = "1.0" - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named "sphinx.ext.*") or your custom -# ones. extensions = ["sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.intersphinx", @@ -38,7 +5,6 @@ "sphinx.ext.viewcode", "sphinx_copybutton", ] -autoclass_content = "class" # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -94,81 +60,12 @@ # html_theme = "furo" -html_theme_options = { - } - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = "pytoolsdoc" - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ("letterpaper" or "a4paper"). - # - # "papersize": "letterpaper", - - # The font size ("10pt", "11pt" or "12pt"). - # - # "pointsize": "10pt", - - # Additional stuff for the LaTeX preamble. - # - # "preamble": '', - - # Latex figure (float) alignment - # - # "figure_align": "htbp", -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, "pytools.tex", "pytools Documentation", - "Andreas Kloeckner", "manual"), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, "pytools", "pytools Documentation", - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, "pytools", "pytools Documentation", - author, "pytools", "One line description of project.", - "Miscellaneous"), -] - - intersphinx_mapping = { "https://docs.python.org/3": None, "https://numpy.org/doc/stable": None, "https://documen.tician.de/pymbolic/": None, "https://documen.tician.de/loopy/": None, } + +autoclass_content = "class" +autodoc_typehints = "description" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/pytools/persistent_dict.py new/pytools-2021.1/pytools/persistent_dict.py --- old/pytools-2020.4.4/pytools/persistent_dict.py 2020-12-07 21:54:53.000000000 +0100 +++ new/pytools-2021.1/pytools/persistent_dict.py 2020-12-22 22:32:38.000000000 +0100 @@ -28,12 +28,13 @@ import logging import hashlib +import collections.abc as abc -try: - import collections.abc as abc -except ImportError: - # Python 2 - import collections as abc +# Removing this in 2020-12 broke a shocking amount of stuff, such as +# https://github.com/OP2/PyOP2/pull/605 +# Bring it back for now to mitigate the breakage, however this is going +# away in 2021 at the latest. +new_hash = hashlib.sha256 import os import shutil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/pytools/tag.py new/pytools-2021.1/pytools/tag.py --- old/pytools-2020.4.4/pytools/tag.py 2020-10-15 07:15:04.000000000 +0200 +++ new/pytools-2021.1/pytools/tag.py 2021-01-09 01:08:43.000000000 +0100 @@ -1,10 +1,12 @@ from dataclasses import dataclass -from typing import Tuple, Any, FrozenSet +from typing import Tuple, Any, FrozenSet, Union, Iterable, TypeVar +from pytools import memoize __copyright__ = """ -Copyright (C) 2020 Andreas Kloeckner +Copyright (C) 2020 Andreas Kl??ckner Copyright (C) 2020 Matt Wala Copyright (C) 2020 Xiaoyu Wei +Copyright (C) 2020 Nicholas Christensen """ __license__ = """ @@ -27,6 +29,7 @@ THE SOFTWARE. """ + # {{{ docs __doc__ = """ @@ -34,6 +37,7 @@ Tag Interface --------------- +.. autoclass:: Taggable .. autoclass:: Tag .. autoclass:: UniqueTag @@ -41,14 +45,14 @@ ------------------------ .. autoclass:: DottedName +.. autoclass:: NonUniqueTagError """ -# }}} - +# }}} -# {{{ dotted name +# {{{ dotted name class DottedName: """ @@ -89,6 +93,7 @@ # }}} + # {{{ tag tag_dataclass = dataclass(init=True, eq=True, frozen=True, repr=True) @@ -117,16 +122,138 @@ def tag_name(self) -> DottedName: return DottedName.from_class(type(self)) +# }}} + + +# {{{ unique tag class UniqueTag(Tag): """ - Only one instance of this type of tag may be assigned - to a single tagged object. + A superclass for tags that are unique on each :class:`Taggable`. + + Each instance of :class:`Taggable` may have no more than one + instance of each subclass of :class:`UniqueTag` in its + set of `tags`. Multiple `UniqueTag` instances of + different (immediate) subclasses are allowed. """ pass +# }}} + TagsType = FrozenSet[Tag] +TagOrIterableType = Union[Iterable[Tag], Tag, None] +T_co = TypeVar("T_co", bound="Taggable") + + +# {{{ taggable + +@memoize +def _immediate_unique_tag_descendants(cls): + if UniqueTag in cls.__bases__: + return frozenset([cls]) + else: + result = frozenset() + for base in cls.__bases__: + result = result | _immediate_unique_tag_descendants(base) + return result + + +class NonUniqueTagError(ValueError): + """ + Raised when a :class:`Taggable` object is instantiated with more + than one :class:`UniqueTag` instances of the same subclass in + its set of tags. + """ + pass + + +class Taggable: + """ + Parent class for objects with a `tags` attribute. + + .. attribute:: tags + + A :class:`frozenset` of :class:`Tag` instances + + .. method:: copy + .. method:: tagged + .. method:: without_tags + + .. versionadded:: 2021.1 + """ + + def __init__(self, tags: TagsType = frozenset()): + # For performance we assert rather than + # normalize the input. + assert isinstance(tags, FrozenSet) + assert all(isinstance(tag, Tag) for tag in tags) + self.tags = tags + self._check_uniqueness() + + def _normalize_tags(self, tags: TagOrIterableType) -> TagsType: + if isinstance(tags, Tag): + t = frozenset([tags]) + elif tags is None: + t = frozenset() + else: + t = frozenset(tags) + return t + + def _check_uniqueness(self): + unique_tag_descendants = set() + for tag in self.tags: + tag_unique_tag_descendants = _immediate_unique_tag_descendants( + type(tag)) + intersection = unique_tag_descendants & tag_unique_tag_descendants + if intersection: + raise NonUniqueTagError("Multiple tags are direct subclasses of " + "the following UniqueTag(s): " + f"{', '.join(d.__name__ for d in intersection)}") + else: + unique_tag_descendants.update(tag_unique_tag_descendants) + + def copy(self: T_co, **kwargs: Any) -> T_co: + """ + Returns of copy of *self* with the specified tags. This method + should be overridden by subclasses. + """ + raise NotImplementedError("The copy function is not implemented.") + + def tagged(self: T_co, tags: TagOrIterableType) -> T_co: + """ + Return a copy of *self* with the specified + tag or tags unioned. If *tags* is a :class:`pytools.tag.UniqueTag` + and other tags of this type are already present, an error is raised + Assumes `self.copy(tags=<NEW VALUE>)` is implemented. + + :arg tags: An instance of :class:`Tag` or + an iterable with instances therein. + """ + new_tags = self._normalize_tags(tags) + union_tags = self.tags | new_tags + cpy = self.copy(tags=union_tags) + return cpy + + def without_tags(self: T_co, + tags: TagOrIterableType, verify_existence: bool = True) -> T_co: + """ + Return a copy of *self* without the specified tags. + `self.copy(tags=<NEW VALUE>)` is implemented. + + :arg tags: An instance of :class:`Tag` or an iterable with instances + therein. + :arg verify_existence: If set + to `True`, this method raises an exception if not all tags specified + for removal are present in the original set of tags. Default `True` + """ + + to_remove = self._normalize_tags(tags) + new_tags = self.tags - to_remove + if verify_existence and len(new_tags) > len(self.tags) - len(to_remove): + raise ValueError("A tag specified for removal was not present.") + + return self.copy(tags=new_tags) # }}} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/pytools/version.py new/pytools-2021.1/pytools/version.py --- old/pytools-2020.4.4/pytools/version.py 2020-12-13 22:57:28.000000000 +0100 +++ new/pytools-2021.1/pytools/version.py 2021-01-09 01:08:43.000000000 +0100 @@ -1,3 +1,3 @@ -VERSION = (2020, 4, 4) +VERSION = (2021, 1) VERSION_STATUS = "" VERSION_TEXT = ".".join(str(x) for x in VERSION) + VERSION_STATUS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/pytools.egg-info/PKG-INFO new/pytools-2021.1/pytools.egg-info/PKG-INFO --- old/pytools-2020.4.4/pytools.egg-info/PKG-INFO 2020-12-13 22:58:32.000000000 +0100 +++ new/pytools-2021.1/pytools.egg-info/PKG-INFO 2021-01-09 20:56:50.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.2 Name: pytools -Version: 2020.4.4 +Version: 2021.1 Summary: A collection of tools for Python Home-page: http://pypi.python.org/pypi/pytools Author: Andreas Kloeckner diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytools-2020.4.4/test/test_pytools.py new/pytools-2021.1/test/test_pytools.py --- old/pytools-2020.4.4/test/test_pytools.py 2020-12-07 21:54:53.000000000 +0100 +++ new/pytools-2021.1/test/test_pytools.py 2021-01-09 01:08:43.000000000 +0100 @@ -283,6 +283,93 @@ # }}} +def test_tag(): + from pytools.tag import Taggable, Tag, UniqueTag, NonUniqueTagError + + # Need a subclass that defines the copy function in order to test. + class TaggableWithCopy(Taggable): + + def copy(self, **kwargs): + return TaggableWithCopy(kwargs["tags"]) + + class FairRibbon(Tag): + pass + + class BlueRibbon(FairRibbon): + pass + + class RedRibbon(FairRibbon): + pass + + class ShowRibbon(FairRibbon, UniqueTag): + pass + + class BestInShowRibbon(ShowRibbon): + pass + + class ReserveBestInShowRibbon(ShowRibbon): + pass + + class BestInClassRibbon(FairRibbon, UniqueTag): + pass + + best_in_show_ribbon = BestInShowRibbon() + reserve_best_in_show_ribbon = ReserveBestInShowRibbon() + blue_ribbon = BlueRibbon() + red_ribbon = RedRibbon() + best_in_class_ribbon = BestInClassRibbon() + + # Test that instantiation fails if tags is not a FrozenSet of Tags + with pytest.raises(AssertionError): + TaggableWithCopy(tags=[best_in_show_ribbon, reserve_best_in_show_ribbon, + blue_ribbon, red_ribbon]) + + # Test that instantiation fails if tags is not a FrozenSet of Tags + with pytest.raises(AssertionError): + TaggableWithCopy(tags=frozenset((1, reserve_best_in_show_ribbon, blue_ribbon, + red_ribbon))) + + # Test that instantiation fails if there are multiple instances + # of the same UniqueTag subclass + with pytest.raises(NonUniqueTagError): + TaggableWithCopy(tags=frozenset((best_in_show_ribbon, + reserve_best_in_show_ribbon, blue_ribbon, red_ribbon))) + + # Test that instantiation succeeds if there are multiple instances + # Tag subclasses. + t1 = TaggableWithCopy(frozenset([reserve_best_in_show_ribbon, blue_ribbon, + red_ribbon])) + assert t1.tags == frozenset((reserve_best_in_show_ribbon, red_ribbon, + blue_ribbon)) + + # Test that instantiation succeeds if there are multiple instances + # of UniqueTag of different subclasses. + t1 = TaggableWithCopy(frozenset([reserve_best_in_show_ribbon, + best_in_class_ribbon, blue_ribbon, + blue_ribbon])) + assert t1.tags == frozenset((reserve_best_in_show_ribbon, best_in_class_ribbon, + blue_ribbon)) + + # Test tagged() function + t2 = t1.tagged(red_ribbon) + print(t2.tags) + assert t2.tags == frozenset((reserve_best_in_show_ribbon, best_in_class_ribbon, + blue_ribbon, red_ribbon)) + + # Test that tagged() fails if a UniqueTag of the same subclass + # is alredy present + with pytest.raises(NonUniqueTagError): + t1.tagged(best_in_show_ribbon) + + # Test without_tags() function + t4 = t2.without_tags(red_ribbon) + assert t4.tags == t1.tags + + # Test that without_tags() fails if the tag is not present. + with pytest.raises(ValueError): + t4.without_tags(red_ribbon) + + if __name__ == "__main__": if len(sys.argv) > 1: exec(sys.argv[1])
