Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-lazy for openSUSE:Factory checked in at 2023-12-08 22:32:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-lazy (Old) and /work/SRC/openSUSE:Factory/.python-lazy.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-lazy" Fri Dec 8 22:32:07 2023 rev:7 rq:1131731 version:1.6 Changes: -------- --- /work/SRC/openSUSE:Factory/python-lazy/python-lazy.changes 2023-06-12 15:26:38.951122195 +0200 +++ /work/SRC/openSUSE:Factory/.python-lazy.new.25432/python-lazy.changes 2023-12-08 22:32:37.937277809 +0100 @@ -1,0 +2,9 @@ +Thu Dec 7 22:32:35 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 1.6: + * Implement ``lazy.__set_name__()`` which helps in cases like + * ``foo=lazy(_foo)``. + * Pin sphinx and sphinx-rtd-theme versions in docs extra. + * Fix stray characters in keywords. + +------------------------------------------------------------------- Old: ---- lazy-1.5.tar.gz New: ---- lazy-1.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-lazy.spec ++++++ --- /var/tmp/diff_new_pack.KRJZbp/_old 2023-12-08 22:32:38.453296796 +0100 +++ /var/tmp/diff_new_pack.KRJZbp/_new 2023-12-08 22:32:38.453296796 +0100 @@ -16,10 +16,9 @@ # -%global modname lazy %{?sle15_python_module_pythons} Name: python-lazy -Version: 1.5 +Version: 1.6 Release: 0 Summary: Lazy attributes for Python objects License: BSD-2-Clause @@ -58,6 +57,6 @@ %files %{python_files} %doc README.rst CHANGES.rst %license LICENSE -%{python_sitelib}/%{modname} -%{python_sitelib}/%{modname}-%{version}*-info +%{python_sitelib}/lazy +%{python_sitelib}/lazy-%{version}*-info ++++++ lazy-1.5.tar.gz -> lazy-1.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/CHANGES.rst new/lazy-1.6/CHANGES.rst --- old/lazy-1.5/CHANGES.rst 2022-09-18 17:56:59.000000000 +0200 +++ new/lazy-1.6/CHANGES.rst 2023-09-14 15:23:31.000000000 +0200 @@ -1,6 +1,31 @@ Changelog ========= +1.6 - 2023-09-14 +---------------- + +- Implement ``lazy.__set_name__()`` which helps in cases like + ``foo=lazy(_foo)``. + [stefan] + +- Update tox.ini for latest tox. + [stefan] + +- Add GitHub CI workflow. + [stefan] + +- Add .readthedocs.yaml file. + [stefan] + +- Pin sphinx and sphinx-rtd-theme versions in docs extra. + [stefan] + +- Add mypy extra which installs mypy. + [stefan] + +- Fix stray characters in keywords. + [stefan] + 1.5 - 2022-09-18 ---------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/LICENSE new/lazy-1.6/LICENSE --- old/lazy-1.5/LICENSE 2022-06-20 19:56:00.000000000 +0200 +++ new/lazy-1.6/LICENSE 2023-08-22 22:17:30.000000000 +0200 @@ -1,4 +1,4 @@ -Copyright (c) 2011-2022 Stefan H. Holek +Copyright (c) 2011-2023 Stefan H. Holek All rights reserved. Redistribution and use in source and binary forms, with or without diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/PKG-INFO new/lazy-1.6/PKG-INFO --- old/lazy-1.5/PKG-INFO 2022-09-18 18:10:27.805467400 +0200 +++ new/lazy-1.6/PKG-INFO 2023-09-14 15:31:09.910073500 +0200 @@ -1,14 +1,13 @@ Metadata-Version: 2.1 Name: lazy -Version: 1.5 +Version: 1.6 Summary: Lazy attributes for Python objects Home-page: https://github.com/stefanholek/lazy Author: Stefan H. Holek Author-email: ste...@epy.co.at License: BSD-2-Clause Project-URL: Documentation, https://lazy.readthedocs.io/en/stable/ -Keywords: decorator,lazy,lazy attribute,descriptor,property' -Platform: UNKNOWN +Keywords: decorator,lazy,lazy attribute,descriptor,property Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License @@ -18,6 +17,7 @@ Classifier: Programming Language :: Python :: 3 Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7 Description-Content-Type: text/x-rst +Provides-Extra: mypy Provides-Extra: docs License-File: LICENSE @@ -76,6 +76,10 @@ def get(self, uid, default=None): return self.store.get(uid, default) + def close(self): + if 'store' in self.__dict__: + self.store.close() + Another application area is caching: .. code-block:: python @@ -101,6 +105,31 @@ Changelog ========= +1.6 - 2023-09-14 +---------------- + +- Implement ``lazy.__set_name__()`` which helps in cases like + ``foo=lazy(_foo)``. + [stefan] + +- Update tox.ini for latest tox. + [stefan] + +- Add GitHub CI workflow. + [stefan] + +- Add .readthedocs.yaml file. + [stefan] + +- Pin sphinx and sphinx-rtd-theme versions in docs extra. + [stefan] + +- Add mypy extra which installs mypy. + [stefan] + +- Fix stray characters in keywords. + [stefan] + 1.5 - 2022-09-18 ---------------- @@ -158,5 +187,3 @@ ---------------- - Initial release. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/README.rst new/lazy-1.6/README.rst --- old/lazy-1.5/README.rst 2022-09-18 17:36:38.000000000 +0200 +++ new/lazy-1.6/README.rst 2023-09-08 13:36:52.000000000 +0200 @@ -53,6 +53,10 @@ def get(self, uid, default=None): return self.store.get(uid, default) + def close(self): + if 'store' in self.__dict__: + self.store.close() + Another application area is caching: .. code-block:: python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy/examples/example.py new/lazy-1.6/lazy/examples/example.py --- old/lazy-1.5/lazy/examples/example.py 2022-09-18 17:21:08.000000000 +0200 +++ new/lazy-1.6/lazy/examples/example.py 2023-09-11 11:36:18.000000000 +0200 @@ -1,6 +1,8 @@ from datetime import date from lazy import lazy +from typing import TypeVar, Any + class C(object): @@ -51,16 +53,11 @@ # Inherit from lazy -# Also see https://github.com/python/mypy/pull/8573/files -from typing import TYPE_CHECKING, TypeVar - _R = TypeVar('_R') -if TYPE_CHECKING: - class cached(lazy[_R]): pass -else: - class cached(lazy): pass +class cached(lazy[_R]): + pass class E(object): @@ -117,18 +114,27 @@ # Check __class_getitem__ declaration -if TYPE_CHECKING: - from typing import Any - from types import GenericAlias - - class supercached(lazy[_R]): - @classmethod - def __class_getitem__(cls, params: Any) -> GenericAlias: - return super().__class_getitem__(params) +def i() -> None: + lazy.__class_getitem__(None) + cached.__class_getitem__(None) + + +# Check __set_name__ declaration +class Z: + @lazy + def foo(self) -> int: + return 1 + + +def j() -> None: + Z.foo.__set_name__(Z, 'bar') + Z.foo.__name__ == 'bar' if __name__ == '__main__': f() g() h() + i() + j() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy/lazy.py new/lazy-1.6/lazy/lazy.py --- old/lazy-1.5/lazy/lazy.py 2022-09-18 17:21:08.000000000 +0200 +++ new/lazy-1.6/lazy/lazy.py 2023-08-27 20:36:25.000000000 +0200 @@ -20,16 +20,19 @@ self.__func = func functools.wraps(self.__func)(self) - def __get__(self, inst, inst_cls): + def __set_name__(self, owner, name): + self.__name__ = name + + def __get__(self, inst, owner): if inst is None: return self if not hasattr(inst, '__dict__'): - raise AttributeError("'%s' object has no attribute '__dict__'" % (inst_cls.__name__,)) + raise AttributeError("'%s' object has no attribute '__dict__'" % (owner.__name__,)) name = self.__name__ if name.startswith('__') and not name.endswith('__'): - name = '_%s%s' % (inst_cls.__name__, name) + name = '_%s%s' % (owner.__name__, name) value = inst.__dict__.get(name, _marker) if value is _marker: @@ -43,16 +46,16 @@ This obviously violates the lazy contract. A subclass of lazy may however have a contract where invalidation is appropriate. """ - inst_cls = inst.__class__ + owner = inst.__class__ if not hasattr(inst, '__dict__'): - raise AttributeError("'%s' object has no attribute '__dict__'" % (inst_cls.__name__,)) + raise AttributeError("'%s' object has no attribute '__dict__'" % (owner.__name__,)) if name.startswith('__') and not name.endswith('__'): - name = '_%s%s' % (inst_cls.__name__, name) + name = '_%s%s' % (owner.__name__, name) - if not isinstance(getattr(inst_cls, name), cls): - raise AttributeError("'%s.%s' is not a %s attribute" % (inst_cls.__name__, name, cls.__name__)) + if not isinstance(getattr(owner, name), cls): + raise AttributeError("'%s.%s' is not a %s attribute" % (owner.__name__, name, cls.__name__)) if name in inst.__dict__: del inst.__dict__[name] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy/lazy.pyi new/lazy-1.6/lazy/lazy.pyi --- old/lazy-1.5/lazy/lazy.pyi 2022-09-18 17:21:08.000000000 +0200 +++ new/lazy-1.6/lazy/lazy.pyi 2023-09-11 07:52:53.000000000 +0200 @@ -11,14 +11,17 @@ class lazy(Generic[_R]): __func: Callable[[Any], _R] + __name__: str def __init__(self, func: Callable[[Any], _R]) -> None: ... + def __set_name__(self, owner: Type[Any], name: str) -> None: ... + @overload - def __get__(self, inst: None, inst_cls: Optional[Type[Any]] = ...) -> lazy[_R]: ... + def __get__(self, inst: None, owner: Optional[Type[Any]] = ...) -> lazy[_R]: ... @overload - def __get__(self, inst: object, inst_cls: Optional[Type[Any]] = ...) -> _R: ... + def __get__(self, inst: object, owner: Optional[Type[Any]] = ...) -> _R: ... @classmethod def invalidate(cls, inst: object, name: str) -> None: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy/tests/test_lazy.py new/lazy-1.6/lazy/tests/test_lazy.py --- old/lazy-1.5/lazy/tests/test_lazy.py 2022-09-18 17:21:08.000000000 +0200 +++ new/lazy-1.6/lazy/tests/test_lazy.py 2023-09-08 07:52:50.000000000 +0200 @@ -1,4 +1,6 @@ import sys +import functools +import inspect import unittest from lazy import lazy @@ -230,6 +232,207 @@ self.assertEqual(super(Bar, b).foo, 'foo') self.assertEqual(b.foo, 'foo') + def test_inherited_attribute(self): + # Inherited attributes should be stored in the instance they are + # called on. + called = [] + + class Foo(object): + @lazy + def foo(self): + called.append('foo') + return 'foo' + + class Bar(Foo): + pass + + b = Bar() + self.assertFalse('foo' in b.__dict__) + self.assertEqual(b.foo, 'foo') + self.assertEqual(len(called), 1) + self.assertEqual(b.foo, 'foo') + self.assertEqual(len(called), 1) + self.assertTrue('foo' in b.__dict__) + + def test_inherited_private_attribute(self): + # Inherited private attributes should be stored in the instance + # they are called on. + called = [] + + class Foo(object): + @lazy + def foo(self): + called.append('foo') + return self.__bar + @lazy + def __bar(self): + called.append('bar') + return 'bar' + + class Bar(Foo): + pass + + b = Bar() + self.assertFalse('foo' in b.__dict__) + self.assertEqual(b.foo, 'bar') + self.assertEqual(len(called), 2) + self.assertEqual(b.foo, 'bar') + self.assertEqual(len(called), 2) + self.assertTrue('foo' in b.__dict__) + + if sys.version_info >= (3,): + self.assertTrue('_Foo__bar' in b.__dict__) + else: + self.assertTrue('_Bar__bar' in b.__dict__) # ! + + def test_find_descriptors(self): + # It should be possible to find all lazy attributes of an object. + + def other(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + + class Foo(object): + @lazy + def foo(self): + return 'foo' + @lazy + def __bar(self): + return 'bar' + @lazy + @other + def baz(self): + return 'baz' + @other + @lazy + def quux(self): + return 'quux' + + f = Foo() + cls = f.__class__ + + descriptors = [] + for name in cls.__dict__: + if isinstance(getattr(cls, name), lazy): + descriptors.append(name) + + self.assertEqual(sorted(descriptors), ['_Foo__bar', 'baz', 'foo']) + + def test_find_inherited_descriptors(self): + # It should be possible to find inherited lazy attributes of + # an object. + + def other(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + + class Foo(object): + @lazy + def foo(self): + return 'foo' + @lazy + def __bar(self): + return 'bar' + @lazy + @other + def baz(self): + return 'baz' + @other + @lazy + def quux(self): + return 'quux' + + class Bar(Foo): + @lazy + def __bar(self): + return 'bar' + + b = Bar() + + descriptors = [] + for cls in inspect.getmro(b.__class__): + for name in cls.__dict__: + if isinstance(getattr(cls, name), lazy): + descriptors.append(name) + + self.assertEqual(sorted(descriptors), ['_Bar__bar', '_Foo__bar', 'baz', 'foo']) + + def test_other_decorators_must_use_functools_wraps(self): + # Other decorators may be combined with lazy if + # a) lazy is the first (outermost) decorator, and + # b) the decorators properly use functools.wraps. + called = [] + + def other(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + + class Foo(object): + @lazy + @other + def foo(self): + called.append('foo') + return 1 + + f = Foo() + self.assertTrue(isinstance(Foo.foo, lazy)) + self.assertTrue(f.foo is f.foo) + self.assertTrue(f.foo is f.__dict__['foo']) + self.assertEqual(len(called), 1) + + def test_lazy_decorator_must_come_first(self): + # Things break when lazy is not the outermost decorator. + called = [] + + def other(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + return wrapper + + class Foo(object): + @other + @lazy + def foo(self): + called.append('foo') + return 1 + + f = Foo() + self.assertFalse(isinstance(Foo.foo, lazy)) + self.assertNotEqual(f.foo, 1) + self.assertException(TypeError, + "'lazy' object is not callable", + f.foo) + + def test_set_name(self): + # In Python >= 3.6 __set_name__ is called on lazy attributes. + called = [] + + class Foo(object): + def _foo(self): + called.append('foo') + return 1 + foo = lazy(_foo) + + f = Foo() + self.assertTrue(isinstance(Foo.foo, lazy)) + self.assertTrue(f.foo is f.foo) + + if sys.version_info >= (3, 6): + self.assertEqual(Foo.foo.__name__, 'foo') + self.assertTrue(f.foo is f.__dict__['foo']) + else: + self.assertEqual(Foo.foo.__name__, '_foo') + self.assertTrue(f.foo is f.__dict__['_foo']) + + self.assertEqual(len(called), 1) + class InvalidateTests(TestCase): @@ -413,6 +616,31 @@ "'Foo' object has no attribute '__dict__'", lazy.invalidate, f, 'foo') + def test_invalidate_inherited_attribute(self): + # It should be possible to invalidate an inherited lazy + # attribute. + called = [] + + class Foo(object): + @lazy + def foo(self): + called.append('foo') + return 1 + + class Bar(Foo): + pass + + b = Bar() + self.assertEqual(b.foo, 1) + self.assertEqual(len(called), 1) + self.assertEqual(b.foo, 1) + self.assertEqual(len(called), 1) + + lazy.invalidate(b, 'foo') + + self.assertEqual(b.foo, 1) + self.assertEqual(len(called), 2) + # A lazy subclass class cached(lazy): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy.egg-info/PKG-INFO new/lazy-1.6/lazy.egg-info/PKG-INFO --- old/lazy-1.5/lazy.egg-info/PKG-INFO 2022-09-18 18:10:27.000000000 +0200 +++ new/lazy-1.6/lazy.egg-info/PKG-INFO 2023-09-14 15:31:09.000000000 +0200 @@ -1,14 +1,13 @@ Metadata-Version: 2.1 Name: lazy -Version: 1.5 +Version: 1.6 Summary: Lazy attributes for Python objects Home-page: https://github.com/stefanholek/lazy Author: Stefan H. Holek Author-email: ste...@epy.co.at License: BSD-2-Clause Project-URL: Documentation, https://lazy.readthedocs.io/en/stable/ -Keywords: decorator,lazy,lazy attribute,descriptor,property' -Platform: UNKNOWN +Keywords: decorator,lazy,lazy attribute,descriptor,property Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License @@ -18,6 +17,7 @@ Classifier: Programming Language :: Python :: 3 Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7 Description-Content-Type: text/x-rst +Provides-Extra: mypy Provides-Extra: docs License-File: LICENSE @@ -76,6 +76,10 @@ def get(self, uid, default=None): return self.store.get(uid, default) + def close(self): + if 'store' in self.__dict__: + self.store.close() + Another application area is caching: .. code-block:: python @@ -101,6 +105,31 @@ Changelog ========= +1.6 - 2023-09-14 +---------------- + +- Implement ``lazy.__set_name__()`` which helps in cases like + ``foo=lazy(_foo)``. + [stefan] + +- Update tox.ini for latest tox. + [stefan] + +- Add GitHub CI workflow. + [stefan] + +- Add .readthedocs.yaml file. + [stefan] + +- Pin sphinx and sphinx-rtd-theme versions in docs extra. + [stefan] + +- Add mypy extra which installs mypy. + [stefan] + +- Fix stray characters in keywords. + [stefan] + 1.5 - 2022-09-18 ---------------- @@ -158,5 +187,3 @@ ---------------- - Initial release. - - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/lazy.egg-info/requires.txt new/lazy-1.6/lazy.egg-info/requires.txt --- old/lazy-1.5/lazy.egg-info/requires.txt 2022-09-18 18:10:27.000000000 +0200 +++ new/lazy-1.6/lazy.egg-info/requires.txt 2023-09-14 15:31:09.000000000 +0200 @@ -1,4 +1,7 @@ [docs] -sphinx -sphinx-rtd-theme +sphinx==5.3.0 +sphinx-rtd-theme==1.0.0 + +[mypy] +mypy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/setup.cfg new/lazy-1.6/setup.cfg --- old/lazy-1.5/setup.cfg 2022-09-18 18:10:27.806642000 +0200 +++ new/lazy-1.6/setup.cfg 2023-09-14 15:31:09.910926800 +0200 @@ -1,6 +1,6 @@ [metadata] name = lazy -version = 1.5 +version = 1.6 description = Lazy attributes for Python objects long_description = file: README.rst, CHANGES.rst long_description_content_type = text/x-rst @@ -12,7 +12,7 @@ Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 3 -keywords = decorator, lazy, lazy attribute, descriptor, property', +keywords = decorator, lazy, lazy attribute, descriptor, property author = Stefan H. Holek author_email = ste...@epy.co.at url = https://github.com/stefanholek/lazy @@ -37,9 +37,11 @@ py.typed [options.extras_require] +mypy = + mypy docs = - sphinx - sphinx-rtd-theme + sphinx == 5.3.0 + sphinx-rtd-theme == 1.0.0 [egg_info] tag_build = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lazy-1.5/tox.ini new/lazy-1.6/tox.ini --- old/lazy-1.5/tox.ini 2022-09-18 17:21:08.000000000 +0200 +++ new/lazy-1.6/tox.ini 2023-09-11 11:36:21.000000000 +0200 @@ -4,20 +4,16 @@ # "pip install tox" and then run "tox" from this directory. [tox] -envlist = py27, py36, py37, py38, py39, py310, py311, pypy, pypy3, mypy +envlist = py27, py36, py37, py38, py39, py310, py311, py312, pypy27, pypy38, mypy +requires = virtualenv<20.22.0 [testenv] +package = wheel +wheel_build_env = .pkg commands = python -m unittest discover {posargs} -[testenv:pypy] -basepython = pypy-2.7 - -[testenv:pypy3] -basepython = pypy-3.8 - [testenv:mypy] -basepython = python3.10 -deps = mypy +extras = mypy commands = python -m mypy --strict --exclude lazy/tests {posargs} lazy python lazy/examples/example.py