[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