[PATCH 06/21] Add a new utils module

Ralph Bean rbean at redhat.com
Wed May 6 14:10:41 UTC 2015


On Wed, May 06, 2015 at 01:53:02PM +0200, Mathieu Bridon wrote:
> From: Mathieu Bridon <bochecha at daitauha.fr>
> 
> This is going to contain random bits and pieces of utilities.
> 
> Currently, it only contains a cached_property class, which is going to
> be useful to create properties which are only computed the first time
> they are called, but have their return value cached for future calls.
> ---
>  src/pyrpkg/utils.py |  43 ++++++++++++++++++
>  test/test_utils.py  | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 166 insertions(+)
>  create mode 100644 src/pyrpkg/utils.py
>  create mode 100644 test/test_utils.py
> 
> diff --git a/src/pyrpkg/utils.py b/src/pyrpkg/utils.py
> new file mode 100644
> index 0000000..2d16ce0
> --- /dev/null
> +++ b/src/pyrpkg/utils.py
> @@ -0,0 +1,43 @@
> +# Copyright (c) 2015 - Red Hat Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify it
> +# under the terms of the GNU General Public License as published by the
> +# Free Software Foundation; either version 2 of the License, or (at your
> +# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
> +# the full text of the license.
> +
> +
> +"""Miscellaneous utilities
> +
> +This module contains a bunch of utilities used elsewhere in pyrpkg.
> +"""
> +
> +
> +class cached_property(property):
> +    """A property caching its return value
> +
> +    This is pretty much the same as a normal Python property, except that the
> +    decorated function is called only once. Its return value is then saved,
> +    subsequent calls will return it without executing the function any more.
> +
> +    Example:
> +        >>> class Foo(object):
> +        ...     @cached_property
> +        ...     def bar(self):
> +        ...         print("Executing Foo.bar...")
> +        ...         return 42
> +        ...
> +        >>> f = Foo()
> +        >>> f.bar
> +        Executing Foo.bar...
> +        42
> +        >>> f.bar
> +        42
> +    """
> +    def __get__(self, inst, type=None):
> +        try:
> +            return getattr(inst, '_%s' % self.fget.__name__)
> +        except AttributeError:
> +            v = super(cached_property, self).__get__(inst, type)
> +            setattr(inst, '_%s' % self.fget.__name__, v)
> +            return v
> diff --git a/test/test_utils.py b/test/test_utils.py
> new file mode 100644
> index 0000000..047dde5
> --- /dev/null
> +++ b/test/test_utils.py
> @@ -0,0 +1,123 @@
> +import os
> +import sys
> +import unittest
> +
> +old_path = list(sys.path)
> +src_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../src')
> +sys.path.insert(0, src_path)
> +from pyrpkg.utils import cached_property
> +sys.path = old_path
> +
> +
> +class CachedPropertyTestCase(unittest.TestCase):
> +    def test_computed_only_once(self):
> +        class Foo(object):
> +            @cached_property
> +            def foo(self):
> +                runs.append("run once")
> +                return 42
> +
> +        runs = []
> +
> +        f = Foo()
> +        self.assertEqual(len(runs), 0)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(runs), 1)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(runs), 1)
> +
> +    def test_not_shared_between_properties(self):
> +        class Foo(object):
> +            @cached_property
> +            def foo(self):
> +                foo_runs.append("run once")
> +                return 42
> +
> +            @cached_property
> +            def bar(self):
> +                bar_runs.append("run once")
> +                return 43
> +
> +        foo_runs = []
> +        bar_runs = []
> +
> +        f = Foo()
> +        self.assertEqual(len(foo_runs), 0)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +
> +        self.assertEqual(len(bar_runs), 0)
> +        self.assertEqual(f.bar, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +        self.assertEqual(f.bar, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +
> +    def test_not_shared_between_instances(self):
> +        class Foo(object):
> +            @cached_property
> +            def foo(self):
> +                foo_runs.append("run once")
> +                return 42
> +
> +        class Bar(object):
> +            @cached_property
> +            def foo(self):
> +                bar_runs.append("run once")
> +                return 43
> +
> +        foo_runs = []
> +        bar_runs = []
> +
> +        f = Foo()
> +        self.assertEqual(len(foo_runs), 0)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +
> +        b = Bar()
> +        self.assertEqual(len(bar_runs), 0)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +
> +    def test_not_shared_when_inheriting(self):
> +        class Foo(object):
> +            @cached_property
> +            def foo(self):
> +                foo_runs.append("run once")
> +                return 42
> +
> +        class Bar(Foo):
> +            @cached_property
> +            def foo(self):
> +                bar_runs.append("run once")
> +                return 43
> +
> +        foo_runs = []
> +        bar_runs = []
> +
> +        b = Bar()
> +        self.assertEqual(len(bar_runs), 0)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +
> +        f = Foo()
> +        self.assertEqual(len(foo_runs), 0)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +        self.assertEqual(f.foo, 42)
> +        self.assertEqual(len(foo_runs), 1)
> +
> +        bar_runs = []
> +        b = Bar()
> +        self.assertEqual(len(bar_runs), 0)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> +        self.assertEqual(b.foo, 43)
> +        self.assertEqual(len(bar_runs), 1)
> -- 
> 2.1.0

Nice tests for this!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.fedoraproject.org/pipermail/rel-eng/attachments/20150506/a13ef8a0/attachment-0001.sig>


More information about the rel-eng mailing list