Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-tri.declarative for openSUSE:Factory checked in at 2021-01-25 18:23:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tri.declarative (Old) and /work/SRC/openSUSE:Factory/.python-tri.declarative.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tri.declarative" Mon Jan 25 18:23:35 2021 rev:6 rq:866163 version:5.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tri.declarative/python-tri.declarative.changes 2020-07-26 16:19:39.328825765 +0200 +++ /work/SRC/openSUSE:Factory/.python-tri.declarative.new.28504/python-tri.declarative.changes 2021-01-25 18:23:53.388452139 +0100 @@ -1,0 +2,18 @@ +Fri Jan 22 15:42:07 UTC 2021 - Benjamin Greiner <c...@bnavigator.de> + +- Update to 5.7.0 + + * Make getattr_path more in line with the standard library + getattr semantics + * If a default value is provided, return that on missing + attributes + * If no default value is given, give a more detailed + error message of what was missing + * Added the special case of the empty path returning the object +- Changes in 5.6.0 + * Fix corner case of class Meta failing to merge with + None namespace values +- Changes in 5.5.0 + * Include tri.struct 4.x as possible requirement + +------------------------------------------------------------------- Old: ---- tri.declarative-5.4.1.tar.gz New: ---- tri.declarative-5.7.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tri.declarative.spec ++++++ --- /var/tmp/diff_new_pack.VdKmHw/_old 2021-01-25 18:23:54.128453215 +0100 +++ /var/tmp/diff_new_pack.VdKmHw/_new 2021-01-25 18:23:54.128453215 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-tri.declarative # -# 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-tri.declarative -Version: 5.4.1 +Version: 5.7.0 Release: 0 Summary: Python class decorators in the style of Django model classes License: BSD-3-Clause @@ -53,13 +53,12 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -# test_namespace_missing_call_target - pytest5 incompatible usage -# https://github.com/TriOptima/tri.declarative/issues/9 -%pytest -k 'not test_namespace_missing_call_target' +%pytest %files %{python_files} %doc AUTHORS.rst README.rst %license LICENSE -%{python_sitelib}/* +%{python_sitelib}/tri_declarative +%{python_sitelib}/tri.declarative-%{version}*-info %changelog ++++++ tri.declarative-5.4.1.tar.gz -> tri.declarative-5.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/HISTORY.rst new/tri.declarative-5.7.0/HISTORY.rst --- old/tri.declarative-5.4.1/HISTORY.rst 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/HISTORY.rst 2020-12-18 13:01:42.000000000 +0100 @@ -1,6 +1,30 @@ Changelog --------- +5.7.0 (2020-12-18) +------------------ + +* Make getattr_path more in line with the standard library getattr semantics + + If a default value is provided, return that on missing attributes + + If no default value is given, give a more detailed error message of what was missing + +* Added the special case of the empty path returning the object + + +5.6.0 (2020-12-02) +------------------ + +* Fix corner case of class Meta failing to merge with None namespace values + + +5.5.0 (2020-08-21) +------------------ + +* Include tri.struct 4.x as possible requirement + + 5.4.1 (2020-06-34) ------------------ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/lib/tri_declarative/__init__.py new/tri.declarative-5.7.0/lib/tri_declarative/__init__.py --- old/tri.declarative-5.4.1/lib/tri_declarative/__init__.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/lib/tri_declarative/__init__.py 2020-12-18 13:01:42.000000000 +0100 @@ -39,7 +39,7 @@ ) from .with_meta import with_meta -__version__ = '5.4.1' +__version__ = '5.7.0' __all__ = [ 'assert_kwargs_empty', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/lib/tri_declarative/namespace.py new/tri.declarative-5.7.0/lib/tri_declarative/namespace.py --- old/tri.declarative-5.4.1/lib/tri_declarative/namespace.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/lib/tri_declarative/namespace.py 2020-12-18 13:01:42.000000000 +0100 @@ -126,18 +126,35 @@ return __target__ -def getattr_path(obj, path): +_MISSING = object() + + +def getattr_path(obj, path, default=_MISSING): """ Get an attribute path, as defined by a string separated by '__'. getattr_path(foo, 'a__b__c') is roughly equivalent to foo.a.b.c but will short circuit to return None if something on the path is None. + If no default value is provided AttributeError is raised if an attribute + is missing somewhere along the path. If a default value is provided that + value is returned. """ - path = path.split('__') - for name in path: - obj = getattr(obj, name) - if obj is None: + if path == '': + return obj + current = obj + parts = path.split('__') + for name in parts: + if default is _MISSING: + try: + current = getattr(current, name) + except AttributeError as e: + raise AttributeError(f"'{type(obj).__name__}' object has no attribute path '{path}', since {e}") + else: + current = getattr(current, name, _MISSING) + if current is _MISSING: + return default + if current is None: return None - return obj + return current def setattr_path(obj, path, value): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/lib/tri_declarative/util.py new/tri.declarative-5.7.0/lib/tri_declarative/util.py --- old/tri.declarative-5.4.1/lib/tri_declarative/util.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/lib/tri_declarative/util.py 2020-12-18 13:01:42.000000000 +0100 @@ -44,8 +44,12 @@ new_args = args for k, v in kwargs.items(): - if merge_namespaces and isinstance(new_kwargs.get(k, None), Namespace): - new_kwargs[k] = Namespace(new_kwargs[k], v) + new_value = new_kwargs.get(k, None) + if merge_namespaces and isinstance(new_value, Namespace): + if v is not None: + new_kwargs[k] = Namespace(new_value, v) + else: + new_kwargs[k] = None else: new_kwargs[k] = v diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/requirements.txt new/tri.declarative-5.7.0/requirements.txt --- old/tri.declarative-5.4.1/requirements.txt 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/requirements.txt 2020-12-18 13:01:42.000000000 +0100 @@ -1 +1 @@ -tri.struct>=3.0.0,<4.0.0 +tri.struct>=3.0.0,<5.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/setup.cfg new/tri.declarative-5.7.0/setup.cfg --- old/tri.declarative-5.4.1/setup.cfg 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/setup.cfg 2020-12-18 13:01:42.000000000 +0100 @@ -1,7 +1,7 @@ [wheel] universal = 1 -[tools:pytest] +[tool:pytest] testpaths=tests # --strict: warnings become errors. # -r fEsxXw: show extra test summary info for everything. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/tests/test_dispatch.py new/tri.declarative-5.7.0/tests/test_dispatch.py --- old/tri.declarative-5.4.1/tests/test_dispatch.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/tests/test_dispatch.py 2020-12-18 13:01:42.000000000 +0100 @@ -84,3 +84,27 @@ return a + b + c + x + y assert foo('1', '2', '3', bar__quux__title='7', baz__a='A', baz__b='B', baz__c='C') == '1235X7ABC' + + +def test_semantics_after_none_from_meta(): + class MyForm: + @dispatch( + actions=None + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm(actions__magic__display_name="A magic button") + assert form.kwargs == Namespace(actions__magic__display_name="A magic button") + + +def test_none_semantics_over_meta(): + class MyForm: + @dispatch( + actions__magic__display_name="A magic button" + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm(actions=None) + assert form.kwargs == Namespace(actions=None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/tests/test_namespace.py new/tri.declarative-5.7.0/tests/test_namespace.py --- old/tri.declarative-5.4.1/tests/test_namespace.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/tests/test_namespace.py 2020-12-18 13:01:42.000000000 +0100 @@ -38,6 +38,31 @@ assert foo.bar is None +def test_getattr_empty_path(): + obj = object() + assert getattr_path(obj, '') is obj + + +def test_getattr_missing_attribute(): + obj = object() + + with pytest.raises(AttributeError) as e: + getattr_path(obj, 'foo') + assert str(e.value) == "'object' object has no attribute path 'foo', since 'object' object has no attribute 'foo'" + + with pytest.raises(AttributeError) as e: + getattr_path(Struct(foo=object()), 'foo__bar') + assert str(e.value) == "'Struct' object has no attribute path 'foo__bar', since 'object' object has no attribute 'bar'" + + +def test_getattr_default(): + assert getattr_path(object(), 'foo', 17) == 17 + assert getattr_path(Struct(foo=object()), 'foo__bar', 42) == 42 + + assert getattr_path(object(), 'foo', default=17) == 17 + assert getattr_path(Struct(foo=object()), 'foo__bar', default=42) == 42 + + def test_setdefaults_path_1(): assert setdefaults_path(dict(), x=17) == dict(x=17) @@ -471,3 +496,11 @@ assert isinstance(EMPTY, Namespace) with pytest.raises(TypeError): EMPTY['foo'] = 'bar' + + +def test_none_semantics(): + assert Namespace(Namespace(foo=None), foo__bar='baz') == Namespace(foo__bar='baz') + + +def test_none_overwrite_semantics(): + assert Namespace(Namespace(foo__bar='baz'), foo=None) == Namespace(foo=None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/tests/test_with_meta.py new/tri.declarative-5.7.0/tests/test_with_meta.py --- old/tri.declarative-5.4.1/tests/test_with_meta.py 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/tests/test_with_meta.py 2020-12-18 13:01:42.000000000 +0100 @@ -232,6 +232,106 @@ ) +def test_semantics_after_none_from_meta(): + @with_meta + class MyForm: + class Meta: + actions = None + + @dispatch + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm(actions__magic__display_name="A magic button") + assert form.kwargs == Namespace(actions__magic__display_name="A magic button") + + +def test_none_semantics_over_meta(): + @with_meta + class MyForm: + class Meta: + actions__magic__display_name = "A magic button" + + @dispatch + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm(actions=None) + assert form.kwargs == Namespace(actions=None) + + +def test_dispatch_semantics_after_none_from_meta(): + @with_meta + class MyForm: + class Meta: + actions = None + + @dispatch( + actions__magic__display_name="A magic button" + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm() + assert form.kwargs == Namespace(actions=None) + + +def test_dispatch_none_semantics_after_meta(): + @with_meta + class MyForm: + class Meta: + actions__magic__display_name = "A magic button" + + @dispatch( + actions=None + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = MyForm() + assert form.kwargs == Namespace(actions__magic__display_name="A magic button") + + +def test_dispatch_none_semantics_after_superclass_meta(): + @with_meta + class MyForm: + class Meta: + actions__magic__display_name = "A magic button" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + class SubForm(MyForm): + @dispatch( + actions=None + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = SubForm() + assert form.kwargs == Namespace(actions=None) + + +def test_dispatch_semantics_after_none_superclass_meta(): + @with_meta + class MyForm: + class Meta: + actions = None + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + class SubForm(MyForm): + @dispatch( + actions__magic__display_name="A magic button" + ) + def __init__(self, **kwargs): + self.kwargs = kwargs + + form = SubForm() + assert form.kwargs == Namespace(actions__magic__display_name="A magic button") + + def test_meta_staticmethod(): @with_meta class Foo: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tri.declarative-5.4.1/tox.ini new/tri.declarative-5.7.0/tox.ini --- old/tri.declarative-5.4.1/tox.ini 2020-06-24 09:04:52.000000000 +0200 +++ new/tri.declarative-5.7.0/tox.ini 2020-12-18 13:01:42.000000000 +0100 @@ -37,7 +37,7 @@ basepython = python3.7 usedevelop = True commands = - {envpython} -m flake8 lib/ tests setup.py {posargs} + {envpython} -m flake8 lib/tri_declarative tests setup.py {posargs} deps = flake8