Hello community, here is the log from the commit of package python-param for openSUSE:Factory checked in at 2019-06-06 18:16:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-param (Old) and /work/SRC/openSUSE:Factory/.python-param.new.4811 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-param" Thu Jun 6 18:16:01 2019 rev:9 rq:707138 version:1.9.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-param/python-param.changes 2019-04-30 13:00:51.590165579 +0200 +++ /work/SRC/openSUSE:Factory/.python-param.new.4811/python-param.changes 2019-06-06 18:16:17.192704227 +0200 @@ -1,0 +2,6 @@ +Mon Jun 3 10:57:01 UTC 2019 - Tomáš Chvátal <[email protected]> + +- Update to 1.9.1: + * No upstream changelog + +------------------------------------------------------------------- Old: ---- v1.9.0.tar.gz New: ---- v1.9.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-param.spec ++++++ --- /var/tmp/diff_new_pack.2zRJGq/_old 2019-06-06 18:16:17.712704074 +0200 +++ /var/tmp/diff_new_pack.2zRJGq/_new 2019-06-06 18:16:17.712704074 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-param -Version: 1.9.0 +Version: 1.9.1 Release: 0 Summary: Declarative Python programming using Parameters License: BSD-3-Clause ++++++ v1.9.0.tar.gz -> v1.9.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/param-1.9.0/.travis.yml new/param-1.9.1/.travis.yml --- old/param-1.9.0/.travis.yml 2019-04-02 23:08:36.000000000 +0200 +++ new/param-1.9.1/.travis.yml 2019-06-01 00:46:54.000000000 +0200 @@ -131,11 +131,9 @@ - pip install -e . script: # TODO: nbsite commands will be simplified eventually... - - nbsite generate-rst --org ioam --project-name param --repo param --examples=./examples --doc=./doc + - nbsite generate-rst --org pyviz --repo param - mkdir doc/Reference_Manual && nbsite_generate_modules.py param -d ./doc/Reference_Manual -n param -e tests - - nbsite build --what=html --examples=examples --doc=doc --output=builtdocs --examples-assets='' - - touch ./builtdocs/.nojekyll # for github pages - - nbsite_cleandisthtml.py ./builtdocs take_a_chance + - nbsite build --examples-assets='' deploy: - provider: pages skip_cleanup: true @@ -148,13 +146,13 @@ - <<: *website stage: website_dev - env: DESC="ioam-docs.github.io/parambokeh-dev" + env: DESC="pyviz-dev.github.io/param" deploy: - provider: pages skip_cleanup: true github_token: $GITHUB_TOKEN local_dir: ./builtdocs - repo: ioam-docs/param-dev + repo: pyviz-dev/param on: tags: true all_branches: true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/param-1.9.0/param/__init__.py new/param-1.9.1/param/__init__.py --- old/param-1.9.0/param/__init__.py 2019-04-02 23:08:36.000000000 +0200 +++ new/param-1.9.1/param/__init__.py 2019-06-01 00:46:54.000000000 +0200 @@ -26,9 +26,10 @@ from .parameterized import ( Parameterized, Parameter, String, ParameterizedFunction, ParamOverrides, - descendents, get_logger, instance_descriptor) + descendents, get_logger, instance_descriptor, basestring) -from .parameterized import batch_watch, depends, output # noqa: api import +from .parameterized import (batch_watch, depends, output, # noqa: api import + discard_events, edit_constant) from .parameterized import logging_level # noqa: api import from .parameterized import shared_parameters # noqa: api import @@ -40,12 +41,12 @@ # only two required files. try: from .version import Version - __version__ = str(Version(fpath=__file__, archive_commit="8f0dafa", reponame="param")) + __version__ = str(Version(fpath=__file__, archive_commit="4d6d346", reponame="param")) except: __version__ = "0.0.0+unknown" -dt_types = (dt.datetime,) +dt_types = (dt.datetime, dt.date) try: import numpy as np @@ -1275,7 +1276,7 @@ else: autodefault = None - default = default if default else autodefault + default = autodefault if default is None else default super(Selector,self).__init__(default=default, objects=objects, instantiate=instantiate, @@ -1847,7 +1848,7 @@ def _validate(self, val): if (self.allow_None and val is None): return - if not isinstance(val, String.basestring): + if not isinstance(val, basestring): raise ValueError("Color '%s' only takes a string value."%self.name) if not re.match('^#?(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$', val): raise ValueError("Color '%s' only accepts valid RGB hex codes." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/param-1.9.0/param/parameterized.py new/param-1.9.1/param/parameterized.py --- old/param-1.9.0/param/parameterized.py 2019-04-02 23:08:36.000000000 +0200 +++ new/param-1.9.1/param/parameterized.py 2019-06-01 00:46:54.000000000 +0200 @@ -27,6 +27,7 @@ except: param_pager = None +basestring = basestring if sys.version_info[0]==2 else str # noqa: it is defined VERBOSE = INFO - 1 logging.addLevelName(VERBOSE, "VERBOSE") @@ -105,6 +106,44 @@ parameterized.param._batch_call_watchers() +@contextmanager +def edit_constant(parameterized): + """ + Temporarily set parameters on Parameterized object to constant=False + to allow editing them. + """ + params = parameterized.objects('existing').values() + constants = [p.constant for p in params] + for p in params: + p.constant = False + try: + yield + except: + raise + finally: + for (p, const) in zip(params, constants): + p.constant = const + + +@contextmanager +def discard_events(parameterized): + """ + Context manager that discards any events within its scope + triggered on the supplied parameterized object. + """ + batch_watch = parameterized.param._BATCH_WATCH + parameterized.param._BATCH_WATCH = True + watchers, events = parameterized.param._watchers, parameterized.param._events + try: + yield + except: + raise + finally: + parameterized.param._BATCH_WATCH = batch_watch + parameterized.param._watchers = watchers + parameterized.param._events = events + + def classlist(class_): """ Return a list of the class hierarchy above (and including) the given class. @@ -261,29 +300,65 @@ @accept_arguments def depends(func, *dependencies, **kw): """ - Annotates a Parameterized method to express its dependencies. - The specified dependencies can be either be Parameters of this - class, or Parameters of subobjects (Parameterized objects that - are values of this object's parameters). Dependencies can either - be on Parameter values, or on other metadata about the Parameter. + Annotates a function or Parameterized method to express its + dependencies. The specified dependencies can be either be + Parameter instances or if a method is supplied they can be + defined as strings referring to Parameters of the class, + or Parameters of subobjects (Parameterized objects that are + values of this object's parameters). Dependencies can either be + on Parameter values, or on other metadata about the Parameter. """ # python3 would allow kw-only args # (i.e. "func,*dependencies,watch=False" rather than **kw and the check below) watch = kw.pop("watch",False) - assert len(kw)==0, "@depends accepts only 'watch' kw" - - # TODO: rename dinfo - _dinfo = getattr(func, '_dinfo', {}) - _dinfo.update({'dependencies': dependencies, - 'watch': watch}) @wraps(func) def _depends(*args,**kw): return func(*args,**kw) - # storing here risks it being tricky to find if other libraries - # mess around with methods + deps = list(dependencies)+list(kw.values()) + string_specs = False + for dep in deps: + if isinstance(dep, basestring): + string_specs = True + elif not isinstance(dep, Parameter): + raise ValueError('The depends decorator only accepts string ' + 'types referencing a parameter or parameter ' + 'instances, found %s type instead.' % + type(dep).__name__) + elif not (isinstance(dep.owner, Parameterized) or + (isinstance(dep.owner, ParameterizedMetaclass))): + owner = 'None' if dep.owner is None else '%s class' % type(dep.owner).__name__ + raise ValueError('Parameters supplied to the depends decorator, ' + 'must be bound to a Parameterized class or ' + 'instance not %s.' % owner) + + if (any(isinstance(dep, Parameter) for dep in deps) and + any(isinstance(dep, basestring) for dep in deps)): + raise ValueError('Dependencies must either be defined as strings ' + 'referencing parameters on the class defining ' + 'the decorated method or as parameter instances. ' + 'Mixing of string specs and parameter instances ' + 'is not supported.') + elif string_specs and kw: + raise AssertionError('Supplying keywords to the decorated method ' + 'or function is not supported when referencing ' + 'parameters by name.') + + if not string_specs and watch: + def cb(event): + args = (getattr(dep.owner, dep.name) for dep in dependencies) + dep_kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw.items()} + return func(*args, **dep_kwargs) + + for dep in deps: + dep.owner.param.watch(cb, dep.name) + + _dinfo = getattr(func, '_dinfo', {}) + _dinfo.update({'dependencies': dependencies, + 'kw': kw, 'watch': watch}) + _depends._dinfo = _dinfo return _depends @@ -862,8 +937,6 @@ __slots__ = ['regex'] - basestring = basestring if sys.version_info[0]==2 else str # noqa: it is defined - def __init__(self, default="", regex=None, allow_None=False, **kwargs): super(String, self).__init__(default=default, allow_None=allow_None, **kwargs) self.regex = regex @@ -874,7 +947,7 @@ if self.allow_None and val is None: return - if not isinstance(val, self.basestring): + if not isinstance(val, basestring): raise ValueError("String '%s' only takes a string value."%self.name) if self.regex is not None and re.match(self.regex, val) is None: @@ -940,7 +1013,7 @@ equalities = { numbers.Number: operator.eq, - String.basestring: operator.eq, + basestring: operator.eq, bytes: operator.eq, type(None): operator.eq } @@ -1348,8 +1421,8 @@ self_.self_or_cls.param._TRIGGER = True self_.set_param(**params) self_.self_or_cls.param._TRIGGER = False - self_.self_or_cls.param._events = events - self_.self_or_cls.param._watchers = watchers + self_.self_or_cls.param._events += events + self_.self_or_cls.param._watchers += watchers def _update_event_type(self_, watcher, event, triggered): @@ -1588,6 +1661,14 @@ def _spec_to_obj(self_,spec): # TODO: when we decide on spec, this method should be # rewritten + + if isinstance(spec, Parameter): + inst = spec.owner if isinstance(spec.owner, Parameterized) else None + cls = spec.owner if inst is None else type(inst) + info = PInfo(inst=inst, cls=cls, name=spec.name, + pobj=spec, what='value') + return [info] + assert spec.count(":")<=1 spec = spec.strip() @@ -1619,7 +1700,10 @@ def _watch(self_, action, watcher, what='value', operation='add'): #'add' | 'remove' parameter_names = watcher.parameter_names for parameter_name in parameter_names: - assert parameter_name in self_.cls.param + if parameter_name not in self_.cls.param: + raise ValueError("%s parameter was not found in list of " + "parameters of class %s" % + (parameter_name, self_.cls.__name__)) if self_.self is not None and what == "value": watchers = self_.self._param_watchers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/param-1.9.0/tests/API1/testparamdepends.py new/param-1.9.1/tests/API1/testparamdepends.py --- old/param-1.9.0/tests/API1/testparamdepends.py 2019-04-02 23:08:36.000000000 +0200 +++ new/param-1.9.1/tests/API1/testparamdepends.py 2019-06-01 00:46:54.000000000 +0200 @@ -26,7 +26,14 @@ def nested(self): pass + class P2(param.Parameterized): + + @param.depends(P.param.a) + def external_param(self, a): + pass + self.P = P + self.P2 = P2 def test_param_depends_instance(self): p = self.P() @@ -70,3 +77,82 @@ info = pinfos[(inst.a, p)] self.assertEqual(info.name, p) self.assertIs(info.inst, inst.a) + + def test_param_external_param_instance(self): + inst = self.P2() + pinfos = inst.param.params_depended_on('external_param') + pinfo = pinfos[0] + self.assertIs(pinfo.cls, self.P) + self.assertIs(pinfo.inst, None) + self.assertEqual(pinfo.name, 'a') + self.assertEqual(pinfo.what, 'value') + + + +class TestParamDependsFunction(API1TestCase): + + def setUp(self): + class P(param.Parameterized): + a = param.Parameter() + b = param.Parameter() + + + self.P = P + + def test_param_depends_function_instance_params(self): + p = self.P() + + @param.depends(p.param.a, c=p.param.b) + def function(value, c): + pass + + dependencies = { + 'dependencies': (p.param.a,), + 'kw': {'c': p.param.b}, + 'watch': False + } + self.assertEqual(function._dinfo, dependencies) + + def test_param_depends_function_class_params(self): + p = self.P + + @param.depends(p.param.a, c=p.param.b) + def function(value, c): + pass + + dependencies = { + 'dependencies': (p.param.a,), + 'kw': {'c': p.param.b}, + 'watch': False + } + self.assertEqual(function._dinfo, dependencies) + + def test_param_depends_function_instance_params_watch(self): + p = self.P(a=1, b=2) + + d = [] + + @param.depends(p.param.a, c=p.param.b, watch=True) + def function(value, c): + d.append(value+c) + + p.a = 2 + self.assertEqual(d, [4]) + p.b = 3 + self.assertEqual(d, [4, 5]) + + def test_param_depends_function_class_params_watch(self): + p = self.P + p.a = 1 + p.b = 2 + + d = [] + + @param.depends(p.param.a, c=p.param.b, watch=True) + def function(value, c): + d.append(value+c) + + p.a = 2 + self.assertEqual(d, [4]) + p.b = 3 + self.assertEqual(d, [4, 5]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/param-1.9.0/tests/API1/testwatch.py new/param-1.9.1/tests/API1/testwatch.py --- old/param-1.9.0/tests/API1/testwatch.py 2019-04-02 23:08:36.000000000 +0200 +++ new/param-1.9.1/tests/API1/testwatch.py 2019-06-01 00:46:54.000000000 +0200 @@ -4,8 +4,11 @@ from . import API1TestCase from .utils import MockLoggingHandler + import param +from param.parameterized import discard_events + class Accumulator(object): @@ -87,6 +90,19 @@ self.assertEqual(self.accumulator, 3) + def test_discard_events_decorator(self): + def accumulator(change): + self.accumulator += change.new + + obj = SimpleWatchExample() + obj.param.watch(accumulator, 'a') + with discard_events(obj): + obj.a = 1 + self.assertEqual(self.accumulator, 0) + obj.a = 2 + self.assertEqual(self.accumulator, 3) + + def test_triggered_when_changed_iterator_type(self): def accumulator(change): self.accumulator = change.new @@ -617,6 +633,21 @@ self.assertEqual(args[0].new, 0) self.assertEqual(args[0].type, 'triggered') + def test_simple_trigger_when_batched(self): + accumulator = Accumulator() + obj = SimpleWatchExample() + obj.param.watch(accumulator, ['a']) + with param.batch_watch(obj): + obj.param.trigger('a') + self.assertEqual(accumulator.call_count(), 1) + + args = accumulator.args_for_call(0) + self.assertEqual(args[0].name, 'a') + self.assertEqual(args[0].old, 0) + self.assertEqual(args[0].new, 0) + # Note: This is not strictly correct + self.assertEqual(args[0].type, 'changed') + def test_simple_trigger_one_param_change(self): accumulator = Accumulator() obj = SimpleWatchExample()
