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])

Reply via email to