Codebase list python-param / a6bc334
Import upstream version 1.12.1 Debian Janitor 2 years ago
82 changed file(s) with 2716 addition(s) and 7953 deletion(s). Raw diff Collapse all Expand all
+0
-15
.appveyor.yml less more
0 environment:
1 matrix:
2 - TOXENV: py36
3 - TOXENV: py35
4 - TOXENV: py34
5 - TOXENV: py27
6
7 matrix:
8 fast_finish: true
9
10 build: false
11
12 install: C:\Python36\python -m pip install -U tox
13
14 test_script: C:\Python36\scripts\tox
+0
-2
.coveragerc less more
0 [report]
1 omit = param/version.py
+0
-1
.gitattributes less more
0 __init__.py export-subst
+0
-11
.gitignore less more
0 *.py[cod]
1 #*#
2 *~
3 *.egg
4 *.egg-info
5 *.swp
6 *.DS_Store
7 *.so
8 *.o
9 *.out
10 *.lock
+0
-0
.gitmodules less more
(Empty file)
+0
-158
.travis.yml less more
0 sudo: false
1
2 language: python
3
4 # quick hack to determine what tag is (improvements welcomed)
5 # release: ^v(\d+|\.)+[^a-z]\d+$
6 # dev release: ^v(\d+|\.)+[a-z]\d+$
7
8 stages:
9 - lint
10 - test
11 - name: pip_dev_package
12 if: tag =~ ^v(\d+|\.)+[a-z]\d+$
13 - name: pip_package
14 if: tag =~ ^v(\d+|\.)+[^a-z]\d+$
15 - name: conda_dev_package
16 if: tag =~ ^v(\d+|\.)+[a-z]\d+$
17 - name: conda_package
18 if: tag =~ ^v(\d+|\.)+[^a-z]\d+$
19 - name: website_dev
20 if: tag =~ ^v(\d+|\.)+[a-z]\d+$ OR tag = website_dev
21 - name: website_release
22 if: tag =~ ^v(\d+|\.)+[^a-z]\d+$ OR tag = website
23
24
25 jobs:
26 fast_finish: true
27 include:
28 - &default
29 stage: test
30 python: 3.6
31 env: TOX_ENV=py36
32 install:
33 - pip install tox
34 script:
35 - tox -e $TOX_ENV
36
37 - <<: *default
38 python: 3.7-dev
39 env: TOX_ENV=py37
40
41 - <<: *default
42 python: 3.5
43 env: TOX_ENV=py35
44
45 - <<: *default
46 python: 3.4
47 env: TOX_ENV=py34
48
49 - <<: *default
50 python: 2.7
51 env: TOX_ENV=py27
52
53 - <<: *default
54 python: pypy
55 env: TOX_ENV=pypy
56
57 # could consider running with_ipython,numpy over py36 and 27
58
59 - <<: *default
60 env: TOX_ENV=with_ipython
61
62 - <<: *default
63 env: TOX_ENV=with_numpy
64
65 - <<: *default
66 env: TOX_ENV=with_pandas
67
68 - <<: *default
69 env: TOX_ENV=coverage
70
71 - <<: *default
72 stage: lint
73 env: TOX_ENV=flakes
74
75 # TODO: the below packaging sections will be simplified with
76 # doit/pyct (and note that using after_success means no alert to
77 # failure uploading)
78
79 - &conda_default
80 env: LABELS="--label dev"
81 stage: conda_dev_package
82 install:
83 - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
84 - bash miniconda.sh -b -p $HOME/miniconda
85 - export PATH="$HOME/miniconda/bin:$PATH"
86 - conda config --set always_yes yes --set changeps1 no
87 - conda update conda
88 - conda install anaconda-client conda-build
89 script:
90 - conda build conda.recipe/
91 after_success:
92 - anaconda --token $CONDA_UPLOAD_TOKEN upload --user pyviz $LABELS $(conda build --output conda.recipe)
93
94 - <<: *conda_default
95 env: LABELS="--label dev --label main"
96 stage: conda_package
97
98 - <<: *default
99 stage: pip_dev_package
100 deploy:
101 provider: pypi
102 server: https://test.pypi.org/legacy/
103 distributions: "sdist bdist_wheel"
104 on:
105 tags: true
106 user: $TESTPYPI_USER
107 password: $TESTPYPI_PWD
108
109 - <<: *default
110 stage: pip_package
111 deploy:
112 provider: pypi
113 distributions: "sdist bdist_wheel"
114 on:
115 tags: true
116 user: $PYPI_USER
117 password: $PYPI_PWD
118
119 - &website
120 <<: *default
121 addons:
122 apt:
123 packages:
124 - graphviz
125 stage: website_release
126 before_install:
127 - pip install graphviz
128 install:
129 - pip install nbsite sphinx_ioam_theme "tornado<6"
130 - pip install -e .
131 script:
132 # TODO: nbsite commands will be simplified eventually...
133 - nbsite generate-rst --org pyviz --repo param --project-name param
134 - mkdir doc/Reference_Manual && nbsite_generate_modules.py param -d ./doc/Reference_Manual -n param -e tests
135 - nbsite build --examples-assets=''
136 deploy:
137 - provider: pages
138 skip_cleanup: true
139 github_token: $GITHUB_TOKEN
140 local_dir: ./builtdocs
141 fqdn: param.pyviz.org
142 on:
143 tags: true
144 all_branches: true
145
146 - <<: *website
147 stage: website_dev
148 env: DESC="pyviz-dev.github.io/param"
149 deploy:
150 - provider: pages
151 skip_cleanup: true
152 github_token: $GITHUB_TOKEN
153 local_dir: ./builtdocs
154 repo: pyviz-dev/param
155 on:
156 tags: true
157 all_branches: true
0 Copyright (c) 2005-2018, IOAM (ioam.github.com)
0 Copyright (c) 2005-2022, HoloViz team.
11 All rights reserved.
22
33 Redistribution and use in source and binary forms, with or without
1212 documentation and/or other materials provided with the
1313 distribution.
1414
15 * Neither the name of IOAM nor the names of its contributors
16 may be used to endorse or promote products derived from this
17 software without specific prior written permission.
15 * Neither the name of the copyright holder nor the names of any
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
1818
1919 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2020 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0 Metadata-Version: 2.1
1 Name: param
2 Version: None
3 Summary: Make your Python code clearer and more reliable by declaring Parameters.
4 Home-page: http://param.holoviz.org/
5 Author: HoloViz
6 Author-email: developers@holoviz.org
7 Maintainer: HoloViz
8 Maintainer-email: developers@holoviz.org
9 License: BSD
10 Project-URL: Documentation, https://param.holoviz.org/
11 Project-URL: Releases, https://github.com/holoviz/param/releases
12 Project-URL: Bug Tracker, https://github.com/holoviz/param/issues
13 Project-URL: Source Code, https://github.com/holoviz/param
14 Project-URL: Panel Examples, https://panel.holoviz.org/user_guide/Param.html
15 Platform: Windows
16 Platform: Mac OS X
17 Platform: Linux
18 Classifier: License :: OSI Approved :: BSD License
19 Classifier: Development Status :: 5 - Production/Stable
20 Classifier: Programming Language :: Python :: 2
21 Classifier: Programming Language :: Python :: 2.7
22 Classifier: Programming Language :: Python :: 3
23 Classifier: Programming Language :: Python :: 3.6
24 Classifier: Programming Language :: Python :: 3.7
25 Classifier: Programming Language :: Python :: 3.8
26 Classifier: Programming Language :: Python :: 3.9
27 Classifier: Programming Language :: Python :: 3.10
28 Classifier: Operating System :: OS Independent
29 Classifier: Intended Audience :: Science/Research
30 Classifier: Intended Audience :: Developers
31 Classifier: Natural Language :: English
32 Classifier: Topic :: Scientific/Engineering
33 Classifier: Topic :: Software Development :: Libraries
34 Provides: param
35 Provides: numbergen
36 Requires-Python: >=2.7
37 Description-Content-Type: text/markdown
38 Provides-Extra: all
39 Provides-Extra: doc
40 Provides-Extra: tests
41 License-File: LICENSE.txt
42
43 <img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250>
44
45 | | |
46 | --- | --- |
47 | Build Status | [![Linux/MacOS/Windows Build Status](https://github.com/holoviz/param/workflows/pytest/badge.svg)](https://github.com/holoviz/param/actions/workflows/test.yml)
48 | Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/master/graph/badge.svg)](https://codecov.io/gh/holoviz/param) ||
49 | Latest dev release | [![Github tag](https://img.shields.io/github/v/tag/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/tags) [![dev-site](https://img.shields.io/website-up-down-green-red/https/pyviz-dev.github.io/param.svg?label=dev%20website)](https://pyviz-dev.github.io/param/) |
50 | Latest release | [![Github release](https://img.shields.io/github/release/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/releases) [![PyPI version](https://img.shields.io/pypi/v/param.svg?colorB=cc77dd)](https://pypi.python.org/pypi/param) [![param version](https://img.shields.io/conda/v/pyviz/param.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/param) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/param.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/param) [![defaults version](https://img.shields.io/conda/v/anaconda/param.svg?label=conda%7Cdefaults&style=flat&colorB=4488ff)](https://anaconda.org/anaconda/param) |
51 | Python | [![Python support](https://img.shields.io/pypi/pyversions/param.svg)](https://pypi.org/project/param/)
52 | Docs | [![gh-pages](https://img.shields.io/github/last-commit/holoviz/param/gh-pages.svg)](https://github.com/holoviz/param/tree/gh-pages) [![site](https://img.shields.io/website-up-down-green-red/https/param.holoviz.org.svg)](https://param.holoviz.org) |
53 | Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/master?labpath=examples) |
54 | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) |
55
56 Param is a library providing Parameters: Python attributes extended to have features such as type and range checking, dynamically generated values, documentation strings, default values, etc., each of which is inherited from parent classes if not specified in a subclass.
57
58 Param contains only two required Python files, with no external dependencies, and is provided freely for both non-commercial and commercial use under a BSD license, so that it can easily be included as part of other projects.
59
60 Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples.
61
62
0 <img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250>
1
2 | | |
3 | --- | --- |
4 | Build Status | [![Linux/MacOS/Windows Build Status](https://github.com/holoviz/param/workflows/pytest/badge.svg)](https://github.com/holoviz/param/actions/workflows/test.yml)
5 | Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/master/graph/badge.svg)](https://codecov.io/gh/holoviz/param) ||
6 | Latest dev release | [![Github tag](https://img.shields.io/github/v/tag/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/tags) [![dev-site](https://img.shields.io/website-up-down-green-red/https/pyviz-dev.github.io/param.svg?label=dev%20website)](https://pyviz-dev.github.io/param/) |
7 | Latest release | [![Github release](https://img.shields.io/github/release/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/releases) [![PyPI version](https://img.shields.io/pypi/v/param.svg?colorB=cc77dd)](https://pypi.python.org/pypi/param) [![param version](https://img.shields.io/conda/v/pyviz/param.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/param) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/param.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/param) [![defaults version](https://img.shields.io/conda/v/anaconda/param.svg?label=conda%7Cdefaults&style=flat&colorB=4488ff)](https://anaconda.org/anaconda/param) |
8 | Python | [![Python support](https://img.shields.io/pypi/pyversions/param.svg)](https://pypi.org/project/param/)
9 | Docs | [![gh-pages](https://img.shields.io/github/last-commit/holoviz/param/gh-pages.svg)](https://github.com/holoviz/param/tree/gh-pages) [![site](https://img.shields.io/website-up-down-green-red/https/param.holoviz.org.svg)](https://param.holoviz.org) |
10 | Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/master?labpath=examples) |
11 | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) |
12
13 Param is a library providing Parameters: Python attributes extended to have features such as type and range checking, dynamically generated values, documentation strings, default values, etc., each of which is inherited from parent classes if not specified in a subclass.
14
15 Param contains only two required Python files, with no external dependencies, and is provided freely for both non-commercial and commercial use under a BSD license, so that it can easily be included as part of other projects.
16
17 Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples.
+0
-35
README.rst less more
0 |LinuxTests|_ |WinTests|_ |Coverage|_ |PyPIVersion|_ |PyVersion|_ |License|_
1
2 Param
3 =====
4
5 Param is a library providing Parameters: Python attributes extended to
6 have features such as type and range checking, dynamically generated
7 values, documentation strings, default values, etc., each of which is
8 inherited from parent classes if not specified in a subclass.
9
10 Param contains only two required Python files, with no external
11 dependencies, and is provided freely for both non-commercial and
12 commercial use under a BSD license, so that it can easily be included
13 as part of other projects.
14
15 Please see `param's website <http://param.pyviz.org>`_ for
16 official releases, installation instructions, documentation, and examples.
17
18 .. |LinuxTests| image:: https://travis-ci.org/pyviz/param.svg?branch=master
19 .. _LinuxTests: https://travis-ci.org/pyviz/param
20
21 .. |WinTests| image:: https://ci.appveyor.com/api/projects/status/1p5aom8o0tfgok1r?svg=true
22 .. _WinTests: https://ci.appveyor.com/project/pyviz/param/branch/master
23
24 .. |Coverage| image:: https://img.shields.io/coveralls/pyviz/param.svg
25 .. _Coverage: https://coveralls.io/r/pyviz/param?branch=master
26
27 .. |PyPIVersion| image:: http://img.shields.io/pypi/v/param.svg
28 .. _PyPIVersion: https://pypi.python.org/pypi/param
29
30 .. |PyVersion| image:: https://img.shields.io/pypi/pyversions/param.svg
31 .. _PyVersion: https://pypi.python.org/pypi/param
32
33 .. |License| image:: https://img.shields.io/pypi/l/param.svg
34 .. _License: https://pypi.python.org/pypi/param
+0
-40
conda.recipe/meta.yaml less more
0 {% set sdata = load_setup_py_data() %}
1
2 package:
3 name: param
4 version: {{ sdata['version'] }}
5
6 source:
7 path: ..
8
9 build:
10 noarch: python
11 script: python setup.py install --single-version-externally-managed --record=record.txt
12
13 requirements:
14 build:
15 - python
16 - setuptools
17 run:
18 - python {{ sdata['python_requires'] }}
19
20 test:
21 requires:
22 {% for dep in sdata['extras_require']['tests'] %}
23 - {{ dep }}
24 {% endfor %}
25 source_files:
26 # for nose config
27 - setup.cfg
28 - tests
29 imports:
30 - param
31 - numbergen
32 commands:
33 # https://github.com/ioam/param/issues/219
34 - nosetests tests
35
36 about:
37 home: {{ sdata['url'] }}
38 summary: {{ sdata['description'] }}
39 license: {{ sdata['license'] }}
doc/_static/favicon.ico less more
Binary diff not shown
doc/_static/logo.png less more
Binary diff not shown
+0
-39
doc/conf.py less more
0 # -*- coding: utf-8 -*-
1
2 from nbsite.shared_conf import *
3
4 project = u'Param'
5 authors = u'PyViz authors'
6 copyright = u'\u00a9 2005-2018, ' + authors
7 description = 'Declarative Python programming using Parameters.'
8
9 import param
10 version = release = param.__version__
11
12 html_static_path += ['_static']
13 html_theme = 'sphinx_ioam_theme'
14 html_theme_options = {
15 'logo':'logo.png',
16 'favicon':'favicon.ico',
17 # 'css':'site.css'
18 }
19
20 _NAV = (
21 ('API', 'Reference_Manual/param'),
22 ('About', 'About'),
23 )
24
25 html_context.update({
26 'PROJECT': project,
27 'DESCRIPTION': description,
28 'AUTHOR': authors,
29 # canonical URL (for search engines); can ignore for local builds
30 'WEBSITE_SERVER': 'https://param.pyviz.org',
31 'VERSION': version,
32 'NAV': _NAV,
33 'LINKS': _NAV,
34 'SOCIAL': (
35 ('Gitter', '//gitter.im/pyviz/pyviz'),
36 ('Github', '//github.com/ioam/param'),
37 )
38 })
+0
-125
doc/historical_release_notes.rst less more
0 ************************
1 Historical release notes
2 ************************
3
4 Note: current release notes are on `GitHub
5 <https://github.com/ioam/param/releases>`_.
6
7 Notable additions, or changes that may require users to alter code,
8 are listed below.
9
10
11 1.4.1 (2016/07)
12 _______________
13
14 * Selector parameters now respect order of options supplied
15 * Allowed softbounds to be accessed like an attribute
16
17 A full list of changes since the previous release is available
18 `on GitHub <https://github.com/ioam/param/compare/v1.4.0...v1.4.1>`_.
19
20
21 1.4.0 (2016/07)
22 _______________
23
24 * Added support for new `ParamNB <https://github.com/ioam/paramnb>`_ project
25 * Added new parameter types Action, FileSelector, and ListSelector
26
27 A full list of changes since the previous release is available
28 `on GitHub <https://github.com/ioam/param/compare/v1.3.2...v1.4.0>`_.
29
30
31 1.3.2 (2015/04)
32 _______________
33
34 * Added Unicode support for param.String.
35 * Minor bugfixes.
36
37 A full list of changes since the previous release is available
38 `on GitHub <https://github.com/ioam/param/compare/v1.3.1...v1.3.2>`_.
39
40
41 1.3.1 (2015/03)
42 _______________
43
44 * Minor bugfix release to restore pre-1.3.0 script_repr behavior
45 (accidentally changed in 1.3.0) and to fix issues with logging.
46 * Param's logging interface now matches that of Python's logging
47 module, making it simpler to use logging (see Python's logging
48 module for details). Note therefore that Param's logging methods (a)
49 no longer call functions that are passed as arguments (instead,
50 Python's logging module does lazy string merges), and (b) no longer
51 automatically combine strings passed as arguments (instead, Python's
52 logging module supports string formatting).
53 * Improved set_param() method, now allowing multiple parameters to be
54 set easily via keyword arguments (as on initialization).
55
56 A full list of changes since the previous release is available
57 `on GitHub <https://github.com/ioam/param/compare/v1.3.0...v1.3.1>`_.
58
59
60 1.3.0 (2015/03)
61 _______________
62
63 * Added 'allow_None' support to all Parameters. Any subclass of
64 Parameter that checks types and/or values should be modified to add
65 appropriate handling of allow_None.
66 * Improved pretty printing (script_repr) of Parameterized instances,
67 and made available via the pprint method. The script_repr name will
68 be removed in a future release.
69 * Added (reproducible) time-dependent random streams
70 (numbergen.TimeAwareRandomState).
71 * Added label and unit parameters to param.Time class.
72 * Improved optional IPython extension.
73
74 A full list of changes since the previous release is available
75 `on GitHub <https://github.com/ioam/param/compare/v1.2.1...v1.3.0>`_.
76
77
78 1.2.1 (2014/06)
79 _______________
80
81 * Minor bugfix release to fix issues with version when param is
82 installed in a foreign git repository
83 * Made version module optional
84 * Improved ClassSelector and ParamOverrides
85
86 A full list of changes since the previous release is available
87 `on GitHub <https://github.com/ioam/param/compare/v1.2.0...v1.2.1>`_.
88
89
90 1.2.0 (2014/06)
91 _______________
92
93 * Added support for Python 3 (thanks to Marco Elver).
94 * Dropped support for Python 2.5.
95 * Added version module.
96 * Added optional numbergen package.
97
98 A full list of changes since the previous release is available
99 `on GitHub <https://github.com/ioam/param/compare/v1.1.0...v1.2.0>`_.
100
101
102 1.1.0 (2014/05)
103 _______________
104
105 * Switched to Python's own logging module.
106 * Improved support for time when using Dynamic parameters.
107 * Optional extension for IPython users.
108
109 A full list of changes since the previous release is available
110 `on GitHub <https://github.com/ioam/param/compare/v1.0.0...v1.1.0>`_.
111
112
113 1.0.0 (2012/07)
114 _______________
115
116 * First standalone release.
117
118
119 Pre-1.0 (2003)
120 ______________
121
122 * Param was originally developed as part of `Topographica
123 <http://ioam.github.io/topographica/>`_, and has been in heavy
124 usage as part of that project since 2003.
+0
-20
doc/index.rst less more
0 ..
1 Originally generated by nbsite (0.4.4a13+gdbf7de7-dirty):
2 nbsite generate-rst --org ioam --project param --repo param --examples-path examples --doc-path doc
3 Will not subsequently be overwritten by nbsite, so can be edited.
4
5 *****
6 Param
7 *****
8
9 .. notebook:: param ../examples/index.ipynb
10 :offset: 0
11
12 .. toctree::
13 :titlesonly:
14 :maxdepth: 2
15
16 Introduction <self>
17 API <Reference_Manual/param>
18 About <About>
19
+0
-19
examples/About.ipynb less more
0 {
1 "cells": [
2 {
3 "cell_type": "markdown",
4 "metadata": {},
5 "source": [
6 "Param is part of [PyViz](http://pyviz.org), a collaborative project to produce a coherent solution to a wide range of Python visualization problems."
7 ]
8 }
9 ],
10 "metadata": {
11 "language_info": {
12 "name": "python",
13 "pygments_lexer": "ipython3"
14 }
15 },
16 "nbformat": 4,
17 "nbformat_minor": 2
18 }
+0
-300
examples/index.ipynb less more
0 {
1 "cells": [
2 {
3 "cell_type": "markdown",
4 "metadata": {},
5 "source": [
6 "Param is a library providing Parameters: Python attributes extended to\n",
7 "have features such as type and range checking, dynamically generated\n",
8 "values, documentation strings, default values, etc., each of which is\n",
9 "inherited from parent classes if not specified in a subclass. Param\n",
10 "lets you program declaratively in Python, by just stating facts about\n",
11 "each of your parameters, and then using them throughout your code.\n",
12 "With Parameters, error checking will be automatic, which eliminates\n",
13 "huge amounts of boilerplate code that would otherwise be required to\n",
14 "verify or test user-supplied values.\n",
15 "\n",
16 "Param-based programs tend to contain much less code than other Python\n",
17 "programs, instead just having easily readable and maintainable\n",
18 "manifests of Parameters for each object or function. This way your\n",
19 "remaining code can be much simpler and clearer, while users can also\n",
20 "easily see how to use it properly."
21 ]
22 },
23 {
24 "cell_type": "markdown",
25 "metadata": {},
26 "source": [
27 "# What is a Parameter?\n",
28 "\n",
29 "A Parameter is a special type of Python attribute extended to have features such as type and range checking, dynamically generated values, documentation strings, default values, etc., each of which is inherited from parent classes if not specified in a subclass.\n",
30 "\n",
31 "```python\n",
32 ">>> import param,random\n",
33 ">>> class A(param.Parameterized):\n",
34 "... a = param.Number(0.5,bounds=(0,1),doc=\"Probability that...\")\n",
35 "... b = param.Boolean(False,doc=\"Enable feature...\")\n",
36 "\n",
37 ">>> class B(A):\n",
38 "... b = param.Boolean(True)\n",
39 "\n",
40 ">>> x = B(a=lambda: random.uniform(0,1))\n",
41 "\n",
42 ">>> x.a\n",
43 "0.37053399325641945\n",
44 "\n",
45 ">>> x.a\n",
46 "0.64907392300071842\n",
47 "```\n",
48 "\n",
49 "## Parameters provide optional range and type checking\n",
50 "\n",
51 "```python\n",
52 ">>> x.a=5\n",
53 "[...]\n",
54 "ValueError: Parameter 'a' must be at most 1\n",
55 "\n",
56 ">>> x.a=\"0.5\"\n",
57 "[...]\n",
58 "ValueError: Parameter 'a' only takes numeric values\n",
59 "```\n",
60 "\n",
61 "## Parameters have docstrings\n",
62 "\n",
63 "```python\n",
64 ">>> help(x)\n",
65 "[...]\n",
66 "class B(A)\n",
67 "[...]\n",
68 " Data descriptors defined here:\n",
69 " b\n",
70 " Enable feature...\n",
71 "[...]\n",
72 " Data descriptors inherited from A:\n",
73 " a\n",
74 " Probability that...\n",
75 "```\n",
76 "\n",
77 "## Param is lightweight\n",
78 "\n",
79 "Param consists of two required BSD-licensed Python files, with no\n",
80 "dependencies outside of the standard library, and so it can easily be\n",
81 "included as part of larger projects without adding external dependencies.\n",
82 "\n",
83 "\n",
84 "## Parameters make GUI programming simpler\n",
85 "\n",
86 "Parameters make it simple to generate GUIs by separating your semantic\n",
87 "information (what is this parameter? what type can it have? does it\n",
88 "have bounds?) from anything to do with a particular GUI library. To\n",
89 "use Parameters in a particular GUI toolkit, you just need to write a\n",
90 "simple set of interfaces that indicate how a given Parameter type\n",
91 "should be displayed, and what widgets to generate for it. Currently,\n",
92 "interfaces are provided for use in Jupyter Notebooks ([ParamNB]\n",
93 "(https://github.com/ioam/paramnb)) \n",
94 "or in Tk ([ParamTk](http://ioam.github.com/paramtk)), both of which\n",
95 "make it simple to provide a property sheet that automatically\n",
96 "generates a set of widgets for viewing and editing an object's\n",
97 "Parameters.\n",
98 "\n",
99 "\n",
100 "## Optional dynamic parameter values using `numbergen`\n",
101 "\n",
102 "Providing random or other types of varying values for parameters can\n",
103 "be tricky, because unnamed (\"lambda\") functions as used above cannot\n",
104 "easily be pickled, causing problems for people who wish to store\n",
105 "Parameterized objects containing random state. To avoid users having\n",
106 "to write a separate function for each random value, Param includes an\n",
107 "optional set of value-generating objects that are easily configured\n",
108 "and support pickling. These objects are available if you import the\n",
109 "optional `numbergen` module. If you wish to use numbergen, the above\n",
110 "example can be rewritten as:\n",
111 "\n",
112 "```python\n",
113 ">>> import param,numbergen\n",
114 ">>> class A(param.Parameterized):\n",
115 "... a = param.Number(0.5,bounds=(0,1),doc=\"Probability that...\")\n",
116 "... b = param.Boolean(False,doc=\"Enable feature...\")\n",
117 "\n",
118 ">>> class B(A):\n",
119 "... b = param.Boolean(True)\n",
120 "\n",
121 ">>> x = B(a=numbergen.UniformRandom())\n",
122 "``` \n",
123 "\n",
124 "Numbergen objects support the usual arithmetic operations like `+`, `-`,\n",
125 "`*`, `/`, `//`, `%`, `**`, and `abs()`, and so they can be freely combined with\n",
126 "each other or with mathematical constants:\n",
127 "\n",
128 "```python\n",
129 ">>> y = B(a=2.0*numbergen.UniformRandom()/(numbergen.NormalRandom()+1.5))\n",
130 "```\n",
131 "\n",
132 "Note that unlike the lambda-function approach, all varying numbergen\n",
133 "objects respect `param.Dynamic.time_fn`, e.g. to ensure that new\n",
134 "values will be generated only when Param's time has changed. \n",
135 "Parameterized programs can define a time function to maintain a\n",
136 "logical/simulated time, such as the state of a simulator, which\n",
137 "allows all Parameter values to be kept synchronized without\n",
138 "any special coordination code.\n",
139 "\n"
140 ]
141 },
142 {
143 "cell_type": "markdown",
144 "metadata": {},
145 "source": [
146 "# Installation\n",
147 "\n",
148 "Param has no required dependencies outside of Python's standard\n",
149 "library.\n",
150 "\n",
151 "Official releases of Param are available on\n",
152 "[Anaconda](https://anaconda.org/ioam/param) and\n",
153 "[PyPI](http://pypi.python.org/pypi/param), and can be installed via\n",
154 "`conda install -c ioam param`, `pip install --user param`, or \n",
155 "`pip install param`.\n",
156 "\n",
157 "The very latest changes can be obtained via `conda install -c pyviz/label/dev\n",
158 "param` or `pip install\n",
159 "https://github.com/ioam/param/archive/master.zip`.\n",
160 "\n",
161 "For development, the [git repository](http://github.com/ioam/param)\n",
162 "can be cloned and then 'develop installed' (`pip install -e .` or\n",
163 "`python setup.py develop`). Tests can be run via [tox]\n",
164 "(https://tox.readthedocs.io/en/latest/): `tox` for all tests, or\n",
165 "e.g. `tox -e coverage` to run unit tests with coverage for the\n",
166 "currently active python. Alternatively, unit tests can be run via\n",
167 "`nosetests` (after installing [nose](http://nose.readthedocs.io/en/latest)."
168 ]
169 },
170 {
171 "cell_type": "markdown",
172 "metadata": {},
173 "source": [
174 "# Comparison to other packages\n",
175 "\n",
176 "Param was first developed in 2003, in the context of the Topographica brain simulator project, and\n",
177 "was made into a separate package in 2012. In the interim other parameter libraries were\n",
178 "developed, including [Traits](http://code.enthought.com/projects/traits) and \n",
179 "[Traitlets](https://github.com/ipython/traitlets/). These libraries have broadly similar goals,\n",
180 "but each differs in important ways:\n",
181 "\n",
182 "**Dependencies**: \n",
183 " Traits is a much more heavyweight solution, requiring \n",
184 " installation of a large suite of tools, including C code, which makes it difficult to include in \n",
185 " separate projects. In contrast, Param and Traitlets are both pure Python projects, with minimal dependencies. \n",
186 "\n",
187 "**GUI toolkits**: \n",
188 " Although any of the packages could in principle add support for any\n",
189 " GUI toolkit, the toolkits actually provided differ: Traits (via the\n",
190 " separate TraitsUI package) supports wxWidgets and QT, while Param\n",
191 " supports Tkinter (via the separate ParamTk package) and\n",
192 " browser-based IPython widgets (via the separate ParamNB package),\n",
193 " while Traitlets only supports IPython widgets.\n",
194 "\n",
195 " ```python\n",
196 " >>> from time import time\n",
197 " >>> import traitlets as tr\n",
198 " >>> class A(tr.HasTraits):\n",
199 " ... instantiation_time = tr.Float()\n",
200 " ... @tr.default('instantiation_time')\n",
201 " ... def _look_up_time(self):\n",
202 " ... return time()\n",
203 " ... \n",
204 " >>> a=A()\n",
205 " >>> a.instantiation_time\n",
206 " 1475587151.967874\n",
207 " >>> a.instantiation_time\n",
208 " 1475587151.967874\n",
209 " >>> b=A()\n",
210 " >>> b.instantiation_time\n",
211 " 1475587164.750875\n",
212 " ```\n",
213 "\n",
214 "**Dynamic values**:\n",
215 " Param, Traits, and Traitlets all allow any Python expression to be\n",
216 " supplied for initializing parameters, allowing parameter default\n",
217 " values to be computed at the time a module is first loaded. Traits\n",
218 " and Traitlets also allow a class author to add code for a given\n",
219 " parameter to compute a default value on first access. Param does\n",
220 " not provide any special support for programmatic default values,\n",
221 " instead allowing fully dynamic values for *any* numeric Parameter\n",
222 " instance:\n",
223 "\n",
224 " ```python\n",
225 " >>> from time import time\n",
226 " >>> import param\n",
227 " >>> class A(param.Parameterized):\n",
228 " ... val=param.Number(0)\n",
229 " ... \n",
230 " >>> a=A()\n",
231 " >>> a.val\n",
232 " 0\n",
233 " >>> a.val=lambda:time()\n",
234 " >>> a.val\n",
235 " 1475587455.437027\n",
236 " >>> a.val\n",
237 " 1475587456.501314\n",
238 " ```\n",
239 " \n",
240 " Note that here it is the *user* of a Parameterized class, not the\n",
241 " author of the class, that decides whether any particular value is\n",
242 " dynamic, without writing any new methods or other code. All the\n",
243 " usual type checking, etc. is done on dynamic values when they are\n",
244 " computed, and so the rest of the code does not need to know or care\n",
245 " whether the user has set a particular parameter to a dynamic value.\n",
246 " This approach provides an enormous amount of power to the user,\n",
247 " without making the code more complex.\n",
248 "\n",
249 "**On_change callbacks**\n",
250 " Traitlets and Traits allow the author of a HasTraits-derived class\n",
251 " to specify code to run when a specific parameter used in that class\n",
252 " instance is modified. Param supports similar capabilities, but not\n",
253 " at the Parameterized class level, only at the Parameter class level\n",
254 " or as part of ParamNB. I.e., a class author needs to first write a\n",
255 " new Parameter class, adding methods to implement checking on\n",
256 " changes, and then add it to a Parameterized class, or else such\n",
257 " functionality can be added as callbacks at the whole-object level,\n",
258 " using ParamNB. Each approach has advantages and disadvantages, and\n",
259 " per-parameter on_change callbacks could be added in the future if\n",
260 " there are clear use cases.\n",
261 "\n",
262 "All of these packages also overlap in functionality with Python\n",
263 "properties, which were added to the language after Traits and Param\n",
264 "were developed. Like parameters and traits, properties act like\n",
265 "attributes with possible method-like actions, and so they can all be\n",
266 "used to provide the same user-visible functionality. However,\n",
267 "implementing Param/Traits-like functionality using properties would\n",
268 "require vastly more code (multiple method definitions for *every*\n",
269 "parameter in a class), and so in practice Parameters and Traits are\n",
270 "much more practical for the use cases that they cover.\n",
271 " "
272 ]
273 },
274 {
275 "cell_type": "markdown",
276 "metadata": {},
277 "source": [
278 "# Release notes\n",
279 "\n",
280 "Recent release notes are available on [GitHub](https://github.com/ioam/param/releases).\n",
281 "\n",
282 "For older releases, see our [historical release notes](historical_release_notes.html).\n",
283 "\n",
284 "\n",
285 "# Support\n",
286 "\n",
287 "Questions and comments are welcome at https://github.com/ioam/param/issues."
288 ]
289 }
290 ],
291 "metadata": {
292 "language_info": {
293 "name": "python",
294 "pygments_lexer": "ipython3"
295 }
296 },
297 "nbformat": 4,
298 "nbformat_minor": 2
299 }
119119 def __abs__ (self): return UnaryOperator(self,operator.abs)
120120
121121
122 operator_symbols = {
123 operator.add:'+',
124 operator.sub:'-',
125 operator.mul:'*',
126 operator.mod:'%',
127 operator.pow:'**',
128 operator.truediv:'/',
129 operator.floordiv:'//',
130 operator.neg:'-',
131 operator.pos:'+',
132 operator.abs:'abs',
133 }
134
135 def pprint(x, *args, **kwargs):
136 "Pretty-print the provided item, translating operators to their symbols"
137 return x.pprint(*args, **kwargs) if hasattr(x,'pprint') else operator_symbols.get(x, repr(x))
138
122139
123140 class BinaryOperator(NumberGenerator):
124141 """Applies any binary operator to NumberGenerators or numbers to yield a NumberGenerator."""
147164 return self.operator(self.lhs() if callable(self.lhs) else self.lhs,
148165 self.rhs() if callable(self.rhs) else self.rhs, **self.args)
149166
167 def pprint(self, *args, **kwargs):
168 return (pprint(self.lhs, *args, **kwargs) +
169 pprint(self.operator, *args, **kwargs) +
170 pprint(self.rhs, *args, **kwargs))
150171
151172
152173 class UnaryOperator(NumberGenerator):
170191 def __call__(self):
171192 return self.operator(self.operand(),**self.args)
172193
194 def pprint(self, *args, **kwargs):
195 return (pprint(self.operator, *args, **kwargs) + '(' +
196 pprint(self.operand, *args, **kwargs) + ')')
173197
174198
175199 class Hash(object):
201225
202226 I32 = 4294967296 # Maximum 32 bit unsigned int (i.e. 'I') value
203227 if isinstance(val, int):
204 numer, denom = val, 1
228 numer, denom = val, 1
205229 elif isinstance(val, fractions.Fraction):
206230 numer, denom = val.numerator, val.denominator
207231 elif hasattr(val, 'numer'):
208232 (numer, denom) = (int(val.numer()), int(val.denom()))
209233 else:
210 param.main.param.warning("Casting type '%s' to Fraction.fraction"
234 param.main.param.log(param.WARNING, "Casting type '%s' to Fraction.fraction"
211235 % type(val).__name__)
212236 frac = fractions.Fraction(str(val))
213237 numer, denom = frac.numerator, frac.denominator
341365 """
342366 Warn if the object name is not explicitly set.
343367 """
344 changed_params = dict(self.param.get_param_values(onlychanged=True))
368 changed_params = self.param.values(onlychanged=True)
345369 if self.time_dependent and ('name' not in changed_params):
346 self.param.warning("Default object name used to set the seed: "
347 "random values conditional on object instantiation order.")
370 self.param.log(param.WARNING, "Default object name used to set the seed: "
371 "random values conditional on object instantiation order.")
348372
349373 def _hash_and_seed(self):
350374 """
414438 self._hash_and_seed()
415439
416440
417
418441 class UniformRandom(RandomDistribution):
419442 """
420443 Specified with lbound and ubound; when called, return a random
0 {"version_string": "None"}
0 from __future__ import print_function
01 """
12 Parameters are a kind of class attribute allowing special behavior,
23 including dynamically generated parameter values, documentation
2728 Parameterized, Parameter, String, ParameterizedFunction, ParamOverrides,
2829 descendents, get_logger, instance_descriptor, basestring)
2930
30 from .parameterized import (batch_watch, depends, output, # noqa: api import
31 discard_events, edit_constant)
31 from .parameterized import (batch_watch, depends, output, script_repr, # noqa: api import
32 discard_events, edit_constant, instance_descriptor)
33 from .parameterized import shared_parameters # noqa: api import
3234 from .parameterized import logging_level # noqa: api import
33 from .parameterized import shared_parameters # noqa: api import
35 from .parameterized import DEBUG, VERBOSE, INFO, WARNING, ERROR, CRITICAL # noqa: api import
3436
3537 from collections import OrderedDict
3638 from numbers import Real
4042 # only two required files.
4143 try:
4244 from .version import Version
43 __version__ = str(Version(fpath=__file__, archive_commit="9123ba0", reponame="param"))
45 __version__ = str(Version(fpath=__file__, archive_commit="$Format:%h$", reponame="param"))
4446 except:
4547 __version__ = "0.0.0+unknown"
4648
6870
6971
7072 # A global random seed (integer or rational) available for controlling
71 # the behaviour of parameterized objects with random state.
73 # the behaviour of Parameterized objects with random state.
7274 random_seed = 42
7375
7476
220222 supplied parameters, inheriting from the specified base(s).
221223 """
222224 if not (isinstance(bases, list) or isinstance(bases, tuple)):
223 bases=[bases]
225 bases=[bases]
224226 return type(name, tuple(bases), params)
225227
226228
467469 raise StopIteration
468470 return self._time
469471
470 # For Python 2 compatibility; can be removed for Python 3.
472 # PARAM2_DEPRECATION: For Python 2 compatibility; can be removed for Python 3.
471473 next = __next__
472474
473475 def __call__(self, val=None, time_type=None):
563565 time_fn = Time()
564566 time_dependent = False
565567
566 # CBENHANCEMENT: Add an 'epsilon' slot.
567 # See email 'Re: simulation-time-controlled Dynamic parameters'
568 # Dec 22, 2007 CB->JAB
569
570568 def __init__(self,**params):
571569 """
572570 Call the superclass's __init__ and set instantiate=True if the
583581 """
584582 Add 'last time' and 'last value' attributes to the generator.
585583 """
586 # CEBALERT: use a dictionary to hold these things.
584 # Could use a dictionary to hold these things.
587585 if hasattr(obj,"_Dynamic_time_fn"):
588586 gen._Dynamic_time_fn = obj._Dynamic_time_fn
589587
590588 gen._Dynamic_last = None
591 # CEB: I'd use None for this, except can't compare a fixedpoint
589 # Would have usede None for this, but can't compare a fixedpoint
592590 # number with None (e.g. 1>None but FixedPoint(1)>None can't be done)
593591 gen._Dynamic_time = -1
594592
702700
703701 def identity_hook(obj,val): return val
704702
703 def get_soft_bounds(bounds, softbounds):
704 """
705 For each soft bound (upper and lower), if there is a defined bound
706 (not equal to None) and does not exceed the hard bound, then it is
707 returned. Otherwise it defaults to the hard bound. The hard bound
708 could still be None.
709 """
710 if bounds is None:
711 hl, hu = (None, None)
712 else:
713 hl, hu = bounds
714
715 if softbounds is None:
716 sl, su = (None, None)
717 else:
718 sl, su = softbounds
719
720 if sl is None or (hl is not None and sl<hl):
721 l = hl
722 else:
723 l = sl
724
725 if su is None or (hu is not None and su>hu):
726 u = hu
727 else:
728 u = su
729
730 return (l, u)
705731
706732
707733 class Number(Dynamic):
749775
750776 """
751777
752 __slots__ = ['bounds','_softbounds','inclusive_bounds','set_hook', 'step']
753
754 def __init__(self,default=0.0,bounds=None,softbounds=None,
778 __slots__ = ['bounds', 'softbounds', 'inclusive_bounds', 'set_hook', 'step']
779
780 def __init__(self, default=0.0, bounds=None, softbounds=None,
755781 inclusive_bounds=(True,True), step=None, **params):
756782 """
757783 Initialize this parameter object and store the bounds.
758784
759785 Non-dynamic default values are checked against the bounds.
760786 """
761 super(Number,self).__init__(default=default,**params)
787 super(Number,self).__init__(default=default, **params)
762788
763789 self.set_hook = identity_hook
764790 self.bounds = bounds
765791 self.inclusive_bounds = inclusive_bounds
766 self._softbounds = softbounds
792 self.softbounds = softbounds
767793 self.step = step
768794 self._validate(default)
769795
770
771 def __get__(self,obj,objtype):
796 def __get__(self, obj, objtype):
772797 """
773798 Same as the superclass's __get__, but if the value was
774799 dynamically generated, check the bounds.
775800 """
776 result = super(Number,self).__get__(obj,objtype)
777 # CEBALERT: results in extra lookups (_value_is_dynamic() is
778 # also looking up 'result' - should just pass it in). Note
779 # that this method is called often.
780 if self._value_is_dynamic(obj,objtype): self._validate(result)
801 result = super(Number, self).__get__(obj, objtype)
802 # Should be able to optimize this commonly used method by
803 # avoiding extra lookups (e.g. _value_is_dynamic() is also
804 # looking up 'result' - should just pass it in).
805 if self._value_is_dynamic(obj, objtype):
806 self._validate(result)
781807 return result
782
783 # Allow softbounds to be used like a normal attribute, as it
784 # probably should have been already (not _softbounds)
785 @property
786 def softbounds(self): return self._softbounds
787
788 @softbounds.setter
789 def softbounds(self,value): self._softbounds = value
790
791808
792809 def set_in_bounds(self,obj,val):
793810 """
799816 bounded_val = self.crop_to_bounds(val)
800817 else:
801818 bounded_val = val
802 super(Number,self).__set__(obj,bounded_val)
803
804
805 # CEBERRORALERT: doesn't take account of exclusive bounds; see
806 # https://github.com/ioam/param/issues/80.
807 def crop_to_bounds(self,val):
819 super(Number, self).__set__(obj, bounded_val)
820
821 def crop_to_bounds(self, val):
808822 """
809823 Return the given value cropped to be within the hard bounds
810824 for this parameter.
815829 returned value could be None. If a non-numeric value is passed
816830 in, set to be the default value (which could be None). In no
817831 case is an exception raised; all values are accepted.
818 """
819 # Currently, values outside the bounds are silently cropped to
820 # be inside the bounds; it may be appropriate to add a warning
821 # in such cases.
832
833 As documented in https://github.com/holoviz/param/issues/80,
834 currently does not respect exclusive bounds, which would
835 strictly require setting to one less for integer values or
836 an epsilon less for floats.
837 """
838 # Values outside the bounds are silently cropped to
839 # be inside the bounds.
822840 if _is_number(val):
823841 if self.bounds is None:
824842 return val
836854
837855 else:
838856 # non-numeric value sent in: reverts to default value
839 return self.default
857 return self.default
840858
841859 return val
842860
843
844 def _checkBounds(self, val):
845
846 if self.bounds is not None:
847 vmin,vmax = self.bounds
848 incmin,incmax = self.inclusive_bounds
849
850 # Could simplify: see https://github.com/ioam/param/issues/83
851 if vmax is not None:
852 if incmax is True:
853 if not val <= vmax:
854 raise ValueError("Parameter '%s' must be at most %s"%(self.name,vmax))
855 else:
856 if not val < vmax:
857 raise ValueError("Parameter '%s' must be less than %s"%(self.name,vmax))
858
859 if vmin is not None:
860 if incmin is True:
861 if not val >= vmin:
862 raise ValueError("Parameter '%s' must be at least %s"%(self.name,vmin))
863 else:
864 if not val > vmin:
865 raise ValueError("Parameter '%s' must be greater than %s"%(self.name,vmin))
866
867
861 def _validate_bounds(self, val, bounds, inclusive_bounds):
862 if bounds is None or (val is None and self.allow_None) or callable(val):
863 return
864 vmin, vmax = bounds
865 incmin, incmax = inclusive_bounds
866 if vmax is not None:
867 if incmax is True:
868 if not val <= vmax:
869 raise ValueError("Parameter %r must be at most %s, "
870 "not %s." % (self.name, vmax, val))
871 else:
872 if not val < vmax:
873 raise ValueError("Parameter %r must be less than %s, "
874 "not %s." % (self.name, vmax, val))
875
876 if vmin is not None:
877 if incmin is True:
878 if not val >= vmin:
879 raise ValueError("Parameter %r must be at least %s, "
880 "not %s." % (self.name, vmin, val))
881 else:
882 if not val > vmin:
883 raise ValueError("Parameter %r must be greater than %s, "
884 "not %s." % (self.name, vmin, val))
885
886 def _validate_value(self, val, allow_None):
887 if (allow_None and val is None) or callable(val):
888 return
889
890 if not _is_number(val):
891 raise ValueError("Parameter %r only takes numeric values, "
892 "not type %r." % (self.name, type(val)))
893
894 def _validate_step(self, val, step):
895 if step is not None and not _is_number(step):
896 raise ValueError("Step can only be None or a "
897 "numeric value, not type %r." % type(step))
868898
869899 def _validate(self, val):
870900 """
871901 Checks that the value is numeric and that it is within the hard
872902 bounds; if not, an exception is raised.
873903 """
874 if callable(val):
875 return val
876
877 if self.allow_None and val is None:
878 return
879
880 if not _is_number(val):
881 raise ValueError("Parameter '%s' only takes numeric values"%(self.name))
882
883 if self.step is not None and not _is_number(self.step):
884 raise ValueError("Step parameter can only be None or a numeric value")
885
886 self._checkBounds(val)
887
904 self._validate_value(val, self.allow_None)
905 self._validate_step(val, self.step)
906 self._validate_bounds(val, self.bounds, self.inclusive_bounds)
888907
889908 def get_soft_bounds(self):
890 """
891 For each soft bound (upper and lower), if there is a defined bound (not equal to None)
892 then it is returned, otherwise it defaults to the hard bound. The hard bound could still be None.
893 """
894 if self.bounds is None:
895 hl,hu=(None,None)
896 else:
897 hl,hu=self.bounds
898
899 if self._softbounds is None:
900 sl,su=(None,None)
901 else:
902 sl,su=self._softbounds
903
904
905 if sl is None: l = hl
906 else: l = sl
907
908 if su is None: u = hu
909 else: u = su
910
911 return (l,u)
912
909 return get_soft_bounds(self.bounds, self.softbounds)
913910
914911 def __setstate__(self,state):
915912 if 'step' not in state:
916913 state['step'] = None
917914
918 super(Number,self).__setstate__(state)
915 super(Number, self).__setstate__(state)
919916
920917
921918
922919 class Integer(Number):
923920 """Numeric Parameter required to be an Integer"""
924921
925 def __init__(self,default=0,**params):
926 Number.__init__(self,default=default,**params)
927
928 def _validate(self, val):
929 if callable(val): return
930
931 if self.allow_None and val is None:
932 return
933
934 if not isinstance(val,int):
935 raise ValueError("Parameter '%s' must be an integer."%self.name)
936
937 if self.step is not None and not isinstance(self.step, int):
938 raise ValueError("Step parameter can only be None or an integer value")
939
940
941 self._checkBounds(val)
922 def __init__(self, default=0, **params):
923 Number.__init__(self, default=default, **params)
924
925 def _validate_value(self, val, allow_None):
926 if callable(val):
927 return
928
929 if allow_None and val is None:
930 return
931
932 if not isinstance(val, int):
933 raise ValueError("Integer parameter %r must be an integer, "
934 "not type %r." % (self.name, type(val)))
935
936 def _validate_step(self, val, step):
937 if step is not None and not isinstance(step, int):
938 raise ValueError("Step can only be None or an "
939 "integer value, not type %r" % type(step))
942940
943941
944942
945943 class Magnitude(Number):
946944 """Numeric Parameter required to be in the range [0.0-1.0]."""
947945
948 def __init__(self,default=1.0,softbounds=None,**params):
949 Number.__init__(self,default=default,bounds=(0.0,1.0),softbounds=softbounds,**params)
946 def __init__(self, default=1.0, softbounds=None, **params):
947 Number.__init__(self, default=default, bounds=(0.0,1.0), softbounds=softbounds, **params)
950948
951949
952950
955953
956954 __slots__ = ['bounds']
957955
958 # CB: bounds have no effect; see https://github.com/ioam/param/issues/82
959 def __init__(self,default=False,bounds=(0,1),**params):
956 # Bounds are set for consistency and are arguably accurate, but have
957 # no effect since values are either False, True, or None (if allowed).
958 def __init__(self, default=False, bounds=(0,1), **params):
960959 self.bounds = bounds
961 super(Boolean, self).__init__(default=default,**params)
962
963 def _validate(self, val):
964 if self.allow_None:
965 if not isinstance(val,bool) and val is not None:
966 raise ValueError("Boolean '%s' only takes a Boolean value or None."
967 %self.name)
968
969 if val is not True and val is not False and val is not None:
970 raise ValueError("Boolean '%s' must be True, False, or None."%self.name)
971 else:
972 if not isinstance(val,bool):
973 raise ValueError("Boolean '%s' only takes a Boolean value."%self.name)
974
975 if val is not True and val is not False:
976 raise ValueError("Boolean '%s' must be True or False."%self.name)
977 super(Boolean, self)._validate(val)
960 super(Boolean, self).__init__(default=default, **params)
961
962 def _validate_value(self, val, allow_None):
963 if allow_None:
964 if not isinstance(val, bool) and val is not None:
965 raise ValueError("Boolean parameter %r only takes a "
966 "Boolean value or None, not %s."
967 % (self.name, val))
968 elif not isinstance(val, bool):
969 raise ValueError("Boolean parameter %r must be True or False, "
970 "not %s." % (self.name, val))
978971
979972
980973
983976
984977 __slots__ = ['length']
985978
986 def __init__(self,default=(0,0),length=None,**params):
979 def __init__(self, default=(0,0), length=None, **params):
987980 """
988981 Initialize a tuple parameter with a fixed length (number of
989982 elements). The length is determined by the initial default
990983 value, if any, and must be supplied explicitly otherwise. The
991984 length is not allowed to change after instantiation.
992985 """
993 super(Tuple,self).__init__(default=default,**params)
986 super(Tuple,self).__init__(default=default, **params)
994987 if length is None and default is not None:
995988 self.length = len(default)
996989 elif length is None and default is None:
1000993 self.length = length
1001994 self._validate(default)
1002995
996 def _validate_value(self, val, allow_None):
997 if val is None and allow_None:
998 return
999
1000 if not isinstance(val, tuple):
1001 raise ValueError("Tuple parameter %r only takes a tuple value, "
1002 "not %r." % (self.name, type(val)))
1003
1004 def _validate_length(self, val, length):
1005 if val is None and self.allow_None:
1006 return
1007
1008 if not len(val) == length:
1009 raise ValueError("Tuple parameter %r is not of the correct "
1010 "length (%d instead of %d)." %
1011 (self.name, len(val), length))
10031012
10041013 def _validate(self, val):
1005 if val is None and self.allow_None:
1006 return
1007
1008 if not isinstance(val,tuple):
1009 raise ValueError("Tuple '%s' only takes a tuple value."%self.name)
1010
1011 if not len(val)==self.length:
1012 raise ValueError("%s: tuple is not of the correct length (%d instead of %d)." %
1013 (self.name,len(val),self.length))
1014
1014 self._validate_value(val, self.allow_None)
1015 self._validate_length(val, self.length)
1016
1017 @classmethod
1018 def serialize(cls, value):
1019 if value is None:
1020 return 'null'
1021 return list(value) # As JSON has no tuple representation
1022
1023 @classmethod
1024 def deserialize(cls, value):
1025 if value == 'null':
1026 return None
1027 return tuple(value) # As JSON has no tuple representation
10151028
10161029
10171030 class NumericTuple(Tuple):
10181031 """A numeric tuple Parameter (e.g. (4.5,7.6,3)) with a fixed tuple length."""
10191032
1020 def _validate(self, val):
1021 super(NumericTuple, self)._validate(val)
1022 if not (self.allow_None and val is None):
1023 for n in val:
1024 if not _is_number(n):
1025 raise ValueError("%s: tuple element is not numeric: %s." %
1026 (self.name,str(n)))
1027
1033 def _validate_value(self, val, allow_None):
1034 super(NumericTuple, self)._validate_value(val, allow_None)
1035 if allow_None and val is None:
1036 return
1037 for n in val:
1038 if _is_number(n):
1039 continue
1040 raise ValueError("NumericTuple parameter %r only takes numeric "
1041 "values, not type %r." % (self.name, type(n)))
10281042
10291043
10301044 class XYCoordinates(NumericTuple):
10311045 """A NumericTuple for an X,Y coordinate."""
10321046
1033 def __init__(self,default=(0.0,0.0),**params):
1034 super(XYCoordinates,self).__init__(default=default,length=2,**params)
1035
1047 def __init__(self, default=(0.0, 0.0), **params):
1048 super(XYCoordinates,self).__init__(default=default, length=2, **params)
10361049
10371050
10381051 class Callable(Parameter):
10451058 2.4, so instantiate must be False for those values.
10461059 """
10471060
1048 def _validate(self, val):
1049 if not (self.allow_None and val is None) and (not callable(val)):
1050 raise ValueError("Callable '%s' only takes a callable object."%self.name)
1051 super(Callable, self)._validate(val)
1052
1061 def _validate_value(self, val, allow_None):
1062 if (allow_None and val is None) or callable(val):
1063 return
1064
1065 raise ValueError("Callable parameter %r only takes a callable object, "
1066 "not objects of type %r." % (self.name, type(val)))
10531067
10541068
10551069 class Action(Callable):
10671081 return False
10681082
10691083
1070
1071 # CEBALERT: this should be a method of ClassSelector.
1084 # Could be a method of ClassSelector.
10721085 def concrete_descendents(parentclass):
10731086 """
10741087 Return a dictionary containing all subclasses of the specified
10811094 """
10821095 return dict((c.__name__,c) for c in descendents(parentclass)
10831096 if not _is_abstract(c))
1084
10851097
10861098
10871099 class Composite(Parameter):
10941106 in the order specified. Likewise, setting the parameter takes a
10951107 sequence of values and sets the value of the constituent
10961108 attributes.
1097 """
1098
1099 __slots__=['attribs','objtype']
1100
1101 def __init__(self,attribs=None,**kw):
1109
1110 This Parameter type has not been tested with watchers and
1111 dependencies, and may not support them properly.
1112 """
1113
1114 __slots__ = ['attribs', 'objtype']
1115
1116 def __init__(self, attribs=None, **kw):
11021117 if attribs is None:
11031118 attribs = []
1104 super(Composite,self).__init__(default=None,**kw)
1119 super(Composite, self).__init__(default=None, **kw)
11051120 self.attribs = attribs
11061121
1107 def __get__(self,obj,objtype):
1122 def __get__(self, obj, objtype):
11081123 """
11091124 Return the values of all the attribs, as a list.
11101125 """
11111126 if obj is None:
1112 return [getattr(objtype,a) for a in self.attribs]
1127 return [getattr(objtype, a) for a in self.attribs]
11131128 else:
1114 return [getattr(obj,a) for a in self.attribs]
1129 return [getattr(obj, a) for a in self.attribs]
1130
1131 def _validate_attribs(self, val, attribs):
1132 if len(val) == len(attribs):
1133 return
1134 raise ValueError("Compound parameter %r got the wrong number "
1135 "of values (needed %d, but got %d)." %
1136 (self.name, len(attribs), len(val)))
11151137
11161138 def _validate(self, val):
1117 assert len(val) == len(self.attribs),"Compound parameter '%s' got the wrong number of values (needed %d, but got %d)." % (self.name,len(self.attribs),len(val))
1139 self._validate_attribs(val, self.attribs)
11181140
11191141 def _post_setter(self, obj, val):
11201142 if obj is None:
1121 for a,v in zip(self.attribs,val):
1122 setattr(self.objtype,a,v)
1143 for a, v in zip(self.attribs, val):
1144 setattr(self.objtype, a, v)
11231145 else:
1124 for a,v in zip(self.attribs,val):
1125 setattr(obj,a,v)
1146 for a, v in zip(self.attribs, val):
1147 setattr(obj, a, v)
11261148
11271149
11281150 class SelectorBase(Parameter):
11381160 raise NotImplementedError("get_range() must be implemented in subclasses.")
11391161
11401162
1141 class ObjectSelector(SelectorBase):
1163 class Selector(SelectorBase):
11421164 """
11431165 Parameter whose value must be one object from a list of possible objects.
1166
1167 By default, if no default is specified, picks the first object from
1168 the provided set of objects, as long as the objects are in an
1169 ordered data collection.
11441170
11451171 check_on_set restricts the value to be among the current list of
11461172 objects. By default, if objects are initially supplied,
11611187 up from the object value.
11621188 """
11631189
1164 __slots__ = ['objects','compute_default_fn','check_on_set','names']
1165
1166 # ObjectSelector is usually used to allow selection from a list of
1190 __slots__ = ['objects', 'compute_default_fn', 'check_on_set', 'names']
1191
1192 # Selector is usually used to allow selection from a list of
11671193 # existing objects, therefore instantiate is False by default.
1168 def __init__(self,default=None,objects=None,instantiate=False,
1169 compute_default_fn=None,check_on_set=None,allow_None=None,**params):
1194 def __init__(self, objects=None, default=None, instantiate=False,
1195 compute_default_fn=None, check_on_set=None,
1196 allow_None=None, empty_default=False, **params):
1197
1198 autodefault = None
1199 if objects:
1200 if is_ordered_dict(objects):
1201 autodefault = list(objects.values())[0]
1202 elif isinstance(objects, dict):
1203 main.param.warning("Parameter default value is arbitrary due to "
1204 "dictionaries prior to Python 3.6 not being "
1205 "ordered; should use an ordered dict or "
1206 "supply an explicit default value.")
1207 autodefault = list(objects.values())[0]
1208 elif isinstance(objects, list):
1209 autodefault = objects[0]
1210
1211 default = autodefault if (not empty_default and default is None) else default
1212
11701213 if objects is None:
11711214 objects = []
11721215 if isinstance(objects, collections_abc.Mapping):
11781221 self.compute_default_fn = compute_default_fn
11791222
11801223 if check_on_set is not None:
1181 self.check_on_set=check_on_set
1182 elif len(objects)==0:
1183 self.check_on_set=False
1224 self.check_on_set = check_on_set
1225 elif len(objects) == 0:
1226 self.check_on_set = False
11841227 else:
1185 self.check_on_set=True
1186
1187 super(ObjectSelector,self).__init__(default=default,instantiate=instantiate,
1188 **params)
1228 self.check_on_set = True
1229
1230 super(Selector,self).__init__(
1231 default=default, instantiate=instantiate, **params)
11891232 # Required as Parameter sets allow_None=True if default is None
11901233 self.allow_None = allow_None
11911234 if default is not None and self.check_on_set is True:
11921235 self._validate(default)
11931236
1194
1195 # CBNOTE: if the list of objects is changed, the current value for
1196 # this parameter in existing POs could be out of the new range.
1237 # Note that if the list of objects is changed, the current value for
1238 # this parameter in existing POs could be outside of the new range.
11971239
11981240 def compute_default(self):
11991241 """
12041246 no longer None).
12051247 """
12061248 if self.default is None and callable(self.compute_default_fn):
1207 self.default=self.compute_default_fn()
1249 self.default = self.compute_default_fn()
12081250 if self.default not in self.objects:
12091251 self.objects.append(self.default)
12101252
1211
12121253 def _validate(self, val):
12131254 """
12141255 val must be None or one of the objects in self.objects.
12181259 return
12191260
12201261 if not (val in self.objects or (self.allow_None and val is None)):
1221 # CEBALERT: can be called before __init__ has called
1222 # super's __init__, i.e. before attrib_name has been set.
1223 try:
1224 attrib_name = self.name
1225 except AttributeError:
1262 # This method can be called before __init__ has called
1263 # super's __init__, so there may not be any name set yet.
1264 if (hasattr(self, "name") and self.name):
1265 attrib_name = " " + self.name
1266 else:
12261267 attrib_name = ""
12271268
12281269 items = []
12371278 limiter = ', ...]'
12381279 break
12391280 items = '[' + ', '.join(items) + limiter
1240 raise ValueError("%s not in Parameter %s's list of possible objects, "
1241 "valid options include %s"%(val,attrib_name, items))
1281 raise ValueError("%s not in parameter%s's list of possible objects, "
1282 "valid options include %s" % (val, attrib_name, items))
12421283
12431284 def _ensure_value_is_in_objects(self,val):
12441285 """
12471288 to check each item instead.
12481289 """
12491290 if not (val in self.objects):
1250 self.objects.append(val)
1291 self.objects.append(val)
12511292
12521293 def get_range(self):
12531294 """
12581299 return named_objs(self.objects, self.names)
12591300
12601301
1261 class Selector(ObjectSelector):
1262 """
1263 A more user friendly ObjectSelector that picks the first object for
1264 the default (by default) given an ordered data collection. As the
1265 first argument is now objects, this can be passed in as a positional
1266 argument which sufficient in many common use cases.
1267 """
1268 def __init__(self,objects=None, default=None, instantiate=False,
1269 compute_default_fn=None,check_on_set=None,allow_None=None,**params):
1270
1271 if is_ordered_dict(objects):
1272 autodefault = list(objects.values())[0]
1273 elif isinstance(objects, dict):
1274 main.param.warning("Parameter default value is arbitrary due to "
1275 "dictionaries prior to Python 3.6 not being "
1276 "ordered; should use an ordered dict or "
1277 "supply an explicit default value.")
1278 autodefault = list(objects.values())[0]
1279 elif isinstance(objects, list):
1280 autodefault = objects[0]
1281 else:
1282 autodefault = None
1283
1284 default = autodefault if default is None else default
1285
1286 super(Selector,self).__init__(default=default, objects=objects,
1287 instantiate=instantiate,
1288 compute_default_fn=compute_default_fn,
1289 check_on_set=check_on_set,
1290 allow_None=allow_None, **params)
1302 class ObjectSelector(Selector):
1303 """
1304 Deprecated. Same as Selector, but with a different constructor for
1305 historical reasons.
1306 """
1307 def __init__(self, default=None, objects=None, **kwargs):
1308 super(ObjectSelector,self).__init__(objects=objects, default=default,
1309 empty_default=True, **kwargs)
1310
12911311
12921312 class ClassSelector(SelectorBase):
12931313 """
12971317 for is_instance=True.
12981318 """
12991319
1300 __slots__ = ['class_','is_instance']
1320 __slots__ = ['class_', 'is_instance']
13011321
13021322 def __init__(self,class_,default=None,instantiate=True,is_instance=True,**params):
13031323 self.class_ = class_
13051325 super(ClassSelector,self).__init__(default=default,instantiate=instantiate,**params)
13061326 self._validate(default)
13071327
1308
1309 def _validate(self,val):
1310 """val must be None, an instance of self.class_ if self.is_instance=True or a subclass of self_class if self.is_instance=False"""
1311 if isinstance(self.class_, tuple):
1312 class_name = ('(%s)' % ', '.join(cl.__name__ for cl in self.class_))
1328 def _validate(self, val):
1329 super(ClassSelector, self)._validate(val)
1330 self._validate_class_(val, self.class_, self.is_instance)
1331
1332 def _validate_class_(self, val, class_, is_instance):
1333 if (val is None and self.allow_None):
1334 return
1335 if isinstance(class_, tuple):
1336 class_name = ('(%s)' % ', '.join(cl.__name__ for cl in class_))
13131337 else:
1314 class_name = self.class_.__name__
1315 if self.is_instance:
1316 if not (isinstance(val,self.class_)) and not (val is None and self.allow_None):
1338 class_name = class_.__name__
1339 param_cls = self.__class__.__name__
1340 if is_instance:
1341 if not (isinstance(val, class_)):
13171342 raise ValueError(
1318 "Parameter '%s' value must be an instance of %s, not '%s'" %
1319 (self.name, class_name, val))
1343 "%s parameter %r value must be an instance of %s, not %r." %
1344 (param_cls, self.name, class_name, val))
13201345 else:
1321 if not (val is None and self.allow_None) and not (issubclass(val,self.class_)):
1346 if not (issubclass(val, class_)):
13221347 raise ValueError(
1323 "Parameter '%s' must be a subclass of %s, not '%s'" %
1324 (val.__name__, class_name, val.__class__.__name__))
1325
1348 "%s parameter %r must be a subclass of %s, not %r." %
1349 (param_cls, self.name, class_name, val.__name__))
13261350
13271351 def get_range(self):
13281352 """
13291353 Return the possible types for this parameter's value.
13301354
1331 (I.e. return {name: <class>} for all classes that are
1332 concrete_descendents() of self.class_.)
1355 (I.e. return `{name: <class>}` for all classes that are
1356 concrete_descendents() of `self.class_`.)
13331357
13341358 Only classes from modules that have been imported are added
13351359 (see concrete_descendents()).
13381362 all_classes = {}
13391363 for cls in classes:
13401364 all_classes.update(concrete_descendents(cls))
1341 d=OrderedDict((name,class_) for name,class_ in all_classes.items())
1365 d = OrderedDict((name, class_) for name,class_ in all_classes.items())
13421366 if self.allow_None:
1343 d['None']=None
1367 d['None'] = None
13441368 return d
13451369
13461370
13491373 Parameter whose value is a list of objects, usually of a specified type.
13501374
13511375 The bounds allow a minimum and/or maximum length of
1352 list to be enforced. If the class is non-None, all
1376 list to be enforced. If the item_type is non-None, all
13531377 items in the list are checked to be of that type.
1354 """
1355
1356 __slots__ = ['class_','bounds']
1357
1358 def __init__(self,default=[],class_=None,instantiate=True,
1359 bounds=(0,None),**params):
1360 self.class_ = class_
1378
1379 `class_` is accepted as an alias for `item_type`, but is
1380 deprecated due to conflict with how the `class_` slot is
1381 used in Selector classes.
1382 """
1383
1384 __slots__ = ['bounds', 'item_type', 'class_']
1385
1386 def __init__(self, default=[], class_=None, item_type=None,
1387 instantiate=True, bounds=(0, None), **params):
1388 self.item_type = item_type or class_
1389 self.class_ = self.item_type
13611390 self.bounds = bounds
1362 Parameter.__init__(self,default=default,instantiate=instantiate,
1391 Parameter.__init__(self, default=default, instantiate=instantiate,
13631392 **params)
13641393 self._validate(default)
13651394
13661395 def _validate(self, val):
13671396 """
1368 Checks that the list is of the right length and has the right contents.
1369 Otherwise, an exception is raised.
1370 """
1371 if self.allow_None and val is None:
1372 return
1373
1397 Checks that the value is numeric and that it is within the hard
1398 bounds; if not, an exception is raised.
1399 """
1400 self._validate_value(val, self.allow_None)
1401 self._validate_bounds(val, self.bounds)
1402 self._validate_item_type(val, self.item_type)
1403
1404 def _validate_bounds(self, val, bounds):
1405 "Checks that the list is of the right length and has the right contents."
1406 if bounds is None or (val is None and self.allow_None):
1407 return
1408 min_length, max_length = bounds
1409 l = len(val)
1410 if min_length is not None and max_length is not None:
1411 if not (min_length <= l <= max_length):
1412 raise ValueError("%s: list length must be between %s and %s (inclusive)"%(self.name,min_length,max_length))
1413 elif min_length is not None:
1414 if not min_length <= l:
1415 raise ValueError("%s: list length must be at least %s."
1416 % (self.name, min_length))
1417 elif max_length is not None:
1418 if not l <= max_length:
1419 raise ValueError("%s: list length must be at most %s."
1420 % (self.name, max_length))
1421
1422 def _validate_value(self, val, allow_None):
1423 if allow_None and val is None:
1424 return
13741425 if not isinstance(val, list):
1375 raise ValueError("List '%s' must be a list."%(self.name))
1376
1377 if self.bounds is not None:
1378 min_length,max_length = self.bounds
1379 l=len(val)
1380 if min_length is not None and max_length is not None:
1381 if not (min_length <= l <= max_length):
1382 raise ValueError("%s: list length must be between %s and %s (inclusive)"%(self.name,min_length,max_length))
1383 elif min_length is not None:
1384 if not min_length <= l:
1385 raise ValueError("%s: list length must be at least %s."%(self.name,min_length))
1386 elif max_length is not None:
1387 if not l <= max_length:
1388 raise ValueError("%s: list length must be at most %s."%(self.name,max_length))
1389
1390 self._check_type(val)
1391
1392 def _check_type(self,val):
1393 if self.class_ is not None:
1394 for v in val:
1395 assert isinstance(v,self.class_),repr(self.name)+": "+repr(v)+" is not an instance of " + repr(self.class_) + "."
1396
1426 raise ValueError("List parameter %r must be a list, not an object of type %s."
1427 % (self.name, type(val)))
1428
1429 def _validate_item_type(self, val, item_type):
1430 if item_type is None or (self.allow_None and val is None):
1431 return
1432 for v in val:
1433 if isinstance(v, item_type):
1434 continue
1435 raise TypeError("List parameter %r items must be instances "
1436 "of type %r, not %r." % (self.name, item_type, val))
13971437
13981438
13991439 class HookList(List):
14041444 for users to register a set of commands to be called at a
14051445 specified place in some sequence of processing steps.
14061446 """
1407 __slots__ = ['class_','bounds']
1408
1409 def _check_type(self,val):
1447 __slots__ = ['class_', 'bounds']
1448
1449 def _validate_value(self, val, allow_None):
1450 super(HookList, self)._validate_value(val, allow_None)
1451 if allow_None and val is None:
1452 return
14101453 for v in val:
1411 assert callable(v),repr(self.name)+": "+repr(v)+" is not callable."
1412
1454 if callable(v):
1455 continue
1456 raise ValueError("HookList parameter %r items must be callable, "
1457 "not %r." % (self.name, v))
14131458
14141459
14151460 class Dict(ClassSelector):
14161461 """
14171462 Parameter whose value is a dictionary.
14181463 """
1464
14191465 def __init__(self, default=None, **params):
1420 super(Dict,self).__init__(dict, default=default, **params)
1466 super(Dict, self).__init__(dict, default=default, **params)
14211467
14221468
14231469 class Array(ClassSelector):
14261472 """
14271473
14281474 def __init__(self, default=None, **params):
1429 # CEBALERT: instead use python array as default?
14301475 from numpy import ndarray
1431 super(Array,self).__init__(ndarray, allow_None=True, default=default, **params)
1476 super(Array, self).__init__(ndarray, allow_None=True, default=default, **params)
1477
1478 @classmethod
1479 def serialize(cls, value):
1480 if value is None:
1481 return 'null'
1482 return value.tolist()
1483
1484 @classmethod
1485 def deserialize(cls, value):
1486 if value == 'null':
1487 return None
1488 from numpy import asarray
1489 return asarray(value)
14321490
14331491
14341492 class DataFrame(ClassSelector):
14491507 if a list is given, the supplied DataFrame must contain exactly the
14501508 same columns and in the same order and no other columns.
14511509 """
1452 __slots__ = ['rows','columns', 'ordered']
1510
1511 __slots__ = ['rows', 'columns', 'ordered']
14531512
14541513 def __init__(self, default=None, rows=None, columns=None, ordered=None, **params):
14551514 from pandas import DataFrame as pdDFrame
14561515 self.rows = rows
14571516 self.columns = columns
14581517 self.ordered = ordered
1459 super(DataFrame,self).__init__(pdDFrame, default=default, allow_None=True, **params)
1518 super(DataFrame,self).__init__(pdDFrame, default=default, **params)
14601519 self._validate(self.default)
1461
14621520
14631521 def _length_bounds_check(self, bounds, length, name):
14641522 message = '{name} length {length} does not match declared bounds of {bounds}'
14781536
14791537 if isinstance(self.columns, set) and self.ordered is True:
14801538 raise ValueError('Columns cannot be ordered when specified as a set')
1539
1540 if self.allow_None and val is None:
1541 return
14811542
14821543 if self.columns is None:
14831544 pass
15011562 if self.rows is not None:
15021563 self._length_bounds_check(self.rows, len(val), 'Row')
15031564
1565 @classmethod
1566 def serialize(cls, value):
1567 if value is None:
1568 return 'null'
1569 return value.to_dict('records')
1570
1571 @classmethod
1572 def deserialize(cls, value):
1573 if value == 'null':
1574 return None
1575 from pandas import DataFrame as pdDFrame
1576 return pdDFrame(value)
1577
15041578
15051579 class Series(ClassSelector):
15061580 """
15101584 which may be a number or an integer bounds tuple to constrain the
15111585 allowable number of rows.
15121586 """
1587
15131588 __slots__ = ['rows']
1589
1590 def __init__(self, default=None, rows=None, allow_None=False, **params):
1591 from pandas import Series as pdSeries
1592 self.rows = rows
1593 super(Series,self).__init__(pdSeries, default=default, allow_None=allow_None,
1594 **params)
1595 self._validate(self.default)
15141596
15151597 def _length_bounds_check(self, bounds, length, name):
15161598 message = '{name} length {length} does not match declared bounds of {bounds}'
15251607 if failure:
15261608 raise ValueError(message.format(name=name,length=length, bounds=bounds))
15271609
1528 def __init__(self, default=None, rows=None, **params):
1529 from pandas import Series as pdSeries
1530 self.rows = rows
1531 super(Series,self).__init__(pdSeries, allow_None=True, default=default, **params)
1532 self._validate(self.default)
1533
15341610 def _validate(self, val):
15351611 super(Series, self)._validate(val)
15361612
1613 if self.allow_None and val is None:
1614 return
1615
15371616 if self.rows is not None:
15381617 self._length_bounds_check(self.rows, len(val), 'Row')
15391618
15411620
15421621 # For portable code:
15431622 # - specify paths in unix (rather than Windows) style;
1544 # - use resolve_file_path() for paths to existing files to be read,
1545 # - use resolve_folder_path() for paths to existing folders to be read,
1623 # - use resolve_path(path_to_file=True) for paths to existing files to be read,
1624 # - use resolve_path(path_to_file=False) for paths to existing folders to be read,
15461625 # and normalize_path() for paths to new files to be written.
15471626
15481627 class resolve_path(ParameterizedFunction):
15661645 Prepended to a non-relative path, in order, until a file is
15671646 found.""")
15681647
1569 path_to_file = Boolean(default=True, pickle_default_value=False, doc="""
1570 String specifying whether the path refers to a 'File' or a 'Folder'.""")
1648 path_to_file = Boolean(default=True, pickle_default_value=False,
1649 allow_None=True, doc="""
1650 String specifying whether the path refers to a 'File' or a
1651 'Folder'. If None, the path may point to *either* a 'File' *or*
1652 a 'Folder'.""")
15711653
15721654 def __call__(self, path, **params):
15731655 p = ParamOverrides(self, params)
1574
15751656 path = os.path.normpath(path)
1657 ftype = "File" if p.path_to_file is True \
1658 else "Folder" if p.path_to_file is False else "Path"
1659
1660 if not p.search_paths:
1661 p.search_paths = [os.getcwd()]
15761662
15771663 if os.path.isabs(path):
1578 if p.path_to_file:
1579 if os.path.isfile(path):
1580 return path
1581 else:
1582 raise IOError("File '%s' not found." %path)
1583 elif not p.path_to_file:
1584 if os.path.isdir(path):
1585 return path
1586 else:
1587 raise IOError("Folder '%s' not found." %path)
1588 else:
1589 raise IOError("Type '%s' not recognised." %p.path_type)
1664 if ((p.path_to_file is None and os.path.exists(path)) or
1665 (p.path_to_file is True and os.path.isfile(path)) or
1666 (p.path_to_file is False and os.path.isdir( path))):
1667 return path
1668 raise IOError("%s '%s' not found." % (ftype,path))
15901669
15911670 else:
15921671 paths_tried = []
15931672 for prefix in p.search_paths:
15941673 try_path = os.path.join(os.path.normpath(prefix), path)
15951674
1596 if p.path_to_file:
1597 if os.path.isfile(try_path):
1598 return try_path
1599 elif not p.path_to_file:
1600 if os.path.isdir(try_path):
1601 return try_path
1602 else:
1603 raise IOError("Type '%s' not recognised." %p.path_type)
1675 if ((p.path_to_file is None and os.path.exists(try_path)) or
1676 (p.path_to_file is True and os.path.isfile(try_path)) or
1677 (p.path_to_file is False and os.path.isdir( try_path))):
1678 return try_path
16041679
16051680 paths_tried.append(try_path)
16061681
1607 raise IOError(os.path.split(path)[1] + " was not found in the following place(s): " + str(paths_tried) + ".")
1682 raise IOError(ftype + " " + os.path.split(path)[1] + " was not found in the following place(s): " + str(paths_tried) + ".")
16081683
16091684
16101685 class normalize_path(ParameterizedFunction):
16451720 The specified path can be absolute, or relative to either:
16461721
16471722 * any of the paths specified in the search_paths attribute (if
1648 search_paths is not None);
1723 search_paths is not None);
16491724
16501725 or
16511726
16631738 super(Path,self).__init__(default,**params)
16641739
16651740 def _resolve(self, path):
1666 if self.search_paths:
1667 return resolve_path(path, search_paths=self.search_paths)
1668 else:
1669 return resolve_path(path)
1741 return resolve_path(path, path_to_file=None, search_paths=self.search_paths)
16701742
16711743 def _validate(self, val):
16721744 if val is None:
16731745 if not self.allow_None:
1674 Parameterized(name="%s.%s"%(self.owner.name,self.name)).warning('None is not allowed')
1746 Parameterized(name="%s.%s"%(self.owner.name,self.name)).param.warning('None is not allowed')
16751747 else:
16761748 try:
16771749 self._resolve(val)
16781750 except IOError as e:
1679 Parameterized(name="%s.%s"%(self.owner.name,self.name)).warning('%s',e.args[0])
1751 Parameterized(name="%s.%s"%(self.owner.name,self.name)).param.warning('%s',e.args[0])
16801752
16811753 def __get__(self, obj, objtype):
16821754 """
17071779
17081780 * any of the paths specified in the search_paths attribute (if
17091781 search_paths is not None);
1782
17101783 or
17111784
17121785 * any of the paths searched by resolve_path() (if search_paths
17141787 """
17151788
17161789 def _resolve(self, path):
1717 if self.search_paths:
1718 return resolve_path(path, path_to_file=True, search_paths=self.search_paths)
1719 else:
1720 return resolve_path(path, path_to_file=True)
1790 return resolve_path(path, path_to_file=True, search_paths=self.search_paths)
17211791
17221792
17231793 class Foldername(Path):
17311801
17321802 * any of the paths specified in the search_paths attribute (if
17331803 search_paths is not None);
1804
17341805 or
17351806
17361807 * any of the paths searched by resolve_dir_path() (if search_paths
17381809 """
17391810
17401811 def _resolve(self, path):
1741 if self.search_paths:
1742 return resolve_path(path, path_to_file=False, search_paths=self.search_paths)
1743 else:
1744 return resolve_path(path, path_to_file=False)
1812 return resolve_path(path, path_to_file=False, search_paths=self.search_paths)
17451813
17461814
17471815
17571825
17581826
17591827
1760 class FileSelector(ObjectSelector):
1828 class FileSelector(Selector):
17611829 """
17621830 Given a path glob, allows one file to be selected from those matching.
17631831 """
17641832 __slots__ = ['path']
17651833
17661834 def __init__(self, default=None, path="", **kwargs):
1767 super(FileSelector, self).__init__(default, **kwargs)
1835 self.default = default
17681836 self.path = path
17691837 self.update()
1838 super(FileSelector, self).__init__(default=default, objects=self.objects,
1839 empty_default=True, **kwargs)
1840
1841 def _on_set(self, attribute, old, new):
1842 super(FileSelector, self)._on_set(attribute, new, old)
1843 if attribute == 'path':
1844 self.update()
17701845
17711846 def update(self):
17721847 self.objects = sorted(glob.glob(self.path))
17781853 return abbreviate_paths(self.path,super(FileSelector, self).get_range())
17791854
17801855
1781 class ListSelector(ObjectSelector):
1782 """
1783 Variant of ObjectSelector where the value can be multiple objects from
1856 class ListSelector(Selector):
1857 """
1858 Variant of Selector where the value can be multiple objects from
17841859 a list of possible objects.
17851860 """
1861
1862 def __init__(self, default=None, objects=None, **kwargs):
1863 super(ListSelector,self).__init__(
1864 objects=objects, default=default, empty_default=True, **kwargs)
17861865
17871866 def compute_default(self):
17881867 if self.default is None and callable(self.compute_default_fn):
17921871 self.objects.append(o)
17931872
17941873 def _validate(self, val):
1874 if (val is None and self.allow_None):
1875 return
17951876 for o in val:
17961877 super(ListSelector, self)._validate(o)
17971878
18041885 __slots__ = ['path']
18051886
18061887 def __init__(self, default=None, path="", **kwargs):
1807 super(MultiFileSelector, self).__init__(default, **kwargs)
1888 self.default = default
18081889 self.path = path
18091890 self.update()
1891 super(MultiFileSelector, self).__init__(default=default, objects=self.objects, **kwargs)
1892
1893 def _on_set(self, attribute, old, new):
1894 super(MultiFileSelector, self)._on_set(attribute, new, old)
1895 if attribute == 'path':
1896 self.update()
18101897
18111898 def update(self):
18121899 self.objects = sorted(glob.glob(self.path))
18261913 def __init__(self, default=None, **kwargs):
18271914 super(Date, self).__init__(default=default, **kwargs)
18281915
1829 def _validate(self, val):
1916 def _validate_value(self, val, allow_None):
18301917 """
18311918 Checks that the value is numeric and that it is within the hard
18321919 bounds; if not, an exception is raised.
18341921 if self.allow_None and val is None:
18351922 return
18361923
1837 if not isinstance(val, dt_types) and not (self.allow_None and val is None):
1838 raise ValueError("Date '%s' only takes datetime and date types."%self.name)
1839
1840 if self.step is not None and not isinstance(self.step, dt_types):
1841 raise ValueError("Step parameter can only be None, a datetime or datetime type")
1842
1843 self._checkBounds(val)
1924 if not isinstance(val, dt_types) and not (allow_None and val is None):
1925 raise ValueError(
1926 "Date parameter %r only takes datetime and date types, "
1927 "not type %r." % (self.name, type(val))
1928 )
1929
1930 def _validate_step(self, val, step):
1931 if step is not None and not isinstance(step, dt_types):
1932 raise ValueError(
1933 "Step can only be None, a datetime "
1934 "or datetime type, not type %r." % type(val)
1935 )
1936
1937 @classmethod
1938 def serialize(cls, value):
1939 if value is None:
1940 return 'null'
1941 if not isinstance(value, (dt.datetime, dt.date)): # i.e np.datetime64
1942 value = value.astype(dt.datetime)
1943 return value.strftime("%Y-%m-%dT%H:%M:%S.%f")
1944
1945 @classmethod
1946 def deserialize(cls, value):
1947 if value == 'null':
1948 return None
1949 return dt.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
18441950
18451951
18461952 class CalendarDate(Number):
18471953 """
1848 CalendarDate parameter of date type.
1954 Parameter specifically allowing dates (not datetimes).
18491955 """
18501956
18511957 def __init__(self, default=None, **kwargs):
18521958 super(CalendarDate, self).__init__(default=default, **kwargs)
18531959
1854 def _validate(self, val):
1960 def _validate_value(self, val, allow_None):
18551961 """
18561962 Checks that the value is numeric and that it is within the hard
18571963 bounds; if not, an exception is raised.
18591965 if self.allow_None and val is None:
18601966 return
18611967
1862 if not isinstance(val, dt.date) and not (self.allow_None and val is None):
1863 raise ValueError("CalendarDate '%s' only takes datetime types."%self.name)
1864
1865 if self.step is not None and not isinstance(self.step, dt.date):
1866 raise ValueError("Step parameter can only be None or a date type")
1867
1868 self._checkBounds(val)
1968 if (not isinstance(val, dt.date) or isinstance(val, dt.datetime)) and not (allow_None and val is None):
1969 raise ValueError("CalendarDate parameter %r only takes date types." % self.name)
1970
1971 def _validate_step(self, val, step):
1972 if step is not None and not isinstance(step, dt.date):
1973 raise ValueError("Step can only be None or a date type.")
1974
1975 @classmethod
1976 def serialize(cls, value):
1977 if value is None:
1978 return 'null'
1979 return value.strftime("%Y-%m-%d")
1980
1981 @classmethod
1982 def deserialize(cls, value):
1983 if value == 'null':
1984 return None
1985 return dt.datetime.strptime(value, "%Y-%m-%d").date()
18691986
18701987
18711988 class Color(Parameter):
18721989 """
18731990 Color parameter defined as a hex RGB string with an optional #
1874 prefix.
1875 """
1876
1877 def __init__(self, default=None, allow_None=False, **kwargs):
1991 prefix or (optionally) as a CSS3 color name.
1992 """
1993
1994 # CSS3 color specification https://www.w3.org/TR/css-color-3/#svg-color
1995 _named_colors = [ 'aliceblue', 'antiquewhite', 'aqua',
1996 'aquamarine', 'azure', 'beige', 'bisque', 'black',
1997 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood',
1998 'cadetblue', 'chartreuse', 'chocolate', 'coral',
1999 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue',
2000 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey',
2001 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen',
2002 'darkorange', 'darkorchid', 'darkred', 'darksalmon',
2003 'darkseagreen', 'darkslateblue', 'darkslategray',
2004 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
2005 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue',
2006 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia',
2007 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray',
2008 'grey', 'green', 'greenyellow', 'honeydew', 'hotpink',
2009 'indianred', 'indigo', 'ivory', 'khaki', 'lavender',
2010 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue',
2011 'lightcoral', 'lightcyan', 'lightgoldenrodyellow',
2012 'lightgray', 'lightgrey', 'lightgreen', 'lightpink',
2013 'lightsalmon', 'lightseagreen', 'lightskyblue',
2014 'lightslategray', 'lightslategrey', 'lightsteelblue',
2015 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta',
2016 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid',
2017 'mediumpurple', 'mediumseagreen', 'mediumslateblue',
2018 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred',
2019 'midnightblue', 'mintcream', 'mistyrose', 'moccasin',
2020 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab',
2021 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen',
2022 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff',
2023 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
2024 'rosybrown', 'royalblue', 'saddlebrown', 'salmon',
2025 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver',
2026 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow',
2027 'springgreen', 'steelblue', 'tan', 'teal', 'thistle',
2028 'tomato', 'turquoise', 'violet', 'wheat', 'white',
2029 'whitesmoke', 'yellow', 'yellowgreen']
2030
2031 __slots__ = ['allow_named']
2032
2033 def __init__(self, default=None, allow_named=True, **kwargs):
18782034 super(Color, self).__init__(default=default, **kwargs)
2035 self.allow_named = allow_named
18792036 self._validate(default)
18802037
18812038 def _validate(self, val):
1882 if (self.allow_None and val is None):
2039 self._validate_value(val, self.allow_None)
2040 self._validate_allow_named(val, self.allow_named)
2041
2042 def _validate_value(self, val, allow_None):
2043 if (allow_None and val is None):
18832044 return
18842045 if not isinstance(val, basestring):
1885 raise ValueError("Color '%s' only takes a string value."%self.name)
1886 if not re.match('^#?(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$', val):
1887 raise ValueError("Color '%s' only accepts valid RGB hex codes."
1888 % self.name)
1889
2046 raise ValueError("Color parameter %r expects a string value, "
2047 "not an object of type %s." % (self.name, type(val)))
2048
2049 def _validate_allow_named(self, val, allow_named):
2050 if (val is None and self.allow_None):
2051 return
2052 is_hex = re.match('^#?(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$', val)
2053 if self.allow_named:
2054 if not is_hex and val not in self._named_colors:
2055 raise ValueError("Color '%s' only takes RGB hex codes "
2056 "or named colors, received '%s'." % (self.name, val))
2057 elif not is_hex:
2058 raise ValueError("Color '%s' only accepts valid RGB hex "
2059 "codes, received '%s'." % (self.name, val))
18902060
18912061
18922062 class Range(NumericTuple):
1893 "A numeric range with optional bounds and softbounds"
1894
1895 __slots__ = ['bounds', 'inclusive_bounds', 'softbounds']
1896
2063 """
2064 A numeric range with optional bounds and softbounds.
2065 """
2066
2067 __slots__ = ['bounds', 'inclusive_bounds', 'softbounds', 'step']
18972068
18982069 def __init__(self,default=None, bounds=None, softbounds=None,
1899 inclusive_bounds=(True,True), **params):
2070 inclusive_bounds=(True,True), step=None, **params):
19002071 self.bounds = bounds
19012072 self.inclusive_bounds = inclusive_bounds
19022073 self.softbounds = softbounds
2074 self.step = step
19032075 super(Range,self).__init__(default=default,length=2,**params)
19042076
1905
19062077 def _validate(self, val):
1907 """
1908 Checks that the value is numeric and that it is within the hard
1909 bounds; if not, an exception is raised.
1910 """
1911 if self.allow_None and val is None:
1912 return
19132078 super(Range, self)._validate(val)
1914
1915 self._checkBounds(val)
2079 self._validate_bounds(val, self.bounds, self.inclusive_bounds)
2080
2081 def _validate_bounds(self, val, bounds, inclusive_bounds):
2082 if bounds is None or (val is None and self.allow_None):
2083 return
2084 vmin, vmax = bounds
2085 incmin, incmax = inclusive_bounds
2086 for bound, v in zip(['lower', 'upper'], val):
2087 too_low = (vmin is not None) and (v < vmin if incmin else v <= vmin)
2088 too_high = (vmax is not None) and (v > vmax if incmax else v >= vmax)
2089 if too_low or too_high:
2090 raise ValueError("Range parameter %r's %s bound must be in range %s."
2091 % (self.name, bound, self.rangestr()))
19162092
19172093
19182094 def get_soft_bounds(self):
1919 """
1920 For each soft bound (upper and lower), if there is a defined bound (not equal to None)
1921 then it is returned, otherwise it defaults to the hard bound. The hard bound could still be None.
1922 """
1923 if self.bounds is None:
1924 hl,hu=(None,None)
1925 else:
1926 hl,hu=self.bounds
1927
1928 if self.softbounds is None:
1929 sl,su=(None,None)
1930 else:
1931 sl,su=self.softbounds
1932
1933
1934 if sl is None: l = hl
1935 else: l = sl
1936
1937 if su is None: u = hu
1938 else: u = su
1939
1940 return (l,u)
2095 return get_soft_bounds(self.bounds, self.softbounds)
19412096
19422097
19432098 def rangestr(self):
19482103 return '%s%s, %s%s' % (incmin, vmin, vmax, incmax)
19492104
19502105
1951 def _checkBounds(self, val):
1952 if self.bounds is not None:
1953 vmin,vmax = self.bounds
1954 incmin,incmax = self.inclusive_bounds
1955 for bound, v in zip(['lower', 'upper'], val):
1956 too_low = (vmin is not None) and (v < vmin if incmin else v <= vmin)
1957 too_high = (vmax is not None) and (v > vmax if incmax else v >= vmax)
1958 if too_low or too_high:
1959 raise ValueError("Parameter '%s' %s bound must be in range %s"
1960 % (self.name, bound, self.rangestr()))
1961
1962
19632106 class DateRange(Range):
19642107 """
19652108 A datetime or date range specified as (start, end).
19662109
19672110 Bounds must be specified as datetime or date types (see param.dt_types).
19682111 """
1969 def _validate(self, val):
1970 if self.allow_None and val is None:
2112
2113 def _validate_value(self, val, allow_None):
2114 if allow_None and val is None:
19712115 return
19722116
19732117 for n in val:
1974 if not isinstance(n, dt_types):
1975 raise ValueError("DateRange '%s' only takes datetime types: %s"%(self.name,val))
2118 if isinstance(n, dt_types):
2119 continue
2120 raise ValueError("DateRange parameter %r only takes datetime "
2121 "types, not %r." % (self.name, type(val)))
19762122
19772123 start, end = val
19782124 if not end >= start:
1979 raise ValueError("DateRange '%s': end datetime %s is before start datetime %s."%(self.name,val[1],val[0]))
1980
1981 # Calling super(DateRange, self)._check(val) would also check
1982 # values are numeric, which is redundant, so just call
1983 # _checkBounds().
1984 self._checkBounds(val)
2125 raise ValueError("DateRange parameter %r's end datetime %s "
2126 "is before start datetime %s." %
2127 (self.name, val[1], val[0]))
2128
19852129
19862130
19872131 class CalendarDateRange(Range):
19882132 """
19892133 A date range specified as (start_date, end_date).
19902134 """
1991 def _validate(self, val):
1992 if self.allow_None and val is None:
2135 def _validate_value(self, val, allow_None):
2136 if allow_None and val is None:
19932137 return
19942138
19952139 for n in val:
19962140 if not isinstance(n, dt.date):
1997 raise ValueError("CalendarDateRange '%s' only takes date types: %s"%(self.name,val))
2141 raise ValueError("CalendarDateRange parameter %r only "
2142 "takes date types, not %s." % (self.name, val))
19982143
19992144 start, end = val
20002145 if not end >= start:
2001 raise ValueError("CalendarDateRange '%s': end date %s is before start date %s."%(self.name,val[1],val[0]))
2002
2003 # Calling super(CalendarDateRange, self)._check(val) would also check
2004 # values are numeric, which is redundant, so just call
2005 # _checkBounds().
2006 self._checkBounds(val)
2146 raise ValueError("CalendarDateRange parameter %r's end date "
2147 "%s is before start date %s." %
2148 (self.name, val[1], val[0]))
2149
2150
2151 class Event(Boolean):
2152 """
2153 An Event Parameter is one whose value is intimately linked to the
2154 triggering of events for watchers to consume. Event has a Boolean
2155 value, which when set to True triggers the associated watchers (as
2156 any Parameter does) and then is automatically set back to
2157 False. Conversely, if events are triggered directly via `.trigger`,
2158 the value is transiently set to True (so that it's clear which of
2159 many parameters being watched may have changed), then restored to
2160 False when the triggering completes. An Event parameter is thus like
2161 a momentary switch or pushbutton with a transient True value that
2162 serves only to launch some other action (e.g. via a param.depends
2163 decorator), rather than encapsulating the action itself as
2164 param.Action does.
2165 """
2166
2167 # _autotrigger_value specifies the value used to set the parameter
2168 # to when the parameter is supplied to the trigger method. This
2169 # value change is then what triggers the watcher callbacks.
2170 __slots__ = ['_autotrigger_value', '_mode', '_autotrigger_reset_value']
2171
2172 def __init__(self,default=False,bounds=(0,1),**params):
2173 self._autotrigger_value = True
2174 self._autotrigger_reset_value = False
2175 self._mode = 'set-reset'
2176 # Mode can be one of 'set', 'set-reset' or 'reset'
2177
2178 # 'set' is normal Boolean parameter behavior when set with a value.
2179 # 'set-reset' temporarily sets the parameter (which triggers
2180 # watching callbacks) but immediately resets the value back to
2181 # False.
2182 # 'reset' applies the reset from True to False without
2183 # triggering watched callbacks
2184
2185 # This _mode attribute is one of the few places where a specific
2186 # parameter has a special behavior that is relied upon by the
2187 # core functionality implemented in
2188 # parameterized.py. Specifically, the set_param method
2189 # temporarily sets this attribute in order to disable resetting
2190 # back to False while triggered callbacks are executing
2191 super(Event, self).__init__(default=default,**params)
2192
2193 def _reset_event(self, obj, val):
2194 val = False
2195 if obj is None:
2196 self.default = val
2197 else:
2198 obj.__dict__[self._internal_name] = val
2199 self._post_setter(obj, val)
2200
2201 @instance_descriptor
2202 def __set__(self, obj, val):
2203 if self._mode in ['set-reset', 'set']:
2204 super(Event, self).__set__(obj, val)
2205 if self._mode in ['set-reset', 'reset']:
2206 self._reset_event(obj, val)
2207
2208
2209 from contextlib import contextmanager
2210 @contextmanager
2211 def exceptions_summarized():
2212 """Useful utility for writing docs that need to show expected errors.
2213 Shows exception only, concisely, without a traceback.
2214 """
2215 try:
2216 yield
2217 except Exception:
2218 import sys
2219 etype, value, tb = sys.exc_info()
2220 print("{}: {}".format(etype.__name__,value), file=sys.stderr)
1919 __author__ = "Jean-Luc Stevens"
2020
2121 import re
22 import sys
23 import itertools
2224 import textwrap
2325 import param
2426
5456 def get_param_info(self, obj, include_super=True):
5557 """
5658 Get the parameter dictionary, the list of modifed parameters
57 and the dictionary or parameter values. If include_super is
59 and the dictionary of parameter values. If include_super is
5860 True, parameters are also collected from the super classes.
5961 """
6062
6466 val_dict = dict((k,p.default) for (k,p) in params.items())
6567 self_class = obj
6668 else:
67 changed = [name for (name,_) in obj.param.get_param_values(onlychanged=True)]
68 val_dict = dict(obj.param.get_param_values())
69 changed = list(obj.param.values(onlychanged=True).keys())
70 val_dict = obj.param.values()
6971 self_class = obj.__class__
7072
7173 if not include_super:
8587
8688 (params, val_dict, changed) = info
8789 contents = []
88 displayed_params = {}
89 for name, p in params.items():
90 displayed_params = []
91 for name in self.sort_by_precedence(params):
9092 if only_changed and not (name in changed):
9193 continue
92 displayed_params[name] = p
93
94 right_shift = max(len(name) for name in displayed_params.keys())+2
95
96 for i, name in enumerate(sorted(displayed_params)):
97 p = displayed_params[name]
94 displayed_params.append((name, params[name]))
95
96 right_shift = max(len(name) for name, _ in displayed_params)+2
97
98 for i, (name, p) in enumerate(displayed_params):
9899 heading = "%s: " % name
99100 unindented = textwrap.dedent("< No docstring available >" if p.doc is None else p.doc)
100101
123124 return "\n".join(contents)
124125
125126
127 def sort_by_precedence(self, parameters):
128 """
129 Sort the provided dictionary of parameters by their precedence value.
130 In Python 3, preserves the original ordering for parameters with the
131 same precedence; for Python 2 sorts them lexicographically by name,
132 unless explicit precedences are provided.
133 """
134 params = [(p, pobj) for p, pobj in parameters.items()]
135 key_fn = lambda x: x[1].precedence if x[1].precedence is not None else 1e-8
136 sorted_params = sorted(params, key=key_fn)
137 groups = itertools.groupby(sorted_params, key=key_fn)
138 # Params preserve definition order in Python 3.6+
139 dict_ordered = (
140 (sys.version_info.major == 3 and sys.version_info.minor >= 6) or
141 (sys.version_info.major > 3) or
142 all(p.precedence is not None for p in parameters.values())
143 )
144 ordered_groups = [list(grp) if dict_ordered else sorted(grp) for (_, grp) in groups]
145 ordered_params = [el[0] for group in ordered_groups for el in group
146 if (el[0] != 'name' or el[0] in parameters)]
147 return ordered_params
148
149
126150 def _build_table(self, info, order, max_col_len=40, only_changed=False):
127151 """
128152 Collect the information about parameters needed to build a
129153 properly formatted table and then tabulate it.
130154 """
131155
132 info_dict, bounds_dict = {}, {}
156 info_list, bounds_dict = [], {}
133157 (params, val_dict, changed) = info
134158 col_widths = dict((k,0) for k in order)
135159
136 for name, p in params.items():
160 ordering = self.sort_by_precedence(params)
161 for name in ordering:
162 p = params[name]
137163 if only_changed and not (name in changed):
138164 continue
139165
142168 allow_None = ' AN' if hasattr(p, 'allow_None') and p.allow_None else ''
143169
144170 mode = '%s %s%s' % (constant, readonly, allow_None)
145 info_dict[name] = {'name': name, 'type':p.__class__.__name__,
146 'mode':mode}
171
172 value = repr(val_dict[name])
173 if len(value) > (max_col_len - 3):
174 value = value[:max_col_len-3] + '...'
175
176 p_dict = {'name': name, 'type': p.__class__.__name__,
177 'mode': mode, 'value': value}
147178
148179 if hasattr(p, 'bounds'):
149180 lbound, ubound = (None,None) if p.bounds is None else p.bounds
161192
162193 if (lbound, ubound) != (None,None):
163194 bounds_dict[name] = (mark_lbound, mark_ubound)
164 info_dict[name]['bounds'] = '(%s, %s)' % (lbound, ubound)
165
166 value = repr(val_dict[name])
167 if len(value) > (max_col_len - 3):
168 value = value[:max_col_len-3] + '...'
169 info_dict[name]['value'] = value
170
171 for col in info_dict[name]:
172 max_width = max([col_widths[col], len(info_dict[name][col])])
195 p_dict['bounds'] = '(%s, %s)' % (lbound, ubound)
196
197 for col in p_dict:
198 max_width = max([col_widths[col], len(p_dict[col])])
173199 col_widths[col] = max_width
174200
175 return self._tabulate(info_dict, col_widths, changed, order, bounds_dict)
176
177
178 def _tabulate(self, info_dict, col_widths, changed, order, bounds_dict):
201 info_list.append((name, p_dict))
202
203 return self._tabulate(info_list, col_widths, changed, order, bounds_dict)
204
205
206 def _tabulate(self, info_list, col_widths, changed, order, bounds_dict):
179207 """
180208 Returns the supplied information as a table suitable for
181209 printing or paging.
182210
183 info_dict: Dictionary of the parameters name, type and mode.
211 info_list: List of the parameters name, type and mode.
184212 col_widths: Dictionary of column widths in characters
185213 changed: List of parameters modified from their defaults.
186214 order: The order of the table columns
188216 """
189217
190218 contents, tail = [], []
191 column_set = set(k for row in info_dict.values() for k in row)
219 column_set = set(k for _, row in info_list for k in row)
192220 columns = [col for col in order if col in column_set]
193221
194222 title_row = []
201229 contents.append(blue % ''.join(title_row)+"\n")
202230
203231 # Format the table rows
204 for row in sorted(info_dict):
232 for row, info in info_list:
205233 row_list = []
206 info = info_dict[row]
207234 for i,col in enumerate(columns):
208235 width = col_widths[col]+2
209236 val = info[col] if (col in info) else ''
257284 heading_text = "%s\n%s\n" % (title, heading_line)
258285
259286 param_info = self.get_param_info(param_obj, include_super=True)
260
261287 if not param_info[0]:
262288 return "%s\n%s" % ((green % heading_text), "Object has no parameters.")
263289
265291 only_changed=False)
266292
267293 docstrings = self.param_docstrings(param_info, max_col_len=100, only_changed=False)
268
269294 dflt_msg = "Parameters changed from their default values are marked in red."
270295 top_heading = (green % heading_text)
271296 top_heading += "\n%s" % (red % dflt_msg)
278303 return "%s\n\n%s\n\n%s\n\n%s" % (top_heading, table, docstring_heading, docstrings)
279304
280305
281 message = """Welcome to the param IPython extension! (http://ioam.github.io/param/)"""
306 message = """Welcome to the param IPython extension! (https://param.holoviz.org/)"""
282307 message += '\nAvailable magics: %params'
283308
284309 _loaded = False
00 """
11 Generic support for objects with full-featured Parameters and
22 messaging.
3
4 This file comes from the Param library (https://github.com/holoviz/param)
5 but can be taken out of the param module and used on its own if desired,
6 either alone (providing basic Parameter support) or with param's
7 __init__.py (providing specialized Parameter types).
38 """
49
510 import copy
1015 import numbers
1116 import operator
1217
13 from collections import namedtuple, OrderedDict
18 # Allow this file to be used standalone if desired, albeit without JSON serialization
19 try:
20 from . import serializer
21 except ImportError:
22 serializer = None
23
24
25 from collections import defaultdict, namedtuple, OrderedDict
26 from functools import partial, wraps, reduce
1427 from operator import itemgetter,attrgetter
1528 from types import FunctionType
16 from functools import partial, wraps, reduce
1729
1830 import logging
1931 from contextlib import contextmanager
2537 param_pager = ParamPager(metaclass=True) # Generates param description
2638 except:
2739 param_pager = None
40
41 try:
42 from inspect import getfullargspec
43 except:
44 from inspect import getargspec as getfullargspec # python2
2845
2946 basestring = basestring if sys.version_info[0]==2 else str # noqa: it is defined
3047
6481 object_count = 0
6582 warning_count = 0
6683
84 class _Undefined:
85 """
86 Dummy value to signal completely undefined values rather than
87 simple None values.
88 """
6789
6890 @contextmanager
6991 def logging_level(level):
87109
88110
89111 @contextmanager
90 def batch_watch(parameterized, enable=True, run=True):
91 """
92 Context manager to batch watcher events on a parameterized object.
93 The context manager will queue any events triggered by setting a
94 parameter on the supplied parameterized object and dispatch them
95 all at once when the context manager exits. If run=False the
96 queued events are not dispatched and should be processed manually.
112 def _batch_call_watchers(parameterized, enable=True, run=True):
113 """
114 Internal version of batch_call_watchers, adding control over queueing and running.
115 Only actually batches events if enable=True; otherwise a no-op. Only actually
116 calls the accumulated watchers on exit if run=True; otherwise they remain queued.
97117 """
98118 BATCH_WATCH = parameterized.param._BATCH_WATCH
99119 parameterized.param._BATCH_WATCH = enable or parameterized.param._BATCH_WATCH
102122 finally:
103123 parameterized.param._BATCH_WATCH = BATCH_WATCH
104124 if run and not BATCH_WATCH:
125 parameterized.param._batch_call_watchers()
126
127 batch_watch = _batch_call_watchers # PARAM2_DEPRECATION: Remove this compatibility alias for param 2.0 and later.
128
129 @contextmanager
130 def batch_call_watchers(parameterized):
131 """
132 Context manager to batch events to provide to Watchers on a
133 parameterized object. This context manager queues any events
134 triggered by setting a parameter on the supplied parameterized
135 object, saving them up to dispatch them all at once when the
136 context manager exits.
137 """
138 BATCH_WATCH = parameterized.param._BATCH_WATCH
139 parameterized.param._BATCH_WATCH = True
140 try:
141 yield
142 finally:
143 parameterized.param._BATCH_WATCH = BATCH_WATCH
144 if not BATCH_WATCH:
105145 parameterized.param._batch_call_watchers()
106146
107147
132172 """
133173 batch_watch = parameterized.param._BATCH_WATCH
134174 parameterized.param._BATCH_WATCH = True
135 watchers, events = parameterized.param._watchers, parameterized.param._events
175 watchers, events = (list(parameterized.param._watchers),
176 list(parameterized.param._events))
136177 try:
137178 yield
138179 except:
143184 parameterized.param._events = events
144185
145186
187 # External components can register an async executor which will run
188 # async functions
189 async_executor = None
190
191
146192 def classlist(class_):
147193 """
148194 Return a list of the class hierarchy above (and including) the given class.
149195
150 Same as inspect.getmro(class_)[::-1]
196 Same as `inspect.getmro(class_)[::-1]`
151197 """
152198 return inspect.getmro(class_)[::-1]
153199
173219
174220 def get_all_slots(class_):
175221 """
176 Return a list of slot names for slots defined in class_ and its
222 Return a list of slot names for slots defined in `class_` and its
177223 superclasses.
178224 """
179225 # A subclass's __slots__ attribute does not contain slots defined
217263 return arg1==arg2
218264
219265
220
221 # For Python 2 compatibility.
266 # PARAM2_DEPRECATION: For Python 2 compatibility only; can be removed in param2.
222267 #
223268 # The syntax to use a metaclass changed incompatibly between 2 and
224269 # 3. The add_metaclass() class decorator below creates a class using a
239284 return wrapper
240285
241286
287
242288 class bothmethod(object): # pylint: disable-msg=R0903
243289 """
244290 'optional @classmethod'
269315 return reduce(_getattr, [obj] + attr.split('.'))
270316
271317
272 # (thought I was going to have a few decorators following this pattern)
273318 def accept_arguments(f):
319 """
320 Decorator for decorators that accept arguments
321 """
274322 @wraps(f)
275323 def _f(*args, **kwargs):
276324 return lambda actual_f: f(actual_f, *args, **kwargs)
285333 return cls
286334
287335
336 def iscoroutinefunction(function):
337 """
338 Whether the function is an asynchronous coroutine function.
339 """
340 if not hasattr(inspect, 'iscoroutinefunction'):
341 return False
342 import asyncio
343 try:
344 return (
345 inspect.isasyncgenfunction(function) or
346 asyncio.iscoroutinefunction(function)
347 )
348 except AttributeError:
349 return False
350
351
288352 def instance_descriptor(f):
289 # If parameter has an instance Parameter delegate setting
353 # If parameter has an instance Parameter, delegate setting
290354 def _f(self, obj, val):
291355 instance_param = getattr(obj, '_instance__params', {}).get(self.name)
292356 if instance_param is not None and self is not instance_param:
296360 return _f
297361
298362
363 def get_method_owner(method):
364 """
365 Gets the instance that owns the supplied method
366 """
367 if not inspect.ismethod(method):
368 return None
369 if isinstance(method, partial):
370 method = method.func
371 return method.__self__ if sys.version_info.major >= 3 else method.im_self
372
373
299374 @accept_arguments
300375 def depends(func, *dependencies, **kw):
301376 """
308383 on Parameter values, or on other metadata about the Parameter.
309384 """
310385
311 # python3 would allow kw-only args
312 # (i.e. "func,*dependencies,watch=False" rather than **kw and the check below)
313 watch = kw.pop("watch",False)
314
315 @wraps(func)
316 def _depends(*args,**kw):
317 return func(*args,**kw)
386 # PARAM2_DEPRECATION: python2 workaround; python3 allows kw-only args
387 # (i.e. "func, *dependencies, watch=False" rather than **kw and the check below)
388 watch = kw.pop("watch", False)
389 on_init = kw.pop("on_init", False)
390
391 if iscoroutinefunction(func):
392 import asyncio
393 @asyncio.coroutine
394 def _depends(*args, **kw):
395 yield from func(*args, **kw)
396 else:
397 @wraps(func)
398 def _depends(*args, **kw):
399 return func(*args, **kw)
318400
319401 deps = list(dependencies)+list(kw.values())
320402 string_specs = False
345427 'or function is not supported when referencing '
346428 'parameters by name.')
347429
348 if not string_specs and watch:
349 def cb(event):
350 args = (getattr(dep.owner, dep.name) for dep in dependencies)
351 dep_kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw.items()}
352 return func(*args, **dep_kwargs)
353
430 if not string_specs and watch: # string_specs case handled elsewhere (later), in Parameterized.__init__
431 if iscoroutinefunction(func):
432 import asyncio
433 @asyncio.coroutine
434 def cb(*events):
435 args = (getattr(dep.owner, dep.name) for dep in dependencies)
436 dep_kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw.items()}
437 yield from func(*args, **dep_kwargs)
438 else:
439 def cb(*events):
440 args = (getattr(dep.owner, dep.name) for dep in dependencies)
441 dep_kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw.items()}
442 return func(*args, **dep_kwargs)
443
444 grouped = defaultdict(list)
354445 for dep in deps:
355 dep.owner.param.watch(cb, dep.name)
446 grouped[id(dep.owner)].append(dep)
447 for group in grouped.values():
448 group[0].owner.param.watch(cb, [dep.name for dep in group])
356449
357450 _dinfo = getattr(func, '_dinfo', {})
358451 _dinfo.update({'dependencies': dependencies,
359 'kw': kw, 'watch': watch})
452 'kw': kw, 'watch': watch, 'on_init': on_init})
360453
361454 _depends._dinfo = _dinfo
362455
458551 return _output
459552
460553
461 def _params_depended_on(minfo):
462 params = []
463 dinfo = getattr(minfo.method,"_dinfo", {})
554 def _parse_dependency_spec(spec):
555 """
556 Parses param.depends specifications into three components:
557
558 1. The dotted path to the sub-object
559 2. The attribute being depended on, i.e. either a parameter or method
560 3. The parameter attribute being depended on
561 """
562 assert spec.count(":")<=1
563 spec = spec.strip()
564 m = re.match("(?P<path>[^:]*):?(?P<what>.*)", spec)
565 what = m.group('what')
566 path = "."+m.group('path')
567 m = re.match(r"(?P<obj>.*)(\.)(?P<attr>.*)", path)
568 obj = m.group('obj')
569 attr = m.group("attr")
570 return obj or None, attr, what or 'value'
571
572
573 def _params_depended_on(minfo, dynamic=True, intermediate=True):
574 """
575 Resolves dependencies declared on a Parameterized method.
576 Dynamic dependencies, i.e. dependencies on sub-objects which may
577 or may not yet be available, are only resolved if dynamic=True.
578 By default intermediate dependencies, i.e. dependencies on the
579 path to a sub-object are returned. For example for a dependency
580 on 'a.b.c' dependencies on 'a' and 'b' are returned as long as
581 intermediate=True.
582
583 Returns lists of concrete dependencies on available parameters
584 and dynamic dependencies specifications which have to resolved
585 if the referenced sub-objects are defined.
586 """
587 deps, dynamic_deps = [], []
588 dinfo = getattr(minfo.method, "_dinfo", {})
464589 for d in dinfo.get('dependencies', list(minfo.cls.param)):
465 things = (minfo.inst or minfo.cls).param._spec_to_obj(d)
466 for thing in things:
467 if isinstance(thing,PInfo):
468 params.append(thing)
590 ddeps, ddynamic_deps = (minfo.inst or minfo.cls).param._spec_to_obj(d, dynamic, intermediate)
591 dynamic_deps += ddynamic_deps
592 for dep in ddeps:
593 if isinstance(dep, PInfo):
594 deps.append(dep)
469595 else:
470 params += _params_depended_on(thing)
471 return params
472
473
474 def _m_caller(self,n):
475 return lambda event: getattr(self,n)()
476
477
478 PInfo = namedtuple("PInfo","inst cls name pobj what")
479 MInfo = namedtuple("MInfo","inst cls name method")
480 Event = namedtuple("Event","what name obj cls old new type")
481 Watcher = namedtuple("Watcher","inst cls fn mode onlychanged parameter_names what queued")
596 method_deps, method_dynamic_deps = _params_depended_on(dep, dynamic, intermediate)
597 deps += method_deps
598 dynamic_deps += method_dynamic_deps
599 return deps, dynamic_deps
600
601
602 def _resolve_mcs_deps(obj, resolved, dynamic, intermediate=True):
603 """
604 Resolves constant and dynamic parameter dependencies previously
605 obtained using the _params_depended_on function. Existing resolved
606 dependencies are updated with a supplied parameter instance while
607 dynamic dependencies are resolved if possible.
608 """
609 dependencies = []
610 for dep in resolved:
611 if not issubclass(type(obj), dep.cls):
612 dependencies.append(dep)
613 continue
614 inst = obj if dep.inst is None else dep.inst
615 dep = PInfo(inst=inst, cls=dep.cls, name=dep.name,
616 pobj=inst.param[dep.name], what=dep.what)
617 dependencies.append(dep)
618 for dep in dynamic:
619 subresolved, _ = obj.param._spec_to_obj(dep.spec, intermediate=intermediate)
620 for subdep in subresolved:
621 if isinstance(subdep, PInfo):
622 dependencies.append(subdep)
623 else:
624 dependencies += _params_depended_on(subdep, intermediate=intermediate)[0]
625 return dependencies
626
627
628 def _skip_event(*events, **kwargs):
629 """
630 Checks whether a subobject event should be skipped.
631 Returns True if all the values on the new subobject
632 match the values on the previous subobject.
633 """
634 what = kwargs.get('what', 'value')
635 changed = kwargs.get('changed')
636 if changed is None:
637 return False
638 for e in events:
639 for p in changed:
640 if what == 'value':
641 old = _Undefined if e.old is None else _getattrr(e.old, p, None)
642 new = _Undefined if e.new is None else _getattrr(e.new, p, None)
643 else:
644 old = _Undefined if e.old is None else _getattrr(e.old.param[p], what, None)
645 new = _Undefined if e.new is None else _getattrr(e.new.param[p], what, None)
646 if not Comparator.is_equal(old, new):
647 return False
648 return True
649
650
651 def _m_caller(self, method_name, what='value', changed=None, callback=None):
652 """
653 Wraps a method call adding support for scheduling a callback
654 before it is executed and skipping events if a subobject has
655 changed but its values have not.
656 """
657 function = getattr(self, method_name)
658 if iscoroutinefunction(function):
659 import asyncio
660 @asyncio.coroutine
661 def caller(*events):
662 if callback: callback(*events)
663 if not _skip_event(*events, what=what, changed=changed):
664 yield from function()
665 else:
666 def caller(*events):
667 if callback: callback(*events)
668 if not _skip_event(*events, what=what, changed=changed):
669 return function()
670 caller._watcher_name = method_name
671 return caller
672
673
674 def _add_doc(obj, docstring):
675 """Add a docstring to a namedtuple, if on python3 where that's allowed"""
676 if sys.version_info[0]>2:
677 obj.__doc__ = docstring
678
679
680 PInfo = namedtuple("PInfo", "inst cls name pobj what"); _add_doc(PInfo,
681 """
682 Object describing something being watched about a Parameter.
683
684 `inst`: Parameterized instance owning the Parameter, or None
685
686 `cls`: Parameterized class owning the Parameter
687
688 `name`: Name of the Parameter being watched
689
690 `pobj`: Parameter object being watched
691
692 `what`: What is being watched on the Parameter (either 'value' or a slot name)
693 """)
694
695 MInfo = namedtuple("MInfo", "inst cls name method"); _add_doc(MInfo,
696 """
697 Object describing a Parameterized method being watched.
698
699 `inst`: Parameterized instance owning the method, or None
700
701 `cls`: Parameterized class owning the method
702
703 `name`: Name of the method being watched
704
705 `method`: bound method of the object being watched
706 """)
707
708 DInfo = namedtuple("DInfo", "spec"); _add_doc(DInfo,
709 """
710 Object describing dynamic dependencies.
711 `spec`: Dependency specification to resolve
712 """)
713
714 Event = namedtuple("Event", "what name obj cls old new type"); _add_doc(Event,
715 """
716 Object representing an event that triggers a Watcher.
717
718 `what`: What is being watched on the Parameter (either value or a slot name)
719
720 `name`: Name of the Parameter that was set or triggered
721
722 `obj`: Parameterized instance owning the watched Parameter, or None
723
724 `cls`: Parameterized class owning the watched Parameter
725
726 `old`: Previous value of the item being watched
727
728 `new`: New value of the item being watched
729
730 `type`: `triggered` if this event was triggered explicitly), `changed` if
731 the item was set and watching for `onlychanged`, `set` if the item was set,
732 or None if type not yet known
733 """)
734
735 _Watcher = namedtuple("Watcher", "inst cls fn mode onlychanged parameter_names what queued precedence")
736
737 class Watcher(_Watcher):
738 """
739 Object declaring a callback function to invoke when an Event is
740 triggered on a watched item.
741
742 `inst`: Parameterized instance owning the watched Parameter, or
743 None
744
745 `cls`: Parameterized class owning the watched Parameter
746
747 `fn`: Callback function to invoke when triggered by a watched
748 Parameter
749
750 `mode`: 'args' for param.watch (call `fn` with PInfo object
751 positional args), or 'kwargs' for param.watch_values (call `fn`
752 with <param_name>:<new_value> keywords)
753
754 `onlychanged`: If True, only trigger for actual changes, not
755 setting to the current value
756
757 `parameter_names`: List of Parameters to watch, by name
758
759 `what`: What to watch on the Parameters (either 'value' or a slot
760 name)
761
762 `queued`: Immediately invoke callbacks triggered during processing
763 of an Event (if False), or queue them up for processing
764 later, after this event has been handled (if True)
765
766 `precedence`: A numeric value which determines the precedence of
767 the watcher. Lower precedence values are executed
768 with higher priority.
769 """
770
771 def __new__(cls_, *args, **kwargs):
772 """
773 Allows creating Watcher without explicit precedence value.
774 """
775 values = dict(zip(cls_._fields, args))
776 values.update(kwargs)
777 if 'precedence' not in values:
778 values['precedence'] = 0
779 return super(Watcher, cls_).__new__(cls_, **values)
780
781 def __iter__(self):
782 """
783 Backward compatibility layer to allow tuple unpacking without
784 the precedence value. Important for Panel which creates a
785 custom Watcher and uses tuple unpacking. Will be dropped in
786 Param 3.x.
787 """
788 return iter(self[:-1])
789
790 def __str__(self):
791 cls = type(self)
792 attrs = ', '.join(['%s=%r' % (f, getattr(self, f)) for f in cls._fields])
793 return "{cls}({attrs})".format(cls=cls.__name__, attrs=attrs)
794
795
796
482797
483798 class ParameterMetaclass(type):
484799 """
485800 Metaclass allowing control over creation of Parameter classes.
486801 """
487802 def __new__(mcs,classname,bases,classdict):
803
488804 # store the class's docstring in __classdoc
489805 if '__doc__' in classdict:
490806 classdict['__classdoc']=classdict['__doc__']
491 # when asking for help on Parameter *object*, return the doc
492 # slot
807
808 # when asking for help on Parameter *object*, return the doc slot
493809 classdict['__doc__']=property(attrgetter('doc'))
494810
495811 # To get the benefit of slots, subclasses must themselves define
498814 # a __dict__ unless it also defines __slots__.
499815 if '__slots__' not in classdict:
500816 classdict['__slots__']=[]
817
818 # No special handling for a __dict__ slot; should there be?
501819
502820 return type.__new__(mcs,classname,bases,classdict)
503821
511829
512830
513831
514 # CEBALERT: we break some aspects of slot handling for Parameter and
515 # Parameterized. The __new__ methods in the metaclasses for those two
516 # classes omit to handle the case where __dict__ is passed in
517 # __slots__ (and they possibly omit other things too). Additionally,
518 # various bits of code in the Parameterized class assumes that all
519 # Parameterized instances have a __dict__, but I'm not sure that's
520 # guaranteed to be true (although it's true at the moment).
521
522
523 # CB: we could maybe reduce the complexity by doing something to allow
524 # a parameter to discover things about itself when created (would also
525 # allow things like checking a Parameter is owned by a
526 # Parameterized). I have some vague ideas about what to do.
527832 @add_metaclass(ParameterMetaclass)
528833 class Parameter(object):
529834 """
541846 objects Foo and Bar, such that Bar has a parameter delta, Foo is a
542847 subclass of Bar, and Foo has parameters alpha, sigma, and gamma
543848 (and delta inherited from Bar). She would begin her class
544 definitions with something like this:
545
546 class Bar(Parameterized):
547 delta = Parameter(default=0.6, doc='The difference between steps.')
548 ...
549
550 class Foo(Bar):
551 alpha = Parameter(default=0.1, doc='The starting value.')
552 sigma = Parameter(default=0.5, doc='The standard deviation.',
553 constant=True)
554 gamma = Parameter(default=1.0, doc='The ending value.')
555 ...
849 definitions with something like this::
850
851 class Bar(Parameterized):
852 delta = Parameter(default=0.6, doc='The difference between steps.')
853 ...
854 class Foo(Bar):
855 alpha = Parameter(default=0.1, doc='The starting value.')
856 sigma = Parameter(default=0.5, doc='The standard deviation.',
857 constant=True)
858 gamma = Parameter(default=1.0, doc='The ending value.')
859 ...
556860
557861 Class Foo would then have four parameters, with delta defaulting
558862 to 0.6.
563867 constructed: The default constructor for Foo (and Bar) will
564868 accept arbitrary keyword arguments, each of which can be used
565869 to specify the value of a Parameter of Foo (or any of Foo's
566 superclasses). E.g., if a script does this:
870 superclasses). E.g., if a script does this::
567871
568872 myfoo = Foo(alpha=0.5)
569873
580884 2. A Parameterized class need specify only the attributes of a
581885 Parameter whose values differ from those declared in
582886 superclasses; the other values will be inherited. E.g. if Foo
583 declares
887 declares::
584888
585889 delta = Parameter(default=0.2)
586890
659963 # attributes. Using __slots__ requires special support for
660964 # operations to copy and restore Parameters (e.g. for Python
661965 # persistent storage pickling); see __getstate__ and __setstate__.
662 __slots__ = ['name','_internal_name','default','doc',
663 'precedence','instantiate','constant','readonly',
664 'pickle_default_value','allow_None', 'per_instance',
966 __slots__ = ['name', '_internal_name', 'default', 'doc',
967 'precedence', 'instantiate', 'constant', 'readonly',
968 'pickle_default_value', 'allow_None', 'per_instance',
665969 'watchers', 'owner', '_label']
666970
667971 # Note: When initially created, a Parameter does not know which
670974 # class is created, owner, name, and _internal_name are
671975 # set.
672976
673 def __init__(self,default=None,doc=None,label=None,precedence=None, # pylint: disable-msg=R0913
674 instantiate=False,constant=False,readonly=False,
977 _serializers = {'json': serializer.JSONSerialization}
978
979 def __init__(self,default=None, doc=None, label=None, precedence=None, # pylint: disable-msg=R0913
980 instantiate=False, constant=False, readonly=False,
675981 pickle_default_value=True, allow_None=False,
676982 per_instance=True):
677 """
678 Initialize a new Parameter object: store the supplied attributes.
679
680 default: the owning class's value for the attribute
681 represented by this Parameter.
682
683 precedence is a value, usually in the range 0.0 to 1.0, that
684 allows the order of Parameters in a class to be defined (for
685 e.g. in GUI menus). A negative precedence indicates a
686 parameter that should be hidden in e.g. GUI menus.
687
688 default, doc, and precedence default to None. This is to allow
983
984 """Initialize a new Parameter object and store the supplied attributes:
985
986 default: the owning class's value for the attribute represented
987 by this Parameter, which can be overridden in an instance.
988
989 doc: docstring explaining what this parameter represents.
990
991 label: optional text label to be used when this Parameter is
992 shown in a listing. If no label is supplied, the attribute name
993 for this parameter in the owning Parameterized object is used.
994
995 precedence: a numeric value, usually in the range 0.0 to 1.0,
996 which allows the order of Parameters in a class to be defined in
997 a listing or e.g. in GUI menus. A negative precedence indicates
998 a parameter that should be hidden in such listings.
999
1000 instantiate: controls whether the value of this Parameter will
1001 be deepcopied when a Parameterized object is instantiated (if
1002 True), or if the single default value will be shared by all
1003 Parameterized instances (if False). For an immutable Parameter
1004 value, it is best to leave instantiate at the default of
1005 False, so that a user can choose to change the value at the
1006 Parameterized instance level (affecting only that instance) or
1007 at the Parameterized class or superclass level (affecting all
1008 existing and future instances of that class or superclass). For
1009 a mutable Parameter value, the default of False is also appropriate
1010 if you want all instances to share the same value state, e.g. if
1011 they are each simply referring to a single global object like
1012 a singleton. If instead each Parameterized should have its own
1013 independently mutable value, instantiate should be set to
1014 True, but note that there is then no simple way to change the
1015 value of this Parameter at the class or superclass level,
1016 because each instance, once created, will then have an
1017 independently instantiated value.
1018
1019 constant: if true, the Parameter value can be changed only at
1020 the class level or in a Parameterized constructor call. The
1021 value is otherwise constant on the Parameterized instance,
1022 once it has been constructed.
1023
1024 readonly: if true, the Parameter value cannot ordinarily be
1025 changed by setting the attribute at the class or instance
1026 levels at all. The value can still be changed in code by
1027 temporarily overriding the value of this slot and then
1028 restoring it, which is useful for reporting values that the
1029 _user_ should never change but which do change during code
1030 execution.
1031
1032 pickle_default_value: whether the default value should be
1033 pickled. Usually, you would want the default value to be pickled,
1034 but there are rare cases where that would not be the case (e.g.
1035 for file search paths that are specific to a certain system).
1036
1037 per_instance: whether a separate Parameter instance will be
1038 created for every Parameterized instance. True by default.
1039 If False, all instances of a Parameterized class will share
1040 the same Parameter object, including all validation
1041 attributes (bounds, etc.). See also instantiate, which is
1042 conceptually similar but affects the Parameter value rather
1043 than the Parameter object.
1044
1045 allow_None: if True, None is accepted as a valid value for
1046 this Parameter, in addition to any other values that are
1047 allowed. If the default value is defined as None, allow_None
1048 is set to True automatically.
1049
1050 default, doc, and precedence all default to None, which allows
6891051 inheritance of Parameter slots (attributes) from the owning-class'
6901052 class hierarchy (see ParameterizedMetaclass).
691
692 per_instance defaults to True and controls whether a new
693 Parameter instance can be created for every Parameterized
694 instance. If False, all instances of a Parameterized class
695 will share the same parameter object, including all validation
696 attributes.
697
698 In rare cases where the default value should not be pickled,
699 set pickle_default_value=False (e.g. for file search paths).
700 """
1053 """
1054
7011055 self.name = None
702 self._internal_name = None
7031056 self.owner = None
704 self._label = label
7051057 self.precedence = precedence
7061058 self.default = default
7071059 self.doc = doc
7081060 self.constant = constant or readonly # readonly => constant
7091061 self.readonly = readonly
1062 self._label = label
1063 self._internal_name = None
7101064 self._set_instantiate(instantiate)
7111065 self.pickle_default_value = pickle_default_value
7121066 self.allow_None = (default is None or allow_None)
7131067 self.watchers = {}
7141068 self.per_instance = per_instance
7151069
1070 @classmethod
1071 def serialize(cls, value):
1072 "Given the parameter value, return a Python value suitable for serialization"
1073 return value
1074
1075 @classmethod
1076 def deserialize(cls, value):
1077 "Given a serializable Python value, return a value that the parameter can be set to"
1078 return value
1079
1080 def schema(self, safe=False, subset=None, mode='json'):
1081 if serializer is None:
1082 raise ImportError('Cannot import serializer.py needed to generate schema')
1083 if mode not in self._serializers:
1084 raise KeyError('Mode %r not in available serialization formats %r'
1085 % (mode, list(self._serializers.keys())))
1086 return self._serializers[mode].param_schema(self.__class__.__name__, self,
1087 safe=safe, subset=subset)
7161088
7171089 @property
7181090 def label(self):
7271099
7281100 def _set_instantiate(self,instantiate):
7291101 """Constant parameters must be instantiated."""
730 # CB: instantiate doesn't actually matter for read-only
1102 # instantiate doesn't actually matter for read-only
7311103 # parameters, since they can't be set even on a class. But
732 # this avoids needless instantiation.
1104 # having this code avoids needless instantiation.
7331105 if self.readonly:
7341106 self.instantiate = False
7351107 else:
7361108 self.instantiate = instantiate or self.constant # pylint: disable-msg=W0201
7371109
738
739 # TODO: quick trick to allow subscription to the setting of
740 # parameter metadata. ParameterParameter?
741
742 # Note that unlike with parameter value setting, there's no access
743 # to the Parameterized instance, so no per-instance subscription.
744
745 def __setattr__(self,attribute,value):
746 implemented = (attribute!="default" and hasattr(self,'watchers') and attribute in self.watchers)
1110 def __setattr__(self, attribute, value):
1111 if attribute == 'name' and getattr(self, 'name', None) and value != self.name:
1112 raise AttributeError("Parameter name cannot be modified after "
1113 "it has been bound to a Parameterized.")
1114
1115 implemented = (attribute != "default" and hasattr(self, 'watchers') and attribute in self.watchers)
1116 slot_attribute = attribute in self.__slots__
7471117 try:
748 old = getattr(self,attribute) if implemented else NotImplemented
1118 old = getattr(self, attribute) if implemented else NotImplemented
1119 if slot_attribute:
1120 self._on_set(attribute, old, value)
7491121 except AttributeError as e:
750 if attribute in self.__slots__:
1122 if slot_attribute:
7511123 # If Parameter slot is defined but an AttributeError was raised
7521124 # we are in __setstate__ and watchers should not be triggered
7531125 old = NotImplemented
7561128
7571129 super(Parameter, self).__setattr__(attribute, value)
7581130
759 if old is not NotImplemented:
760 event = Event(what=attribute,name=self.name,obj=None,cls=self.owner,old=old,new=value, type=None)
761 for watcher in self.watchers[attribute]:
762 self.owner.param._call_watcher(watcher, event)
763 if not self.owner.param._BATCH_WATCH:
764 self.owner.param._batch_call_watchers()
765
766
767 def __get__(self,obj,objtype): # pylint: disable-msg=W0613
1131 if old is NotImplemented:
1132 return
1133
1134 event = Event(what=attribute, name=self.name, obj=None, cls=self.owner,
1135 old=old, new=value, type=None)
1136 for watcher in self.watchers[attribute]:
1137 self.owner.param._call_watcher(watcher, event)
1138 if not self.owner.param._BATCH_WATCH:
1139 self.owner.param._batch_call_watchers()
1140
1141 def _on_set(self, attribute, old, value):
1142 """
1143 Can be overridden on subclasses to handle changes when parameter
1144 attribute is set.
1145 """
1146
1147 def __get__(self, obj, objtype): # pylint: disable-msg=W0613
7681148 """
7691149 Return the value for this Parameter.
7701150
7761156 instance's value, if one has been set - otherwise produce the
7771157 class's value (default).
7781158 """
779 # NB: obj can be None (when __get__ called for a
780 # Parameterized class); objtype is never None
781
782 if obj is None:
1159 if obj is None: # e.g. when __get__ called for a Parameterized class
7831160 result = self.default
7841161 else:
7851162 result = obj.__dict__.get(self._internal_name,self.default)
7861163 return result
7871164
788
7891165 @instance_descriptor
790 def __set__(self,obj,val):
1166 def __set__(self, obj, val):
7911167 """
7921168 Set the value for this Parameter.
7931169
7971173 If called for a Parameterized instance, set the value of
7981174 this Parameter on that instance (i.e. in the instance's
7991175 __dict__, under the parameter's internal_name).
800
8011176
8021177 If the Parameter's constant attribute is True, only allows
8031178 the value to be set for a Parameterized class or on
8101185
8111186 Note that until we support some form of read-only
8121187 object, it is still possible to change the attributes of the
813 object stored in a constant or read-only Parameter (e.g. the
814 left bound of a BoundingBox).
815 """
816
817 # ALERT: Deprecated Number set_hook called here to avoid duplicating
818 # setter, should be removed in 2.0
1188 object stored in a constant or read-only Parameter (e.g. one
1189 item in a list).
1190 """
1191
1192 # PARAM2_DEPRECATION: For Python 2 compatibility only;
1193 # Deprecated Number set_hook called here to avoid duplicating setter
8191194 if hasattr(self, 'set_hook'):
8201195 val = self.set_hook(obj,val)
8211196
8221197 self._validate(val)
8231198
8241199 _old = NotImplemented
825 # NB: obj can be None (when __set__ called for a
826 # Parameterized class)
1200 # obj can be None if __set__ is called for a Parameterized class
8271201 if self.constant or self.readonly:
8281202 if self.readonly:
829 raise TypeError("Read-only parameter '%s' cannot be modified"%self.name)
830 elif obj is None: #not obj
1203 raise TypeError("Read-only parameter '%s' cannot be modified" % self.name)
1204 elif obj is None:
8311205 _old = self.default
8321206 self.default = val
8331207 elif not obj.initialized:
834 _old = obj.__dict__.get(self._internal_name,self.default)
1208 _old = obj.__dict__.get(self._internal_name, self.default)
8351209 obj.__dict__[self._internal_name] = val
8361210 else:
837 raise TypeError("Constant parameter '%s' cannot be modified"%self.name)
838
1211 _old = obj.__dict__.get(self._internal_name, self.default)
1212 if val is not _old:
1213 raise TypeError("Constant parameter '%s' cannot be modified"%self.name)
8391214 else:
8401215 if obj is None:
8411216 _old = self.default
8421217 self.default = val
8431218 else:
844 _old = obj.__dict__.get(self._internal_name,self.default)
1219 _old = obj.__dict__.get(self._internal_name, self.default)
8451220 obj.__dict__[self._internal_name] = val
8461221
8471222 self._post_setter(obj, val)
8481223
1224 if obj is not None:
1225 if not getattr(obj, 'initialized', False):
1226 return
1227 obj.param._update_deps(self.name)
1228
8491229 if obj is None:
850 watchers = self.watchers.get("value",[])
1230 watchers = self.watchers.get("value")
1231 elif hasattr(obj, '_param_watchers') and self.name in obj._param_watchers:
1232 watchers = obj._param_watchers[self.name].get('value')
1233 if watchers is None:
1234 watchers = self.watchers.get("value")
8511235 else:
852 watchers = getattr(obj,"_param_watchers",{}).get(self.name,{}).get('value',self.watchers.get("value",[]))
853
854 event = Event(what='value',name=self.name,obj=obj,cls=self.owner,old=_old,new=val, type=None)
1236 watchers = None
1237
8551238 obj = self.owner if obj is None else obj
856 if obj is None:
1239
1240 if obj is None or not watchers:
8571241 return
8581242
859 for watcher in watchers:
1243 event = Event(what='value', name=self.name, obj=obj, cls=self.owner,
1244 old=_old, new=val, type=None)
1245
1246 # Copy watchers here since they may be modified inplace during iteration
1247 for watcher in sorted(watchers, key=lambda w: w.precedence):
8601248 obj.param._call_watcher(watcher, event)
8611249 if not obj.param._BATCH_WATCH:
8621250 obj.param._batch_call_watchers()
8631251
1252 def _validate_value(self, value, allow_None):
1253 """Implements validation for parameter value"""
8641254
8651255 def _validate(self, val):
866 """Implements validation for the parameter"""
867
1256 """Implements validation for the parameter value and attributes"""
1257 self._validate_value(val, self.allow_None)
8681258
8691259 def _post_setter(self, obj, val):
8701260 """Called after the parameter value has been validated and set"""
8711261
872
8731262 def __delete__(self,obj):
8741263 raise TypeError("Cannot delete '%s': Parameters deletion not allowed." % self.name)
875
8761264
8771265 def _set_names(self, attrib_name):
8781266 if None not in (self.owner, self.name) and attrib_name != self.name:
8851273 % (type(self).__name__, self.name,
8861274 self.owner.name, attrib_name))
8871275 self.name = attrib_name
888
889 self._internal_name = "_%s_param_value"%attrib_name
890
1276 self._internal_name = "_%s_param_value" % attrib_name
8911277
8921278 def __getstate__(self):
8931279 """
9201306
9211307 # Define one particular type of Parameter that is used in this file
9221308 class String(Parameter):
923 """
1309 r"""
9241310 A String Parameter, with a default value and optional regular expression (regex) matching.
9251311
9261312 Example of using a regex to implement IPv4 address matching::
9281314 class IPAddress(String):
9291315 '''IPv4 address as a string (dotted decimal notation)'''
9301316 def __init__(self, default="0.0.0.0", allow_None=False, **kwargs):
931 ip_regex = '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
1317 ip_regex = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
9321318 super(IPAddress, self).__init__(default=default, regex=ip_regex, **kwargs)
933
9341319
9351320 """
9361321
9421327 self.allow_None = (default is None or allow_None)
9431328 self._validate(default)
9441329
1330 def _validate_regex(self, val, regex):
1331 if (val is None and self.allow_None):
1332 return
1333 if regex is not None and re.match(regex, val) is None:
1334 raise ValueError("String parameter %r value %r does not match regex %r."
1335 % (self.name, val, regex))
1336
1337 def _validate_value(self, val, allow_None):
1338 if allow_None and val is None:
1339 return
1340 if not isinstance(val, basestring):
1341 raise ValueError("String parameter %r only takes a string value, "
1342 "not value of type %s." % (self.name, type(val)))
1343
9451344 def _validate(self, val):
946 if self.allow_None and val is None:
947 return
948
949 if not isinstance(val, basestring):
950 raise ValueError("String '%s' only takes a string value."%self.name)
951
952 if self.regex is not None and re.match(self.regex, val) is None:
953 raise ValueError("String '%s': '%s' does not match regex '%s'."%(self.name,val,self.regex))
1345 self._validate_value(val, self.allow_None)
1346 self._validate_regex(val, self.regex)
9541347
9551348
9561349 class shared_parameters(object):
9851378 @wraps(fn)
9861379 def override_initialization(self_,*args,**kw):
9871380 parameterized_instance = self_.self
988 original_initialized=parameterized_instance.initialized
989 parameterized_instance.initialized=False
990 fn(parameterized_instance,*args,**kw)
991 parameterized_instance.initialized=original_initialized
1381 original_initialized = parameterized_instance.initialized
1382 parameterized_instance.initialized = False
1383 fn(parameterized_instance, *args, **kw)
1384 parameterized_instance.initialized = original_initialized
9921385 return override_initialization
9931386
9941387
10611454 class or the instance as necessary.
10621455 """
10631456
1064 _disable_stubs = None # Flag used to disable stubs in the API1 tests
1457 _disable_stubs = False # Flag used to disable stubs in the API1 tests
10651458 # None for no action, True to raise and False to warn.
10661459
10671460 def __init__(self_, cls, self=None):
10711464 """
10721465 self_.cls = cls
10731466 self_.self = self
1074 self_._BATCH_WATCH = False # If true, Event and watcher objects are queued.
1075 self_._TRIGGER = False
1076 self_._events = [] # Queue of batched eventd
1077 self_._watchers = [] # Queue of batched watchers
1467
1468 @property
1469 def _BATCH_WATCH(self_):
1470 return self_.self_or_cls._parameters_state['BATCH_WATCH']
1471
1472 @_BATCH_WATCH.setter
1473 def _BATCH_WATCH(self_, value):
1474 self_.self_or_cls._parameters_state['BATCH_WATCH'] = value
1475
1476 @property
1477 def _TRIGGER(self_):
1478 return self_.self_or_cls._parameters_state['TRIGGER']
1479
1480 @_TRIGGER.setter
1481 def _TRIGGER(self_, value):
1482 self_.self_or_cls._parameters_state['TRIGGER'] = value
1483
1484 @property
1485 def _events(self_):
1486 return self_.self_or_cls._parameters_state['events']
1487
1488 @_events.setter
1489 def _events(self_, value):
1490 self_.self_or_cls._parameters_state['events'] = value
1491
1492 @property
1493 def _watchers(self_):
1494 return self_.self_or_cls._parameters_state['watchers']
1495
1496 @_watchers.setter
1497 def _watchers(self_, value):
1498 self_.self_or_cls._parameters_state['watchers'] = value
1499
1500 @property
1501 def watchers(self):
1502 """Read-only list of watchers on this Parameterized"""
1503 return self._watchers
10781504
10791505 @property
10801506 def self_or_cls(self_):
10811507 return self_.cls if self_.self is None else self_.self
10821508
1509 def __setstate__(self, state):
1510 # Set old parameters state on Parameterized._parameters_state
1511 self_or_cls = state.get('self', state.get('cls'))
1512 for k in self_or_cls._parameters_state:
1513 key = '_'+k
1514 if key in state:
1515 self_or_cls._parameters_state[k] = state.pop(key)
1516 for k, v in state.items():
1517 setattr(self, k, v)
10831518
10841519 def __getitem__(self_, key):
10851520 """
10881523 inst = self_.self
10891524 parameters = self_.objects(False) if inst is None else inst.param.objects(False)
10901525 p = parameters[key]
1091 if (inst is not None and p.per_instance and
1526 if (inst is not None and getattr(inst, 'initialized', False) and p.per_instance and
10921527 not getattr(inst, '_disable_instance__params', False)):
10931528 if key not in inst._instance__params:
10941529 try:
10991534 except:
11001535 raise
11011536 finally:
1102 p.watchers = watchers
1537 p.watchers = {k: list(v) for k, v in watchers.items()}
11031538 p.owner = inst
11041539 inst._instance__params[key] = p
11051540 else:
11701605 First, ensures that all Parameters with 'instantiate=True'
11711606 (typically used for mutable Parameters) are copied directly
11721607 into each object, to ensure that there is an independent copy
1173 (to avoid suprising aliasing errors). Then sets each of the
1608 (to avoid surprising aliasing errors). Then sets each of the
11741609 keyword arguments, warning when any of them are not defined as
11751610 parameters.
11761611
11781613 """
11791614 self = self_.param.self
11801615 ## Deepcopy all 'instantiate=True' parameters
1181 # (build a set of names first to avoid redundantly instantiating
1182 # a later-overridden parent class's parameter)
1616 # (building a set of names first to avoid redundantly
1617 # instantiating a later-overridden parent class's parameter)
11831618 params_to_instantiate = {}
11841619 for class_ in classlist(type(self)):
11851620 if not issubclass(class_, Parameterized):
11861621 continue
1187 for (k,v) in class_.__dict__.items():
1622 for (k, v) in class_.param._parameters.items():
11881623 # (avoid replacing name with the default of None)
1189 if isinstance(v,Parameter) and v.instantiate and k!="name":
1190 params_to_instantiate[k]=v
1624 if v.instantiate and k != "name":
1625 params_to_instantiate[k] = v
11911626
11921627 for p in params_to_instantiate.values():
11931628 self.param._instantiate_param(p)
11941629
11951630 ## keyword arg setting
1196 for name,val in params.items():
1631 for name, val in params.items():
11971632 desc = self.__class__.get_param_descriptor(name)[0] # pylint: disable-msg=E1101
11981633 if not desc:
1199 self.param.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters",name,val)
1634 self.param.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters", name, val)
12001635 # i.e. if not desc it's setting an attribute in __dict__, not a Parameter
1201 setattr(self,name,val)
1202
1636 setattr(self, name, val)
1637
1638 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
12031639 @classmethod
12041640 def deprecate(cls, fn):
12051641 """
12301666 return not Comparator.is_equal(event.old, event.new)
12311667
12321668
1233 # CEBALERT: this is a bit ugly
1234 def _instantiate_param(self_,param_obj,dict_=None,key=None):
1669 def _instantiate_param(self_, param_obj, dict_=None, key=None):
12351670 # deepcopy param_obj.default into self.__dict__ (or dict_ if supplied)
12361671 # under the parameter's _internal_name (or key if supplied)
12371672 self = self_.self
12381673 dict_ = dict_ or self.__dict__
12391674 key = key or param_obj._internal_name
1240 param_key = (str(type(self)), param_obj.name)
12411675 if shared_parameters._share:
1676 param_key = (str(type(self)), param_obj.name)
12421677 if param_key in shared_parameters._shared_cache:
12431678 new_object = shared_parameters._shared_cache[param_key]
12441679 else:
12461681 shared_parameters._shared_cache[param_key] = new_object
12471682 else:
12481683 new_object = copy.deepcopy(param_obj.default)
1249 dict_[key]=new_object
1250
1251 if isinstance(new_object,Parameterized):
1684
1685 dict_[key] = new_object
1686
1687 if isinstance(new_object, Parameterized):
12521688 global object_count
1253 object_count+=1
1254 # CB: writes over name given to the original object;
1255 # should it instead keep the same name?
1689 object_count += 1
1690 # Writes over name given to the original object;
1691 # could instead have kept the same name
12561692 new_object.param._generate_name()
12571693
1694 def _update_deps(self_, attribute=None, init=False):
1695 obj = self_.self
1696 init_methods = []
1697 for method, queued, on_init, constant, dynamic in type(obj).param._depends['watch']:
1698 # On initialization set up constant watchers; otherwise
1699 # clean up previous dynamic watchers for the updated attribute
1700 dynamic = [d for d in dynamic if attribute is None or d.spec.startswith(attribute)]
1701 if init:
1702 constant_grouped = defaultdict(list)
1703 for dep in _resolve_mcs_deps(obj, constant, []):
1704 constant_grouped[(id(dep.inst), id(dep.cls), dep.what)].append((None, dep))
1705 for group in constant_grouped.values():
1706 self_._watch_group(obj, method, queued, group)
1707 m = getattr(self_.self, method)
1708 if on_init and m not in init_methods:
1709 init_methods.append(m)
1710 elif dynamic:
1711 for w in obj._dynamic_watchers.pop(method, []):
1712 (w.inst or w.cls).param.unwatch(w)
1713 else:
1714 continue
1715
1716 # Resolve dynamic dependencies one-by-one to be able to trace their watchers
1717 grouped = defaultdict(list)
1718 for ddep in dynamic:
1719 for dep in _resolve_mcs_deps(obj, [], [ddep]):
1720 grouped[(id(dep.inst), id(dep.cls), dep.what)].append((ddep, dep))
1721
1722 for group in grouped.values():
1723 watcher = self_._watch_group(obj, method, queued, group, attribute)
1724 obj._dynamic_watchers[method].append(watcher)
1725 for m in init_methods:
1726 m()
1727
1728 def _resolve_dynamic_deps(self, obj, dynamic_dep, param_dep, attribute):
1729 """
1730 If a subobject whose parameters are being depended on changes
1731 we should only trigger events if the actual parameter values
1732 of the new object differ from those on the old subobject,
1733 therefore we accumulate parameters to compare on a subobject
1734 change event.
1735
1736 Additionally we need to make sure to notify the parent object
1737 if a subobject changes so the dependencies can be
1738 reinitialized so we return a callback which updates the
1739 dependencies.
1740 """
1741 subobj = obj
1742 subobjs = [obj]
1743 for subpath in dynamic_dep.spec.split('.')[:-1]:
1744 subobj = getattr(subobj, subpath.split(':')[0], None)
1745 subobjs.append(subobj)
1746
1747 dep_obj = (param_dep.inst or param_dep.cls)
1748 if dep_obj not in subobjs[:-1]:
1749 return None, None, param_dep.what
1750
1751 depth = subobjs.index(dep_obj)
1752 callback = None
1753 if depth > 0:
1754 def callback(*events):
1755 """
1756 If a subobject changes, we need to notify the main
1757 object to update the dependencies.
1758 """
1759 obj.param._update_deps(attribute)
1760
1761 p = '.'.join(dynamic_dep.spec.split(':')[0].split('.')[depth+1:])
1762 if p == 'param':
1763 subparams = [sp for sp in list(subobjs[-1].param)]
1764 else:
1765 subparams = [p]
1766
1767 if ':' in dynamic_dep.spec:
1768 what = dynamic_dep.spec.split(':')[-1]
1769 else:
1770 what = param_dep.what
1771
1772 return subparams, callback, what
1773
1774 def _watch_group(self_, obj, name, queued, group, attribute=None):
1775 """
1776 Sets up a watcher for a group of dependencies. Ensures that
1777 if the dependency was dynamically generated we check whether
1778 a subobject change event actually causes a value change and
1779 that we update the existing watchers, i.e. clean up watchers
1780 on the old subobject and create watchers on the new subobject.
1781 """
1782 dynamic_dep, param_dep = group[0]
1783 dep_obj = (param_dep.inst or param_dep.cls)
1784 params = []
1785 for _, g in group:
1786 if g.name not in params:
1787 params.append(g.name)
1788
1789 if dynamic_dep is None:
1790 subparams, callback, what = None, None, param_dep.what
1791 else:
1792 subparams, callback, what = self_._resolve_dynamic_deps(
1793 obj, dynamic_dep, param_dep, attribute)
1794
1795 mcaller = _m_caller(obj, name, what, subparams, callback)
1796 return dep_obj.param._watch(
1797 mcaller, params, param_dep.what, queued=queued, precedence=-1)
1798
12581799 # Classmethods
12591800
1801 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
12601802 def print_param_defaults(self_):
12611803 """Print the default values of all cls's Parameters."""
12621804 cls = self_.cls
12651807 print(cls.__name__+'.'+key+ '='+ repr(val.default))
12661808
12671809
1810 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
12681811 def set_default(self_,param_name,value):
12691812 """
12701813 Set the default value of param_name.
12751818 setattr(cls,param_name,value)
12761819
12771820
1278 def _add_parameter(self_, param_name,param_obj):
1821 def add_parameter(self_, param_name, param_obj):
12791822 """
12801823 Add a new Parameter object into this object's class.
12811824
1282 Supposed to result in a Parameter equivalent to one declared
1825 Should result in a Parameter equivalent to one declared
12831826 in the class's source code.
12841827 """
1285 # CEBALERT: can't we just do
1286 # setattr(cls,param_name,param_obj)? The metaclass's
1287 # __setattr__ is actually written to handle that. (Would also
1288 # need to do something about the params() cache. That cache
1289 # is a pain, but it definitely improved the startup time; it
1290 # would be worthwhile making sure no method except for one
1291 # "add_param()" method has to deal with it (plus any future
1292 # remove_param() method.)
1828 # Could have just done setattr(cls,param_name,param_obj),
1829 # which is supported by the metaclass's __setattr__ , but
1830 # would need to handle the params() cache as well
1831 # (which is tricky but important for startup speed).
12931832 cls = self_.cls
12941833 type.__setattr__(cls,param_name,param_obj)
12951834 ParameterizedMetaclass._initialize_parameter(cls,param_name,param_obj)
12991838 except AttributeError:
13001839 pass
13011840
1302
1841 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
1842 _add_parameter = add_parameter
1843
1844
1845 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
13031846 def params(self_, parameter_name=None):
13041847 """
13051848 Return the Parameters of this class as the
13081851 Includes Parameters from this class and its
13091852 superclasses.
13101853 """
1311 if self_.self is not None and self_.self._instance__params:
1312 self_.warning('The Parameterized instance has instance '
1313 'parameters created using new-style param '
1314 'APIs, which are incompatible with .params. '
1315 'Use the new more explicit APIs on the '
1316 '.param accessor to query parameter instances.'
1317 'To query all parameter instances use '
1318 '.param.objects with the option to return '
1319 'either class or instance parameter objects. '
1320 'Alternatively use .param[name] indexing to '
1321 'access a specific parameter object by name.')
1322
13231854 pdict = self_.objects(instance='existing')
13241855 if parameter_name is None:
13251856 return pdict
13281859
13291860 # Bothmethods
13301861
1331 def set_param(self_, *args,**kwargs):
1332 """
1333 For each param=value keyword argument, sets the corresponding
1334 parameter of this object or class to the given value.
1335
1336 For backwards compatibility, also accepts
1337 set_param("param",value) for a single parameter value using
1338 positional arguments, but the keyword interface is preferred
1339 because it is more compact and can set multiple values.
1862 def update(self_, *args, **kwargs):
1863 """
1864 For the given dictionary or iterable or set of param=value keyword arguments,
1865 sets the corresponding parameter of this object or class to the given value.
13401866 """
13411867 BATCH_WATCH = self_.self_or_cls.param._BATCH_WATCH
13421868 self_.self_or_cls.param._BATCH_WATCH = True
13431869 self_or_cls = self_.self_or_cls
13441870 if args:
1345 if len(args) == 2 and not args[0] in kwargs and not kwargs:
1346 kwargs[args[0]] = args[1]
1871 if len(args) == 1 and not kwargs:
1872 kwargs = args[0]
13471873 else:
13481874 self_.self_or_cls.param._BATCH_WATCH = False
1349 raise ValueError("Invalid positional arguments for %s.set_param" %
1875 raise ValueError("%s.update accepts *either* an iterable or key=value pairs, not both" %
13501876 (self_or_cls.name))
1877
1878 trigger_params = [k for k in kwargs
1879 if ((k in self_.self_or_cls.param) and
1880 hasattr(self_.self_or_cls.param[k], '_autotrigger_value'))]
1881
1882 for tp in trigger_params:
1883 self_.self_or_cls.param[tp]._mode = 'set'
13511884
13521885 for (k, v) in kwargs.items():
13531886 if k not in self_or_cls.param:
13631896 if not BATCH_WATCH:
13641897 self_._batch_call_watchers()
13651898
1899 for tp in trigger_params:
1900 p = self_.self_or_cls.param[tp]
1901 p._mode = 'reset'
1902 setattr(self_or_cls, tp, p._autotrigger_reset_value)
1903 p._mode = 'set-reset'
1904
1905
1906 # PARAM2_DEPRECATION: Could be removed post param 2.0; use update() instead.
1907 def set_param(self_, *args,**kwargs):
1908 """
1909 For each param=value keyword argument, sets the corresponding
1910 parameter of this object or class to the given value.
1911
1912 For backwards compatibility, also accepts
1913 set_param("param",value) for a single parameter value using
1914 positional arguments, but the keyword interface is preferred
1915 because it is more compact and can set multiple values.
1916 """
1917 self_or_cls = self_.self_or_cls
1918 if args:
1919 if len(args) == 2 and not args[0] in kwargs and not kwargs:
1920 kwargs[args[0]] = args[1]
1921 else:
1922 raise ValueError("Invalid positional arguments for %s.set_param" %
1923 (self_or_cls.name))
1924 return self_.update(kwargs)
1925
13661926
13671927 def objects(self_, instance=True):
13681928 """
13771937 instance='existing'.
13781938 """
13791939 cls = self_.cls
1380 # CB: we cache the parameters because this method is called often,
1940 # We cache the parameters because this method is called often,
13811941 # and parameters are rarely added (and cannot be deleted)
13821942 try:
13831943 pdict = getattr(cls, '_%s__params' % cls.__name__)
13971957
13981958 if instance and self_.self is not None:
13991959 if instance == 'existing':
1400 if self_.self._instance__params:
1960 if getattr(self_.self, 'initialized', False) and self_.self._instance__params:
14011961 return dict(pdict, **self_.self._instance__params)
14021962 return pdict
14031963 else:
14091969 """
14101970 Trigger watchers for the given set of parameter names. Watchers
14111971 will be triggered whether or not the parameter values have
1412 actually changed.
1413 """
1972 actually changed. As a special case, the value will actually be
1973 changed for a Parameter of type Event, setting it to True so
1974 that it is clear which Event parameter has been triggered.
1975 """
1976 trigger_params = [p for p in self_.self_or_cls.param
1977 if hasattr(self_.self_or_cls.param[p], '_autotrigger_value')]
1978 triggers = {p:self_.self_or_cls.param[p]._autotrigger_value
1979 for p in trigger_params if p in param_names}
1980
14141981 events = self_.self_or_cls.param._events
14151982 watchers = self_.self_or_cls.param._watchers
14161983 self_.self_or_cls.param._events = []
14171984 self_.self_or_cls.param._watchers = []
1418 param_values = dict(self_.get_param_values())
1985 param_values = self_.values()
14191986 params = {name: param_values[name] for name in param_names}
14201987 self_.self_or_cls.param._TRIGGER = True
1421 self_.set_param(**params)
1988 self_.set_param(**dict(params, **triggers))
14221989 self_.self_or_cls.param._TRIGGER = False
14231990 self_.self_or_cls.param._events += events
14241991 self_.self_or_cls.param._watchers += watchers
14352002 return Event(what=event.what, name=event.name, obj=event.obj, cls=event.cls,
14362003 old=event.old, new=event.new, type=event_type)
14372004
2005 def _execute_watcher(self, watcher, events):
2006 if watcher.mode == 'args':
2007 args, kwargs = events, {}
2008 else:
2009 args, kwargs = (), {event.name: event.new for event in events}
2010
2011 if iscoroutinefunction(watcher.fn):
2012 if async_executor is None:
2013 raise RuntimeError("Could not execute %s coroutine function. "
2014 "Please register a asynchronous executor on "
2015 "param.parameterized.async_executor, which "
2016 "schedules the function on an event loop." %
2017 watcher.fn)
2018 async_executor(partial(watcher.fn, *args, **kwargs))
2019 else:
2020 watcher.fn(*args, **kwargs)
2021
14382022 def _call_watcher(self_, watcher, event):
14392023 """
1440 Invoke the given the watcher appropriately given a Event object.
2024 Invoke the given watcher appropriately given an Event object.
14412025 """
14422026 if self_.self_or_cls.param._TRIGGER:
14432027 pass
14462030
14472031 if self_.self_or_cls.param._BATCH_WATCH:
14482032 self_._events.append(event)
1449 if watcher not in self_._watchers:
2033 if not any(watcher is w for w in self_._watchers):
14502034 self_._watchers.append(watcher)
14512035 else:
14522036 event = self_._update_event_type(watcher, event, self_.self_or_cls.param._TRIGGER)
1453 with batch_watch(self_.self_or_cls, enable=watcher.queued, run=False):
1454 if watcher.mode == 'args':
1455 watcher.fn(event)
1456 else:
1457 watcher.fn(**{event.name: event.new})
1458
2037 with _batch_call_watchers(self_.self_or_cls, enable=watcher.queued, run=False):
2038 self_._execute_watcher(watcher, (event,))
14592039
14602040 def _batch_call_watchers(self_):
14612041 """
14692049 self_.self_or_cls.param._events = []
14702050 self_.self_or_cls.param._watchers = []
14712051
1472 for watcher in watchers:
2052 for watcher in sorted(watchers, key=lambda w: w.precedence):
14732053 events = [self_._update_event_type(watcher, event_dict[(name, watcher.what)],
14742054 self_.self_or_cls.param._TRIGGER)
14752055 for name in watcher.parameter_names
14762056 if (name, watcher.what) in event_dict]
1477 with batch_watch(self_.self_or_cls, enable=watcher.queued, run=False):
1478 if watcher.mode == 'args':
1479 watcher.fn(*events)
1480 else:
1481 watcher.fn(**{c.name:c.new for c in events})
1482
2057 with _batch_call_watchers(self_.self_or_cls, enable=watcher.queued, run=False):
2058 self_._execute_watcher(watcher, events)
14832059
14842060 def set_dynamic_time_fn(self_,time_fn,sublistattr=None):
14852061 """
15222098 for obj in sublist:
15232099 obj.param.set_dynamic_time_fn(time_fn,sublistattr)
15242100
1525 def get_param_values(self_,onlychanged=False):
1526 """
2101 def serialize_parameters(self_, subset=None, mode='json'):
2102 self_or_cls = self_.self_or_cls
2103 if mode not in Parameter._serializers:
2104 raise ValueError('Mode %r not in available serialization formats %r'
2105 % (mode, list(Parameter._serializers.keys())))
2106 serializer = Parameter._serializers[mode]
2107 return serializer.serialize_parameters(self_or_cls, subset=subset)
2108
2109 def serialize_value(self_, pname, mode='json'):
2110 self_or_cls = self_.self_or_cls
2111 if mode not in Parameter._serializers:
2112 raise ValueError('Mode %r not in available serialization formats %r'
2113 % (mode, list(Parameter._serializers.keys())))
2114 serializer = Parameter._serializers[mode]
2115 return serializer.serialize_parameter_value(self_or_cls, pname)
2116
2117 def deserialize_parameters(self_, serialization, subset=None, mode='json'):
2118 self_or_cls = self_.self_or_cls
2119 serializer = Parameter._serializers[mode]
2120 return serializer.deserialize_parameters(self_or_cls, serialization, subset=subset)
2121
2122 def deserialize_value(self_, pname, value, mode='json'):
2123 self_or_cls = self_.self_or_cls
2124 if mode not in Parameter._serializers:
2125 raise ValueError('Mode %r not in available serialization formats %r'
2126 % (mode, list(Parameter._serializers.keys())))
2127 serializer = Parameter._serializers[mode]
2128 return serializer.deserialize_parameter_value(self_or_cls, pname, value)
2129
2130 def schema(self_, safe=False, subset=None, mode='json'):
2131 """
2132 Returns a schema for the parameters on this Parameterized object.
2133 """
2134 self_or_cls = self_.self_or_cls
2135 if mode not in Parameter._serializers:
2136 raise ValueError('Mode %r not in available serialization formats %r'
2137 % (mode, list(Parameter._serializers.keys())))
2138 serializer = Parameter._serializers[mode]
2139 return serializer.schema(self_or_cls, safe=safe, subset=subset)
2140
2141 # PARAM2_DEPRECATION: Could be removed post param 2.0; same as values() but returns list, not dict
2142 def get_param_values(self_, onlychanged=False):
2143 """
2144 (Deprecated; use .values() instead.)
2145
15272146 Return a list of name,value pairs for all Parameters of this
15282147 object.
15292148
15322151 (onlychanged has no effect when called on a class).
15332152 """
15342153 self_or_cls = self_.self_or_cls
1535 # CEB: we'd actually like to know whether a value has been
1536 # explicitly set on the instance, but I'm not sure that's easy
1537 # (would need to distinguish instantiation of default from
1538 # user setting of value).
15392154 vals = []
1540 for name,val in self_or_cls.param.objects('existing').items():
2155 for name, val in self_or_cls.param.objects('existing').items():
15412156 value = self_or_cls.param.get_value_generator(name)
1542 # (this is pointless for cls)
1543 if not onlychanged or not all_equal(value,val.default):
1544 vals.append((name,value))
2157 if not onlychanged or not all_equal(value, val.default):
2158 vals.append((name, value))
15452159
15462160 vals.sort(key=itemgetter(0))
15472161 return vals
15482162
2163 def values(self_, onlychanged=False):
2164 """
2165 Return a dictionary of name,value pairs for the Parameters of this
2166 object.
2167
2168 When called on an instance with onlychanged set to True, will
2169 only return values that are not equal to the default value
2170 (onlychanged has no effect when called on a class).
2171 """
2172 # Defined in terms of get_param_values() to avoid ordering
2173 # issues in python2, but can be inverted if get_param_values
2174 # is removed when python2 support is dropped
2175 return dict(self_.get_param_values(onlychanged))
15492176
15502177 def force_new_dynamic_value(self_, name): # pylint: disable-msg=E0213
15512178 """
15712198 return param_obj.__get__(slf, cls)
15722199 else:
15732200 return param_obj._force(slf, cls)
1574
15752201
15762202 def get_value_generator(self_,name): # pylint: disable-msg=E0213
15772203 """
16322258
16332259 return value
16342260
1635
1636 def params_depended_on(self_,name):
1637 return _params_depended_on(MInfo(cls=self_.cls,inst=self_.self,name=name,method=getattr(self_.self_or_cls,name)))
1638
2261 def method_dependencies(self_, name, intermediate=False):
2262 """
2263 Given the name of a method, returns a PInfo object for each dependency
2264 of this method. See help(PInfo) for the contents of these objects.
2265
2266 By default intermediate dependencies on sub-objects are not
2267 returned as these are primarily useful for internal use to
2268 determine when a sub-object dependency has to be updated.
2269 """
2270 method = getattr(self_.self_or_cls, name)
2271 minfo = MInfo(cls=self_.cls, inst=self_.self, name=name,
2272 method=method)
2273 deps, dynamic = _params_depended_on(
2274 minfo, dynamic=False, intermediate=intermediate)
2275 if self_.self is None:
2276 return deps
2277 return _resolve_mcs_deps(
2278 self_.self, deps, dynamic, intermediate=intermediate)
2279
2280 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
2281 params_depended_on = method_dependencies
16392282
16402283 def outputs(self_):
16412284 """
16562299 outputs[name] = (otype, method, idx)
16572300 return outputs
16582301
1659
1660 def _spec_to_obj(self_,spec):
1661 # TODO: when we decide on spec, this method should be
1662 # rewritten
1663
2302 def _spec_to_obj(self_, spec, dynamic=True, intermediate=True):
2303 """
2304 Resolves a dependency specification into lists of explicit
2305 parameter dependencies and dynamic dependencies.
2306
2307 Dynamic dependencies are specifications to be resolved when
2308 the sub-object whose parameters are being depended on is
2309 defined.
2310
2311 During class creation dynamic=False which means sub-object
2312 dependencies are not resolved. At instance creation and
2313 whenever a sub-object is set on an object this method will be
2314 invoked to determine whether the dependency is available.
2315
2316 For sub-object dependencies we also return dependencies for
2317 every part of the path, e.g. for a dependency specification
2318 like "a.b.c" we return dependencies for sub-object "a" and the
2319 sub-sub-object "b" in addition to the dependency on the actual
2320 parameter "c" on object "b". This is to ensure that if a
2321 sub-object is swapped out we are notified and can update the
2322 dynamic dependency to the new object. Even if a sub-object
2323 dependency can only partially resolved, e.g. if object "a"
2324 does not yet have a sub-object "b" we must watch for changes
2325 to "b" on sub-object "a" in case such a subobject is put in "b".
2326 """
16642327 if isinstance(spec, Parameter):
16652328 inst = spec.owner if isinstance(spec.owner, Parameterized) else None
16662329 cls = spec.owner if inst is None else type(inst)
16672330 info = PInfo(inst=inst, cls=cls, name=spec.name,
16682331 pobj=spec, what='value')
1669 return [info]
1670
1671 assert spec.count(":")<=1
1672
1673 spec = spec.strip()
1674 m = re.match("(?P<path>[^:]*):?(?P<what>.*)", spec)
1675 what = m.group('what')
1676 path = "."+m.group('path')
1677 m = re.match(r"(?P<obj>.*)(\.)(?P<attr>.*)",path)
1678 obj = m.group('obj')
1679 attr = m.group("attr")
1680
1681 src = self_.self_or_cls if obj=='' else _getattrr(self_.self_or_cls,obj[1::])
1682 cls,inst = (src, None) if isinstance(src, type) else (type(src), src)
1683
2332 return [] if intermediate == 'only' else [info], []
2333
2334 obj, attr, what = _parse_dependency_spec(spec)
2335 if obj is None:
2336 src = self_.self_or_cls
2337 elif not dynamic:
2338 return [], [DInfo(spec=spec)]
2339 else:
2340 src = _getattrr(self_.self_or_cls, obj[1::], None)
2341 if src is None:
2342 path = obj[1:].split('.')
2343 deps = []
2344 # Attempt to partially resolve subobject path to ensure
2345 # that if a subobject is later updated making the full
2346 # subobject path available we have to be notified and
2347 # set up watchers
2348 if len(path) >= 1 and intermediate:
2349 sub_src = None
2350 subpath = path
2351 while sub_src is None and subpath:
2352 subpath = subpath[:-1]
2353 sub_src = _getattrr(self_.self_or_cls, '.'.join(subpath), None)
2354 if subpath:
2355 subdeps, _ = self_._spec_to_obj(
2356 '.'.join(path[:len(subpath)+1]), dynamic, intermediate)
2357 deps += subdeps
2358 return deps, [] if intermediate == 'only' else [DInfo(spec=spec)]
2359
2360 cls, inst = (src, None) if isinstance(src, type) else (type(src), src)
16842361 if attr == 'param':
1685 dependencies = self_._spec_to_obj(obj[1:])
2362 deps, dynamic_deps = self_._spec_to_obj(obj[1:], dynamic, intermediate)
16862363 for p in src.param:
1687 dependencies += src.param._spec_to_obj(p)
1688 return dependencies
2364 param_deps, param_dynamic_deps = src.param._spec_to_obj(p, dynamic, intermediate)
2365 deps += param_deps
2366 dynamic_deps += param_dynamic_deps
2367 return deps, dynamic_deps
16892368 elif attr in src.param:
1690 what = what if what != '' else 'value'
16912369 info = PInfo(inst=inst, cls=cls, name=attr,
16922370 pobj=src.param[attr], what=what)
2371 elif hasattr(src, attr):
2372 info = MInfo(inst=inst, cls=cls, name=attr,
2373 method=getattr(src, attr))
2374 elif getattr(src, "abstract", None):
2375 return [], [] if intermediate == 'only' else [DInfo(spec=spec)]
16932376 else:
1694 info = MInfo(inst=inst, cls=cls, name=attr,
1695 method=getattr(src,attr))
1696 return [info]
1697
1698
1699 def _watch(self_, action, watcher, what='value', operation='add'): #'add' | 'remove'
2377 raise AttributeError("Attribute %r could not be resolved on %s."
2378 % (attr, src))
2379
2380 if obj is None or not intermediate:
2381 return [info], []
2382 deps, dynamic_deps = self_._spec_to_obj(obj[1:], dynamic, intermediate)
2383 if intermediate != 'only':
2384 deps.append(info)
2385 return deps, dynamic_deps
2386
2387 def _register_watcher(self_, action, watcher, what='value'):
17002388 parameter_names = watcher.parameter_names
17012389 for parameter_name in parameter_names:
17022390 if parameter_name not in self_.cls.param:
17172405 watchers[what] = []
17182406 getattr(watchers[what], action)(watcher)
17192407
1720 def watch(self_,fn,parameter_names, what='value', onlychanged=True, queued=False):
2408 def watch(self_, fn, parameter_names, what='value', onlychanged=True, queued=False, precedence=0):
2409 """
2410 Register the given callback function `fn` to be invoked for
2411 events on the indicated parameters.
2412
2413 `what`: What to watch on each parameter; either the value (by
2414 default) or else the indicated slot (e.g. 'constant').
2415
2416 `onlychanged`: By default, only invokes the function when the
2417 watched item changes, but if `onlychanged=False` also invokes
2418 it when the `what` item is set to its current value again.
2419
2420 `queued`: By default, additional watcher events generated
2421 inside the callback fn are dispatched immediately, effectively
2422 doing depth-first processing of Watcher events. However, in
2423 certain scenarios, it is helpful to wait to dispatch such
2424 downstream events until all events that triggered this watcher
2425 have been processed. In such cases setting `queued=True` on
2426 this Watcher will queue up new downstream events generated
2427 during `fn` until `fn` completes and all other watchers
2428 invoked by that same event have finished executing),
2429 effectively doing breadth-first processing of Watcher events.
2430
2431 `precedence`: Declares a precedence level for the Watcher that
2432 determines the priority with which the callback is executed.
2433 Lower precedence levels are executed earlier. Negative
2434 precedences are reserved for internal Watchers, i.e. those
2435 set up by param.depends.
2436
2437 When the `fn` is called, it will be provided the relevant
2438 Event objects as positional arguments, which allows it to
2439 determine which of the possible triggering events occurred.
2440
2441 Returns a Watcher object.
2442
2443 See help(Watcher) and help(Event) for the contents of those objects.
2444 """
2445 if precedence < 0:
2446 raise ValueError("User-defined watch callbacks must declare "
2447 "a positive precedence. Negative precedences "
2448 "are reserved for internal Watchers.")
2449 return self_._watch(fn, parameter_names, what, onlychanged, queued, precedence)
2450
2451 def _watch(self_, fn, parameter_names, what='value', onlychanged=True, queued=False, precedence=-1):
17212452 parameter_names = tuple(parameter_names) if isinstance(parameter_names, list) else (parameter_names,)
17222453 watcher = Watcher(inst=self_.self, cls=self_.cls, fn=fn, mode='args',
17232454 onlychanged=onlychanged, parameter_names=parameter_names,
1724 what=what, queued=queued)
1725 self_._watch('append', watcher, what)
2455 what=what, queued=queued, precedence=precedence)
2456 self_._register_watcher('append', watcher, what)
17262457 return watcher
17272458
1728 def unwatch(self_,watcher):
1729 """
1730 Unwatch watchers set either with watch or watch_values.
2459 def unwatch(self_, watcher):
2460 """
2461 Remove the given Watcher object (from `watch` or `watch_values`) from this object's list.
17312462 """
17322463 try:
1733 self_._watch('remove',watcher)
1734 except:
1735 self_.warning('No such watcher {watcher} to remove.'.format(watcher=watcher))
1736
1737
1738 def watch_values(self_, fn, parameter_names, what='value', onlychanged=True, queued=False):
1739 parameter_names = tuple(parameter_names) if isinstance(parameter_names, list) else (parameter_names,)
2464 self_._register_watcher('remove', watcher, what=watcher.what)
2465 except Exception:
2466 self_.warning('No such watcher {watcher} to remove.'.format(watcher=str(watcher)))
2467
2468 def watch_values(self_, fn, parameter_names, what='value', onlychanged=True, queued=False, precedence=0):
2469 """
2470 Easier-to-use version of `watch` specific to watching for changes in parameter values.
2471
2472 Only allows `what` to be 'value', and invokes the callback `fn` using keyword
2473 arguments <param_name>=<new_value> rather than with a list of Event objects.
2474 """
2475 if precedence < 0:
2476 raise ValueError("User-defined watch callbacks must declare "
2477 "a positive precedence. Negative precedences "
2478 "are reserved for internal Watchers.")
2479 assert what == 'value'
2480 if isinstance(parameter_names, list):
2481 parameter_names = tuple(parameter_names)
2482 else:
2483 parameter_names = (parameter_names,)
17402484 watcher = Watcher(inst=self_.self, cls=self_.cls, fn=fn,
17412485 mode='kwargs', onlychanged=onlychanged,
1742 parameter_names=parameter_names, what='value',
1743 queued=queued)
1744 self_._watch('append', watcher, what)
2486 parameter_names=parameter_names, what=what,
2487 queued=queued, precedence=precedence)
2488 self_._register_watcher('append', watcher, what)
17452489 return watcher
17462490
1747
17482491 # Instance methods
17492492
1750
2493 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
17512494 def defaults(self_):
17522495 """
17532496 Return {parameter_name:parameter.default} for all non-constant
17582501 """
17592502 self = self_.self
17602503 d = {}
1761 for param_name,param in self.param.objects('existing').items():
2504 for param_name, param in self.param.objects('existing').items():
17622505 if param.constant:
17632506 pass
1764 elif param.instantiate:
1765 self.param._instantiate_param(param,dict_=d,key=param_name)
1766 else:
1767 d[param_name]=param.default
2507 if param.instantiate:
2508 self.param._instantiate_param(param, dict_=d, key=param_name)
2509 d[param_name] = param.default
17682510 return d
17692511
1770 # CEBALERT: designed to avoid any processing unless the print
1771 # level is high enough, but not all callers of message(),
1772 # verbose(), debug(), etc are taking advantage of this. Need to
1773 # document, and also check other ioam projects.
2512 # Designed to avoid any processing unless the print
2513 # level is high enough, though not all callers of message(),
2514 # verbose(), debug(), etc are taking advantage of this.
17742515 def __db_print(self_,level,msg,*args,**kw):
17752516 """
17762517 Calls the logger returned by the get_logger() function,
17862527
17872528 get_logger(name=self_or_cls.name).log(level, msg, *args, **kw)
17882529
2530 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
17892531 def print_param_values(self_):
17902532 """Print the values of all this object's Parameters."""
17912533 self = self_.self
1792 for name,val in self.param.get_param_values():
2534 for name, val in self.param.values().items():
17932535 print('%s.%s = %s' % (self.name,name,val))
17942536
17952537 def warning(self_, msg,*args,**kw):
18002542
18012543 See Python's logging module for details of message formatting.
18022544 """
1803 if not warnings_as_exceptions:
1804 global warning_count
1805 warning_count+=1
1806 self_.__db_print(WARNING,msg,*args,**kw)
1807 else:
1808 raise Exception("Warning: " + msg % args)
1809
2545 self_.log(WARNING, msg, *args, **kw)
2546
2547 # PARAM2_DEPRECATION: Could be removed post param 2.0
18102548 def message(self_,msg,*args,**kw):
18112549 """
18122550 Print msg merged with args as a message.
18152553 """
18162554 self_.__db_print(INFO,msg,*args,**kw)
18172555
2556 # PARAM2_DEPRECATION: Could be removed post param 2.0
18182557 def verbose(self_,msg,*args,**kw):
18192558 """
18202559 Print msg merged with args as a verbose message.
18232562 """
18242563 self_.__db_print(VERBOSE,msg,*args,**kw)
18252564
2565 # PARAM2_DEPRECATION: Could be removed post param 2.0
18262566 def debug(self_,msg,*args,**kw):
18272567 """
18282568 Print msg merged with args as a debugging statement.
18312571 """
18322572 self_.__db_print(DEBUG,msg,*args,**kw)
18332573
1834
1835 # CEBALERT: I think I've noted elsewhere the fact that we
1836 # sometimes have a method on Parameter that requires passing the
1837 # owning Parameterized instance or class, and other times we have
1838 # the method on Parameterized itself. In case I haven't written
1839 # that down elsewhere, here it is again. We should clean that up
1840 # (at least we should be consistent).
1841
1842 # cebalert: it's really time to stop and clean up this bothmethod
1843 # stuff and repeated code in methods using it.
2574 def log(self_, level, msg, *args, **kw):
2575 """
2576 Print msg merged with args as a message at the indicated logging level.
2577
2578 Logging levels include those provided by the Python logging module
2579 plus VERBOSE, either obtained directly from the logging module like
2580 `logging.INFO`, or from parameterized like `param.parameterized.INFO`.
2581
2582 Supported logging levels include (in order of severity)
2583 DEBUG, VERBOSE, INFO, WARNING, ERROR, CRITICAL
2584
2585 See Python's logging module for details of message formatting.
2586 """
2587 if level is WARNING:
2588 if warnings_as_exceptions:
2589 raise Exception("Warning: " + msg % args)
2590 else:
2591 global warning_count
2592 warning_count+=1
2593 self_.__db_print(level, msg, *args, **kw)
2594
2595
2596 def pprint(self_, imports=None, prefix=" ", unknown_value='<?>',
2597 qualify=False, separator=""):
2598 """See Parameterized.pprint"""
2599 self = self_.self
2600 return self._pprint(imports, prefix, unknown_value, qualify, separator)
18442601
18452602
18462603
18672624 attribute __abstract set to True. The 'abstract' attribute can be
18682625 used to find out if a class is abstract or not.
18692626 """
1870 def __init__(mcs,name,bases,dict_):
2627 def __init__(mcs, name, bases, dict_):
18712628 """
18722629 Initialize the class object (not an instance of the class, but
18732630 the class itself).
18762633 default values (see __param_inheritance()) and setting
18772634 attrib_names (see _set_names()).
18782635 """
1879 type.__init__(mcs,name,bases,dict_)
2636 type.__init__(mcs, name, bases, dict_)
18802637
18812638 # Give Parameterized classes a useful 'name' attribute.
1882 # (Could instead consider changing the instance Parameter
1883 # 'name' to '__name__'?)
18842639 mcs.name = name
18852640
1886 mcs.param = Parameters(mcs)
2641 mcs._parameters_state = {
2642 "BATCH_WATCH": False, # If true, Event and watcher objects are queued.
2643 "TRIGGER": False,
2644 "events": [], # Queue of batched events
2645 "watchers": [] # Queue of batched watchers
2646 }
2647 mcs._param = Parameters(mcs)
18872648
18882649 # All objects (with their names) of type Parameter that are
18892650 # defined in this class
1890 parameters = [(n,o) for (n,o) in dict_.items()
1891 if isinstance(o,Parameter)]
2651 parameters = [(n, o) for (n, o) in dict_.items()
2652 if isinstance(o, Parameter)]
2653
2654 mcs._param._parameters = dict(parameters)
18922655
18932656 for param_name,param in parameters:
1894 mcs._initialize_parameter(param_name,param)
2657 mcs._initialize_parameter(param_name, param)
18952658
18962659 # retrieve depends info from methods and store more conveniently
1897 dependers = [(n,m._dinfo) for (n,m) in dict_.items()
1898 if hasattr(m,'_dinfo')]
2660 dependers = [(n, m, m._dinfo) for (n, m) in dict_.items()
2661 if hasattr(m, '_dinfo')]
18992662
19002663 _watch = []
1901 # TODO: probably copy dependencies here too and have
1902 # everything else access from here rather than from method
1903 # object
1904 for n,dinfo in dependers:
2664 for name, method, dinfo in dependers:
19052665 watch = dinfo.get('watch', False)
1906 if watch:
1907 _watch.append((n, watch == 'queued'))
2666 on_init = dinfo.get('on_init', False)
2667 if not watch:
2668 continue
2669 minfo = MInfo(cls=mcs, inst=None, name=name,
2670 method=method)
2671 deps, dynamic_deps = _params_depended_on(minfo, dynamic=False)
2672 _watch.append((name, watch == 'queued', on_init, deps, dynamic_deps))
2673
2674 # Resolve other dependencies in remainder of class hierarchy
2675 for cls in classlist(mcs)[:-1][::-1]:
2676 if not hasattr(cls, '_param'):
2677 continue
2678 for dep in cls.param._depends['watch']:
2679 method = getattr(mcs, dep[0], None)
2680 dinfo = getattr(method, '_dinfo', {'watch': False})
2681 if (not any(dep[0] == w[0] for w in _watch)
2682 and dinfo.get('watch')):
2683 _watch.append(dep)
19082684
19092685 mcs.param._depends = {'watch': _watch}
19102686
19382714
19392715
19402716 def _initialize_parameter(mcs,param_name,param):
1941 # parameter has no way to find out the name a
2717 # A Parameter has no way to find out the name a
19422718 # Parameterized class has for it
19432719 param._set_names(param_name)
19442720 mcs.__param_inheritance(param_name,param)
19452721
19462722
1947 # Python 2.6 added abstract base classes; see
1948 # https://github.com/ioam/param/issues/84
2723 # Should use the official Python 2.6+ abstract base classes; see
2724 # https://github.com/holoviz/param/issues/84
19492725 def __is_abstract(mcs):
19502726 """
19512727 Return True if the class has an attribute __abstract set to True.
19602736 # _ParameterizedMetaclass__abstract before running, but
19612737 # the actual class object will have an attribute
19622738 # _ClassName__abstract. So, we have to mangle it ourselves at
1963 # runtime.
2739 # runtime. Mangling follows description in
2740 # https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
19642741 try:
1965 return getattr(mcs,'_%s__abstract'%mcs.__name__)
2742 return getattr(mcs,'_%s__abstract'%mcs.__name__.lstrip("_"))
19662743 except AttributeError:
19672744 return False
19682745
19692746 abstract = property(__is_abstract)
19702747
1971
1972
1973 def __setattr__(mcs,attribute_name,value):
2748 def _get_param(mcs):
2749 return mcs._param
2750
2751 param = property(_get_param)
2752
2753 def __setattr__(mcs, attribute_name, value):
19742754 """
19752755 Implements 'self.attribute_name=value' in a way that also supports Parameters.
19762756
20122792 # (For instance, python's own pickling mechanism
20132793 # caches __slotnames__ on the class:
20142794 # http://mail.python.org/pipermail/python-checkins/2003-February/033517.html.)
2015 # CEBALERT: this warning bypasses the usual
2016 # mechanisms, which has have consequences for warning
2017 # counts, warnings as exceptions, etc.
2795 # This warning bypasses the usual mechanisms, which
2796 # has have consequences for warning counts, warnings
2797 # as exceptions, etc.
20182798 if not attribute_name.startswith('_'):
20192799 get_logger().log(WARNING,
20202800 "Setting non-Parameter class attribute %s.%s = %s ",
20482828 Note that instantiate is handled differently: if there is a
20492829 parameter with the same name in one of the superclasses with
20502830 instantiate set to True, this parameter will inherit
2051 instatiate=True.
2831 instantiate=True.
20522832 """
20532833 # get all relevant slots (i.e. slots defined in all
20542834 # superclasses of this parameter)
21122892
21132893
21142894
2115 # JABALERT: Only partially achieved so far -- objects of the same
2116 # type and parameter values are treated as different, so anything
2117 # for which instantiate == True is reported as being non-default.
2118
21192895 # Whether script_repr should avoid reporting the values of parameters
21202896 # that are just inheriting their values from the class defaults.
2897 # Because deepcopying creates a new object, cannot detect such
2898 # inheritance when instantiate = True, so such values will be printed
2899 # even if they are just being copied from the default.
21212900 script_repr_suppress_defaults=True
21222901
21232902
2124 # CEBALERT: How about some defaults?
2125 # Also, do we need an option to return repr without path, if desired?
2126 # E.g. to get 'pre_plot_hooks()' instead of
2127 # 'topo.command.analysis.pre_plot_hooks()' in the gui?
2128 def script_repr(val,imports,prefix,settings):
2129 """
2130 Variant of repr() designed for generating a runnable script.
2131
2132 Instances of types that require special handling can use the
2133 script_repr_reg dictionary. Using the type as a key, add a
2134 function that returns a suitable representation of instances of
2135 that type, and adds the required import statement.
2136
2137 The repr of a parameter can be suppressed by returning None from
2138 the appropriate hook in script_repr_reg.
2139 """
2140 return pprint(val,imports,prefix,settings,unknown_value=None,
2141 qualify=True,separator="\n")
2142
2143
2144 # CB: when removing script_repr, merge its docstring here and improve.
2145 # And the ALERT by script_repr about defaults can go.
2146 # CEBALERT: remove settings, add default argument for imports
2147 def pprint(val,imports, prefix="\n ", settings=[],
2903 def script_repr(val, imports=None, prefix="\n ", settings=[],
2904 qualify=True, unknown_value=None, separator="\n",
2905 show_imports=True):
2906 """
2907 Variant of pprint() designed for generating a (nearly) runnable script.
2908
2909 The output of script_repr(parameterized_obj) is meant to be a
2910 string suitable for running using `python file.py`. Not every
2911 object is guaranteed to have a runnable script_repr
2912 representation, but it is meant to be a good starting point for
2913 generating a Python script that (after minor edits) can be
2914 evaluated to get a newly initialized object similar to the one
2915 provided.
2916
2917 The new object will only have the same parameter state, not the
2918 same internal (attribute) state; the script_repr captures only
2919 the state of the Parameters of that object and not any other
2920 attributes it may have.
2921
2922 If show_imports is True (default), includes import statements
2923 for each of the modules required for the objects being
2924 instantiated. This list may not be complete, as it typically
2925 includes only the imports needed for the Parameterized object
2926 itself, not for values that may have been supplied to Parameters.
2927
2928 Apart from show_imports, accepts the same arguments as pprint(),
2929 so see pprint() for explanations of the arguments accepted. The
2930 default values of each of these arguments differ from pprint() in
2931 ways that are more suitable for saving as a separate script than
2932 for e.g. pretty-printing at the Python prompt.
2933 """
2934
2935 if imports is None:
2936 imports = []
2937
2938 rep = pprint(val, imports, prefix, settings, unknown_value,
2939 qualify, separator)
2940
2941 imports = list(set(imports))
2942 imports_str = ("\n".join(imports) + "\n\n") if show_imports else ""
2943
2944 return imports_str + rep
2945
2946
2947 # PARAM2_DEPRECATION: Remove entirely unused settings argument
2948 def pprint(val,imports=None, prefix="\n ", settings=[],
21482949 unknown_value='<?>', qualify=False, separator=''):
21492950 """
2150 (Experimental) Pretty printed representation of a parameterized
2951 Pretty printed representation of a parameterized
21512952 object that may be evaluated with eval.
21522953
21532954 Similar to repr except introspection of the constructor (__init__)
21812982 (e.g. a newline could be supplied to have each Parameter appear on a
21822983 separate line).
21832984
2184 NOTE: pprint will replace script_repr in a future version of
2185 param, but is not yet a complete replacement for script_repr.
2186 """
2187 # CB: doc prefix & settings or realize they don't need to be
2188 # passed around, etc.
2189 # JLS: The settings argument is not used anywhere. To be removed
2190 # in a separate PR.
2985 Instances of types that require special handling can use the
2986 script_repr_reg dictionary. Using the type as a key, add a
2987 function that returns a suitable representation of instances of
2988 that type, and adds the required import statement. The repr of a
2989 parameter can be suppressed by returning None from the appropriate
2990 hook in script_repr_reg.
2991 """
2992
2993 if imports is None:
2994 imports = []
2995
21912996 if isinstance(val,type):
21922997 rep = type_script_repr(val,imports,prefix,settings)
21932998
21942999 elif type(val) in script_repr_reg:
21953000 rep = script_repr_reg[type(val)](val,imports,prefix,settings)
21963001
2197 # CEBALERT: remove with script_repr
2198 elif hasattr(val,'script_repr'):
2199 rep=val.script_repr(imports, prefix+" ")
2200
2201 elif hasattr(val,'pprint'):
2202 rep=val.pprint(imports=imports, prefix=prefix+" ",
2203 qualify=qualify, unknown_value=unknown_value,
2204 separator=separator)
2205
3002 elif hasattr(val,'_pprint'):
3003 rep=val._pprint(imports=imports, prefix=prefix+" ",
3004 qualify=qualify, unknown_value=unknown_value,
3005 separator=separator)
22063006 else:
22073007 rep=repr(val)
22083008
22093009 return rep
22103010
22113011
2212 #: see script_repr()
3012 # Registry for special handling for certain types in script_repr and pprint
22133013 script_repr_reg = {}
22143014
22153015
22463046 pass # Support added only if those libraries are available
22473047
22483048
2249 # why I have to type prefix and settings?
22503049 def function_script_repr(fn,imports,prefix,settings):
22513050 name = fn.__name__
22523051 module = fn.__module__
22713070 dbprint_prefix=None
22723071
22733072
2274
2275
2276
3073 # Copy of Python 3.2 reprlib's recursive_repr but allowing extra arguments
3074 if sys.version_info.major >= 3:
3075 from threading import get_ident
3076 def recursive_repr(fillvalue='...'):
3077 'Decorator to make a repr function return fillvalue for a recursive call'
3078
3079 def decorating_function(user_function):
3080 repr_running = set()
3081
3082 def wrapper(self, *args, **kwargs):
3083 key = id(self), get_ident()
3084 if key in repr_running:
3085 return fillvalue
3086 repr_running.add(key)
3087 try:
3088 result = user_function(self, *args, **kwargs)
3089 finally:
3090 repr_running.discard(key)
3091 return result
3092 return wrapper
3093
3094 return decorating_function
3095 else:
3096 def recursive_repr(fillvalue='...'):
3097 def decorating_function(user_function):
3098 return user_function
3099 return decorating_function
22773100
22783101
22793102 @add_metaclass(ParameterizedMetaclass)
23203143 see documentation for the 'logging' module.
23213144 """
23223145
2323 name = String(default=None,constant=True,doc="""
2324 String identifier for this object.""")
2325
2326
2327 def __init__(self,**params):
3146 name = String(default=None, constant=True, doc="""
3147 String identifier for this object.""")
3148
3149 def __init__(self, **params):
23283150 global object_count
23293151
23303152 # Flag that can be tested to see if e.g. constant Parameters
23313153 # can still be set
2332 self.initialized=False
2333 # Override class level param namespace with instance namespace
2334 self.param = Parameters(self.__class__, self=self)
3154 self.initialized = False
3155 self._parameters_state = {
3156 "BATCH_WATCH": False, # If true, Event and watcher objects are queued.
3157 "TRIGGER": False,
3158 "events": [], # Queue of batched events
3159 "watchers": [] # Queue of batched watchers
3160 }
23353161 self._instance__params = {}
23363162 self._param_watchers = {}
3163 self._dynamic_watchers = defaultdict(list)
23373164
23383165 self.param._generate_name()
23393166 self.param._setup_params(**params)
23403167 object_count += 1
23413168
2342 # add watched dependencies
2343 for cls in classlist(self.__class__):
2344 if not issubclass(cls, Parameterized):
2345 continue
2346 for n, queued in cls.param._depends['watch']:
2347 # TODO: should improve this - will happen for every
2348 # instantiation of Parameterized with watched deps. Will
2349 # probably store expanded deps on class - see metaclass
2350 # 'dependers'.
2351 for p in self.param.params_depended_on(n):
2352 # TODO: can't remember why not just pass m (rather than _m_caller) here
2353 (p.inst or p.cls).param.watch(_m_caller(self, n), p.name, p.what, queued=queued)
2354
2355 self.initialized=True
3169 self.param._update_deps(init=True)
3170
3171 self.initialized = True
3172
3173 @property
3174 def param(self):
3175 return Parameters(self.__class__, self=self)
23563176
23573177 # 'Special' methods
23583178
23623182 copy of the object's __dict__ and that also includes the
23633183 object's __slots__ (if it has any).
23643184 """
2365 # remind me, why is it a copy? why not just state.update(self.__dict__)?
3185 # Unclear why this is a copy and not simply state.update(self.__dict__)
23663186 state = self.__dict__.copy()
2367
23683187 for slot in get_occupied_slots(self):
23693188 state[slot] = getattr(self,slot)
23703189
23853204 """
23863205 self.initialized=False
23873206
3207 # When making a copy the internal watchers have to be
3208 # recreated and point to the new instance
3209 if '_param_watchers' in state:
3210 param_watchers = state['_param_watchers']
3211 for p, attrs in param_watchers.items():
3212 for attr, watchers in attrs.items():
3213 new_watchers = []
3214 for watcher in watchers:
3215 watcher_args = list(watcher)
3216 if watcher.inst is not None:
3217 watcher_args[0] = self
3218 fn = watcher.fn
3219 if hasattr(fn, '_watcher_name'):
3220 watcher_args[2] = _m_caller(self, fn._watcher_name)
3221 elif get_method_owner(fn) is watcher.inst:
3222 watcher_args[2] = getattr(self, fn.__name__)
3223 new_watchers.append(Watcher(*watcher_args))
3224 param_watchers[p][attr] = new_watchers
3225
23883226 if '_instance__params' not in state:
23893227 state['_instance__params'] = {}
23903228 if '_param_watchers' not in state:
23913229 state['_param_watchers'] = {}
3230 state.pop('param', None)
23923231
23933232 for name,value in state.items():
23943233 setattr(self,name,value)
23953234 self.initialized=True
23963235
3236 @recursive_repr()
23973237 def __repr__(self):
23983238 """
23993239 Provide a nearly valid Python representation that could be used to recreate
24043244 """
24053245 try:
24063246 settings = ['%s=%s' % (name, repr(val))
2407 for name,val in self.param.get_param_values()]
3247 # PARAM2_DEPRECATION: Update to self.param.values.items()
3248 # (once python2 support is dropped)
3249 for name, val in self.param.get_param_values()]
24083250 except RuntimeError: # Handle recursion in parameter depth
24093251 settings = []
24103252 return self.__class__.__name__ + "(" + ", ".join(settings) + ")"
24143256 return "<%s %s>" % (self.__class__.__name__,self.name)
24153257
24163258
3259 # PARAM2_DEPRECATION: Remove this compatibility alias for param 2.0 and later; use self.param.pprint instead
24173260 def script_repr(self,imports=[],prefix=" "):
24183261 """
2419 Variant of __repr__ designed for generating a runnable script.
3262 Deprecated variant of __repr__ designed for generating a runnable script.
24203263 """
24213264 return self.pprint(imports,prefix, unknown_value=None, qualify=True,
24223265 separator="\n")
24233266
2424 # CEBALERT: not yet properly documented
2425 def pprint(self, imports=None, prefix=" ", unknown_value='<?>',
3267 @recursive_repr()
3268 def _pprint(self, imports=None, prefix=" ", unknown_value='<?>',
24263269 qualify=False, separator=""):
24273270 """
24283271 (Experimental) Pretty printed representation that may be
24293272 evaluated with eval. See pprint() function for more details.
24303273 """
24313274 if imports is None:
2432 imports = []
2433
2434 # CEBALERT: imports should just be a set rather than a list;
2435 # change in next release?
3275 imports = [] # would have been simpler to use a set from the start
24363276 imports[:] = list(set(imports))
3277
24373278 # Generate import statement
24383279 mod = self.__module__
24393280 bits = mod.split('.')
24403281 imports.append("import %s"%mod)
24413282 imports.append("import %s"%bits[0])
24423283
2443 changed_params = dict(self.param.get_param_values(onlychanged=script_repr_suppress_defaults))
2444 values = dict(self.param.get_param_values())
2445 spec = inspect.getargspec(self.__init__)
3284 changed_params = self.param.values(onlychanged=script_repr_suppress_defaults)
3285 values = self.param.values()
3286 spec = getfullargspec(self.__init__)
24463287 args = spec.args[1:] if spec.args[0] == 'self' else spec.args
24473288
24483289 if spec.defaults is not None:
24863327 if k in posargs:
24873328 # value will be unknown_value unless k is a parameter
24883329 arglist.append(value)
2489 elif k in kwargs or (spec.keywords is not None):
3330 elif (k in kwargs or
3331 (hasattr(spec, 'varkw') and (spec.varkw is not None)) or
3332 (hasattr(spec, 'keywords') and (spec.keywords is not None))):
24903333 # Explicit modified keywords or parameters in
24913334 # precendence order (if **kwargs present)
24923335 keywords.append('%s=%s' % (k, value))
24973340 arguments = arglist + keywords + (['**%s' % spec.varargs] if spec.varargs else [])
24983341 return qualifier + '%s(%s)' % (self.__class__.__name__, (','+separator+prefix).join(arguments))
24993342
2500
2501 # CEBALERT: note there's no state_push method on the class, so
3343 # PARAM2_DEPRECATION: Backwards compatibilitity for param<1.12
3344 pprint = _pprint
3345
3346 # Note that there's no state_push method on the class, so
25023347 # dynamic parameters set on a class can't have state saved. This
25033348 # is because, to do this, state_push() would need to be a
25043349 # @bothmethod, but that complicates inheritance in cases where we
2505 # already have a state_push() method. I need to decide what to do
2506 # about that. (isinstance(g,Parameterized) below is used to exclude classes.)
3350 # already have a state_push() method.
3351 # (isinstance(g,Parameterized) below is used to exclude classes.)
25073352
25083353 def state_push(self):
25093354 """
26443489
26453490
26463491
2647 # Note that with Python 2.6, a fn's **args no longer has to be a
3492 # As of Python 2.6+, a fn's **args no longer has to be a
26483493 # dictionary. This might allow us to use a decorator to simplify using
26493494 # ParamOverrides (if that does indeed make them simpler to use).
26503495 # http://docs.python.org/whatsnew/2.6.html
26713516 supplied dict_ that are not also parameters of the overridden
26723517 object will be available via the extra_keywords() method.
26733518 """
2674 # we'd like __init__ to be fast because it's going to be
2675 # called a lot. What's the fastest way to move the existing
2676 # params dictionary into this one? Would
3519 # This method should be fast because it's going to be
3520 # called a lot. This _might_ be faster (not tested):
26773521 # def __init__(self,overridden,**kw):
26783522 # ...
26793523 # dict.__init__(self,**kw)
2680 # be faster/easier to use?
26813524 self._overridden = overridden
26823525 dict.__init__(self,dict_)
26833526
26893532 def extra_keywords(self):
26903533 """
26913534 Return a dictionary containing items from the originally
2692 supplied dict_ whose names are not parameters of the
3535 supplied `dict_` whose names are not parameters of the
26933536 overridden object.
26943537 """
26953538 return self._extra_keywords
26973540 def param_keywords(self):
26983541 """
26993542 Return a dictionary containing items from the originally
2700 supplied dict_ whose names are parameters of the
3543 supplied `dict_` whose names are parameters of the
27013544 overridden object (i.e. not extra keywords/parameters).
27023545 """
27033546 return dict((key, self[key]) for key in self if key not in self.extra_keywords())
27543597 for name, val in params.items():
27553598 if name not in overridden_object_params:
27563599 extra_keywords[name]=val
2757 # CEBALERT: should we remove name from params
2758 # (i.e. del params[name]) so that it's only available
2759 # via extra_keywords()?
3600 # Could remove name from params (i.e. del params[name])
3601 # so that it's only available via extra_keywords()
27603602 return extra_keywords
27613603
27623604
27773619 """
27783620 __abstract = True
27793621
2780 # CEBALERT: shouldn't this have come from a parent class
2781 # somewhere?
27823622 def __str__(self):
27833623 return self.__class__.__name__+"()"
27843624
27933633 cls = self_or_cls
27943634 else:
27953635 p = params
2796 params = dict(self_or_cls.get_param_values())
3636 params = self_or_cls.param.values()
27973637 params.update(p)
27983638 params.pop('name')
27993639 cls = self_or_cls.__class__
28173657 # Control reconstruction (during unpickling and copying):
28183658 # ensure that ParameterizedFunction.__new__ is skipped
28193659 state = ParameterizedFunction.__getstate__(self)
2820 # CB: here it's necessary to use a function defined at the
3660 # Here it's necessary to use a function defined at the
28213661 # module level rather than Parameterized.__new__ directly
28223662 # because otherwise pickle will find .__new__'s module to be
2823 # __main__. Pretty obscure aspect of pickle.py, or a bug?
3663 # __main__. Pretty obscure aspect of pickle.py...
28243664 return (_new_parameterized,(self.__class__,),state)
28253665
3666 # PARAM2_DEPRECATION: Remove this compatibility alias for param 2.0 and later; use self.param.pprint instead
28263667 def script_repr(self,imports=[],prefix=" "):
28273668 """
28283669 Same as Parameterized.script_repr, except that X.classname(Y
28323673 separator="\n")
28333674
28343675
2835 def pprint(self, imports=None, prefix="\n ",unknown_value='<?>',
2836 qualify=False, separator=""):
2837 """
2838 Same as Parameterized.pprint, except that X.classname(Y
3676 def _pprint(self, imports=None, prefix="\n ",unknown_value='<?>',
3677 qualify=False, separator=""):
3678 """
3679 Same as Parameterized._pprint, except that X.classname(Y
28393680 is replaced with X.classname.instance(Y
28403681 """
2841 r = Parameterized.pprint(self,imports,prefix,
2842 unknown_value=unknown_value,
2843 qualify=qualify,separator=separator)
3682 r = Parameterized._pprint(self,imports,prefix,
3683 unknown_value=unknown_value,
3684 qualify=qualify,separator=separator)
28443685 classname=self.__class__.__name__
28453686 return r.replace(".%s("%classname,".%s.instance("%classname)
28463687
28733714 label_formatter = default_label_formatter
28743715
28753716
2876 # CBENHANCEMENT: should be able to remove overridable_property when we
2877 # switch to Python 2.6:
2878 # "Properties now have three attributes, getter, setter and deleter,
2879 # that are decorators providing useful shortcuts for adding a getter,
2880 # setter or deleter function to an existing property."
2881 # http://docs.python.org/whatsnew/2.6.html
2882
2883 # Renamed & documented version of OProperty from
3717 # PARAM2_DEPRECATION: Should be able to remove this; was originally
3718 # adapted from OProperty from
28843719 # infinitesque.net/articles/2005/enhancing%20Python's%20property.xhtml
3720 # but since python 2.6 the getter, setter, and deleter attributes of
3721 # a property should provide similar functionality already.
28853722 class overridable_property(object):
28863723 """
28873724 The same as Python's "property" attribute, but allows the accessor
0 """
1 Classes used to support string serialization of Parameters and
2 Parameterized objects.
3 """
4
5 import json
6 import textwrap
7
8 class UnserializableException(Exception):
9 pass
10
11 class UnsafeserializableException(Exception):
12 pass
13
14 def JSONNullable(json_type):
15 "Express a JSON schema type as nullable to easily support Parameters that allow_None"
16 return {'anyOf': [ json_type, {'type': 'null'}] }
17
18
19
20 class Serialization(object):
21 """
22 Base class used to implement different types of serialization.
23 """
24
25 @classmethod
26 def schema(cls, pobj, subset=None):
27 raise NotImplementedError # noqa: unimplemented method
28
29 @classmethod
30 def serialize_parameters(cls, pobj, subset=None):
31 """
32 Serialize the parameters on a Parameterized object into a
33 single serialized object, e.g. a JSON string.
34 """
35 raise NotImplementedError # noqa: unimplemented method
36
37 @classmethod
38 def deserialize_parameters(cls, pobj, serialized, subset=None):
39 """
40 Deserialize a serialized object representing one or
41 more Parameters into a dictionary of parameter values.
42 """
43 raise NotImplementedError # noqa: unimplemented method
44
45 @classmethod
46 def serialize_parameter_value(cls, pobj, pname):
47 """
48 Serialize a single parameter value.
49 """
50 raise NotImplementedError # noqa: unimplemented method
51
52 @classmethod
53 def deserialize_parameter_value(cls, pobj, pname, value):
54 """
55 Deserialize a single parameter value.
56 """
57 raise NotImplementedError # noqa: unimplemented method
58
59
60 class JSONSerialization(Serialization):
61 """
62 Class responsible for specifying JSON serialization, deserialization
63 and JSON schemas for Parameters and Parameterized classes and
64 objects.
65 """
66
67 unserializable_parameter_types = ['Callable']
68
69 json_schema_literal_types = {
70 int:'integer', float:'number', str:'string',
71 type(None): 'null'
72 }
73
74 @classmethod
75 def loads(cls, serialized):
76 return json.loads(serialized)
77
78 @classmethod
79 def dumps(cls, obj):
80 return json.dumps(obj)
81
82 @classmethod
83 def schema(cls, pobj, safe=False, subset=None):
84 schema = {}
85 for name, p in pobj.param.objects('existing').items():
86 if subset is not None and name not in subset:
87 continue
88 schema[name] = p.schema(safe=safe)
89 if p.doc:
90 schema[name]['description'] = textwrap.dedent(p.doc).replace('\n', ' ').strip()
91 if p.label:
92 schema[name]['title'] = p.label
93 return schema
94
95 @classmethod
96 def serialize_parameters(cls, pobj, subset=None):
97 components = {}
98 for name, p in pobj.param.objects('existing').items():
99 if subset is not None and name not in subset:
100 continue
101 value = pobj.param.get_value_generator(name)
102 components[name] = p.serialize(value)
103 return cls.dumps(components)
104
105 @classmethod
106 def deserialize_parameters(cls, pobj, serialization, subset=None):
107 deserialized = cls.loads(serialization)
108 components = {}
109 for name, value in deserialized.items():
110 if subset is not None and name not in subset:
111 continue
112 deserialized = pobj.param[name].deserialize(value)
113 components[name] = deserialized
114 return components
115
116 # Parameter level methods
117
118 @classmethod
119 def _get_method(cls, ptype, suffix):
120 "Returns specialized method if available, otherwise None"
121 method_name = ptype.lower()+'_' + suffix
122 return getattr(cls, method_name, None)
123
124 @classmethod
125 def param_schema(cls, ptype, p, safe=False, subset=None):
126 if ptype in cls.unserializable_parameter_types:
127 raise UnserializableException
128 dispatch_method = cls._get_method(ptype, 'schema')
129 if dispatch_method:
130 schema = dispatch_method(p, safe=safe)
131 else:
132 schema = {'type': ptype.lower()}
133 return JSONNullable(schema) if p.allow_None else schema
134
135 @classmethod
136 def serialize_parameter_value(cls, pobj, pname):
137 value = pobj.param.get_value_generator(pname)
138 return cls.dumps(pobj.param[pname].serialize(value))
139
140 @classmethod
141 def deserialize_parameter_value(cls, pobj, pname, value):
142 value = cls.loads(value)
143 return pobj.param[pname].deserialize(value)
144
145 # Custom Schemas
146
147 @classmethod
148 def class__schema(cls, class_, safe=False):
149 from .parameterized import Parameterized
150 if isinstance(class_, tuple):
151 return {'anyOf': [cls.class__schema(cls_) for cls_ in class_]}
152 elif class_ in cls.json_schema_literal_types:
153 return {'type': cls.json_schema_literal_types[class_]}
154 elif issubclass(class_, Parameterized):
155 return {'type': 'object', 'properties': class_.param.schema(safe)}
156 else:
157 return {'type': 'object'}
158
159 @classmethod
160 def array_schema(cls, p, safe=False):
161 if safe is True:
162 msg = ('Array is not guaranteed to be safe for '
163 'serialization as the dtype is unknown')
164 raise UnsafeserializableException(msg)
165 return {'type': 'array'}
166
167 @classmethod
168 def classselector_schema(cls, p, safe=False):
169 return cls.class__schema(p.class_, safe=safe)
170
171 @classmethod
172 def dict_schema(cls, p, safe=False):
173 if safe is True:
174 msg = ('Dict is not guaranteed to be safe for '
175 'serialization as the key and value types are unknown')
176 raise UnsafeserializableException(msg)
177 return {'type': 'object'}
178
179 @classmethod
180 def date_schema(cls, p, safe=False):
181 return {'type': 'string', 'format': 'date-time'}
182
183 @classmethod
184 def calendardate_schema(cls, p, safe=False):
185 return {'type': 'string', 'format': 'date'}
186
187 @classmethod
188 def tuple_schema(cls, p, safe=False):
189 schema = {'type': 'array'}
190 if p.length is not None:
191 schema['minItems'] = p.length
192 schema['maxItems'] = p.length
193 return schema
194
195 @classmethod
196 def number_schema(cls, p, safe=False):
197 schema = {'type': p.__class__.__name__.lower() }
198 return cls.declare_numeric_bounds(schema, p.bounds, p.inclusive_bounds)
199
200 @classmethod
201 def declare_numeric_bounds(cls, schema, bounds, inclusive_bounds):
202 "Given an applicable numeric schema, augment with bounds information"
203 if bounds is not None:
204 (low, high) = bounds
205 if low is not None:
206 key = 'minimum' if inclusive_bounds[0] else 'exclusiveMinimum'
207 schema[key] = low
208 if high is not None:
209 key = 'maximum' if inclusive_bounds[1] else 'exclusiveMaximum'
210 schema[key] = high
211 return schema
212
213 @classmethod
214 def integer_schema(cls, p, safe=False):
215 return cls.number_schema(p)
216
217 @classmethod
218 def numerictuple_schema(cls, p, safe=False):
219 schema = cls.tuple_schema(p, safe=safe)
220 schema['additionalItems'] = {'type': 'number'}
221 return schema
222
223 @classmethod
224 def xycoordinates_schema(cls, p, safe=False):
225 return cls.numerictuple_schema(p, safe=safe)
226
227 @classmethod
228 def range_schema(cls, p, safe=False):
229 schema = cls.tuple_schema(p, safe=safe)
230 bounded_number = cls.declare_numeric_bounds(
231 {'type': 'number'}, p.bounds, p.inclusive_bounds)
232 schema['additionalItems'] = bounded_number
233 return schema
234
235 @classmethod
236 def list_schema(cls, p, safe=False):
237 schema = {'type': 'array'}
238 if safe is True and p.item_type is None:
239 msg = ('List without a class specified cannot be guaranteed '
240 'to be safe for serialization')
241 raise UnsafeserializableException(msg)
242 if p.class_ is not None:
243 schema['items'] = cls.class__schema(p.item_type, safe=safe)
244 return schema
245
246 @classmethod
247 def objectselector_schema(cls, p, safe=False):
248 try:
249 allowed_types = [{'type': cls.json_schema_literal_types[type(obj)]}
250 for obj in p.objects]
251 schema = {'anyOf': allowed_types}
252 schema['enum'] = p.objects
253 return schema
254 except:
255 if safe is True:
256 msg = ('ObjectSelector cannot be guaranteed to be safe for '
257 'serialization due to unserializable type in objects')
258 raise UnsafeserializableException(msg)
259 return {}
260
261 @classmethod
262 def selector_schema(cls, p, safe=False):
263 try:
264 allowed_types = [{'type': cls.json_schema_literal_types[type(obj)]}
265 for obj in p.objects.values()]
266 schema = {'anyOf': allowed_types}
267 schema['enum'] = p.objects
268 return schema
269 except:
270 if safe is True:
271 msg = ('Selector cannot be guaranteed to be safe for '
272 'serialization due to unserializable type in objects')
273 raise UnsafeserializableException(msg)
274 return {}
275
276 @classmethod
277 def listselector_schema(cls, p, safe=False):
278 if p.objects is None:
279 if safe is True:
280 msg = ('ListSelector cannot be guaranteed to be safe for '
281 'serialization as allowed objects unspecified')
282 return {'type': 'array'}
283 for obj in p.objects:
284 if type(obj) not in cls.json_schema_literal_types:
285 msg = 'ListSelector cannot serialize type %s' % type(obj)
286 raise UnserializableException(msg)
287 return {'type': 'array', 'items': {'enum': p.objects}}
288
289 @classmethod
290 def dataframe_schema(cls, p, safe=False):
291 schema = {'type': 'array'}
292 if safe is True:
293 msg = ('DataFrame is not guaranteed to be safe for '
294 'serialization as the column dtypes are unknown')
295 raise UnsafeserializableException(msg)
296 if p.columns is None:
297 schema['items'] = {'type': 'object'}
298 return schema
299
300 mincols, maxcols = None, None
301 if isinstance(p.columns, int):
302 mincols, maxcols = p.columns, p.columns
303 elif isinstance(p.columns, tuple):
304 mincols, maxcols = p.columns
305
306 if isinstance(p.columns, int) or isinstance(p.columns, tuple):
307 schema['items'] = {'type': 'object', 'minItems': mincols,
308 'maxItems': maxcols}
309
310 if isinstance(p.columns, list) or isinstance(p.columns, set):
311 literal_types = [{'type':el} for el in cls.json_schema_literal_types.values()]
312 allowable_types = {'anyOf': literal_types}
313 properties = {name: allowable_types for name in p.columns}
314 schema['items'] = {'type': 'object', 'properties': properties}
315
316 minrows, maxrows = None, None
317 if isinstance(p.rows, int):
318 minrows, maxrows = p.rows, p.rows
319 elif isinstance(p.rows, tuple):
320 minrows, maxrows = p.rows
321
322 if minrows is not None:
323 schema['minItems'] = minrows
324 if maxrows is not None:
325 schema['maxItems'] = maxrows
326
327 return schema
11 Provide consistent and up-to-date ``__version__`` strings for
22 Python packages.
33
4 See https://github.com/pyviz/autover for more information.
4 See https://github.com/holoviz/autover for more information.
55 """
66
77 # The Version class is a copy of autover.version.Version v0.2.5,
2525 cwd=cwd)
2626 output, error = (str(s.decode()).strip() for s in proc.communicate())
2727
28 if proc.returncode != 0:
28 # Detects errors as _either_ a non-zero return code _or_ messages
29 # printed to stderr, because the return code is erroneously fixed at
30 # zero in some cases (see https://github.com/holoviz/param/pull/389).
31 if proc.returncode != 0 or len(error) > 0:
2932 raise Exception(proc.returncode, error)
3033 return output
3134
173176 # Verify this is the correct repository (since fpath could
174177 # be an unrelated git repository, and autover could just have
175178 # been copied/installed into it).
176 output = run_cmd([cmd, 'remote', '-v'],
177 cwd=os.path.dirname(self.fpath))
178 repo_matches = ['', # No remote set
179 '/' + self.reponame + '.git' ,
179 remotes = run_cmd([cmd, 'remote', '-v'],
180 cwd=os.path.dirname(self.fpath))
181 repo_matches = ['/' + self.reponame + '.git' ,
180182 # A remote 'server:reponame.git' can also be referred
181183 # to (i.e. cloned) as `server:reponame`.
182184 '/' + self.reponame + ' ']
183 if not any(m in output for m in repo_matches):
184 return self
185
186 output = run_cmd([cmd, 'describe', '--long', '--match',
187 "v[0-9]*.[0-9]*.[0-9]*", '--dirty'],
188 cwd=os.path.dirname(self.fpath))
185 if not any(m in remotes for m in repo_matches):
186 try:
187 output = self._output_from_file()
188 if output is not None:
189 self._update_from_vcs(output)
190 except: pass
191 if output is None:
192 # glob pattern (not regexp) matching vX.Y.Z* tags
193 output = run_cmd([cmd, 'describe', '--long', '--match',
194 "v[0-9]*.[0-9]*.[0-9]*", '--dirty'],
195 cwd=os.path.dirname(self.fpath))
189196 if as_string: return output
190197 except Exception as e1:
191198 try:
0 Metadata-Version: 2.1
1 Name: param
2 Version: None
3 Summary: Make your Python code clearer and more reliable by declaring Parameters.
4 Home-page: http://param.holoviz.org/
5 Author: HoloViz
6 Author-email: developers@holoviz.org
7 Maintainer: HoloViz
8 Maintainer-email: developers@holoviz.org
9 License: BSD
10 Project-URL: Documentation, https://param.holoviz.org/
11 Project-URL: Releases, https://github.com/holoviz/param/releases
12 Project-URL: Bug Tracker, https://github.com/holoviz/param/issues
13 Project-URL: Source Code, https://github.com/holoviz/param
14 Project-URL: Panel Examples, https://panel.holoviz.org/user_guide/Param.html
15 Platform: Windows
16 Platform: Mac OS X
17 Platform: Linux
18 Classifier: License :: OSI Approved :: BSD License
19 Classifier: Development Status :: 5 - Production/Stable
20 Classifier: Programming Language :: Python :: 2
21 Classifier: Programming Language :: Python :: 2.7
22 Classifier: Programming Language :: Python :: 3
23 Classifier: Programming Language :: Python :: 3.6
24 Classifier: Programming Language :: Python :: 3.7
25 Classifier: Programming Language :: Python :: 3.8
26 Classifier: Programming Language :: Python :: 3.9
27 Classifier: Programming Language :: Python :: 3.10
28 Classifier: Operating System :: OS Independent
29 Classifier: Intended Audience :: Science/Research
30 Classifier: Intended Audience :: Developers
31 Classifier: Natural Language :: English
32 Classifier: Topic :: Scientific/Engineering
33 Classifier: Topic :: Software Development :: Libraries
34 Provides: param
35 Provides: numbergen
36 Requires-Python: >=2.7
37 Description-Content-Type: text/markdown
38 Provides-Extra: all
39 Provides-Extra: doc
40 Provides-Extra: tests
41 License-File: LICENSE.txt
42
43 <img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250>
44
45 | | |
46 | --- | --- |
47 | Build Status | [![Linux/MacOS/Windows Build Status](https://github.com/holoviz/param/workflows/pytest/badge.svg)](https://github.com/holoviz/param/actions/workflows/test.yml)
48 | Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/master/graph/badge.svg)](https://codecov.io/gh/holoviz/param) ||
49 | Latest dev release | [![Github tag](https://img.shields.io/github/v/tag/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/tags) [![dev-site](https://img.shields.io/website-up-down-green-red/https/pyviz-dev.github.io/param.svg?label=dev%20website)](https://pyviz-dev.github.io/param/) |
50 | Latest release | [![Github release](https://img.shields.io/github/release/holoviz/param.svg?label=tag&colorB=11ccbb)](https://github.com/holoviz/param/releases) [![PyPI version](https://img.shields.io/pypi/v/param.svg?colorB=cc77dd)](https://pypi.python.org/pypi/param) [![param version](https://img.shields.io/conda/v/pyviz/param.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/param) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/param.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/param) [![defaults version](https://img.shields.io/conda/v/anaconda/param.svg?label=conda%7Cdefaults&style=flat&colorB=4488ff)](https://anaconda.org/anaconda/param) |
51 | Python | [![Python support](https://img.shields.io/pypi/pyversions/param.svg)](https://pypi.org/project/param/)
52 | Docs | [![gh-pages](https://img.shields.io/github/last-commit/holoviz/param/gh-pages.svg)](https://github.com/holoviz/param/tree/gh-pages) [![site](https://img.shields.io/website-up-down-green-red/https/param.holoviz.org.svg)](https://param.holoviz.org) |
53 | Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/master?labpath=examples) |
54 | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) |
55
56 Param is a library providing Parameters: Python attributes extended to have features such as type and range checking, dynamically generated values, documentation strings, default values, etc., each of which is inherited from parent classes if not specified in a subclass.
57
58 Param contains only two required Python files, with no external dependencies, and is provided freely for both non-commercial and commercial use under a BSD license, so that it can easily be included as part of other projects.
59
60 Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples.
61
62
0 LICENSE.txt
1 MANIFEST.in
2 README.md
3 setup.cfg
4 setup.py
5 numbergen/__init__.py
6 param/.version
7 param/__init__.py
8 param/ipython.py
9 param/parameterized.py
10 param/serializer.py
11 param/version.py
12 param.egg-info/PKG-INFO
13 param.egg-info/SOURCES.txt
14 param.egg-info/dependency_links.txt
15 param.egg-info/requires.txt
16 param.egg-info/top_level.txt
0
1 [all]
2 aiohttp
3 flake8
4 graphviz
5 jinja2<3.1
6 myst-parser
7 myst_nb==0.12.2
8 nbconvert
9 nbsite>=0.7.1
10 pandas
11 panel
12 pydata-sphinx-theme
13 pygraphviz
14 pytest
15 pytest-cov
16 sphinx-copybutton
17
18 [doc]
19 aiohttp
20 graphviz
21 jinja2<3.1
22 myst-parser
23 myst_nb==0.12.2
24 nbconvert
25 nbsite>=0.7.1
26 pandas
27 panel
28 pydata-sphinx-theme
29 pygraphviz
30 sphinx-copybutton
31
32 [tests]
33 flake8
34 pytest
35 pytest-cov
11 universal = 1
22
33 [flake8]
4 # TODO tests (one day)
54 include = setup.py param numbergen
6 exclude = .git,__pycache__,.tox,.eggs,*.egg,doc,dist,build,_build,tests
7 ignore = E1,
8 E2,
9 E3,
10 E4,
11 E5,
12 E701,
13 E702,
14 E703,
15 E704,
16 E722,
17 E741,
18 E742,
19 E743,
20 W503
5 exclude = .git,__pycache__,.tox,.eggs,*.egg,doc,dist,build,_build,tests,.ipynb_checkpoints,run_test.py
6 ignore = E114,
7 E116,
8 E126,
9 E128,
10 E129,
11 E2,
12 E3,
13 E4,
14 E5,
15 E731,
16 E701,
17 E702,
18 E703,
19 E704,
20 E722,
21 E741,
22 E742,
23 E743,
24 W503,
25 W504,
2126
22 [nosetests]
23 verbosity = 2
24 with-doctest = 1
25 nologcapture = 1
27 [tool:pytest]
28 python_files = test*.py
29
30 [egg_info]
31 tag_build =
32 tag_date = 0
33
1818 # pip doesn't support tests_require
1919 # (https://github.com/pypa/pip/issues/1197)
2020 'tests': [
21 'nose',
22 'flake8'
21 'pytest',
22 'pytest-cov',
23 'flake8',
24 ],
25 'doc': [
26 'pygraphviz',
27 'nbsite >=0.7.1',
28 'pydata-sphinx-theme',
29 'jinja2 <3.1', # API breakage
30 'myst-parser',
31 'nbconvert',
32 'graphviz',
33 'myst_nb ==0.12.2',
34 'sphinx-copybutton',
35 'aiohttp',
36 'panel',
37 'pandas',
2338 ]
2439 }
2540
3146 setup_args = dict(
3247 name='param',
3348 version=get_setup_version("param"),
34 description='Declarative Python programming using Parameters.',
35 long_description=open('README.rst').read() if os.path.isfile('README.rst') else 'Consult README.rst',
36 author="IOAM",
37 author_email="developers@topographica.org",
38 maintainer="IOAM",
39 maintainer_email="developers@topographica.org",
49 description='Make your Python code clearer and more reliable by declaring Parameters.',
50 long_description=open('README.md').read() if os.path.isfile('README.md') else 'Consult README.md',
51 long_description_content_type="text/markdown",
52 author="HoloViz",
53 author_email="developers@holoviz.org",
54 maintainer="HoloViz",
55 maintainer_email="developers@holoviz.org",
4056 platforms=['Windows', 'Mac OS X', 'Linux'],
4157 license='BSD',
42 url='http://ioam.github.com/param/',
58 url='http://param.holoviz.org/',
4359 packages=["param","numbergen"],
4460 provides=["param","numbergen"],
4561 include_package_data = True,
4763 install_requires=[],
4864 extras_require=extras_require,
4965 tests_require=extras_require['tests'],
66 project_urls={
67 "Documentation": "https://param.holoviz.org/",
68 "Releases": "https://github.com/holoviz/param/releases",
69 "Bug Tracker": "https://github.com/holoviz/param/issues",
70 "Source Code": "https://github.com/holoviz/param",
71 "Panel Examples": "https://panel.holoviz.org/user_guide/Param.html",
72 },
5073 classifiers=[
5174 "License :: OSI Approved :: BSD License",
5275 "Development Status :: 5 - Production/Stable",
5376 "Programming Language :: Python :: 2",
5477 "Programming Language :: Python :: 2.7",
5578 "Programming Language :: Python :: 3",
56 "Programming Language :: Python :: 3.4",
57 "Programming Language :: Python :: 3.5",
5879 "Programming Language :: Python :: 3.6",
80 "Programming Language :: Python :: 3.7",
81 "Programming Language :: Python :: 3.8",
82 "Programming Language :: Python :: 3.9",
83 "Programming Language :: Python :: 3.10",
5984 "Operating System :: OS Independent",
6085 "Intended Audience :: Science/Research",
6186 "Intended Audience :: Developers",
+0
-0
tests/API0/__init__.py less more
(Empty file)
+0
-53
tests/API0/testcalendardateparam.py less more
0 """
1 Unit test for CalendarDate parameters.
2 """
3
4 import unittest
5 import datetime as dt
6 import param
7
8
9 class TestDateTimeParameters(unittest.TestCase):
10
11 def test_initialization_out_of_bounds(self):
12 try:
13 class Q(param.Parameterized):
14 q = param.Date(dt.date(2017,2,27),
15 bounds=(dt.date(2017,2,1),
16 dt.date(2017,2,26)))
17 except ValueError:
18 pass
19 else:
20 raise AssertionError("No exception raised on out-of-bounds date")
21
22 def test_set_out_of_bounds(self):
23 class Q(param.Parameterized):
24 q = param.Date(bounds=(dt.date(2017,2,1),
25 dt.date(2017,2,26)))
26 try:
27 Q.q = dt.date(2017,2,27)
28 except ValueError:
29 pass
30 else:
31 raise AssertionError("No exception raised on out-of-bounds date")
32
33 def test_set_exclusive_out_of_bounds(self):
34 class Q(param.Parameterized):
35 q = param.Date(bounds=(dt.date(2017,2,1),
36 dt.date(2017,2,26)),
37 inclusive_bounds=(True, False))
38 try:
39 Q.q = dt.date(2017,2,26)
40 except ValueError:
41 pass
42 else:
43 raise AssertionError("No exception raised on out-of-bounds date")
44
45 def test_get_soft_bounds(self):
46 q = param.Date(dt.date(2017,2,25),
47 bounds=(dt.date(2017,2,1),
48 dt.date(2017,2,26)),
49 softbounds=(dt.date(2017,2,1),
50 dt.date(2017,2,25)))
51 self.assertEqual(q.get_soft_bounds(), (dt.date(2017,2,1),
52 dt.date(2017,2,25)))
+0
-79
tests/API0/testcalendardaterangeparam.py less more
0 """
1 Unit tests for CalendarDateRange parameter.
2 """
3
4 import unittest
5 import datetime as dt
6 import param
7
8
9 # Assuming tests of range parameter cover most of what's needed to
10 # test date range.
11
12 class TestDateTimeRange(unittest.TestCase):
13
14 bad_range = (dt.date(2017,2,27),dt.date(2017,2,26))
15
16 def test_wrong_type_default(self):
17 try:
18 class Q(param.Parameterized):
19 a = param.CalendarDateRange(default=(1.0,2.0))
20 except ValueError:
21 pass
22 else:
23 raise AssertionError("Bad date type was accepted.")
24
25 def test_wrong_type_init(self):
26 class Q(param.Parameterized):
27 a = param.CalendarDateRange()
28
29 try:
30 Q(a=self.bad_range)
31 except ValueError:
32 pass
33 else:
34 raise AssertionError("Bad date type was accepted.")
35
36 def test_wrong_type_set(self):
37 class Q(param.Parameterized):
38 a = param.CalendarDateRange()
39 q = Q()
40
41 try:
42 q.a = self.bad_range
43 except ValueError:
44 pass
45 else:
46 raise AssertionError("Bad date type was accepted.")
47
48 def test_start_before_end_default(self):
49 try:
50 class Q(param.Parameterized):
51 a = param.CalendarDateRange(default=self.bad_range)
52 except ValueError:
53 pass
54 else:
55 raise AssertionError("Bad date range was accepted.")
56
57 def test_start_before_end_init(self):
58 class Q(param.Parameterized):
59 a = param.CalendarDateRange()
60
61 try:
62 Q(a=self.bad_range)
63 except ValueError:
64 pass
65 else:
66 raise AssertionError("Bad date range was accepted.")
67
68 def test_start_before_end_set(self):
69 class Q(param.Parameterized):
70 a = param.CalendarDateRange()
71
72 q = Q()
73 try:
74 q.a = self.bad_range
75 except ValueError:
76 pass
77 else:
78 raise AssertionError("Bad date range was accepted.")
+0
-65
tests/API0/testclassselector.py less more
0 """
1 Unit test for ClassSelector parameters.
2 """
3
4 import unittest
5 from numbers import Number
6
7 import param
8
9
10 class TestClassSelectorParameters(unittest.TestCase):
11
12 def setUp(self):
13
14 class P(param.Parameterized):
15 e = param.ClassSelector(default=1,class_=int)
16 f = param.ClassSelector(default=int,class_=Number, is_instance=False)
17 g = param.ClassSelector(default=1,class_=(int,str))
18 h = param.ClassSelector(default=int,class_=(int,str), is_instance=False)
19
20 self.P = P
21
22 def test_single_class_instance_constructor(self):
23 p = self.P(e=6)
24 self.assertEqual(p.e, 6)
25
26 def test_single_class_instance_error(self):
27 exception = "Parameter 'e' value must be an instance of int, not 'a'"
28 with self.assertRaisesRegexp(ValueError, exception):
29 self.P(e='a')
30
31 def test_single_class_type_constructor(self):
32 p = self.P(f=float)
33 self.assertEqual(p.f, float)
34
35 def test_single_class_type_error(self):
36 exception = "Parameter 'str' must be a subclass of Number, not 'type'"
37 with self.assertRaisesRegexp(ValueError, exception):
38 self.P(f=str)
39
40 def test_multiple_class_instance_constructor1(self):
41 p = self.P(g=1)
42 self.assertEqual(p.g, 1)
43
44 def test_multiple_class_instance_constructor2(self):
45 p = self.P(g='A')
46 self.assertEqual(p.g, 'A')
47
48 def test_multiple_class_instance_error(self):
49 exception = "Parameter 'g' value must be an instance of \(int, str\), not '3.0'"
50 with self.assertRaisesRegexp(ValueError, exception):
51 self.P(g=3.0)
52
53 def test_multiple_class_type_constructor1(self):
54 p = self.P(h=int)
55 self.assertEqual(p.h, int)
56
57 def test_multiple_class_type_constructor2(self):
58 p = self.P(h=str)
59 self.assertEqual(p.h, str)
60
61 def test_multiple_class_type_error(self):
62 exception = "Parameter 'float' must be a subclass of \(int, str\), not 'type'"
63 with self.assertRaisesRegexp(ValueError, exception):
64 self.P(h=float)
+0
-41
tests/API0/testcolorparameter.py less more
0 """
1 Unit test for Color parameters.
2 """
3
4 import unittest
5 import param
6
7
8 class TestColorParameters(unittest.TestCase):
9
10 def test_initialization_invalid_string(self):
11 try:
12 class Q(param.Parameterized):
13 q = param.Color('red')
14 except ValueError:
15 pass
16 else:
17 raise AssertionError("No exception raised on out-of-bounds date")
18
19 def test_set_invalid_string(self):
20 class Q(param.Parameterized):
21 q = param.Color()
22 try:
23 Q.q = 'red'
24 except ValueError:
25 pass
26 else:
27 raise AssertionError("No exception raised on out-of-bounds date")
28
29 def test_valid_long_hex(self):
30 class Q(param.Parameterized):
31 q = param.Color()
32 Q.q = '#ffffff'
33 self.assertEqual(Q.q, '#ffffff')
34
35 def test_valid_short_hex(self):
36 class Q(param.Parameterized):
37 q = param.Color()
38 Q.q = '#fff'
39 self.assertEqual(Q.q, '#fff')
40
+0
-99
tests/API0/testcompositeparams.py less more
0 """
1 Unit test for composite parameters.
2
3 Originally implemented as doctests in Topographica in the file
4 testCompositeParameter.txt
5 """
6
7 import unittest
8 import param
9
10 class TestCompositeParameters(unittest.TestCase):
11
12 def setUp(self):
13 # initialize a class with a compound parameter
14 class A(param.Parameterized):
15 x = param.Number(default=0)
16 y = param.Number(default=0)
17 xy = param.Composite(attribs=['x','y'])
18
19 self.A = A
20 self.a = self.A()
21
22 class SomeSequence(object):
23 "Can't use iter with Dynamic (doesn't pickle, doesn't copy)"
24 def __init__(self,sequence):
25 self.sequence=sequence
26 self.index=0
27 def __call__(self):
28 val=self.sequence[self.index]
29 self.index+=1
30 return val
31
32 self.SomeSequence = SomeSequence
33
34 def test_initialization(self):
35 "Make an instance and do default checks"
36 self.assertEqual(self.a.x, 0)
37 self.assertEqual(self.a.y, 0)
38 self.assertEqual(self.a.xy, [0,0])
39
40
41 def test_set_component(self):
42 self.a.x = 1
43 self.assertEqual(self.a.xy, [1,0])
44
45 def test_set_compound(self):
46 self.a.xy = (2,3)
47 self.assertEqual(self.a.x, 2)
48 self.assertEqual(self.a.y, 3)
49
50 def test_compound_class(self):
51 " Get the compound on the class "
52 self.assertEqual(self.A.xy, [0,0])
53
54 def test_set_compound_class_set(self):
55 self.A.xy = (5,6)
56 self.assertEqual(self.A.x, 5)
57 self.assertEqual(self.A.y, 6)
58
59 def test_set_compound_class_instance(self):
60 self.A.xy = (5,6)
61 # # Make a new instance
62 b = self.A()
63 self.assertEqual(b.x, 5)
64 self.assertEqual(b.y, 6)
65
66 def test_set_compound_class_instance_unchanged(self):
67 self.a.xy = (2,3)
68 self.A.xy = (5,6)
69 self.assertEqual(self.a.x, 2)
70 self.assertEqual(self.a.y, 3)
71
72 def test_composite_dynamic(self):
73 """
74 Check CompositeParameter is ok with Dynamic
75 CB: this test is really of Parameterized.
76 """
77 a2 = self.A(x=self.SomeSequence([1,2,3]),
78 y=self.SomeSequence([4,5,6]))
79
80 a2.x, a2.y # Call of x and y params
81 # inspect should not advance numbers
82 self.assertEqual(a2.inspect_value('xy'), [1, 4])
83
84 def test_composite_dynamic_generator(self):
85
86 a2 = self.A(x=self.SomeSequence([1,2,3]),
87 y=self.SomeSequence([4,5,6]))
88
89 a2.x, a2.y # Call of x and y params
90 ix,iy = a2.get_value_generator('xy')
91 # get_value_generator() should give the objects
92 self.assertEqual(ix(), 2)
93 self.assertEqual(iy(), 5)
94
95
96 if __name__ == "__main__":
97 import nose
98 nose.runmodule()
+0
-54
tests/API0/testdateparam.py less more
0 """
1 Unit test for Date parameters.
2 """
3
4 import unittest
5 import datetime as dt
6 import param
7
8
9 class TestDateParameters(unittest.TestCase):
10
11 def test_initialization_out_of_bounds(self):
12 try:
13 class Q(param.Parameterized):
14 q = param.Date(dt.datetime(2017,2,27),
15 bounds=(dt.datetime(2017,2,1),
16 dt.datetime(2017,2,26)))
17 except ValueError:
18 pass
19 else:
20 raise AssertionError("No exception raised on out-of-bounds date")
21
22 def test_set_out_of_bounds(self):
23 class Q(param.Parameterized):
24 q = param.Date(bounds=(dt.datetime(2017,2,1),
25 dt.datetime(2017,2,26)))
26 try:
27 Q.q = dt.datetime(2017,2,27)
28 except ValueError:
29 pass
30 else:
31 raise AssertionError("No exception raised on out-of-bounds date")
32
33 def test_set_exclusive_out_of_bounds(self):
34 class Q(param.Parameterized):
35 q = param.Date(bounds=(dt.datetime(2017,2,1),
36 dt.datetime(2017,2,26)),
37 inclusive_bounds=(True, False))
38 try:
39 Q.q = dt.datetime(2017,2,26)
40 except ValueError:
41 pass
42 else:
43 raise AssertionError("No exception raised on out-of-bounds date")
44
45 def test_get_soft_bounds(self):
46 q = param.Date(dt.datetime(2017,2,25),
47 bounds=(dt.datetime(2017,2,1),
48 dt.datetime(2017,2,26)),
49 softbounds=(dt.datetime(2017,2,1),
50 dt.datetime(2017,2,25)))
51 self.assertEqual(q.get_soft_bounds(), (dt.datetime(2017,2,1),
52 dt.datetime(2017,2,25)))
53
+0
-81
tests/API0/testdaterangeparam.py less more
0 """
1 Unit tests for DateRange parameter.
2 """
3
4 import unittest
5 import datetime as dt
6 import param
7
8
9 # Assuming tests of range parameter cover most of what's needed to
10 # test date range.
11
12 class TestDateRange(unittest.TestCase):
13
14 bad_range = (dt.datetime(2017,2,27),dt.datetime(2017,2,26))
15
16 def test_wrong_type_default(self):
17 try:
18 class Q(param.Parameterized):
19 a = param.DateRange(default=(1.0,2.0))
20 except ValueError:
21 pass
22 else:
23 raise AssertionError("Bad date type was accepted.")
24
25 def test_wrong_type_init(self):
26 class Q(param.Parameterized):
27 a = param.DateRange()
28
29 try:
30 Q(a=self.bad_range)
31 except ValueError:
32 pass
33 else:
34 raise AssertionError("Bad date type was accepted.")
35
36 def test_wrong_type_set(self):
37 class Q(param.Parameterized):
38 a = param.DateRange()
39 q = Q()
40
41 try:
42 q.a = self.bad_range
43 except ValueError:
44 pass
45 else:
46 raise AssertionError("Bad date type was accepted.")
47
48 def test_start_before_end_default(self):
49 try:
50 class Q(param.Parameterized):
51 a = param.DateRange(default=self.bad_range)
52 except ValueError:
53 pass
54 else:
55 raise AssertionError("Bad date range was accepted.")
56
57 def test_start_before_end_init(self):
58 class Q(param.Parameterized):
59 a = param.DateRange()
60
61 try:
62 Q(a=self.bad_range)
63 except ValueError:
64 pass
65 else:
66 raise AssertionError("Bad date range was accepted.")
67
68 def test_start_before_end_set(self):
69 class Q(param.Parameterized):
70 a = param.DateRange()
71
72 q = Q()
73 try:
74 q.a = self.bad_range
75 except ValueError:
76 pass
77 else:
78 raise AssertionError("Bad date range was accepted.")
79
80
+0
-60
tests/API0/testdefaults.py less more
0 """
1 Do all subclasses of Parameter supply a valid default?
2 """
3
4 import unittest
5
6 from param.parameterized import add_metaclass
7 from param import concrete_descendents, Parameter
8
9 # import all parameter types
10 from param import ClassSelector
11 from param import * # noqa
12
13
14 positional_args = {
15 ClassSelector: (object,)
16 }
17
18 skip = []
19
20 try:
21 import numpy # noqa
22 except ImportError:
23 skip.append('Array')
24
25 try:
26 import pandas # noqa
27 except ImportError:
28 skip.append('DataFrame')
29 skip.append('Series')
30
31
32 class TestDefaultsMetaclass(type):
33 def __new__(mcs, name, bases, dict_):
34
35 def test_skip(*args,**kw):
36 from nose.exc import SkipTest
37 raise SkipTest
38
39 def add_test(p):
40 def test(self):
41 # instantiate parameter with no default (but supply
42 # any required args)
43 p(*positional_args.get(p,tuple()))
44 return test
45
46 for p_name, p_type in concrete_descendents(Parameter).items():
47 dict_["test_default_of_%s"%p_name] = add_test(p_type) if p_name not in skip else test_skip
48
49 return type.__new__(mcs, name, bases, dict_)
50
51
52 @add_metaclass(TestDefaultsMetaclass)
53 class TestDefaults(unittest.TestCase):
54 pass
55
56
57 if __name__ == "__main__":
58 import nose
59 nose.runmodule()
+0
-293
tests/API0/testdynamicparams.py less more
0 """
1 Unit test for dynamic parameters.
2
3 Tests __get__, __set__ and that inspect_value() and
4 get_value_generator() work.
5
6 Originally implemented as doctests in Topographica in the file
7 testDynamicParameter.txt
8 """
9
10 import copy
11 import unittest
12 import param
13 import numbergen
14
15
16 class TestDynamicParameters(unittest.TestCase):
17
18 def setUp(self):
19 param.Dynamic.time_dependent = False
20
21 class TestPO1(param.Parameterized):
22 x = param.Dynamic(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=1),doc="nothing")
23 y = param.Dynamic(default=1)
24
25 class TestPO2(param.Parameterized):
26 x = param.Dynamic(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=30))
27 y = param.Dynamic(default=1.0)
28
29 self.TestPO2 = TestPO2
30 self.TestPO1 = TestPO1
31
32 self.t1 = self.TestPO1()
33 self.t2 = self.TestPO1(x=numbergen.UniformRandom(lbound=-1,ubound=1,seed=10))
34 self.t3 = self.TestPO1(x=numbergen.UniformRandom(lbound=-1,ubound=1,seed=10))
35 self.t2.set_dynamic_time_fn(None)
36 self.t3.set_dynamic_time_fn(None)
37
38 self.t6 = self.TestPO2()
39 self.t7 = self.TestPO2()
40
41
42 class TestDynamicParameterBasics(TestDynamicParameters):
43
44 def test_set_dynamic_time_fn_x(self):
45 self.t1.set_dynamic_time_fn(None)
46 self.assertEqual(
47 self.t1.params()['x']._value_is_dynamic(self.t1), True)
48
49 def test_set_dynamic_time_fn_y(self):
50 self.assertEqual(
51 self.t1.params()['y']._value_is_dynamic(self.t1), False)
52
53 def test_inspect_x(self):
54 "no value generated yet"
55 self.assertEqual(self.t1.inspect_value('x'), None)
56
57 def test_inspect_y(self):
58 self.assertEqual(self.t1.inspect_value('y'), 1)
59
60 def test_inspect_y_set(self):
61 self.t1.y = 2
62 self.assertEqual(self.t1.inspect_value('y'), 2)
63
64 def test_set_dynamic_numbergen(self):
65 is_numbergen = isinstance(self.t2.get_value_generator('x'),
66 numbergen.UniformRandom)
67 self.assertEqual(is_numbergen, True)
68
69 def test_matching_numbergen_streams(self):
70 "check that t2 and t3 have identical streams"
71 self.assertEqual(self.t2.x, self.t3.x)
72
73 def test_numbergen_objects_distinct(self):
74 "check t2 and t3 do not share UniformRandom objects"
75 self.t2.x
76 self.assertNotEqual(self.t2.inspect_value('x'),
77 self.t3.inspect_value('x'))
78
79 def test_numbergen_inspect(self):
80 " inspect_value() should return last generated value "
81 self.t2.x # Call 1
82 self.t2.x # Call 2
83 t2_last_value = self.t2.x # advance t2 beyond t3
84
85 self.assertEqual(self.t2.inspect_value('x'),
86 t2_last_value)
87 # ensure last_value is not shared
88 self.assertNotEqual(self.t3.inspect_value('x'), t2_last_value)
89
90 def test_dynamic_value_instantiated(self):
91 t6_first_value = self.t6.x
92 self.assertNotEqual(self.t7.inspect_value('x'),
93 t6_first_value)
94
95 def test_non_dynamic_value_not_instantiated(self):
96 " non-dynamic value not instantiated"
97 self.TestPO2.y = 4
98 self.assertEqual(self.t6.y, 4)
99 self.assertEqual(self.t7.y, 4)
100
101 def test_dynamic_value_setting(self):
102 self.t6.y = numbergen.UniformRandom()
103 t8 = self.TestPO2()
104 self.TestPO2.y = 10
105 # t6 got a dynamic value, but shouldn't have changed Parameter's instantiate
106 self.assertEqual(t8.y, 10)
107
108 def test_setting_y_param_numbergen(self):
109 self.TestPO2.y=numbergen.UniformRandom() # now the Parameter instantiate should be true
110 t9 = self.TestPO2()
111 self.assertEqual('_y_param_value' in t9.__dict__, True)
112
113 def test_shared_numbergen(self):
114 """
115 Instances of TestPO2 that don't have their own value for the
116 parameter share one UniformRandom object
117 """
118 self.TestPO2.y=numbergen.UniformRandom() # now the Parameter instantiate should be true
119 self.assertEqual(self.t7.get_value_generator('y') is self.TestPO2().params()['y'].default, True)
120 self.assertEqual(self.TestPO2().params()['y'].default.__class__.__name__, 'UniformRandom')
121
122 def test_copy_match(self):
123 "check a copy is the same"
124 t9 = copy.deepcopy(self.t7)
125 self.assertEqual(t9.get_value_generator('y') is self.TestPO2().params()['y'].default, True)
126
127
128
129 class TestDynamicTimeDependent(TestDynamicParameters):
130
131 def setUp(self):
132 super(TestDynamicTimeDependent, self).setUp()
133 param.Dynamic.time_dependent = True
134
135 class TestPO3(param.Parameterized):
136 x = param.Dynamic(default=numbergen.UniformRandom(name='xgen',
137 time_dependent=True))
138
139 class TestPO4(self.TestPO1):
140 "Nested parameterized objects"
141 z = param.Parameter(default=self.TestPO1())
142
143 self.TestPO3 = TestPO3
144 self.TestPO4 = TestPO4
145
146 self.t10 = self.TestPO1()
147 self.t11 = TestPO3()
148
149 def test_dynamic_values_unchanged_dependent(self):
150 param.Dynamic.time_dependent = True
151 call_1 = self.t10.x
152 call_2 = self.t10.x
153 call_3 = self.t10.x
154 self.assertEqual(call_1, call_2)
155 self.assertEqual(call_2, call_3)
156
157 def test_dynamic_values_changed_independent(self):
158 param.Dynamic.time_dependent = False
159 call_1 = self.t10.x
160 call_2 = self.t10.x
161 call_3 = self.t10.x
162 self.assertNotEqual(call_1, call_2)
163 self.assertNotEqual(call_2, call_3)
164
165 def test_dynamic_values_change(self):
166 param.Dynamic.time_dependent = True
167 with param.Dynamic.time_fn as t:
168 t(0)
169 call_1 = self.t10.x
170 t += 1
171 call_2 = self.t10.x
172 t(0)
173 call_3 = self.t10.x
174 self.assertNotEqual(call_1, call_2)
175 self.assertNotEqual(call_1, call_3)
176
177 def test_dynamic_values_time_dependent(self):
178 param.Dynamic.time_dependent = True
179 with param.Dynamic.time_fn as t:
180 t(0)
181 call_1 = self.t11.x
182 t += 1
183 call_2 = self.t11.x
184 t(0)
185 call_3 = self.t11.x
186 self.assertNotEqual(call_1, call_2)
187 self.assertEqual(call_1, call_3)
188
189 def test_class_dynamic_values_change(self):
190 call_1 = self.TestPO3.x
191 call_2 = self.TestPO3.x
192 self.assertEqual(call_1, call_2)
193 with param.Dynamic.time_fn as t:
194 t += 1
195 call_3 = self.TestPO3.x
196 self.assertNotEqual(call_2, call_3)
197
198 def test_dynamic_value_change_independent(self):
199 t12 = self.TestPO1()
200 t12.set_dynamic_time_fn(None)
201 self.assertNotEqual(t12.x, t12.x)
202 self.assertEqual(t12.y, t12.y)
203
204 def test_dynamic_value_change_disabled(self):
205 " time_fn set on the UniformRandom() when t13.y was set"
206 t13 = self.TestPO1()
207 t13.set_dynamic_time_fn(None)
208 t13.y = numbergen.UniformRandom()
209 self.assertNotEqual(t13.y, t13.y)
210
211 def test_dynamic_value_change_enabled(self):
212 " time_fn set on the UniformRandom() when t13.y was set"
213 t14 = self.TestPO1()
214 t14.y = numbergen.UniformRandom()
215 self.assertEqual(t14.y, t14.y)
216
217
218 def test_dynamic_time_fn_not_inherited(self):
219 " time_fn not inherited"
220 t15 = self.TestPO4()
221 t15.set_dynamic_time_fn(None)
222 with param.Dynamic.time_fn as t:
223 call_1 = t15.z.x
224 t += 1
225 call_2 = t15.z.x
226 self.assertNotEqual(call_1, call_2)
227
228
229
230 class TestDynamicSharedNumbergen(TestDynamicParameters):
231 "Check shared generator"
232 def setUp(self):
233 super(TestDynamicSharedNumbergen, self).setUp()
234 self.shared = numbergen.UniformRandom(lbound=-1,ubound=1,seed=20)
235
236 def test_dynamic_shared_numbergen(self):
237 param.Dynamic.time_dependent = True
238 t11 = self.TestPO1(x=self.shared)
239 t12 = self.TestPO1(x=self.shared)
240
241 with param.Dynamic.time_fn as t:
242 t += 1
243 call_1 = t11.x
244 self.assertEqual(call_1, t12.x)
245 t += 1
246 self.assertNotEqual(call_1, t12.x)
247
248
249
250 if __name__ == "__main__":
251 import nose
252 nose.runmodule()
253
254
255 # Commented out block in the original doctest version.
256 # Maybe these are features originally planned but never implemented
257
258 """
259 It is not yet possible to set time_fn for a Parameter instance
260 >>> class TestPO5(param.Parameterized):
261 ... x = param.Dynamic(default=numbergen.UniformRandom(),dynamic_time_fn=None)
262 """
263
264 """
265 We currently don't support iterators/generators in Dynamic unless
266 they're wrapped.
267
268 >>> i = iter([1,2,3])
269 >>> t11.x = i
270
271 >>> topo.sim.run(1)
272
273 >>> t11.x
274 1
275
276 >>> def gen():
277 ... yield 2
278 ... yield 4
279 ... yield 6
280
281 >>> g = gen()
282
283 >>> t11.x = g
284
285 >>> t11.x
286 2
287
288 >>> topo.sim.run(1)
289
290 >>> t11.x
291 4
292 """
+0
-67
tests/API0/testipythonmagic.py less more
0 """
1 Unit test for the IPython magic
2 """
3
4 import re
5 import sys
6 import unittest
7 import param
8
9
10 try:
11 import IPython # noqa
12 except ImportError:
13 import os
14 if os.getenv('PARAM_TEST_IPYTHON','0') == '1':
15 raise ImportError("PARAM_TEST_IPYTHON=1 but ipython not available.")
16
17 # TODO: is the below actually true?
18
19 # SkipTest will be raised if IPython unavailable
20 from param.ipython import ParamPager
21
22 test1_repr = """\x1b[1;32mParameters of 'TestClass'\n=========================\n\x1b[0m\n\x1b[1;31mParameters changed from their default values are marked in red.\x1b[0m\n\x1b[1;36mSoft bound values are marked in cyan.\x1b[0m\nC/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None\n\n\x1b[1;34mNameValue Type Bounds Mode \x1b[0m\n\nu 4 Number V RW \nv 4 Number C RW \nw 4 Number C RO \nx None String V RW AN \ny 4 Number (-1, None) V RW \nz 4 Number (-1, 100) V RW \n\n\x1b[1;32mParameter docstrings:\n=====================\x1b[0m\n\n\x1b[1;34mu: < No docstring available >\x1b[0m\n\x1b[1;31mv: < No docstring available >\x1b[0m\n\x1b[1;34mw: < No docstring available >\x1b[0m\n\x1b[1;31mx: < No docstring available >\x1b[0m\n\x1b[1;34my: < No docstring available >\x1b[0m\n\x1b[1;31mz: < No docstring available >\x1b[0m"""
23
24
25 test2_repr = """\x1b[1;32mParameters of 'TestClass' instance\n==================================\n\x1b[0m\n\x1b[1;31mParameters changed from their default values are marked in red.\x1b[0m\n\x1b[1;36mSoft bound values are marked in cyan.\x1b[0m\nC/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None\n\n\x1b[1;34mNameValue Type Bounds Mode \x1b[0m\n\nu 4 Number V RW \nv 4 Number C RW \nw 4 Number C RO \nx None String V RW AN \ny 4 Number (-1, None) V RW \nz 4 Number (-1, 100) V RW \n\n\x1b[1;32mParameter docstrings:\n=====================\x1b[0m\n\n\x1b[1;34mu: < No docstring available >\x1b[0m\n\x1b[1;31mv: < No docstring available >\x1b[0m\n\x1b[1;34mw: < No docstring available >\x1b[0m\n\x1b[1;31mx: < No docstring available >\x1b[0m\n\x1b[1;34my: < No docstring available >\x1b[0m\n\x1b[1;31mz: < No docstring available >\x1b[0m"""
26
27 class TestParamPager(unittest.TestCase):
28
29 def setUp(self):
30 self.maxDiff = None
31 class TestClass(param.Parameterized):
32 u = param.Number(4)
33 v = param.Number(4, constant=True)
34 w = param.Number(4, readonly=True)
35 x = param.String(None, allow_None=True)
36 y = param.Number(4, bounds=(-1, None))
37 z = param.Number(4, bounds=(-1, 100), softbounds=(-100, -200))
38
39 self.TestClass = TestClass
40 self.pager = ParamPager()
41
42 def test_parameterized_class(self):
43 page_string = self.pager(self.TestClass)
44 # Remove params automatic numbered names
45 page_string = re.sub('TestClass(\d+)', 'TestClass', page_string)
46 ref_string = re.sub('TestClass(\d+)', 'TestClass', test1_repr)
47
48 try:
49 self.assertEqual(page_string, ref_string)
50 except Exception as e:
51 sys.stderr.write(page_string) # Coloured output
52 sys.stderr.write("\nRAW STRING:\n\n%r\n\n" % page_string)
53 raise e
54
55 def test_parameterized_instance(self):
56 page_string = self.pager(self.TestClass())
57 # Remove params automatic numbered names
58 page_string = re.sub('TestClass(\d+)', 'TestClass', page_string)
59 ref_string = re.sub('TestClass(\d+)', 'TestClass', test2_repr)
60
61 try:
62 self.assertEqual(page_string, ref_string)
63 except Exception as e:
64 sys.stderr.write(page_string) # Coloured output
65 sys.stderr.write("\nRAW STRING:\n\n%r\n\n" % page_string)
66 raise e
+0
-161
tests/API0/testlistselector.py less more
0 import unittest
1 import param
2
3 # TODO: I copied the tests from testobjectselector, although I
4 # struggled to understand some of them. Both files should be reviewed
5 # and cleaned up together.
6
7 # TODO: tests copied from testobjectselector could use assertRaises
8 # context manager (and could be updated in testobjectselector too).
9
10 class TestListSelectorParameters(unittest.TestCase):
11
12 def setUp(self):
13
14 class P(param.Parameterized):
15 e = param.ListSelector(default=[5],objects=[5,6,7])
16 f = param.ListSelector(default=10)
17 h = param.ListSelector(default=None)
18 g = param.ListSelector(default=None,objects=[7,8])
19 i = param.ListSelector(default=[7],objects=[9],check_on_set=False)
20
21 self.P = P
22
23 def test_default_None(self):
24 class Q(param.Parameterized):
25 r = param.ListSelector(default=None)
26
27 def test_set_object_constructor(self):
28 p = self.P(e=[6])
29 self.assertEqual(p.e, [6])
30
31 def test_set_object_outside_bounds(self):
32 p = self.P(e=[6])
33 try:
34 p.e = [9]
35 except ValueError:
36 pass
37 else:
38 raise AssertionError("Object set outside range.")
39
40 def test_set_object_setattr(self):
41 p = self.P(e=[6])
42 p.f = [9]
43 self.assertEqual(p.f, [9])
44 p.g = [7]
45 self.assertEqual(p.g, [7])
46 p.i = [12]
47 self.assertEqual(p.i, [12])
48
49
50 def test_set_object_not_None(self):
51 p = self.P(e=[6])
52 p.g = [7]
53 try:
54 p.g = None
55 except TypeError:
56 pass
57 else:
58 raise AssertionError("Object set outside range.")
59
60 def test_set_one_object_not_None(self):
61 p = self.P(e=[6])
62 p.g = [7]
63 try:
64 p.g = [None]
65 except ValueError:
66 pass
67 else:
68 raise AssertionError("Object set outside range.")
69
70
71 def test_set_object_setattr_post_error(self):
72 p = self.P(e=[6])
73 p.f = [9]
74 self.assertEqual(p.f, [9])
75 p.g = [7]
76 try:
77 p.g = [None]
78 except ValueError:
79 pass
80 else:
81 raise AssertionError("Object set outside range.")
82
83 self.assertEqual(p.g, [7])
84 p.i = [12]
85 self.assertEqual(p.i, [12])
86
87 def test_initialization_out_of_bounds(self):
88 try:
89 class Q(param.Parameterized):
90 q = param.ListSelector([5],objects=[4])
91 except ValueError:
92 pass
93 else:
94 raise AssertionError("ListSelector created outside range.")
95
96
97 def test_initialization_no_bounds(self):
98 try:
99 class Q(param.Parameterized):
100 q = param.ListSelector([5],objects=10)
101 except TypeError:
102 pass
103 else:
104 raise AssertionError("ListSelector created without range.")
105
106
107 ##################################################################
108 ##################################################################
109 ### new tests (not copied from testobjectselector)
110
111 def test_bad_default(self):
112 with self.assertRaises(TypeError):
113 class Q(param.Parameterized):
114 r = param.ListSelector(default=6,check_on_set=True)
115
116 def test_implied_check_on_set(self):
117 with self.assertRaises(TypeError):
118 class Q(param.Parameterized):
119 r = param.ListSelector(default=7,objects=[7,8])
120
121 def test_default_not_checked(self):
122 class Q(param.Parameterized):
123 r = param.ListSelector(default=[6])
124
125 ##########################
126 # CEBALERT: not sure it makes sense for ListSelector to be set to
127 # a non-iterable value (except None). I.e. I think this first test
128 # should fail.
129 def test_default_not_checked_to_be_iterable(self):
130 class Q(param.Parameterized):
131 r = param.ListSelector(default=6)
132
133 def test_set_checked_to_be_iterable(self):
134 class Q(param.Parameterized):
135 r = param.ListSelector(default=6,check_on_set=False)
136
137 with self.assertRaises(TypeError):
138 Q.r = 6
139 ##########################
140
141
142 def test_compute_default(self):
143 class Q(param.Parameterized):
144 r = param.ListSelector(default=None, compute_default_fn=lambda: [1,2,3])
145
146 self.assertEqual(Q.r, None)
147 Q.params('r').compute_default()
148 self.assertEqual(Q.r, [1,2,3])
149 self.assertEqual(Q.params('r').objects, [1,2,3])
150
151 def test_bad_compute_default(self):
152 class Q(param.Parameterized):
153 r = param.ListSelector(default=None,compute_default_fn=lambda:1)
154
155 with self.assertRaises(TypeError):
156 Q.params('r').compute_default()
157
158 if __name__ == "__main__":
159 import nose
160 nose.runmodule()
+0
-39
tests/API0/testnumbergen.py less more
0 """
1 Test cases for the numbergen module.
2 """
3
4 import unittest
5 import numbergen
6
7
8 _seed = 0 # keep tests deterministic
9 _iterations = 1000
10
11
12 class TestUniformRandom(unittest.TestCase):
13 def test_range(self):
14 lbound = 2.0
15 ubound = 5.0
16 gen = numbergen.UniformRandom(
17 seed=_seed,
18 lbound=lbound,
19 ubound=ubound)
20 for _ in range(_iterations):
21 value = gen()
22 self.assertTrue(lbound <= value < ubound)
23
24 class TestUniformRandomOffset(unittest.TestCase):
25 def test_range(self):
26 lbound = 2.0
27 ubound = 5.0
28 gen = numbergen.UniformRandomOffset(
29 seed=_seed,
30 mean=(ubound + lbound) / 2,
31 range=ubound - lbound)
32 for _ in range(_iterations):
33 value = gen()
34 self.assertTrue(lbound <= value < ubound)
35
36 if __name__ == "__main__":
37 import nose
38 nose.runmodule()
+0
-40
tests/API0/testnumpy.py less more
0 """
1 If numpy's present, is numpy stuff ok?
2 """
3
4 import os
5 import unittest
6
7 import param
8
9
10 try:
11 import numpy
12 import numpy.testing
13 except ImportError:
14 if os.getenv('PARAM_TEST_NUMPY','0') == '1':
15 raise ImportError("PARAM_TEST_NUMPY=1 but numpy not available.")
16 else:
17 raise unittest.SkipTest("numpy not available")
18
19
20 def _is_array_and_equal(test,ref):
21 if not type(test) == numpy.ndarray:
22 raise AssertionError
23 numpy.testing.assert_array_equal(test,ref)
24
25 # TODO: incomplete
26 class TestNumpy(unittest.TestCase):
27 def test_array_param(self):
28 class Z(param.Parameterized):
29 z = param.Array(default=numpy.array([1]))
30
31 _is_array_and_equal(Z.z,[1])
32
33 z = Z(z=numpy.array([1,2]))
34 _is_array_and_equal(z.z,[1,2])
35
36
37 if __name__ == "__main__":
38 import nose
39 nose.runmodule()
+0
-108
tests/API0/testobjectselector.py less more
0 """
1 Unit test for object selector parameters.
2
3 Originally implemented as doctests in Topographica in the file
4 testEnumerationParameter.txt
5 """
6
7 import unittest
8 import param
9
10
11 opts=dict(A=[1,2],B=[3,4],C=dict(a=1,b=2))
12
13
14 class TestObjectSelectorParameters(unittest.TestCase):
15
16 def setUp(self):
17
18 class P(param.Parameterized):
19 e = param.ObjectSelector(default=5,objects=[5,6,7])
20 f = param.ObjectSelector(default=10)
21 h = param.ObjectSelector(default=None)
22 g = param.ObjectSelector(default=None,objects=[7,8])
23 i = param.ObjectSelector(default=7,objects=[9],check_on_set=False)
24 d = param.ObjectSelector(default=opts['B'],objects=opts)
25
26 self.P = P
27
28 def test_set_object_constructor(self):
29 p = self.P(e=6)
30 self.assertEqual(p.e, 6)
31
32 def test_get_range_mutable(self):
33 r = self.P.param.params("d").get_range()
34 self.assertEqual(r['A'],opts['A'])
35 self.assertEqual(r['C'],opts['C'])
36 self.d=opts['A']
37 self.d=opts['C']
38 self.d=opts['B']
39
40 def test_set_object_outside_bounds(self):
41 p = self.P(e=6)
42 try:
43 p.e = 9
44 except ValueError:
45 pass
46 else:
47 raise AssertionError("Object set outside range.")
48
49 def test_set_object_setattr(self):
50 p = self.P(e=6)
51 p.f = 9
52 self.assertEqual(p.f, 9)
53 p.g = 7
54 self.assertEqual(p.g, 7)
55 p.i = 12
56 self.assertEqual(p.i, 12)
57
58
59 def test_set_object_not_None(self):
60 p = self.P(e=6)
61 p.g = 7
62 try:
63 p.g = None
64 except ValueError:
65 pass
66 else:
67 raise AssertionError("Object set outside range.")
68
69 def test_set_object_setattr_post_error(self):
70 p = self.P(e=6)
71 p.f = 9
72 self.assertEqual(p.f, 9)
73 p.g = 7
74 try:
75 p.g = None
76 except ValueError:
77 pass
78 else:
79 raise AssertionError("Object set outside range.")
80
81 self.assertEqual(p.g, 7)
82 p.i = 12
83 self.assertEqual(p.i, 12)
84
85 def test_initialization_out_of_bounds(self):
86 try:
87 class Q(param.Parameterized):
88 q = param.ObjectSelector(5,objects=[4])
89 except ValueError:
90 pass
91 else:
92 raise AssertionError("ObjectSelector created outside range.")
93
94
95 def test_initialization_no_bounds(self):
96 try:
97 class Q(param.Parameterized):
98 q = param.ObjectSelector(5,objects=10)
99 except TypeError:
100 pass
101 else:
102 raise AssertionError("ObjectSelector created without range.")
103
104
105 if __name__ == "__main__":
106 import nose
107 nose.runmodule()
+0
-300
tests/API0/testparameterizedobject.py less more
0 """
1 Unit test for Parameterized.
2 """
3
4 import unittest
5 import param
6 import numbergen
7
8 # CEBALERT: not anything like a complete test of Parameterized!
9
10
11 import random
12 from nose.tools import istest, nottest
13
14
15 from param.parameterized import ParamOverrides, shared_parameters
16
17 @nottest
18 class _SomeRandomNumbers(object):
19 def __call__(self):
20 return random.random()
21
22 @nottest
23 class TestPO(param.Parameterized):
24 inst = param.Parameter(default=[1,2,3],instantiate=True)
25 notinst = param.Parameter(default=[1,2,3],instantiate=False)
26 const = param.Parameter(default=1,constant=True)
27 ro = param.Parameter(default="Hello",readonly=True)
28 ro2 = param.Parameter(default=object(),readonly=True,instantiate=True)
29
30 dyn = param.Dynamic(default=1)
31
32 @nottest
33 class AnotherTestPO(param.Parameterized):
34 instPO = param.Parameter(default=TestPO(),instantiate=True)
35 notinstPO = param.Parameter(default=TestPO(),instantiate=False)
36
37 @nottest
38 class TestAbstractPO(param.Parameterized):
39 __abstract = True
40
41 @nottest
42 class TestParamInstantiation(AnotherTestPO):
43 instPO = param.Parameter(default=AnotherTestPO(),instantiate=False)
44
45 @istest
46 class TestParameterized(unittest.TestCase):
47
48 def test_constant_parameter(self):
49 """Test that you can't set a constant parameter after construction."""
50 testpo = TestPO(const=17)
51 self.assertEqual(testpo.const,17)
52 self.assertRaises(TypeError,setattr,testpo,'const',10)
53
54 # check you can set on class
55 TestPO.const=9
56 testpo = TestPO()
57 self.assertEqual(testpo.const,9)
58
59 def test_readonly_parameter(self):
60 """Test that you can't set a read-only parameter on construction or as an attribute."""
61 testpo = TestPO()
62 self.assertEqual(testpo.ro,"Hello")
63
64 with self.assertRaises(TypeError):
65 t = TestPO(ro=20)
66
67 t=TestPO()
68 self.assertRaises(TypeError,setattr,t,'ro',10)
69
70 # check you cannot set on class
71 self.assertRaises(TypeError,setattr,TestPO,'ro',5)
72
73 self.assertEqual(testpo.params()['ro'].constant,True)
74
75 # check that instantiate was ignored for readonly
76 self.assertEqual(testpo.params()['ro2'].instantiate,False)
77
78
79
80 def test_basic_instantiation(self):
81 """Check that instantiated parameters are copied into objects."""
82
83 testpo = TestPO()
84
85 self.assertEqual(testpo.inst,TestPO.inst)
86 self.assertEqual(testpo.notinst,TestPO.notinst)
87
88 TestPO.inst[1]=7
89 TestPO.notinst[1]=7
90
91 self.assertEqual(testpo.notinst,[1,7,3])
92 self.assertEqual(testpo.inst,[1,2,3])
93
94
95 def test_more_instantiation(self):
96 """Show that objects in instantiated Parameters can still share data."""
97 anothertestpo = AnotherTestPO()
98
99 ### CB: AnotherTestPO.instPO is instantiated, but
100 ### TestPO.notinst is not instantiated - so notinst is still
101 ### shared, even by instantiated parameters of AnotherTestPO.
102 ### Seems like this behavior of Parameterized could be
103 ### confusing, so maybe mention it in documentation somewhere.
104 TestPO.notinst[1]=7
105 # (if you thought your instPO was completely an independent object, you
106 # might be expecting [1,2,3] here)
107 self.assertEqual(anothertestpo.instPO.notinst,[1,7,3])
108
109
110 def test_instantiation_inheritance(self):
111 """Check that instantiate=True is always inherited (SF.net #2483932)."""
112 t = TestParamInstantiation()
113 assert t.params('instPO').instantiate is True
114 assert isinstance(t.instPO,AnotherTestPO)
115
116
117 def test_abstract_class(self):
118 """Check that a class declared abstract actually shows up as abstract."""
119 self.assertEqual(TestAbstractPO.abstract,True)
120 self.assertEqual(TestPO.abstract,False)
121
122
123 def test_params(self):
124 """Basic tests of params() method."""
125
126
127 # CB: test not so good because it requires changes if params
128 # of PO are changed
129 assert 'name' in param.Parameterized.params()
130 assert len(param.Parameterized.params()) in [1,2]
131
132 ## check for bug where subclass Parameters were not showing up
133 ## if params() already called on a super class.
134 assert 'inst' in TestPO.params()
135 assert 'notinst' in TestPO.params()
136
137 ## check caching
138 assert param.Parameterized.params() is param.Parameterized().params(), "Results of params() should be cached." # just for performance reasons
139
140
141 def test_state_saving(self):
142 t = TestPO(dyn=_SomeRandomNumbers())
143 g = t.get_value_generator('dyn')
144 g._Dynamic_time_fn=None
145 assert t.dyn!=t.dyn
146 orig = t.dyn
147 t.state_push()
148 t.dyn
149 assert t.inspect_value('dyn')!=orig
150 t.state_pop()
151 assert t.inspect_value('dyn')==orig
152
153
154
155 from param import parameterized
156
157 @nottest
158 class some_fn(param.ParameterizedFunction):
159 num_phase = param.Number(18)
160 frequencies = param.List([99])
161 scale = param.Number(0.3)
162
163 def __call__(self,**params_to_override):
164 params = parameterized.ParamOverrides(self,params_to_override)
165 num_phase = params['num_phase']
166 frequencies = params['frequencies']
167 scale = params['scale']
168 return scale,num_phase,frequencies
169
170 instance = some_fn.instance()
171
172 @istest
173 class TestParameterizedFunction(unittest.TestCase):
174
175 def _basic_tests(self,fn):
176 self.assertEqual(fn(),(0.3,18,[99]))
177 self.assertEqual(fn(frequencies=[1,2,3]),(0.3,18,[1,2,3]))
178 self.assertEqual(fn(),(0.3,18,[99]))
179
180 fn.frequencies=[10,20,30]
181 self.assertEqual(fn(frequencies=[1,2,3]),(0.3,18,[1,2,3]))
182 self.assertEqual(fn(),(0.3,18,[10,20,30]))
183
184 def test_parameterized_function(self):
185 self._basic_tests(some_fn)
186
187 def test_parameterized_function_instance(self):
188 self._basic_tests(instance)
189
190 def test_pickle_instance(self):
191 import pickle
192 s = pickle.dumps(instance)
193 instance.scale=0.8
194 i = pickle.loads(s)
195 self.assertEqual(i(),(0.3,18,[10,20,30]))
196
197
198 @nottest
199 class TestPO1(param.Parameterized):
200 x = param.Number(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=1),bounds=(-1,1))
201 y = param.Number(default=1,bounds=(-1,1))
202
203 @istest
204 class TestNumberParameter(unittest.TestCase):
205
206 def test_outside_bounds(self):
207 t1 = TestPO1()
208 # Test bounds (non-dynamic number)
209 try:
210 t1.y = 10
211 except ValueError:
212 pass
213 else:
214 assert False, "Should raise ValueError."
215
216 def test_outside_bounds_numbergen(self):
217 t1 = TestPO1()
218 # Test bounds (dynamic number)
219 t1.x = numbergen.UniformRandom(lbound=2,ubound=3) # bounds not checked on set
220 try:
221 t1.x
222 except ValueError:
223 pass
224 else:
225 assert False, "Should raise ValueError."
226
227
228 @istest
229 class TestStringParameter(unittest.TestCase):
230
231 def setUp(self):
232 super(TestStringParameter, self).setUp()
233
234 class TestString(param.Parameterized):
235 a = param.String()
236 b = param.String(default='',allow_None=True)
237 c = param.String(default=None)
238
239 self._TestString = TestString
240
241 def test_handling_of_None(self):
242 t = self._TestString()
243
244 with self.assertRaises(ValueError):
245 t.a = None
246
247 t.b = None
248
249 assert t.c is None
250
251
252
253 @istest
254 class TestParamOverrides(unittest.TestCase):
255
256 def setUp(self):
257 super(TestParamOverrides, self).setUp()
258 self.po = param.Parameterized(name='A',print_level=0)
259
260 def test_init_name(self):
261 self.assertEqual(self.po.name, 'A')
262
263 def test_simple_override(self):
264 overrides = ParamOverrides(self.po,{'name':'B'})
265 self.assertEqual(overrides['name'], 'B')
266 self.assertEqual(overrides['print_level'], 0)
267
268 # CEBALERT: missing test for allow_extra_keywords (e.g. getting a
269 # warning on attempting to override non-existent parameter when
270 # allow_extra_keywords is False)
271
272 def test_missing_key(self):
273 overrides = ParamOverrides(self.po,{'name':'B'})
274 with self.assertRaises(AttributeError):
275 overrides['doesnotexist']
276
277
278 class TestSharedParameters(unittest.TestCase):
279
280 def setUp(self):
281 with shared_parameters():
282 self.p1 = TestPO(name='A', print_level=0)
283 self.p2 = TestPO(name='B', print_level=0)
284 self.ap1 = AnotherTestPO(name='A', print_level=0)
285 self.ap2 = AnotherTestPO(name='B', print_level=0)
286
287 def test_shared_object(self):
288 self.assertTrue(self.ap1.instPO is self.ap2.instPO)
289 self.assertTrue(self.ap1.params('instPO').default is not self.ap2.instPO)
290
291 def test_shared_list(self):
292 self.assertTrue(self.p1.inst is self.p2.inst)
293 self.assertTrue(self.p1.params('inst').default is not self.p2.inst)
294
295
296
297 if __name__ == "__main__":
298 import nose
299 nose.runmodule()
+0
-167
tests/API0/testparameterizedrepr.py less more
0 """
1 Unit test for the repr and pprint of parameterized objects.
2 """
3
4 import unittest
5 import param
6
7
8
9 class TestParameterizedRepr(unittest.TestCase):
10
11 def setUp(self):
12 # initialize a parameterized class
13 class A(param.Parameterized):
14 a = param.Number(4, precedence=-5)
15 b = param.String('B', precedence=-4)
16 c = param.Number(4, precedence=0)
17 d = param.Integer(-22, precedence=1)
18
19 x = param.Number(1, precedence=2)
20 y = param.Number(2, precedence=-1)
21 z = param.Number(3, precedence=-2)
22 def __init__(self, a, b, c=4, d=-22, **kwargs):
23 super(A, self).__init__(a=a, b=b, c=c, **kwargs)
24
25 self.A = A
26
27 class B(param.Parameterized): # Similar to A but no **kwargs
28 a = param.Number(4, precedence=-5)
29 b = param.String('B', precedence=-4)
30 c = param.Number(4, precedence=0)
31 d = param.Integer(-22, precedence=1)
32
33 x = param.Number(1, precedence=2)
34 def __init__(self, a, b, c=4, d=-22):
35 super(B, self).__init__(a=a, b=b, c=c, name='ClassB')
36
37 self.B = B
38
39 class C(param.Parameterized): # Similar to A but with *varargs
40 a = param.Number(4, precedence=-5)
41 b = param.String('B', precedence=-4)
42 c = param.Number(4, precedence=0)
43 d = param.Integer(-22, precedence=1)
44
45 x = param.Number(1, precedence=2)
46 y = param.Number(2, precedence=-1)
47 z = param.Number(3, precedence=-2)
48
49 def __init__(self, a, b, c=4, d=-22, *varargs, **kwargs):
50 super(C, self).__init__(a=a, b=b, c=c, **kwargs)
51
52 self.C = C
53
54
55 class D(param.Parameterized): # Similar to A but with missing parameters
56 a = param.Number(4, precedence=-5)
57 b = param.String('B', precedence=-4)
58
59 def __init__(self, a, b, c=4, d=-22, **kwargs):
60 super(D, self).__init__(a=a, b=b, **kwargs)
61
62 self.D = D
63
64
65 # More realistically, positional args are not params
66 class E(param.Parameterized):
67 a = param.Number(4, precedence=-5)
68
69 def __init__(self, p, q=4, **params): # (plus non-param kw too)
70 super(E, self).__init__(**params)
71
72 self.E = E
73
74
75 def testparameterizedrepr(self):
76 obj = self.A(4,'B', name='test1')
77 self.assertEqual(repr(obj),
78 "A(a=4, b='B', c=4, d=-22, name='test1', x=1, y=2, z=3)")
79
80 def testparameterizedscriptrepr1(self):
81 obj = self.A(4,'B', name='test')
82 self.assertEqual(obj.pprint(),
83 "A(4, 'B', name='test')")
84
85 def testparameterizedscriptrepr2(self):
86 obj = self.A(4,'B', c=5, name='test')
87 self.assertEqual(obj.pprint(),
88 "A(4, 'B', c=5, name='test')")
89
90 def testparameterizedscriptrepr3(self):
91 obj = self.A(4,'B', c=5, x=True, name='test')
92 self.assertEqual(obj.pprint(),
93 "A(4, 'B', c=5, name='test')")
94
95 def testparameterizedscriptrepr4(self):
96 obj = self.A(4,'B', c=5, x=10, name='test')
97 self.assertEqual(obj.pprint(),
98 "A(4, 'B', c=5, name='test', x=10)")
99
100
101 def testparameterizedscriptrepr5(self):
102 obj = self.A(4,'B', x=10, y=11, z=12, name='test')
103 self.assertEqual(obj.pprint(),
104 "A(4, 'B', name='test', z=12, y=11, x=10)")
105
106 def testparameterizedscriptrepr_nokwargs(self):
107 obj = self.B(4,'B', c=99)
108 obj.x = 10 # Modified but not passable through constructor
109 self.assertEqual(obj.pprint(),
110 "B(4, 'B', c=99)")
111
112 def testparameterizedscriptrepr_varags(self):
113 obj = self.C(4,'C', c=99)
114 self.assertEqual(obj.pprint(),
115 "C(4, 'C', c=99, **varargs)")
116
117 def testparameterizedscriptrepr_varags_kwargs(self):
118 obj = self.C(4,'C', c=99, x=10, y=11, z=12)
119 self.assertEqual(obj.pprint(),
120 "C(4, 'C', c=99, z=12, y=11, x=10, **varargs)")
121
122 def testparameterizedscriptrepr_missing_values(self):
123 obj = self.D(4,'D', c=99)
124 self.assertEqual(obj.pprint(),
125 "D(4, 'D', c=<?>, d=<?>)")
126
127 def testparameterizedscriptrepr_nonparams(self):
128 obj = self.E(10,q='hi', a=99)
129 self.assertEqual(obj.pprint(),
130 "E(<?>, q=<?>, a=99)")
131
132 def test_exceptions(self):
133 obj = self.E(10,q='hi',a=99)
134 try:
135 obj.pprint(unknown_value=False)
136 except Exception:
137 pass
138 else:
139 raise AssertionError
140
141 def test_suppression(self):
142 obj = self.E(10,q='hi',a=99)
143 self.assertEqual(obj.pprint(unknown_value=None),
144 "E(a=99)")
145
146 def test_imports_deduplication(self):
147 obj = self.E(10,q='hi', a=99)
148 imports = ['import me','import me']
149 obj.pprint(imports=imports)
150 self.assertEqual(imports.count('import me'),1)
151
152 def test_qualify(self):
153 obj = self.E(10,q='hi', a=99)
154
155 r = "E(<?>, q=<?>, a=99)"
156 self.assertEqual(obj.pprint(qualify=False),
157 r)
158
159 self.assertEqual(obj.pprint(qualify=True),
160 "tests.API0.testparameterizedrepr."+r)
161
162
163
164 if __name__ == "__main__":
165 import nose
166 nose.runmodule()
+0
-53
tests/API0/testrangeparameter.py less more
0 """
1 Unit test for Range parameters.
2 """
3
4 import unittest
5 import param
6
7
8 class TestRangeParameters(unittest.TestCase):
9
10 def test_initialization_out_of_bounds(self):
11 try:
12 class Q(param.Parameterized):
13 q = param.Range((0, 2), bounds=(0, 1))
14 except ValueError:
15 pass
16 else:
17 raise AssertionError("No exception raised on out-of-bounds date")
18
19 def test_set_exclusive_out_of_bounds_upper(self):
20 class Q(param.Parameterized):
21 q = param.Range(bounds=(0, 10), inclusive_bounds=(True, False))
22 try:
23 Q.q = (0, 10)
24 except ValueError:
25 pass
26 else:
27 raise AssertionError("No exception raised on out-of-bounds date")
28
29 def test_set_exclusive_out_of_bounds_lower(self):
30 class Q(param.Parameterized):
31 q = param.Range(bounds=(0, 10), inclusive_bounds=(False, True))
32 try:
33 Q.q = (0, 10)
34 except ValueError:
35 pass
36 else:
37 raise AssertionError("No exception raised on out-of-bounds date")
38
39 def test_set_out_of_bounds(self):
40 class Q(param.Parameterized):
41 q = param.Range(bounds=(0, 10))
42 try:
43 Q.q = (5, 11)
44 except ValueError:
45 pass
46 else:
47 raise AssertionError("No exception raised on out-of-bounds date")
48
49 def test_get_soft_bounds(self):
50 q = param.Range((1,3), bounds=(0, 10), softbounds=(1, 9))
51 self.assertEqual(q.get_soft_bounds(), (1, 9))
52
+0
-57
tests/API0/teststringparam.py less more
0 """
1 Unit test for String parameters
2 """
3
4 import unittest
5
6 import param
7
8
9 ip_regex = '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
10
11 class TestStringParameters(unittest.TestCase):
12
13 def test_regex_ok(self):
14 class A(param.Parameterized):
15 s = param.String('0.0.0.0', ip_regex)
16
17 a = A()
18 a.s = '123.123.0.1'
19
20 def test_reject_none(self):
21 class A(param.Parameterized):
22 s = param.String('0.0.0.0', ip_regex)
23
24 a = A()
25
26 exception = "String 's' only takes a string value."
27 with self.assertRaisesRegexp(ValueError, exception):
28 a.s = None # because allow_None should be False
29
30 def test_default_none(self):
31 class A(param.Parameterized):
32 s = param.String(None, ip_regex)
33
34 a = A()
35 a.s = '123.123.0.1'
36 a.s = None # because allow_None should be True with default of None
37
38 def test_regex_incorrect(self):
39
40 class A(param.Parameterized):
41 s = param.String('0.0.0.0', regex=ip_regex)
42
43 a = A()
44
45 exception = "String 's': '123.123.0.256' does not match regex"
46 with self.assertRaisesRegexp(ValueError, exception):
47 a.s = '123.123.0.256'
48
49 def test_regex_incorrect_default(self):
50
51 exception = "String 'None': '' does not match regex"
52 with self.assertRaisesRegexp(ValueError, exception):
53 class A(param.Parameterized):
54 s = param.String(regex=ip_regex) # default value '' does not match regular expression
55
56
+0
-302
tests/API0/testtimedependent.py less more
0 """
1 Unit tests for the param.Time class, time dependent parameters and
2 time-dependent numbergenerators.
3 """
4 import unittest
5 import param
6 import numbergen
7 import copy
8
9 from nose.plugins.skip import SkipTest
10 import fractions
11
12 try:
13 import gmpy
14 except:
15 gmpy = None
16
17
18
19 class TestTimeClass(unittest.TestCase):
20
21 def test_time_init(self):
22 param.Time()
23
24 def test_time_init_int(self):
25 t = param.Time(time_type=int)
26 self.assertEqual(t(), 0)
27
28 def test_time_int_iter(self):
29 t = param.Time(time_type=int)
30 self.assertEqual(next(t), 0)
31 self.assertEqual(next(t), 1)
32
33 def test_time_init_timestep(self):
34 t = param.Time(time_type=int, timestep=2)
35 self.assertEqual(next(t), 0)
36 self.assertEqual(next(t), 2)
37
38 def test_time_int_until(self):
39 t = param.Time(time_type=int, until=3)
40 self.assertEqual(next(t), 0)
41 self.assertEqual(next(t), 1)
42 self.assertEqual(next(t), 2)
43 self.assertEqual(next(t), 3)
44 try:
45 self.assertEqual(next(t), 4)
46 raise AssertionError("StopIteration should have been raised")
47 except StopIteration:
48 pass
49
50 def test_time_int_eq(self):
51 t = param.Time(time_type=int)
52 s = param.Time(time_type=int)
53 t(3); s(3)
54 self.assertEqual(t == s, True)
55
56 def test_time_int_context(self):
57 t = param.Time(time_type=int)
58 t(3)
59 with t:
60 self.assertEqual(t(), 3)
61 t(5)
62 self.assertEqual(t(), 5)
63 self.assertEqual(t(), 3)
64
65 def test_time_int_context_iadd(self):
66
67 with param.Time(time_type=int) as t:
68 self.assertEqual(t(), 0)
69 t += 5
70 self.assertEqual(t(), 5)
71 self.assertEqual(t(), 0)
72
73 def test_time_int_change_type(self):
74 t = param.Time(time_type=int)
75 self.assertEqual(t(), 0)
76 t(1, fractions.Fraction)
77 self.assertEqual(t(), 1)
78 self.assertEqual(t.time_type, fractions.Fraction)
79
80 def test_time_init_gmpy(self):
81 if gmpy is None: raise SkipTest
82
83 t = param.Time(time_type=gmpy.mpq)
84 self.assertEqual(t(), gmpy.mpq(0))
85 t.advance(gmpy.mpq(0.25))
86 self.assertEqual(t(), gmpy.mpq(1,4))
87
88 def test_time_init_gmpy_advanced(self):
89 if gmpy is None: raise SkipTest
90 t = param.Time(time_type=gmpy.mpq,
91 timestep=gmpy.mpq(0.25),
92 until=1.5)
93 self.assertEqual(t(), gmpy.mpq(0,1))
94 t(0.5)
95 self.assertEqual(t(), gmpy.mpq(1,2))
96 with t:
97 t.advance(0.25)
98 self.assertEqual(t(), gmpy.mpq(3,4))
99 self.assertEqual(t(), gmpy.mpq(1,2))
100 tvals = [tval for tval in t]
101 self.assertEqual(tvals, [gmpy.mpq(1,2),
102 gmpy.mpq(3,4),
103 gmpy.mpq(1,1),
104 gmpy.mpq(5,4),
105 gmpy.mpq(3,2)])
106
107
108 class TestTimeDependentDynamic(unittest.TestCase):
109
110 def setUp(self):
111 param.Dynamic.time_dependent=None
112 self.time_fn= param.Time(time_type=int)
113
114 class Incrementer(object):
115 def __init__(self):
116 self.i = -1
117 def __call__(self):
118 self.i+=1
119 return self.i
120
121 self.Incrementer = Incrementer
122
123 class DynamicClass(param.Parameterized):
124 a = param.Number(default = self.Incrementer())
125
126 self.DynamicClass = DynamicClass
127 self._start_state = copy.copy([param.Dynamic.time_dependent,
128 numbergen.TimeAware.time_dependent,
129 param.Dynamic.time_fn,
130 numbergen.TimeAware.time_fn,
131 param.random_seed])
132
133 def tearDown(self):
134 param.Dynamic.time_dependent = self._start_state[0]
135 numbergen.TimeAware.time_dependent = self._start_state[1]
136 param.Dynamic.time_fn = self._start_state[2]
137 numbergen.TimeAware.time_fn = self._start_state[3]
138 param.random_seed = self._start_state[4]
139
140
141 def test_non_time_dependent(self):
142 """
143 With param.Dynamic.time_dependent=None every call should
144 increment.
145 """
146 param.Dynamic.time_dependent=None
147 param.Dynamic.time_fn = self.time_fn
148
149 dynamic = self.DynamicClass()
150 self.assertEqual(dynamic.a, 0)
151 self.assertEqual(dynamic.a, 1)
152 self.assertEqual(dynamic.a, 2)
153
154 def test_time_fixed(self):
155 """
156 With param.Dynamic.time_dependent=True the value should only
157 increment when the time value changes.
158 """
159 param.Dynamic.time_dependent=True
160 param.Dynamic.time_fn = self.time_fn
161
162 dynamic = self.DynamicClass()
163 self.assertEqual(dynamic.a, 0)
164 self.assertEqual(dynamic.a, 0)
165
166 self.time_fn += 1
167 self.assertEqual(dynamic.a, 1)
168 self.assertEqual(dynamic.a, 1)
169 param.Dynamic.time_fn -= 5
170 self.assertEqual(dynamic.a, 2)
171 self.assertEqual(dynamic.a, 2)
172
173
174 def test_time_dependent(self):
175 """
176 With param.Dynamic.time_dependent=True and param.Dynamic and
177 numbergen.TimeDependent sharing a common time_fn, the value
178 should be a function of time.
179 """
180 param.Dynamic.time_dependent=True
181 param.Dynamic.time_fn = self.time_fn
182 numbergen.TimeDependent.time_fn = self.time_fn
183
184 class DynamicClass(param.Parameterized):
185 b = param.Number(default = numbergen.ScaledTime(factor=2))
186
187 dynamic = DynamicClass()
188 self.time_fn(0)
189 self.assertEqual(dynamic.b, 0.0)
190 self.time_fn += 5
191 self.assertEqual(dynamic.b, 10.0)
192 self.assertEqual(dynamic.b, 10.0)
193 self.time_fn -= 2
194 self.assertEqual(dynamic.b, 6.0)
195 self.assertEqual(dynamic.b, 6.0)
196 self.time_fn -= 3
197 self.assertEqual(dynamic.b, 0.0)
198
199
200 def test_time_dependent_random(self):
201 """
202 When set to time_dependent=True, random number generators
203 should also be a function of time.
204 """
205 param.Dynamic.time_dependent=True
206 numbergen.TimeAware.time_dependent=True
207 param.Dynamic.time_fn = self.time_fn
208 numbergen.TimeAware.time_fn = self.time_fn
209 param.random_seed = 42
210
211 class DynamicClass(param.Parameterized):
212 c = param.Number(default = numbergen.UniformRandom(name = 'test1'))
213 d = param.Number(default = numbergen.UniformRandom(name = 'test2'))
214 e = param.Number(default = numbergen.UniformRandom(name = 'test1'))
215
216 dynamic = DynamicClass()
217
218 test1_t1 = 0.23589388250988552
219 test2_t1 = 0.12576257837158122
220 test1_t2 = 0.14117586161849593
221 test2_t2 = 0.9134917395930359
222
223 self.time_fn(0)
224 self.assertEqual(dynamic.c, test1_t1)
225 self.assertEqual(dynamic.c, dynamic.e)
226 self.assertNotEqual(dynamic.c, dynamic.d)
227 self.assertEqual(dynamic.d, test2_t1)
228 self.time_fn(1)
229 self.assertEqual(dynamic.c, test1_t2)
230 self.assertEqual(dynamic.c, test1_t2)
231 self.assertEqual(dynamic.d, test2_t2)
232 self.time_fn(0)
233 self.assertEqual(dynamic.c, test1_t1)
234 self.assertEqual(dynamic.d, test2_t1)
235
236
237 def test_time_hashing_integers(self):
238 """
239 Check that ints, fractions and strings hash to the same value
240 for integer values.
241 """
242 hashfn = numbergen.Hash("test", input_count=1)
243 hash_1 = hashfn(1)
244 hash_42 = hashfn(42)
245 hash_200001 = hashfn(200001)
246
247 self.assertEqual(hash_1, hashfn(fractions.Fraction(1)))
248 self.assertEqual(hash_1, hashfn("1"))
249
250 self.assertEqual(hash_42, hashfn(fractions.Fraction(42)))
251 self.assertEqual(hash_42, hashfn("42"))
252
253 self.assertEqual(hash_200001, hashfn(fractions.Fraction(200001)))
254 self.assertEqual(hash_200001, hashfn("200001"))
255
256
257 def test_time_hashing_rationals(self):
258 """
259 Check that hashes fractions and strings match for some
260 reasonable rational numbers.
261 """
262 hashfn = numbergen.Hash("test", input_count=1)
263 pi = "3.141592"
264 half = fractions.Fraction(0.5)
265 self.assertEqual(hashfn(0.5), hashfn(half))
266 self.assertEqual(hashfn(pi), hashfn(fractions.Fraction(pi)))
267
268
269 def test_time_hashing_integers_gmpy(self):
270 """
271 Check that hashes for gmpy values at the integers also matches
272 those of ints, fractions and strings.
273 """
274 if gmpy is None: raise SkipTest
275 hashfn = numbergen.Hash("test", input_count=1)
276 hash_1 = hashfn(1)
277 hash_42 = hashfn(42)
278
279 self.assertEqual(hash_1, hashfn(gmpy.mpq(1)))
280 self.assertEqual(hash_1, hashfn(1))
281
282 self.assertEqual(hash_42, hashfn(gmpy.mpq(42)))
283 self.assertEqual(hash_42, hashfn(42))
284
285 def test_time_hashing_rationals_gmpy(self):
286 """
287 Check that hashes of fractions and gmpy mpqs match for some
288 reasonable rational numbers.
289 """
290 if gmpy is None: raise SkipTest
291 pi = "3.141592"
292 hashfn = numbergen.Hash("test", input_count=1)
293 self.assertEqual(hashfn(0.5), hashfn(gmpy.mpq(0.5)))
294 self.assertEqual(hashfn(pi), hashfn(gmpy.mpq(3.141592)))
295
296
297
298
299 if __name__ == "__main__":
300 import nose
301 nose.runmodule()
+0
-10
tests/API1/__init__.py less more
0 import param
1 import unittest
2
3 class API1TestCase(unittest.TestCase):
4
5 def setUp(self):
6 param.parameterized.Parameters._disable_stubs = True
7
8 def tearDown(self):
9 param.parameterized.Parameters._disable_stubs = False
+0
-54
tests/API1/testcalendardateparam.py less more
0 """
1 Unit test for CalendarDate parameters.
2 """
3
4
5 import datetime as dt
6 import param
7 from . import API1TestCase
8
9
10 class TestDateTimeParameters(API1TestCase):
11
12 def test_initialization_out_of_bounds(self):
13 try:
14 class Q(param.Parameterized):
15 q = param.CalendarDate(dt.date(2017,2,27),
16 bounds=(dt.date(2017,2,1),
17 dt.date(2017,2,26)))
18 except ValueError:
19 pass
20 else:
21 raise AssertionError("No exception raised on out-of-bounds date")
22
23 def test_set_out_of_bounds(self):
24 class Q(param.Parameterized):
25 q = param.CalendarDate(bounds=(dt.date(2017,2,1),
26 dt.date(2017,2,26)))
27 try:
28 Q.q = dt.date(2017,2,27)
29 except ValueError:
30 pass
31 else:
32 raise AssertionError("No exception raised on out-of-bounds date")
33
34 def test_set_exclusive_out_of_bounds(self):
35 class Q(param.Parameterized):
36 q = param.CalendarDate(bounds=(dt.date(2017,2,1),
37 dt.date(2017,2,26)),
38 inclusive_bounds=(True, False))
39 try:
40 Q.q = dt.date(2017,2,26)
41 except ValueError:
42 pass
43 else:
44 raise AssertionError("No exception raised on out-of-bounds date")
45
46 def test_get_soft_bounds(self):
47 q = param.CalendarDate(dt.date(2017,2,25),
48 bounds=(dt.date(2017,2,1),
49 dt.date(2017,2,26)),
50 softbounds=(dt.date(2017,2,1),
51 dt.date(2017,2,25)))
52 self.assertEqual(q.get_soft_bounds(), (dt.date(2017,2,1),
53 dt.date(2017,2,25)))
+0
-78
tests/API1/testcalendardaterangeparam.py less more
0 """
1 Unit tests for CalendarDateRange parameter.
2 """
3
4 import datetime as dt
5 import param
6 from . import API1TestCase
7
8 # Assuming tests of range parameter cover most of what's needed to
9 # test date range.
10
11 class TestDateTimeRange(API1TestCase):
12
13 bad_range = (dt.date(2017,2,27),dt.date(2017,2,26))
14
15 def test_wrong_type_default(self):
16 try:
17 class Q(param.Parameterized):
18 a = param.CalendarDateRange(default=(1.0,2.0))
19 except ValueError:
20 pass
21 else:
22 raise AssertionError("Bad date type was accepted.")
23
24 def test_wrong_type_init(self):
25 class Q(param.Parameterized):
26 a = param.CalendarDateRange()
27
28 try:
29 Q(a=self.bad_range)
30 except ValueError:
31 pass
32 else:
33 raise AssertionError("Bad date type was accepted.")
34
35 def test_wrong_type_set(self):
36 class Q(param.Parameterized):
37 a = param.CalendarDateRange()
38 q = Q()
39
40 try:
41 q.a = self.bad_range
42 except ValueError:
43 pass
44 else:
45 raise AssertionError("Bad date type was accepted.")
46
47 def test_start_before_end_default(self):
48 try:
49 class Q(param.Parameterized):
50 a = param.CalendarDateRange(default=self.bad_range)
51 except ValueError:
52 pass
53 else:
54 raise AssertionError("Bad date range was accepted.")
55
56 def test_start_before_end_init(self):
57 class Q(param.Parameterized):
58 a = param.CalendarDateRange()
59
60 try:
61 Q(a=self.bad_range)
62 except ValueError:
63 pass
64 else:
65 raise AssertionError("Bad date range was accepted.")
66
67 def test_start_before_end_set(self):
68 class Q(param.Parameterized):
69 a = param.CalendarDateRange()
70
71 q = Q()
72 try:
73 q.a = self.bad_range
74 except ValueError:
75 pass
76 else:
77 raise AssertionError("Bad date range was accepted.")
+0
-97
tests/API1/testclassselector.py less more
0 """
1 Unit test for ClassSelector parameters.
2 """
3
4
5 from numbers import Number
6
7 import param
8 from . import API1TestCase
9
10
11 class TestClassSelectorParameters(API1TestCase):
12
13 def setUp(self):
14 super(TestClassSelectorParameters, self).setUp()
15 class P(param.Parameterized):
16 e = param.ClassSelector(default=1,class_=int)
17 f = param.ClassSelector(default=int,class_=Number, is_instance=False)
18 g = param.ClassSelector(default=1,class_=(int,str))
19 h = param.ClassSelector(default=int,class_=(int,str), is_instance=False)
20
21 self.P = P
22
23 def test_single_class_instance_constructor(self):
24 p = self.P(e=6)
25 self.assertEqual(p.e, 6)
26
27 def test_single_class_instance_error(self):
28 exception = "Parameter 'e' value must be an instance of int, not 'a'"
29 with self.assertRaisesRegexp(ValueError, exception):
30 self.P(e='a')
31
32 def test_single_class_type_constructor(self):
33 p = self.P(f=float)
34 self.assertEqual(p.f, float)
35
36 def test_single_class_type_error(self):
37 exception = "Parameter 'str' must be a subclass of Number, not 'type'"
38 with self.assertRaisesRegexp(ValueError, exception):
39 self.P(f=str)
40
41 def test_multiple_class_instance_constructor1(self):
42 p = self.P(g=1)
43 self.assertEqual(p.g, 1)
44
45 def test_multiple_class_instance_constructor2(self):
46 p = self.P(g='A')
47 self.assertEqual(p.g, 'A')
48
49 def test_multiple_class_instance_error(self):
50 exception = "Parameter 'g' value must be an instance of \(int, str\), not '3.0'"
51 with self.assertRaisesRegexp(ValueError, exception):
52 self.P(g=3.0)
53
54 def test_multiple_class_type_constructor1(self):
55 p = self.P(h=int)
56 self.assertEqual(p.h, int)
57
58 def test_multiple_class_type_constructor2(self):
59 p = self.P(h=str)
60 self.assertEqual(p.h, str)
61
62 def test_class_selector_get_range(self):
63 p = self.P()
64 classes = p.param.g.get_range()
65 self.assertIn('int', classes)
66 self.assertIn('str', classes)
67
68 def test_multiple_class_type_error(self):
69 exception = "Parameter 'float' must be a subclass of \(int, str\), not 'type'"
70 with self.assertRaisesRegexp(ValueError, exception):
71 self.P(h=float)
72
73
74 class TestDictParameters(API1TestCase):
75
76 def test_valid_dict_parameter(self):
77 valid_dict = {1:2, 3:3}
78
79 class Test(param.Parameterized):
80 items = param.Dict(default=valid_dict)
81
82 def test_valid_dict_parameter_positional(self):
83 valid_dict = {1:2, 3:3}
84
85 class Test(param.Parameterized):
86 items = param.Dict(valid_dict)
87
88 def test_dict_invalid_set(self):
89 valid_dict = {1:2, 3:3}
90 class Test(param.Parameterized):
91 items = param.Dict(valid_dict)
92
93 test = Test()
94 exception = "Parameter 'items' value must be an instance of dict, not '3'"
95 with self.assertRaisesRegexp(ValueError, exception):
96 test.items = 3
+0
-39
tests/API1/testcolorparameter.py less more
0 """
1 Unit test for Color parameters.
2 """
3 import param
4 from . import API1TestCase
5
6 class TestColorParameters(API1TestCase):
7
8 def test_initialization_invalid_string(self):
9 try:
10 class Q(param.Parameterized):
11 q = param.Color('red')
12 except ValueError:
13 pass
14 else:
15 raise AssertionError("No exception raised on out-of-bounds date")
16
17 def test_set_invalid_string(self):
18 class Q(param.Parameterized):
19 q = param.Color()
20 try:
21 Q.q = 'red'
22 except ValueError:
23 pass
24 else:
25 raise AssertionError("No exception raised on out-of-bounds date")
26
27 def test_valid_long_hex(self):
28 class Q(param.Parameterized):
29 q = param.Color()
30 Q.q = '#ffffff'
31 self.assertEqual(Q.q, '#ffffff')
32
33 def test_valid_short_hex(self):
34 class Q(param.Parameterized):
35 q = param.Color()
36 Q.q = '#fff'
37 self.assertEqual(Q.q, '#fff')
38
+0
-100
tests/API1/testcompositeparams.py less more
0 """
1 Unit test for composite parameters.
2
3 Originally implemented as doctests in Topographica in the file
4 testCompositeParameter.txt
5 """
6
7 import param
8 from . import API1TestCase
9
10 class TestCompositeParameters(API1TestCase):
11
12 def setUp(self):
13 super(TestCompositeParameters, self).setUp()
14 # initialize a class with a compound parameter
15 class A(param.Parameterized):
16 x = param.Number(default=0)
17 y = param.Number(default=0)
18 xy = param.Composite(attribs=['x','y'])
19
20 self.A = A
21 self.a = self.A()
22
23 class SomeSequence(object):
24 "Can't use iter with Dynamic (doesn't pickle, doesn't copy)"
25 def __init__(self,sequence):
26 self.sequence=sequence
27 self.index=0
28 def __call__(self):
29 val=self.sequence[self.index]
30 self.index+=1
31 return val
32
33 self.SomeSequence = SomeSequence
34
35 def test_initialization(self):
36 "Make an instance and do default checks"
37 self.assertEqual(self.a.x, 0)
38 self.assertEqual(self.a.y, 0)
39 self.assertEqual(self.a.xy, [0,0])
40
41
42 def test_set_component(self):
43 self.a.x = 1
44 self.assertEqual(self.a.xy, [1,0])
45
46 def test_set_compound(self):
47 self.a.xy = (2,3)
48 self.assertEqual(self.a.x, 2)
49 self.assertEqual(self.a.y, 3)
50
51 def test_compound_class(self):
52 " Get the compound on the class "
53 self.assertEqual(self.A.xy, [0,0])
54
55 def test_set_compound_class_set(self):
56 self.A.xy = (5,6)
57 self.assertEqual(self.A.x, 5)
58 self.assertEqual(self.A.y, 6)
59
60 def test_set_compound_class_instance(self):
61 self.A.xy = (5,6)
62 # # Make a new instance
63 b = self.A()
64 self.assertEqual(b.x, 5)
65 self.assertEqual(b.y, 6)
66
67 def test_set_compound_class_instance_unchanged(self):
68 self.a.xy = (2,3)
69 self.A.xy = (5,6)
70 self.assertEqual(self.a.x, 2)
71 self.assertEqual(self.a.y, 3)
72
73 def test_composite_dynamic(self):
74 """
75 Check CompositeParameter is ok with Dynamic
76 CB: this test is really of Parameterized.
77 """
78 a2 = self.A(x=self.SomeSequence([1,2,3]),
79 y=self.SomeSequence([4,5,6]))
80
81 a2.x, a2.y # Call of x and y params
82 # inspect should not advance numbers
83 self.assertEqual(a2.param.inspect_value('xy'), [1, 4])
84
85 def test_composite_dynamic_generator(self):
86
87 a2 = self.A(x=self.SomeSequence([1,2,3]),
88 y=self.SomeSequence([4,5,6]))
89
90 a2.x, a2.y # Call of x and y params
91 ix,iy = a2.param.get_value_generator('xy')
92 # get_value_generator() should give the objects
93 self.assertEqual(ix(), 2)
94 self.assertEqual(iy(), 5)
95
96
97 if __name__ == "__main__":
98 import nose
99 nose.runmodule()
+0
-54
tests/API1/testdateparam.py less more
0 """
1 Unit test for Date parameters.
2 """
3
4
5 import datetime as dt
6 import param
7 from . import API1TestCase
8
9 class TestDateParameters(API1TestCase):
10
11 def test_initialization_out_of_bounds(self):
12 try:
13 class Q(param.Parameterized):
14 q = param.Date(dt.datetime(2017,2,27),
15 bounds=(dt.datetime(2017,2,1),
16 dt.datetime(2017,2,26)))
17 except ValueError:
18 pass
19 else:
20 raise AssertionError("No exception raised on out-of-bounds date")
21
22 def test_set_out_of_bounds(self):
23 class Q(param.Parameterized):
24 q = param.Date(bounds=(dt.datetime(2017,2,1),
25 dt.datetime(2017,2,26)))
26 try:
27 Q.q = dt.datetime(2017,2,27)
28 except ValueError:
29 pass
30 else:
31 raise AssertionError("No exception raised on out-of-bounds date")
32
33 def test_set_exclusive_out_of_bounds(self):
34 class Q(param.Parameterized):
35 q = param.Date(bounds=(dt.datetime(2017,2,1),
36 dt.datetime(2017,2,26)),
37 inclusive_bounds=(True, False))
38 try:
39 Q.q = dt.datetime(2017,2,26)
40 except ValueError:
41 pass
42 else:
43 raise AssertionError("No exception raised on out-of-bounds date")
44
45 def test_get_soft_bounds(self):
46 q = param.Date(dt.datetime(2017,2,25),
47 bounds=(dt.datetime(2017,2,1),
48 dt.datetime(2017,2,26)),
49 softbounds=(dt.datetime(2017,2,1),
50 dt.datetime(2017,2,25)))
51 self.assertEqual(q.get_soft_bounds(), (dt.datetime(2017,2,1),
52 dt.datetime(2017,2,25)))
53
+0
-80
tests/API1/testdaterangeparam.py less more
0 """
1 Unit tests for DateRange parameter.
2 """
3
4 import datetime as dt
5 import param
6 from . import API1TestCase
7
8 # Assuming tests of range parameter cover most of what's needed to
9 # test date range.
10
11 class TestDateRange(API1TestCase):
12
13 bad_range = (dt.datetime(2017,2,27),dt.datetime(2017,2,26))
14
15 def test_wrong_type_default(self):
16 try:
17 class Q(param.Parameterized):
18 a = param.DateRange(default=(1.0,2.0))
19 except ValueError:
20 pass
21 else:
22 raise AssertionError("Bad date type was accepted.")
23
24 def test_wrong_type_init(self):
25 class Q(param.Parameterized):
26 a = param.DateRange()
27
28 try:
29 Q(a=self.bad_range)
30 except ValueError:
31 pass
32 else:
33 raise AssertionError("Bad date type was accepted.")
34
35 def test_wrong_type_set(self):
36 class Q(param.Parameterized):
37 a = param.DateRange()
38 q = Q()
39
40 try:
41 q.a = self.bad_range
42 except ValueError:
43 pass
44 else:
45 raise AssertionError("Bad date type was accepted.")
46
47 def test_start_before_end_default(self):
48 try:
49 class Q(param.Parameterized):
50 a = param.DateRange(default=self.bad_range)
51 except ValueError:
52 pass
53 else:
54 raise AssertionError("Bad date range was accepted.")
55
56 def test_start_before_end_init(self):
57 class Q(param.Parameterized):
58 a = param.DateRange()
59
60 try:
61 Q(a=self.bad_range)
62 except ValueError:
63 pass
64 else:
65 raise AssertionError("Bad date range was accepted.")
66
67 def test_start_before_end_set(self):
68 class Q(param.Parameterized):
69 a = param.DateRange()
70
71 q = Q()
72 try:
73 q.a = self.bad_range
74 except ValueError:
75 pass
76 else:
77 raise AssertionError("Bad date range was accepted.")
78
79
+0
-57
tests/API1/testdefaults.py less more
0 """
1 Do all subclasses of Parameter supply a valid default?
2 """
3
4 from param.parameterized import add_metaclass
5 from param import concrete_descendents, Parameter
6
7 # import all parameter types
8 from param import * # noqa
9 from param import ClassSelector
10 from . import API1TestCase
11
12 positional_args = {
13 ClassSelector: (object,)
14 }
15
16 skip = []
17
18 try:
19 import numpy # noqa
20 except ImportError:
21 skip.append('Array')
22 try:
23 import pandas # noqa
24 except ImportError:
25 skip.append('DataFrame')
26 skip.append('Series')
27
28
29 class TestDefaultsMetaclass(type):
30 def __new__(mcs, name, bases, dict_):
31
32 def test_skip(*args,**kw):
33 from nose.exc import SkipTest
34 raise SkipTest
35
36 def add_test(p):
37 def test(self):
38 # instantiate parameter with no default (but supply
39 # any required args)
40 p(*positional_args.get(p,tuple()))
41 return test
42
43 for p_name, p_type in concrete_descendents(Parameter).items():
44 dict_["test_default_of_%s"%p_name] = add_test(p_type) if p_name not in skip else test_skip
45
46 return type.__new__(mcs, name, bases, dict_)
47
48
49 @add_metaclass(TestDefaultsMetaclass)
50 class TestDefaults(API1TestCase):
51 pass
52
53
54 if __name__ == "__main__":
55 import nose
56 nose.runmodule()
+0
-294
tests/API1/testdynamicparams.py less more
0 """
1 Unit test for dynamic parameters.
2
3 Tests __get__, __set__ and that inspect_value() and
4 get_value_generator() work.
5
6 Originally implemented as doctests in Topographica in the file
7 testDynamicParameter.txt
8 """
9
10 import copy
11 import param
12 import numbergen
13 from . import API1TestCase
14
15
16 class TestDynamicParameters(API1TestCase):
17
18 def setUp(self):
19 super(TestDynamicParameters, self).setUp()
20 param.Dynamic.time_dependent = False
21
22 class TestPO1(param.Parameterized):
23 x = param.Dynamic(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=1),doc="nothing")
24 y = param.Dynamic(default=1)
25
26 class TestPO2(param.Parameterized):
27 x = param.Dynamic(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=30))
28 y = param.Dynamic(default=1.0)
29
30 self.TestPO2 = TestPO2
31 self.TestPO1 = TestPO1
32
33 self.t1 = self.TestPO1()
34 self.t2 = self.TestPO1(x=numbergen.UniformRandom(lbound=-1,ubound=1,seed=10))
35 self.t3 = self.TestPO1(x=numbergen.UniformRandom(lbound=-1,ubound=1,seed=10))
36 self.t2.param.set_dynamic_time_fn(None)
37 self.t3.param.set_dynamic_time_fn(None)
38
39 self.t6 = self.TestPO2()
40 self.t7 = self.TestPO2()
41
42
43 class TestDynamicParameterBasics(TestDynamicParameters):
44
45 def test_set_dynamic_time_fn_x(self):
46 self.t1.param.set_dynamic_time_fn(None)
47 self.assertEqual(
48 self.t1.param.params()['x']._value_is_dynamic(self.t1), True)
49
50 def test_set_dynamic_time_fn_y(self):
51 self.assertEqual(
52 self.t1.param.params()['y']._value_is_dynamic(self.t1), False)
53
54 def test_inspect_x(self):
55 "no value generated yet"
56 self.assertEqual(self.t1.param.inspect_value('x'), None)
57
58 def test_inspect_y(self):
59 self.assertEqual(self.t1.param.inspect_value('y'), 1)
60
61 def test_inspect_y_set(self):
62 self.t1.y = 2
63 self.assertEqual(self.t1.param.inspect_value('y'), 2)
64
65 def test_set_dynamic_numbergen(self):
66 is_numbergen = isinstance(self.t2.param.get_value_generator('x'),
67 numbergen.UniformRandom)
68 self.assertEqual(is_numbergen, True)
69
70 def test_matching_numbergen_streams(self):
71 "check that t2 and t3 have identical streams"
72 self.assertEqual(self.t2.x, self.t3.x)
73
74 def test_numbergen_objects_distinct(self):
75 "check t2 and t3 do not share UniformRandom objects"
76 self.t2.x
77 self.assertNotEqual(self.t2.param.inspect_value('x'),
78 self.t3.param.inspect_value('x'))
79
80 def test_numbergen_inspect(self):
81 " inspect_value() should return last generated value "
82 self.t2.x # Call 1
83 self.t2.x # Call 2
84 t2_last_value = self.t2.x # advance t2 beyond t3
85
86 self.assertEqual(self.t2.param.inspect_value('x'),
87 t2_last_value)
88 # ensure last_value is not shared
89 self.assertNotEqual(self.t3.param.inspect_value('x'), t2_last_value)
90
91 def test_dynamic_value_instantiated(self):
92 t6_first_value = self.t6.x
93 self.assertNotEqual(self.t7.param.inspect_value('x'),
94 t6_first_value)
95
96 def test_non_dynamic_value_not_instantiated(self):
97 " non-dynamic value not instantiated"
98 self.TestPO2.y = 4
99 self.assertEqual(self.t6.y, 4)
100 self.assertEqual(self.t7.y, 4)
101
102 def test_dynamic_value_setting(self):
103 self.t6.y = numbergen.UniformRandom()
104 t8 = self.TestPO2()
105 self.TestPO2.y = 10
106 # t6 got a dynamic value, but shouldn't have changed Parameter's instantiate
107 self.assertEqual(t8.y, 10)
108
109 def test_setting_y_param_numbergen(self):
110 self.TestPO2.y=numbergen.UniformRandom() # now the Parameter instantiate should be true
111 t9 = self.TestPO2()
112 self.assertEqual('_y_param_value' in t9.__dict__, True)
113
114 def test_shared_numbergen(self):
115 """
116 Instances of TestPO2 that don't have their own value for the
117 parameter share one UniformRandom object
118 """
119 self.TestPO2.y=numbergen.UniformRandom() # now the Parameter instantiate should be true
120 self.assertEqual(self.t7.param.get_value_generator('y') is self.TestPO2().param.params()['y'].default, True)
121 self.assertEqual(self.TestPO2().param.params()['y'].default.__class__.__name__, 'UniformRandom')
122
123 def test_copy_match(self):
124 "check a copy is the same"
125 t9 = copy.deepcopy(self.t7)
126 self.assertEqual(t9.param.get_value_generator('y') is self.TestPO2().param.params()['y'].default, True)
127
128
129
130 class TestDynamicTimeDependent(TestDynamicParameters):
131
132 def setUp(self):
133 super(TestDynamicTimeDependent, self).setUp()
134 param.Dynamic.time_dependent = True
135
136 class TestPO3(param.Parameterized):
137 x = param.Dynamic(default=numbergen.UniformRandom(name='xgen',
138 time_dependent=True))
139
140 class TestPO4(self.TestPO1):
141 "Nested parameterized objects"
142 z = param.Parameter(default=self.TestPO1())
143
144 self.TestPO3 = TestPO3
145 self.TestPO4 = TestPO4
146
147 self.t10 = self.TestPO1()
148 self.t11 = TestPO3()
149
150 def test_dynamic_values_unchanged_dependent(self):
151 param.Dynamic.time_dependent = True
152 call_1 = self.t10.x
153 call_2 = self.t10.x
154 call_3 = self.t10.x
155 self.assertEqual(call_1, call_2)
156 self.assertEqual(call_2, call_3)
157
158 def test_dynamic_values_changed_independent(self):
159 param.Dynamic.time_dependent = False
160 call_1 = self.t10.x
161 call_2 = self.t10.x
162 call_3 = self.t10.x
163 self.assertNotEqual(call_1, call_2)
164 self.assertNotEqual(call_2, call_3)
165
166 def test_dynamic_values_change(self):
167 param.Dynamic.time_dependent = True
168 with param.Dynamic.time_fn as t:
169 t(0)
170 call_1 = self.t10.x
171 t += 1
172 call_2 = self.t10.x
173 t(0)
174 call_3 = self.t10.x
175 self.assertNotEqual(call_1, call_2)
176 self.assertNotEqual(call_1, call_3)
177
178 def test_dynamic_values_time_dependent(self):
179 param.Dynamic.time_dependent = True
180 with param.Dynamic.time_fn as t:
181 t(0)
182 call_1 = self.t11.x
183 t += 1
184 call_2 = self.t11.x
185 t(0)
186 call_3 = self.t11.x
187 self.assertNotEqual(call_1, call_2)
188 self.assertEqual(call_1, call_3)
189
190 def test_class_dynamic_values_change(self):
191 call_1 = self.TestPO3.x
192 call_2 = self.TestPO3.x
193 self.assertEqual(call_1, call_2)
194 with param.Dynamic.time_fn as t:
195 t += 1
196 call_3 = self.TestPO3.x
197 self.assertNotEqual(call_2, call_3)
198
199 def test_dynamic_value_change_independent(self):
200 t12 = self.TestPO1()
201 t12.param.set_dynamic_time_fn(None)
202 self.assertNotEqual(t12.x, t12.x)
203 self.assertEqual(t12.y, t12.y)
204
205 def test_dynamic_value_change_disabled(self):
206 " time_fn set on the UniformRandom() when t13.y was set"
207 t13 = self.TestPO1()
208 t13.param.set_dynamic_time_fn(None)
209 t13.y = numbergen.UniformRandom()
210 self.assertNotEqual(t13.y, t13.y)
211
212 def test_dynamic_value_change_enabled(self):
213 " time_fn set on the UniformRandom() when t13.y was set"
214 t14 = self.TestPO1()
215 t14.y = numbergen.UniformRandom()
216 self.assertEqual(t14.y, t14.y)
217
218
219 def test_dynamic_time_fn_not_inherited(self):
220 " time_fn not inherited"
221 t15 = self.TestPO4()
222 t15.param.set_dynamic_time_fn(None)
223 with param.Dynamic.time_fn as t:
224 call_1 = t15.z.x
225 t += 1
226 call_2 = t15.z.x
227 self.assertNotEqual(call_1, call_2)
228
229
230
231 class TestDynamicSharedNumbergen(TestDynamicParameters):
232 "Check shared generator"
233 def setUp(self):
234 super(TestDynamicSharedNumbergen, self).setUp()
235 self.shared = numbergen.UniformRandom(lbound=-1,ubound=1,seed=20)
236
237 def test_dynamic_shared_numbergen(self):
238 param.Dynamic.time_dependent = True
239 t11 = self.TestPO1(x=self.shared)
240 t12 = self.TestPO1(x=self.shared)
241
242 with param.Dynamic.time_fn as t:
243 t += 1
244 call_1 = t11.x
245 self.assertEqual(call_1, t12.x)
246 t += 1
247 self.assertNotEqual(call_1, t12.x)
248
249
250
251 if __name__ == "__main__":
252 import nose
253 nose.runmodule()
254
255
256 # Commented out block in the original doctest version.
257 # Maybe these are features originally planned but never implemented
258
259 """
260 It is not yet possible to set time_fn for a Parameter instance
261 >>> class TestPO5(param.Parameterized):
262 ... x = param.Dynamic(default=numbergen.UniformRandom(),dynamic_time_fn=None)
263 """
264
265 """
266 We currently don't support iterators/generators in Dynamic unless
267 they're wrapped.
268
269 >>> i = iter([1,2,3])
270 >>> t11.x = i
271
272 >>> topo.sim.run(1)
273
274 >>> t11.x
275 1
276
277 >>> def gen():
278 ... yield 2
279 ... yield 4
280 ... yield 6
281
282 >>> g = gen()
283
284 >>> t11.x = g
285
286 >>> t11.x
287 2
288
289 >>> topo.sim.run(1)
290
291 >>> t11.x
292 4
293 """
+0
-67
tests/API1/testipythonmagic.py less more
0 """
1 Unit test for the IPython magic
2 """
3
4 import re
5 import sys
6 import param
7 from . import API1TestCase
8
9 try:
10 import IPython # noqa
11 except ImportError:
12 import os
13 if os.getenv('PARAM_TEST_IPYTHON','0') == '1':
14 raise ImportError("PARAM_TEST_IPYTHON=1 but ipython not available.")
15
16 # TODO: is the below actually true?
17
18 # SkipTest will be raised if IPython unavailable
19 from param.ipython import ParamPager
20
21 test1_repr = """\x1b[1;32mParameters of 'TestClass'\n=========================\n\x1b[0m\n\x1b[1;31mParameters changed from their default values are marked in red.\x1b[0m\n\x1b[1;36mSoft bound values are marked in cyan.\x1b[0m\nC/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None\n\n\x1b[1;34mNameValue Type Bounds Mode \x1b[0m\n\nu 4 Number V RW \nv 4 Number C RW \nw 4 Number C RO \nx None String V RW AN \ny 4 Number (-1, None) V RW \nz 4 Number (-1, 100) V RW \n\n\x1b[1;32mParameter docstrings:\n=====================\x1b[0m\n\n\x1b[1;34mu: < No docstring available >\x1b[0m\n\x1b[1;31mv: < No docstring available >\x1b[0m\n\x1b[1;34mw: < No docstring available >\x1b[0m\n\x1b[1;31mx: < No docstring available >\x1b[0m\n\x1b[1;34my: < No docstring available >\x1b[0m\n\x1b[1;31mz: < No docstring available >\x1b[0m"""
22
23
24 test2_repr = """\x1b[1;32mParameters of 'TestClass' instance\n==================================\n\x1b[0m\n\x1b[1;31mParameters changed from their default values are marked in red.\x1b[0m\n\x1b[1;36mSoft bound values are marked in cyan.\x1b[0m\nC/V= Constant/Variable, RO/RW = ReadOnly/ReadWrite, AN=Allow None\n\n\x1b[1;34mNameValue Type Bounds Mode \x1b[0m\n\nu 4 Number V RW \nv 4 Number C RW \nw 4 Number C RO \nx None String V RW AN \ny 4 Number (-1, None) V RW \nz 4 Number (-1, 100) V RW \n\n\x1b[1;32mParameter docstrings:\n=====================\x1b[0m\n\n\x1b[1;34mu: < No docstring available >\x1b[0m\n\x1b[1;31mv: < No docstring available >\x1b[0m\n\x1b[1;34mw: < No docstring available >\x1b[0m\n\x1b[1;31mx: < No docstring available >\x1b[0m\n\x1b[1;34my: < No docstring available >\x1b[0m\n\x1b[1;31mz: < No docstring available >\x1b[0m"""
25
26 class TestParamPager(API1TestCase):
27
28 def setUp(self):
29 super(TestParamPager, self).setUp()
30 self.maxDiff = None
31 class TestClass(param.Parameterized):
32 u = param.Number(4)
33 v = param.Number(4, constant=True)
34 w = param.Number(4, readonly=True)
35 x = param.String(None, allow_None=True)
36 y = param.Number(4, bounds=(-1, None))
37 z = param.Number(4, bounds=(-1, 100), softbounds=(-100, -200))
38
39 self.TestClass = TestClass
40 self.pager = ParamPager()
41
42 def test_parameterized_class(self):
43 page_string = self.pager(self.TestClass)
44 # Remove params automatic numbered names
45 page_string = re.sub('TestClass(\d+)', 'TestClass', page_string)
46 ref_string = re.sub('TestClass(\d+)', 'TestClass', test1_repr)
47
48 try:
49 self.assertEqual(page_string, ref_string)
50 except Exception as e:
51 sys.stderr.write(page_string) # Coloured output
52 sys.stderr.write("\nRAW STRING:\n\n%r\n\n" % page_string)
53 raise e
54
55 def test_parameterized_instance(self):
56 page_string = self.pager(self.TestClass())
57 # Remove params automatic numbered names
58 page_string = re.sub('TestClass(\d+)', 'TestClass', page_string)
59 ref_string = re.sub('TestClass(\d+)', 'TestClass', test2_repr)
60
61 try:
62 self.assertEqual(page_string, ref_string)
63 except Exception as e:
64 sys.stderr.write(page_string) # Coloured output
65 sys.stderr.write("\nRAW STRING:\n\n%r\n\n" % page_string)
66 raise e
+0
-160
tests/API1/testlistselector.py less more
0 import param
1 from . import API1TestCase
2 # TODO: I copied the tests from testobjectselector, although I
3 # struggled to understand some of them. Both files should be reviewed
4 # and cleaned up together.
5
6 # TODO: tests copied from testobjectselector could use assertRaises
7 # context manager (and could be updated in testobjectselector too).
8
9 class TestListSelectorParameters(API1TestCase):
10
11 def setUp(self):
12 super(TestListSelectorParameters, self).setUp()
13 class P(param.Parameterized):
14 e = param.ListSelector(default=[5],objects=[5,6,7])
15 f = param.ListSelector(default=10)
16 h = param.ListSelector(default=None)
17 g = param.ListSelector(default=None,objects=[7,8])
18 i = param.ListSelector(default=[7],objects=[9],check_on_set=False)
19
20 self.P = P
21
22 def test_default_None(self):
23 class Q(param.Parameterized):
24 r = param.ListSelector(default=None)
25
26 def test_set_object_constructor(self):
27 p = self.P(e=[6])
28 self.assertEqual(p.e, [6])
29
30 def test_set_object_outside_bounds(self):
31 p = self.P(e=[6])
32 try:
33 p.e = [9]
34 except ValueError:
35 pass
36 else:
37 raise AssertionError("Object set outside range.")
38
39 def test_set_object_setattr(self):
40 p = self.P(e=[6])
41 p.f = [9]
42 self.assertEqual(p.f, [9])
43 p.g = [7]
44 self.assertEqual(p.g, [7])
45 p.i = [12]
46 self.assertEqual(p.i, [12])
47
48
49 def test_set_object_not_None(self):
50 p = self.P(e=[6])
51 p.g = [7]
52 try:
53 p.g = None
54 except TypeError:
55 pass
56 else:
57 raise AssertionError("Object set outside range.")
58
59 def test_set_one_object_not_None(self):
60 p = self.P(e=[6])
61 p.g = [7]
62 try:
63 p.g = [None]
64 except ValueError:
65 pass
66 else:
67 raise AssertionError("Object set outside range.")
68
69
70 def test_set_object_setattr_post_error(self):
71 p = self.P(e=[6])
72 p.f = [9]
73 self.assertEqual(p.f, [9])
74 p.g = [7]
75 try:
76 p.g = [None]
77 except ValueError:
78 pass
79 else:
80 raise AssertionError("Object set outside range.")
81
82 self.assertEqual(p.g, [7])
83 p.i = [12]
84 self.assertEqual(p.i, [12])
85
86 def test_initialization_out_of_bounds(self):
87 try:
88 class Q(param.Parameterized):
89 q = param.ListSelector([5],objects=[4])
90 except ValueError:
91 pass
92 else:
93 raise AssertionError("ListSelector created outside range.")
94
95
96 def test_initialization_no_bounds(self):
97 try:
98 class Q(param.Parameterized):
99 q = param.ListSelector([5],objects=10)
100 except TypeError:
101 pass
102 else:
103 raise AssertionError("ListSelector created without range.")
104
105
106 ##################################################################
107 ##################################################################
108 ### new tests (not copied from testobjectselector)
109
110 def test_bad_default(self):
111 with self.assertRaises(TypeError):
112 class Q(param.Parameterized):
113 r = param.ListSelector(default=6,check_on_set=True)
114
115 def test_implied_check_on_set(self):
116 with self.assertRaises(TypeError):
117 class Q(param.Parameterized):
118 r = param.ListSelector(default=7,objects=[7,8])
119
120 def test_default_not_checked(self):
121 class Q(param.Parameterized):
122 r = param.ListSelector(default=[6])
123
124 ##########################
125 # CEBALERT: not sure it makes sense for ListSelector to be set to
126 # a non-iterable value (except None). I.e. I think this first test
127 # should fail.
128 def test_default_not_checked_to_be_iterable(self):
129 class Q(param.Parameterized):
130 r = param.ListSelector(default=6)
131
132 def test_set_checked_to_be_iterable(self):
133 class Q(param.Parameterized):
134 r = param.ListSelector(default=6,check_on_set=False)
135
136 with self.assertRaises(TypeError):
137 Q.r = 6
138 ##########################
139
140
141 def test_compute_default(self):
142 class Q(param.Parameterized):
143 r = param.ListSelector(default=None, compute_default_fn=lambda: [1,2,3])
144
145 self.assertEqual(Q.r, None)
146 Q.param.params('r').compute_default()
147 self.assertEqual(Q.r, [1,2,3])
148 self.assertEqual(Q.param.params('r').objects, [1,2,3])
149
150 def test_bad_compute_default(self):
151 class Q(param.Parameterized):
152 r = param.ListSelector(default=None,compute_default_fn=lambda:1)
153
154 with self.assertRaises(TypeError):
155 Q.param.params('r').compute_default()
156
157 if __name__ == "__main__":
158 import nose
159 nose.runmodule()
+0
-37
tests/API1/testnumbergen.py less more
0 """
1 Test cases for the numbergen module.
2 """
3 import numbergen
4 from . import API1TestCase
5
6 _seed = 0 # keep tests deterministic
7 _iterations = 1000
8
9
10 class TestUniformRandom(API1TestCase):
11 def test_range(self):
12 lbound = 2.0
13 ubound = 5.0
14 gen = numbergen.UniformRandom(
15 seed=_seed,
16 lbound=lbound,
17 ubound=ubound)
18 for _ in range(_iterations):
19 value = gen()
20 self.assertTrue(lbound <= value < ubound)
21
22 class TestUniformRandomOffset(API1TestCase):
23 def test_range(self):
24 lbound = 2.0
25 ubound = 5.0
26 gen = numbergen.UniformRandomOffset(
27 seed=_seed,
28 mean=(ubound + lbound) / 2,
29 range=ubound - lbound)
30 for _ in range(_iterations):
31 value = gen()
32 self.assertTrue(lbound <= value < ubound)
33
34 if __name__ == "__main__":
35 import nose
36 nose.runmodule()
+0
-54
tests/API1/testnumberparameter.py less more
0 """
1 Unit test for Number parameters and their subclasses.
2 """
3 import param
4 import datetime as dt
5 from . import API1TestCase
6
7
8 class TestNumberParameters(API1TestCase):
9
10 def test_initialization_without_step_class(self):
11 class Q(param.Parameterized):
12 q = param.Number(default=1)
13
14 self.assertEqual(Q.param['q'].step, None)
15
16 def test_initialization_with_step_class(self):
17 class Q(param.Parameterized):
18 q = param.Number(default=1, step=0.5)
19
20 self.assertEqual(Q.param['q'].step, 0.5)
21
22 def test_initialization_without_step_instance(self):
23 class Q(param.Parameterized):
24 q = param.Number(default=1)
25
26 self.assertEqual(Q.param['q'].step, None)
27
28 def test_initialization_with_step_instance(self):
29 class Q(param.Parameterized):
30 q = param.Number(default=1, step=0.5)
31
32 qobj = Q()
33 self.assertEqual(qobj.param['q'].step, 0.5)
34
35 def test_step_invalid_type_number_parameter(self):
36 exception = "Step parameter can only be None or a numeric value"
37 with self.assertRaisesRegexp(ValueError, exception):
38 param.Number(step='invalid value')
39
40 def test_step_invalid_type_integer_parameter(self):
41 exception = "Step parameter can only be None or an integer value"
42 with self.assertRaisesRegexp(ValueError, exception):
43 param.Integer(step=3.4)
44
45 def test_step_invalid_type_datetime_parameter(self):
46 exception = "Step parameter can only be None, a datetime or datetime type"
47 with self.assertRaisesRegexp(ValueError, exception):
48 param.Date(dt.datetime(2017,2,27), step=3.2)
49
50 def test_step_invalid_type_date_parameter(self):
51 exception = "Step parameter can only be None or a date type"
52 with self.assertRaisesRegexp(ValueError, exception):
53 param.CalendarDate(dt.date(2017,2,27), step=3.2)
+0
-47
tests/API1/testnumpy.py less more
0 """
1 If numpy's present, is numpy stuff ok?
2 """
3 import unittest
4 import os
5
6 import param
7 from . import API1TestCase
8
9 try:
10 import numpy
11 import numpy.testing
12 except ImportError:
13 if os.getenv('PARAM_TEST_NUMPY','0') == '1':
14 raise ImportError("PARAM_TEST_NUMPY=1 but numpy not available.")
15 else:
16 raise unittest.SkipTest("numpy not available")
17
18
19 def _is_array_and_equal(test,ref):
20 if not type(test) == numpy.ndarray:
21 raise AssertionError
22 numpy.testing.assert_array_equal(test,ref)
23
24 # TODO: incomplete
25 class TestNumpy(API1TestCase):
26 def test_array_param(self):
27 class Z(param.Parameterized):
28 z = param.Array(default=numpy.array([1]))
29
30 _is_array_and_equal(Z.z,[1])
31
32 z = Z(z=numpy.array([1,2]))
33 _is_array_and_equal(z.z,[1,2])
34
35 def test_array_param_positional(self):
36 class Z(param.Parameterized):
37 z = param.Array(numpy.array([1]))
38
39 _is_array_and_equal(Z.z,[1])
40
41 z = Z(z=numpy.array([1,2]))
42 _is_array_and_equal(z.z,[1,2])
43
44 if __name__ == "__main__":
45 import nose
46 nose.runmodule()
+0
-120
tests/API1/testobjectselector.py less more
0 """
1 Unit test for object selector parameters.
2
3 Originally implemented as doctests in Topographica in the file
4 testEnumerationParameter.txt
5 """
6
7 import param
8 from . import API1TestCase
9 from collections import OrderedDict
10
11
12 opts=dict(A=[1,2],B=[3,4],C=dict(a=1,b=2))
13
14
15 class TestObjectSelectorParameters(API1TestCase):
16
17 def setUp(self):
18 super(TestObjectSelectorParameters, self).setUp()
19 class P(param.Parameterized):
20 e = param.ObjectSelector(default=5,objects=[5,6,7])
21 f = param.ObjectSelector(default=10)
22 h = param.ObjectSelector(default=None)
23 g = param.ObjectSelector(default=None,objects=[7,8])
24 i = param.ObjectSelector(default=7,objects=[9],check_on_set=False)
25 s = param.ObjectSelector(default=3,objects=OrderedDict(one=1,two=2,three=3))
26 d = param.ObjectSelector(default=opts['B'],objects=opts)
27
28 self.P = P
29
30 def test_set_object_constructor(self):
31 p = self.P(e=6)
32 self.assertEqual(p.e, 6)
33
34 def test_get_range_list(self):
35 r = self.P.param.params("g").get_range()
36 self.assertEqual(r['7'],7)
37 self.assertEqual(r['8'],8)
38
39 def test_get_range_dict(self):
40 r = self.P.param.params("s").get_range()
41 self.assertEqual(r['one'],1)
42 self.assertEqual(r['two'],2)
43
44 def test_get_range_mutable(self):
45 r = self.P.param.params("d").get_range()
46 self.assertEqual(r['A'],opts['A'])
47 self.assertEqual(r['C'],opts['C'])
48 self.d=opts['A']
49 self.d=opts['C']
50 self.d=opts['B']
51
52 def test_set_object_outside_bounds(self):
53 p = self.P(e=6)
54 try:
55 p.e = 9
56 except ValueError:
57 pass
58 else:
59 raise AssertionError("Object set outside range.")
60
61 def test_set_object_setattr(self):
62 p = self.P(e=6)
63 p.f = 9
64 self.assertEqual(p.f, 9)
65 p.g = 7
66 self.assertEqual(p.g, 7)
67 p.i = 12
68 self.assertEqual(p.i, 12)
69
70
71 def test_set_object_not_None(self):
72 p = self.P(e=6)
73 p.g = 7
74 try:
75 p.g = None
76 except ValueError:
77 pass
78 else:
79 raise AssertionError("Object set outside range.")
80
81 def test_set_object_setattr_post_error(self):
82 p = self.P(e=6)
83 p.f = 9
84 self.assertEqual(p.f, 9)
85 p.g = 7
86 try:
87 p.g = None
88 except ValueError:
89 pass
90 else:
91 raise AssertionError("Object set outside range.")
92
93 self.assertEqual(p.g, 7)
94 p.i = 12
95 self.assertEqual(p.i, 12)
96
97 def test_initialization_out_of_bounds(self):
98 try:
99 class Q(param.Parameterized):
100 q = param.ObjectSelector(5,objects=[4])
101 except ValueError:
102 pass
103 else:
104 raise AssertionError("ObjectSelector created outside range.")
105
106
107 def test_initialization_no_bounds(self):
108 try:
109 class Q(param.Parameterized):
110 q = param.ObjectSelector(5,objects=10)
111 except TypeError:
112 pass
113 else:
114 raise AssertionError("ObjectSelector created without range.")
115
116
117 if __name__ == "__main__":
118 import nose
119 nose.runmodule()
+0
-181
tests/API1/testpandas.py less more
0 """
1 Test Parameters based on pandas
2 """
3 import unittest
4 import os
5
6 import param
7 from . import API1TestCase
8
9 try:
10 import pandas
11 except ImportError:
12 if os.getenv('PARAM_TEST_PANDAS','0') == '1':
13 raise ImportError("PARAM_TEST_PANDAS=1 but pandas not available.")
14 else:
15 raise unittest.SkipTest("pandas not available")
16
17
18 class TestDataFrame(API1TestCase):
19
20 def test_dataframe_positional_argument(self):
21 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]},
22 columns=['b', 'a', 'c'])
23 class Test(param.Parameterized):
24 df = param.DataFrame(valid_df)
25
26 def test_empty_dataframe_param_invalid_set(self):
27 empty = pandas.DataFrame()
28 class Test(param.Parameterized):
29 df = param.DataFrame(default=empty)
30
31 test = Test()
32 exception = "Parameter 'df' value must be an instance of DataFrame, not '3'"
33 with self.assertRaisesRegexp(ValueError, exception):
34 test.df = 3
35
36 def test_dataframe_unordered_column_set_valid(self):
37 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
38 class Test(param.Parameterized):
39 df = param.DataFrame(default=valid_df, columns={'a', 'b'})
40
41
42 def test_dataframe_unordered_column_set_invalid(self):
43 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'd':[4,5]}, columns=['b', 'a', 'd'])
44 invalid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
45
46 class Test(param.Parameterized):
47 df = param.DataFrame(default=valid_df, columns={'a', 'd'})
48
49
50 test = Test()
51 self.assertEquals(test.param.params('df').ordered, False)
52 exception = "Provided DataFrame columns \['b', 'a', 'c'\] does not contain required columns \['a', 'd'\]"
53 with self.assertRaisesRegexp(ValueError, exception):
54 test.df = invalid_df
55
56 def test_dataframe_ordered_column_list_valid(self):
57 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
58 class Test(param.Parameterized):
59 test = param.DataFrame(default=valid_df, columns=['b', 'a', 'c'])
60
61
62 def test_dataframe_ordered_column_list_invalid(self):
63 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'd':[4,5]}, columns=['b', 'a', 'd'])
64 invalid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['a', 'b', 'd'])
65
66 class Test(param.Parameterized):
67 df = param.DataFrame(default=valid_df, columns=['b', 'a', 'd'])
68
69 test = Test()
70 self.assertEquals(test.param.params('df').ordered, True)
71
72 exception = "Provided DataFrame columns \['a', 'b', 'd'\] must exactly match \['b', 'a', 'd'\]"
73 with self.assertRaisesRegexp(ValueError, exception):
74 test.df = invalid_df
75
76
77 def test_dataframe_unordered_column_number_valid_df(self):
78 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
79 class Test(param.Parameterized):
80 df = param.DataFrame(default=valid_df, columns=3)
81
82 def test_dataframe_unordered_column_number_invalid(self):
83 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
84 invalid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3]}, columns=['b', 'a'])
85 class Test(param.Parameterized):
86 df = param.DataFrame(default=valid_df, columns=3)
87
88 test = Test()
89 self.assertEquals(test.param.params('df').ordered, None)
90
91 exception = "Column length 2 does not match declared bounds of 3"
92 with self.assertRaisesRegexp(ValueError, exception):
93 test.df = invalid_df
94
95
96 def test_dataframe_unordered_column_tuple_valid(self):
97 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
98 class Test(param.Parameterized):
99 df = param.DataFrame(default=valid_df, columns=(None,3))
100
101 def test_dataframe_unordered_column_tuple_invalid(self):
102
103 invalid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
104
105 exception = "Columns length 3 does not match declared bounds of \(None, 2\)"
106 with self.assertRaisesRegexp(ValueError, exception):
107 class Test(param.Parameterized):
108 df = param.DataFrame(default=invalid_df, columns=(None,2))
109
110 def test_dataframe_row_number_valid_df(self):
111 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
112 class Test(param.Parameterized):
113 df = param.DataFrame(default=valid_df, rows=2)
114
115 def test_dataframe_row_number_invalid(self):
116 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3]}, columns=['b', 'a'])
117 invalid_df = pandas.DataFrame({'a':[1,2,4], 'b':[2,3,4]}, columns=['b', 'a'])
118 class Test(param.Parameterized):
119 df = param.DataFrame(default=valid_df, rows=2)
120
121 test = Test()
122 exception = "Row length 3 does not match declared bounds of 2"
123 with self.assertRaisesRegexp(ValueError, exception):
124 test.df = invalid_df
125
126 def test_dataframe_unordered_row_tuple_valid(self):
127 valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
128 class Test(param.Parameterized):
129 df = param.DataFrame(default=valid_df, rows=(None,3))
130
131 def test_dataframe_unordered_row_tuple_invalid(self):
132
133 invalid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}, columns=['b', 'a', 'c'])
134
135 exception = "Row length 2 does not match declared bounds of \(5, 7\)"
136 with self.assertRaisesRegexp(ValueError, exception):
137 class Test(param.Parameterized):
138 df = param.DataFrame(default=invalid_df, rows=(5,7))
139
140
141 class TestSeries(API1TestCase):
142
143 def test_series_positional_argument(self):
144 valid_series = pandas.Series([1,2])
145 class Test(param.Parameterized):
146 series = param.Series(valid_series, rows=2)
147
148 def test_series_row_number_valid(self):
149 valid_series = pandas.Series([1,2])
150 class Test(param.Parameterized):
151 series = param.Series(default=valid_series, rows=2)
152
153 def test_series_row_number_invalid(self):
154 valid_series = pandas.Series([1,2])
155 invalid_series = pandas.Series([1,2,3])
156 class Test(param.Parameterized):
157 series = param.Series(default=valid_series, rows=2)
158
159 test = Test()
160 exception = "Row length 3 does not match declared bounds of 2"
161 with self.assertRaisesRegexp(ValueError, exception):
162 test.series = invalid_series
163
164 def test_series_unordered_row_tuple_valid(self):
165 valid_series = pandas.Series([1,2,3])
166 class Test(param.Parameterized):
167 series = param.Series(default=valid_series, rows=(None,3))
168
169 def test_series_unordered_row_tuple_invalid(self):
170
171 invalid_series = pandas.Series([1,2])
172
173 exception = "Row length 2 does not match declared bounds of \(5, 7\)"
174 with self.assertRaisesRegexp(ValueError, exception):
175 class Test(param.Parameterized):
176 series = param.Series(default=invalid_series, rows=(5,7))
177
178 if __name__ == "__main__":
179 import nose
180 nose.runmodule()
+0
-158
tests/API1/testparamdepends.py less more
0 """
1 Unit test for param.depends.
2 """
3
4
5 import param
6 from . import API1TestCase
7
8
9 class TestParamDepends(API1TestCase):
10
11 def setUp(self):
12 class P(param.Parameterized):
13 a = param.Parameter()
14 b = param.Parameter()
15
16 @param.depends('a')
17 def single_parameter(self):
18 pass
19
20 @param.depends('a:constant')
21 def constant(self):
22 pass
23
24 @param.depends('a.param')
25 def nested(self):
26 pass
27
28 class P2(param.Parameterized):
29
30 @param.depends(P.param.a)
31 def external_param(self, a):
32 pass
33
34 self.P = P
35 self.P2 = P2
36
37 def test_param_depends_instance(self):
38 p = self.P()
39 pinfos = p.param.params_depended_on('single_parameter')
40 self.assertEqual(len(pinfos), 1)
41 pinfo = pinfos[0]
42 self.assertIs(pinfo.cls, self.P)
43 self.assertIs(pinfo.inst, p)
44 self.assertEqual(pinfo.name, 'a')
45 self.assertEqual(pinfo.what, 'value')
46
47 def test_param_depends_class(self):
48 pinfos = self.P.param.params_depended_on('single_parameter')
49 self.assertEqual(len(pinfos), 1)
50 pinfo = pinfos[0]
51 self.assertIs(pinfo.cls, self.P)
52 self.assertIs(pinfo.inst, None)
53 self.assertEqual(pinfo.name, 'a')
54 self.assertEqual(pinfo.what, 'value')
55
56 def test_param_depends_constant(self):
57 pinfos = self.P.param.params_depended_on('constant')
58 self.assertEqual(len(pinfos), 1)
59 pinfo = pinfos[0]
60 self.assertIs(pinfo.cls, self.P)
61 self.assertIs(pinfo.inst, None)
62 self.assertEqual(pinfo.name, 'a')
63 self.assertEqual(pinfo.what, 'constant')
64
65 def test_param_depends_nested(self):
66 inst = self.P(a=self.P())
67 pinfos = inst.param.params_depended_on('nested')
68 self.assertEqual(len(pinfos), 4)
69 pinfos = {(pi.inst, pi.name): pi for pi in pinfos}
70 pinfo = pinfos[(inst, 'a')]
71 self.assertIs(pinfo.cls, self.P)
72 self.assertIs(pinfo.inst, inst)
73 self.assertEqual(pinfo.name, 'a')
74 self.assertEqual(pinfo.what, 'value')
75 for p in ['name', 'a', 'b']:
76 info = pinfos[(inst.a, p)]
77 self.assertEqual(info.name, p)
78 self.assertIs(info.inst, inst.a)
79
80 def test_param_external_param_instance(self):
81 inst = self.P2()
82 pinfos = inst.param.params_depended_on('external_param')
83 pinfo = pinfos[0]
84 self.assertIs(pinfo.cls, self.P)
85 self.assertIs(pinfo.inst, None)
86 self.assertEqual(pinfo.name, 'a')
87 self.assertEqual(pinfo.what, 'value')
88
89
90
91 class TestParamDependsFunction(API1TestCase):
92
93 def setUp(self):
94 class P(param.Parameterized):
95 a = param.Parameter()
96 b = param.Parameter()
97
98
99 self.P = P
100
101 def test_param_depends_function_instance_params(self):
102 p = self.P()
103
104 @param.depends(p.param.a, c=p.param.b)
105 def function(value, c):
106 pass
107
108 dependencies = {
109 'dependencies': (p.param.a,),
110 'kw': {'c': p.param.b},
111 'watch': False
112 }
113 self.assertEqual(function._dinfo, dependencies)
114
115 def test_param_depends_function_class_params(self):
116 p = self.P
117
118 @param.depends(p.param.a, c=p.param.b)
119 def function(value, c):
120 pass
121
122 dependencies = {
123 'dependencies': (p.param.a,),
124 'kw': {'c': p.param.b},
125 'watch': False
126 }
127 self.assertEqual(function._dinfo, dependencies)
128
129 def test_param_depends_function_instance_params_watch(self):
130 p = self.P(a=1, b=2)
131
132 d = []
133
134 @param.depends(p.param.a, c=p.param.b, watch=True)
135 def function(value, c):
136 d.append(value+c)
137
138 p.a = 2
139 self.assertEqual(d, [4])
140 p.b = 3
141 self.assertEqual(d, [4, 5])
142
143 def test_param_depends_function_class_params_watch(self):
144 p = self.P
145 p.a = 1
146 p.b = 2
147
148 d = []
149
150 @param.depends(p.param.a, c=p.param.b, watch=True)
151 def function(value, c):
152 d.append(value+c)
153
154 p.a = 2
155 self.assertEqual(d, [4])
156 p.b = 3
157 self.assertEqual(d, [4, 5])
+0
-500
tests/API1/testparameterizedobject.py less more
0 """
1 Unit test for Parameterized.
2 """
3
4 import param
5 import numbergen
6
7 from . import API1TestCase
8 from .utils import MockLoggingHandler
9
10 # CEBALERT: not anything like a complete test of Parameterized!
11
12
13 import random
14 from nose.tools import istest, nottest
15
16
17 from param.parameterized import ParamOverrides, shared_parameters
18 from param.parameterized import default_label_formatter, no_instance_params
19
20 @nottest
21 class _SomeRandomNumbers(object):
22 def __call__(self):
23 return random.random()
24
25 @nottest
26 class TestPO(param.Parameterized):
27 inst = param.Parameter(default=[1,2,3],instantiate=True)
28 notinst = param.Parameter(default=[1,2,3],instantiate=False, per_instance=False)
29 const = param.Parameter(default=1,constant=True)
30 ro = param.Parameter(default="Hello",readonly=True)
31 ro2 = param.Parameter(default=object(),readonly=True,instantiate=True)
32 ro_label = param.Parameter(default=object(), label='Ro Label')
33 ro_format = param.Parameter(default=object())
34
35 dyn = param.Dynamic(default=1)
36
37 @nottest
38 class TestPOValidation(param.Parameterized):
39 value = param.Number(default=2, bounds=(0, 4))
40
41 @nottest
42 @no_instance_params
43 class TestPONoInstance(TestPO):
44 pass
45
46 @nottest
47 class AnotherTestPO(param.Parameterized):
48 instPO = param.Parameter(default=TestPO(),instantiate=True)
49 notinstPO = param.Parameter(default=TestPO(),instantiate=False)
50
51 @nottest
52 class TestAbstractPO(param.Parameterized):
53 __abstract = True
54
55 @nottest
56 class TestParamInstantiation(AnotherTestPO):
57 instPO = param.Parameter(default=AnotherTestPO(),instantiate=False)
58
59 @istest
60 class TestParameterized(API1TestCase):
61
62 @classmethod
63 def setUpClass(cls):
64 super(TestParameterized, cls).setUpClass()
65 log = param.parameterized.get_logger()
66 cls.log_handler = MockLoggingHandler(level='DEBUG')
67 log.addHandler(cls.log_handler)
68
69
70 def test_constant_parameter(self):
71 """Test that you can't set a constant parameter after construction."""
72 testpo = TestPO(const=17)
73 self.assertEqual(testpo.const,17)
74 self.assertRaises(TypeError,setattr,testpo,'const',10)
75
76 # check you can set on class
77 TestPO.const=9
78 testpo = TestPO()
79 self.assertEqual(testpo.const,9)
80
81
82 def test_readonly_parameter(self):
83 """Test that you can't set a read-only parameter on construction or as an attribute."""
84 testpo = TestPO()
85 self.assertEqual(testpo.ro,"Hello")
86
87 with self.assertRaises(TypeError):
88 t = TestPO(ro=20)
89
90 t=TestPO()
91 self.assertRaises(TypeError,setattr,t,'ro',10)
92
93 # check you cannot set on class
94 self.assertRaises(TypeError,setattr,TestPO,'ro',5)
95
96 self.assertEqual(testpo.param.params()['ro'].constant,True)
97
98 # check that instantiate was ignored for readonly
99 self.assertEqual(testpo.param.params()['ro2'].instantiate,False)
100
101
102 def test_basic_instantiation(self):
103 """Check that instantiated parameters are copied into objects."""
104
105 testpo = TestPO()
106
107 self.assertEqual(testpo.inst,TestPO.inst)
108 self.assertEqual(testpo.notinst,TestPO.notinst)
109
110 TestPO.inst[1]=7
111 TestPO.notinst[1]=7
112
113 self.assertEqual(testpo.notinst,[1,7,3])
114 self.assertEqual(testpo.inst,[1,2,3])
115
116
117 def test_more_instantiation(self):
118 """Show that objects in instantiated Parameters can still share data."""
119 anothertestpo = AnotherTestPO()
120
121 ### CB: AnotherTestPO.instPO is instantiated, but
122 ### TestPO.notinst is not instantiated - so notinst is still
123 ### shared, even by instantiated parameters of AnotherTestPO.
124 ### Seems like this behavior of Parameterized could be
125 ### confusing, so maybe mention it in documentation somewhere.
126 TestPO.notinst[1]=7
127 # (if you thought your instPO was completely an independent object, you
128 # might be expecting [1,2,3] here)
129 self.assertEqual(anothertestpo.instPO.notinst,[1,7,3])
130
131
132 def test_instantiation_inheritance(self):
133 """Check that instantiate=True is always inherited (SF.net #2483932)."""
134 t = TestParamInstantiation()
135 assert t.param.params('instPO').instantiate is True
136 assert isinstance(t.instPO,AnotherTestPO)
137
138
139 def test_abstract_class(self):
140 """Check that a class declared abstract actually shows up as abstract."""
141 self.assertEqual(TestAbstractPO.abstract,True)
142 self.assertEqual(TestPO.abstract,False)
143
144
145 def test_override_class_param_validation(self):
146 test = TestPOValidation()
147 test.param.value.bounds = (0, 3)
148 with self.assertRaises(ValueError):
149 test.value = 4
150 TestPOValidation.value = 4
151
152
153 def test_remove_class_param_validation(self):
154 test = TestPOValidation()
155 test.param.value.bounds = None
156 test.value = 20
157 with self.assertRaises(ValueError):
158 TestPOValidation.value = 10
159
160
161 def test_params(self):
162 """Basic tests of params() method."""
163
164 # CB: test not so good because it requires changes if params
165 # of PO are changed
166 assert 'name' in param.Parameterized.param.params()
167 assert len(param.Parameterized.param.params()) in [1,2]
168
169 ## check for bug where subclass Parameters were not showing up
170 ## if params() already called on a super class.
171 assert 'inst' in TestPO.param.params()
172 assert 'notinst' in TestPO.param.params()
173
174 ## check caching
175 assert param.Parameterized.param.params() is param.Parameterized().param.params(), "Results of params() should be cached." # just for performance reasons
176
177
178 def test_param_iterator(self):
179 self.assertEqual(set(TestPO.param), {'name', 'inst', 'notinst', 'const', 'dyn',
180 'ro', 'ro2', 'ro_label', 'ro_format'})
181
182
183 def test_param_contains(self):
184 for p in ['name', 'inst', 'notinst', 'const', 'dyn', 'ro', 'ro2']:
185 self.assertIn(p, TestPO.param)
186
187
188 def test_class_param_objects(self):
189 objects = TestPO.param.objects()
190
191 self.assertEqual(set(objects),
192 {'name', 'inst', 'notinst', 'const', 'dyn',
193 'ro', 'ro2', 'ro_label', 'ro_format'})
194
195 # Check caching
196 assert TestPO.param.objects() is objects
197
198
199 def test_instance_param_objects(self):
200 inst = TestPO()
201 objects = inst.param.objects()
202
203 for p, obj in objects.items():
204 if p == 'notinst':
205 assert obj is TestPO.param[p]
206 else:
207 assert obj is not TestPO.param[p]
208
209
210 def test_instance_param_objects_set_to_false(self):
211 inst = TestPO()
212 objects = inst.param.objects(instance=False)
213
214 for p, obj in objects.items():
215 assert obj is TestPO.param[p]
216
217
218 def test_instance_param_objects_set_to_current(self):
219 inst = TestPO()
220 inst_param = inst.param.inst
221 objects = inst.param.objects(instance='existing')
222
223 for p, obj in objects.items():
224 if p == 'inst':
225 assert obj is inst_param
226 else:
227 assert obj is TestPO.param[p]
228
229
230 def test_instance_param_objects_warn_on_params(self):
231 inst = TestPO()
232 inst.param['inst']
233
234 inst.param.params()
235 self.log_handler.assertContains(
236 'WARNING', 'The Parameterized instance has instance parameters')
237
238
239 def test_instance_param_getitem(self):
240 test = TestPO()
241 assert test.param['inst'] is not TestPO.param['inst']
242
243
244 def test_instance_param_getitem_not_per_instance(self):
245 test = TestPO()
246 assert test.param['notinst'] is TestPO.param['notinst']
247
248
249 def test_instance_param_getitem_no_instance_params(self):
250 test = TestPONoInstance()
251 assert test.param['inst'] is TestPO.param['inst']
252
253
254 def test_instance_param_getattr(self):
255 test = TestPO()
256 assert test.param.inst is not TestPO.param.inst
257
258 # Assert no deep copy
259 assert test.param.inst.default is TestPO.param.inst.default
260
261
262 def test_pprint_instance_params(self):
263 # Ensure pprint does not make instance parameter copies
264 test = TestPO()
265 test.pprint()
266 for p, obj in TestPO.param.objects('current').items():
267 assert obj is TestPO.param[p]
268
269
270 def test_set_param_instance_params(self):
271 # Ensure set_param does not make instance parameter copies
272 test = TestPO()
273 test.param.set_param(inst=3)
274 for p, obj in TestPO.param.objects('current').items():
275 assert obj is TestPO.param[p]
276
277
278 def test_get_param_values_instance_params(self):
279 # Ensure get_param_values does not make instance parameter copies
280 test = TestPO()
281 test.param.get_param_values()
282 for p, obj in TestPO.param.objects('current').items():
283 assert obj is TestPO.param[p]
284
285
286 def test_defaults_instance_params(self):
287 # Ensure get_param_values does not make instance parameter copies
288 test = TestPO()
289 test.param.defaults()
290 for p, obj in TestPO.param.objects('current').items():
291 assert obj is TestPO.param[p]
292
293
294 def test_state_saving(self):
295 t = TestPO(dyn=_SomeRandomNumbers())
296 g = t.param.get_value_generator('dyn')
297 g._Dynamic_time_fn=None
298 assert t.dyn!=t.dyn
299 orig = t.dyn
300 t.state_push()
301 t.dyn
302 assert t.param.inspect_value('dyn')!=orig
303 t.state_pop()
304 assert t.param.inspect_value('dyn')==orig
305
306
307 def test_label(self):
308 t = TestPO()
309 assert t.param.params('ro_label').label == 'Ro Label'
310
311 def test_label_set(self):
312 t = TestPO()
313 assert t.param.params('ro_label').label == 'Ro Label'
314 t.param.params('ro_label').label = 'Ro relabeled'
315 assert t.param.params('ro_label').label == 'Ro relabeled'
316
317 def test_label_default_format(self):
318 t = TestPO()
319 assert t.param.params('ro_format').label == 'Ro format'
320
321
322 def test_label_custom_format(self):
323 param.parameterized.label_formatter = default_label_formatter.instance(capitalize=False)
324 t = TestPO()
325 assert t.param.params('ro_format').label == 'ro format'
326 param.parameterized.label_formatter = default_label_formatter
327
328 def test_label_constant_format(self):
329 param.parameterized.label_formatter = lambda x: 'Foo'
330 t = TestPO()
331 assert t.param.params('ro_format').label == 'Foo'
332 param.parameterized.label_formatter = default_label_formatter
333
334
335 from param import parameterized
336
337 @nottest
338 class some_fn(param.ParameterizedFunction):
339 num_phase = param.Number(18)
340 frequencies = param.List([99])
341 scale = param.Number(0.3)
342
343 def __call__(self,**params_to_override):
344 params = parameterized.ParamOverrides(self,params_to_override)
345 num_phase = params['num_phase']
346 frequencies = params['frequencies']
347 scale = params['scale']
348 return scale,num_phase,frequencies
349
350 instance = some_fn.instance()
351
352 @istest
353 class TestParameterizedFunction(API1TestCase):
354
355 def _basic_tests(self,fn):
356 self.assertEqual(fn(),(0.3,18,[99]))
357 self.assertEqual(fn(frequencies=[1,2,3]),(0.3,18,[1,2,3]))
358 self.assertEqual(fn(),(0.3,18,[99]))
359
360 fn.frequencies=[10,20,30]
361 self.assertEqual(fn(frequencies=[1,2,3]),(0.3,18,[1,2,3]))
362 self.assertEqual(fn(),(0.3,18,[10,20,30]))
363
364 def test_parameterized_function(self):
365 self._basic_tests(some_fn)
366
367 def test_parameterized_function_instance(self):
368 self._basic_tests(instance)
369
370 def test_pickle_instance(self):
371 import pickle
372 s = pickle.dumps(instance)
373 instance.scale=0.8
374 i = pickle.loads(s)
375 self.assertEqual(i(),(0.3,18,[10,20,30]))
376
377
378 @nottest
379 class TestPO1(param.Parameterized):
380 x = param.Number(default=numbergen.UniformRandom(lbound=-1,ubound=1,seed=1),bounds=(-1,1))
381 y = param.Number(default=1,bounds=(-1,1))
382
383 @istest
384 class TestNumberParameter(API1TestCase):
385
386 def test_outside_bounds(self):
387 t1 = TestPO1()
388 # Test bounds (non-dynamic number)
389 try:
390 t1.y = 10
391 except ValueError:
392 pass
393 else:
394 assert False, "Should raise ValueError."
395
396 def test_outside_bounds_numbergen(self):
397 t1 = TestPO1()
398 # Test bounds (dynamic number)
399 t1.x = numbergen.UniformRandom(lbound=2,ubound=3) # bounds not checked on set
400 try:
401 t1.x
402 except ValueError:
403 pass
404 else:
405 assert False, "Should raise ValueError."
406
407
408 @istest
409 class TestStringParameter(API1TestCase):
410
411 def setUp(self):
412 super(TestStringParameter, self).setUp()
413
414 class TestString(param.Parameterized):
415 a = param.String()
416 b = param.String(default='',allow_None=True)
417 c = param.String(default=None)
418
419 self._TestString = TestString
420
421 def test_handling_of_None(self):
422 t = self._TestString()
423
424 with self.assertRaises(ValueError):
425 t.a = None
426
427 t.b = None
428
429 assert t.c is None
430
431
432 @istest
433 class TestParameterizedUtilities(API1TestCase):
434
435 def setUp(self):
436 super(TestParameterizedUtilities, self).setUp()
437
438
439 def test_default_label_formatter(self):
440 assert default_label_formatter('a_b_C') == 'A b C'
441
442
443 def test_default_label_formatter_not_capitalized(self):
444 assert default_label_formatter.instance(capitalize=False)('a_b_C') == 'a b C'
445
446
447 def test_default_label_formatter_not_replace_underscores(self):
448 assert default_label_formatter.instance(replace_underscores=False)('a_b_C') == 'A_b_C'
449 def test_default_label_formatter_overrides(self):
450 assert default_label_formatter.instance(overrides={'a': 'b'})('a') == 'b'
451
452 @istest
453 class TestParamOverrides(API1TestCase):
454
455 def setUp(self):
456 super(TestParamOverrides, self).setUp()
457 self.po = param.Parameterized(name='A',print_level=0)
458
459 def test_init_name(self):
460 self.assertEqual(self.po.name, 'A')
461
462 def test_simple_override(self):
463 overrides = ParamOverrides(self.po,{'name':'B'})
464 self.assertEqual(overrides['name'], 'B')
465 self.assertEqual(overrides['print_level'], 0)
466
467 # CEBALERT: missing test for allow_extra_keywords (e.g. getting a
468 # warning on attempting to override non-existent parameter when
469 # allow_extra_keywords is False)
470
471 def test_missing_key(self):
472 overrides = ParamOverrides(self.po,{'name':'B'})
473 with self.assertRaises(AttributeError):
474 overrides['doesnotexist']
475
476
477 class TestSharedParameters(API1TestCase):
478
479 def setUp(self):
480 super(TestSharedParameters, self).setUp()
481 with shared_parameters():
482 self.p1 = TestPO(name='A', print_level=0)
483 self.p2 = TestPO(name='B', print_level=0)
484 self.ap1 = AnotherTestPO(name='A', print_level=0)
485 self.ap2 = AnotherTestPO(name='B', print_level=0)
486
487 def test_shared_object(self):
488 self.assertTrue(self.ap1.instPO is self.ap2.instPO)
489 self.assertTrue(self.ap1.param.params('instPO').default is not self.ap2.instPO)
490
491 def test_shared_list(self):
492 self.assertTrue(self.p1.inst is self.p2.inst)
493 self.assertTrue(self.p1.param.params('inst').default is not self.p2.inst)
494
495
496
497 if __name__ == "__main__":
498 import nose
499 nose.runmodule()
+0
-167
tests/API1/testparameterizedrepr.py less more
0 """
1 Unit test for the repr and pprint of parameterized objects.
2 """
3
4 import param
5 from . import API1TestCase
6
7
8 class TestParameterizedRepr(API1TestCase):
9
10 def setUp(self):
11 super(TestParameterizedRepr, self).setUp()
12 # initialize a parameterized class
13 class A(param.Parameterized):
14 a = param.Number(4, precedence=-5)
15 b = param.String('B', precedence=-4)
16 c = param.Number(4, precedence=0)
17 d = param.Integer(-22, precedence=1)
18
19 x = param.Number(1, precedence=2)
20 y = param.Number(2, precedence=-1)
21 z = param.Number(3, precedence=-2)
22 def __init__(self, a, b, c=4, d=-22, **kwargs):
23 super(A, self).__init__(a=a, b=b, c=c, **kwargs)
24
25 self.A = A
26
27 class B(param.Parameterized): # Similar to A but no **kwargs
28 a = param.Number(4, precedence=-5)
29 b = param.String('B', precedence=-4)
30 c = param.Number(4, precedence=0)
31 d = param.Integer(-22, precedence=1)
32
33 x = param.Number(1, precedence=2)
34 def __init__(self, a, b, c=4, d=-22):
35 super(B, self).__init__(a=a, b=b, c=c, name='ClassB')
36
37 self.B = B
38
39 class C(param.Parameterized): # Similar to A but with *varargs
40 a = param.Number(4, precedence=-5)
41 b = param.String('B', precedence=-4)
42 c = param.Number(4, precedence=0)
43 d = param.Integer(-22, precedence=1)
44
45 x = param.Number(1, precedence=2)
46 y = param.Number(2, precedence=-1)
47 z = param.Number(3, precedence=-2)
48
49 def __init__(self, a, b, c=4, d=-22, *varargs, **kwargs):
50 super(C, self).__init__(a=a, b=b, c=c, **kwargs)
51
52 self.C = C
53
54
55 class D(param.Parameterized): # Similar to A but with missing parameters
56 a = param.Number(4, precedence=-5)
57 b = param.String('B', precedence=-4)
58
59 def __init__(self, a, b, c=4, d=-22, **kwargs):
60 super(D, self).__init__(a=a, b=b, **kwargs)
61
62 self.D = D
63
64
65 # More realistically, positional args are not params
66 class E(param.Parameterized):
67 a = param.Number(4, precedence=-5)
68
69 def __init__(self, p, q=4, **params): # (plus non-param kw too)
70 super(E, self).__init__(**params)
71
72 self.E = E
73
74
75 def testparameterizedrepr(self):
76 obj = self.A(4,'B', name='test1')
77 self.assertEqual(repr(obj),
78 "A(a=4, b='B', c=4, d=-22, name='test1', x=1, y=2, z=3)")
79
80 def testparameterizedscriptrepr1(self):
81 obj = self.A(4,'B', name='test')
82 self.assertEqual(obj.pprint(),
83 "A(4, 'B', name='test')")
84
85 def testparameterizedscriptrepr2(self):
86 obj = self.A(4,'B', c=5, name='test')
87 self.assertEqual(obj.pprint(),
88 "A(4, 'B', c=5, name='test')")
89
90 def testparameterizedscriptrepr3(self):
91 obj = self.A(4,'B', c=5, x=True, name='test')
92 self.assertEqual(obj.pprint(),
93 "A(4, 'B', c=5, name='test')")
94
95 def testparameterizedscriptrepr4(self):
96 obj = self.A(4,'B', c=5, x=10, name='test')
97 self.assertEqual(obj.pprint(),
98 "A(4, 'B', c=5, name='test', x=10)")
99
100
101 def testparameterizedscriptrepr5(self):
102 obj = self.A(4,'B', x=10, y=11, z=12, name='test')
103 self.assertEqual(obj.pprint(),
104 "A(4, 'B', name='test', z=12, y=11, x=10)")
105
106 def testparameterizedscriptrepr_nokwargs(self):
107 obj = self.B(4,'B', c=99)
108 obj.x = 10 # Modified but not passable through constructor
109 self.assertEqual(obj.pprint(),
110 "B(4, 'B', c=99)")
111
112 def testparameterizedscriptrepr_varags(self):
113 obj = self.C(4,'C', c=99)
114 self.assertEqual(obj.pprint(),
115 "C(4, 'C', c=99, **varargs)")
116
117 def testparameterizedscriptrepr_varags_kwargs(self):
118 obj = self.C(4,'C', c=99, x=10, y=11, z=12)
119 self.assertEqual(obj.pprint(),
120 "C(4, 'C', c=99, z=12, y=11, x=10, **varargs)")
121
122 def testparameterizedscriptrepr_missing_values(self):
123 obj = self.D(4,'D', c=99)
124 self.assertEqual(obj.pprint(),
125 "D(4, 'D', c=<?>, d=<?>)")
126
127 def testparameterizedscriptrepr_nonparams(self):
128 obj = self.E(10,q='hi', a=99)
129 self.assertEqual(obj.pprint(),
130 "E(<?>, q=<?>, a=99)")
131
132 def test_exceptions(self):
133 obj = self.E(10,q='hi',a=99)
134 try:
135 obj.pprint(unknown_value=False)
136 except Exception:
137 pass
138 else:
139 raise AssertionError
140
141 def test_suppression(self):
142 obj = self.E(10,q='hi',a=99)
143 self.assertEqual(obj.pprint(unknown_value=None),
144 "E(a=99)")
145
146 def test_imports_deduplication(self):
147 obj = self.E(10,q='hi', a=99)
148 imports = ['import me','import me']
149 obj.pprint(imports=imports)
150 self.assertEqual(imports.count('import me'),1)
151
152 def test_qualify(self):
153 obj = self.E(10,q='hi', a=99)
154
155 r = "E(<?>, q=<?>, a=99)"
156 self.assertEqual(obj.pprint(qualify=False),
157 r)
158
159 self.assertEqual(obj.pprint(qualify=True),
160 "tests.API1.testparameterizedrepr."+r)
161
162
163
164 if __name__ == "__main__":
165 import nose
166 nose.runmodule()
+0
-232
tests/API1/testparamoutput.py less more
0 """
1 Unit test for param.output.
2 """
3 import sys
4
5 from unittest import SkipTest
6
7 import param
8
9 from . import API1TestCase
10
11
12 class TestParamDepends(API1TestCase):
13
14 def test_simple_output(self):
15 class P(param.Parameterized):
16
17 @param.output()
18 def single_output(self):
19 return 1
20
21 p = P()
22 outputs = p.param.outputs()
23 self.assertEqual(list(outputs), ['single_output'])
24
25 otype, method, idx = outputs['single_output']
26 self.assertIs(type(otype), param.Parameter)
27 self.assertEqual(method, p.single_output)
28 self.assertEqual(idx, None)
29
30 def test_subclass_output(self):
31 class A(param.Parameterized):
32
33 @param.output()
34 def single_output(self):
35 return 1
36
37 class B(param.Parameterized):
38
39 @param.output()
40 def another_output(self):
41 return 2
42
43 class C(A, B):
44 pass
45
46 p = C()
47 outputs = p.param.outputs()
48 self.assertEqual(sorted(outputs), ['another_output', 'single_output'])
49
50 otype, method, idx = outputs['single_output']
51 self.assertIs(type(otype), param.Parameter)
52 self.assertEqual(method, p.single_output)
53 self.assertEqual(idx, None)
54
55 otype, method, idx = outputs['another_output']
56 self.assertIs(type(otype), param.Parameter)
57 self.assertEqual(method, p.another_output)
58 self.assertEqual(idx, None)
59
60
61 def test_named_kwarg_output(self):
62 class P(param.Parameterized):
63
64 @param.output(value=param.Integer)
65 def single_output(self):
66 return 1
67
68 p = P()
69 outputs = p.param.outputs()
70 self.assertEqual(list(outputs), ['value'])
71
72 otype, method, idx = outputs['value']
73 self.assertIs(type(otype), param.Integer)
74 self.assertEqual(method, p.single_output)
75 self.assertEqual(idx, None)
76
77 def test_named_and_typed_arg_output(self):
78 class P(param.Parameterized):
79
80 @param.output(('value', param.Integer))
81 def single_output(self):
82 return 1
83
84 p = P()
85 outputs = p.param.outputs()
86 self.assertEqual(list(outputs), ['value'])
87
88 otype, method, idx = outputs['value']
89 self.assertIs(type(otype), param.Integer)
90 self.assertEqual(method, p.single_output)
91 self.assertEqual(idx, None)
92
93 def test_named_arg_output(self):
94 class P(param.Parameterized):
95
96 @param.output('value')
97 def single_output(self):
98 return 1
99
100 p = P()
101 outputs = p.param.outputs()
102 self.assertEqual(list(outputs), ['value'])
103
104 otype, method, idx = outputs['value']
105 self.assertIs(type(otype), param.Parameter)
106 self.assertEqual(method, p.single_output)
107 self.assertEqual(idx, None)
108
109 def test_typed_arg_output(self):
110 class P(param.Parameterized):
111
112 @param.output(int)
113 def single_output(self):
114 return 1
115
116 p = P()
117 outputs = p.param.outputs()
118 self.assertEqual(list(outputs), ['single_output'])
119
120 otype, method, idx = outputs['single_output']
121 self.assertIs(type(otype), param.ClassSelector)
122 self.assertIs(otype.class_, int)
123 self.assertEqual(method, p.single_output)
124 self.assertEqual(idx, None)
125
126 def test_multiple_named_kwarg_output(self):
127 py_major = sys.version_info.major
128 py_minor = sys.version_info.minor
129 if (py_major < 3 or (py_major == 3 and py_minor < 6)):
130 raise SkipTest('Multiple keyword output declarations only '
131 'supported in Python >= 3.6, skipping test.')
132
133 class P(param.Parameterized):
134
135 @param.output(value=param.Integer, value2=param.String)
136 def multi_output(self):
137 return (1, 'string')
138
139 p = P()
140 outputs = p.param.outputs()
141 self.assertEqual(set(outputs), {'value', 'value2'})
142
143 otype, method, idx = outputs['value']
144 self.assertIs(type(otype), param.Integer)
145 self.assertEqual(method, p.multi_output)
146 self.assertEqual(idx, 0)
147
148 otype, method, idx = outputs['value2']
149 self.assertIs(type(otype), param.String)
150 self.assertEqual(method, p.multi_output)
151 self.assertEqual(idx, 1)
152
153 def test_multi_named_and_typed_arg_output(self):
154 class P(param.Parameterized):
155
156 @param.output(('value', param.Integer), ('value2', param.String))
157 def multi_output(self):
158 return (1, 'string')
159
160 p = P()
161 outputs = p.param.outputs()
162 self.assertEqual(set(outputs), {'value', 'value2'})
163 otype, method, idx = outputs['value']
164 self.assertIs(type(otype), param.Integer)
165 self.assertEqual(method, p.multi_output)
166 self.assertEqual(idx, 0)
167
168 otype, method, idx = outputs['value2']
169 self.assertIs(type(otype), param.String)
170 self.assertEqual(method, p.multi_output)
171 self.assertEqual(idx, 1)
172
173 def test_multi_named_arg_output(self):
174 class P(param.Parameterized):
175
176 @param.output('value', 'value2')
177 def multi_output(self):
178 return (1, 2)
179
180 p = P()
181 outputs = p.param.outputs()
182 self.assertEqual(set(outputs), {'value', 'value2'})
183
184 otype, method, idx = outputs['value']
185 self.assertIs(type(otype), param.Parameter)
186 self.assertEqual(method, p.multi_output)
187 self.assertEqual(idx, 0)
188
189 otype, method, idx = outputs['value2']
190 self.assertIs(type(otype), param.Parameter)
191 self.assertEqual(method, p.multi_output)
192 self.assertEqual(idx, 1)
193
194 def test_multi_typed_arg_output(self):
195 with self.assertRaises(ValueError):
196 class P(param.Parameterized):
197
198 @param.output(int, str)
199 def single_output(self):
200 return 1
201
202 def test_multi_method_named_and_typed_arg_output(self):
203 class P(param.Parameterized):
204
205 @param.output(('value', param.Integer), ('value2', str))
206 def multi_output(self):
207 return (1, 'string')
208
209 @param.output(('value3', param.Number))
210 def single_output(self):
211 return 3.0
212
213 p = P()
214 outputs = p.param.outputs()
215 self.assertEqual(set(outputs), {'value', 'value2', 'value3'})
216
217 otype, method, idx = outputs['value']
218 self.assertIs(type(otype), param.Integer)
219 self.assertEqual(method, p.multi_output)
220 self.assertEqual(idx, 0)
221
222 otype, method, idx = outputs['value2']
223 self.assertIs(type(otype), param.ClassSelector)
224 self.assertIs(otype.class_, str)
225 self.assertEqual(method, p.multi_output)
226 self.assertEqual(idx, 1)
227
228 otype, method, idx = outputs['value3']
229 self.assertIs(type(otype), param.Number)
230 self.assertEqual(method, p.single_output)
231 self.assertEqual(idx, None)
+0
-64
tests/API1/testparamunion.py less more
0 """
1 UnitTest for param_union helper
2 """
3
4 import logging
5 import param
6 from . import API1TestCase
7
8 class MyHandler(logging.StreamHandler):
9
10 def __init__(self):
11 super(MyHandler, self).__init__()
12 self.records = []
13
14 def emit(self, record):
15 self.records.append(record)
16
17 class TestParamUnion(API1TestCase):
18
19 def setUp(self):
20 self.logger = param.get_logger()
21 self.handler = MyHandler()
22 self.logger.addHandler(self.handler)
23
24 def tearDown(self):
25 self.logger.removeHandler(self.handler)
26
27 def test_param_union_values(self):
28 class A(param.Parameterized):
29 a = param.Number(1)
30 class B(param.Parameterized):
31 b = param.Number(2)
32 class C(A, B):
33 pass
34 a = A()
35 a.a = 10
36 b = B()
37 b.b = 5
38 c_1 = C(**param.param_union(a))
39 self.assertTrue(c_1.a == 10 and c_1.b == 2)
40 c_2 = C(**param.param_union(b))
41 self.assertTrue(c_2.a == 1 and c_2.b == 5)
42 c_3 = C(**param.param_union(a, b))
43 self.assertTrue(c_3.a == 10 and c_3.b == 5)
44 c_4 = C(**param.param_union())
45 self.assertTrue(c_4.a == 1 and c_4.b == 2)
46
47 def test_param_union_warnings(self):
48 class A(param.Parameterized):
49 a = param.Number(1)
50 a = A()
51 A(**param.param_union(a))
52 self.assertFalse(self.handler.records)
53 A(**param.param_union())
54 self.assertFalse(self.handler.records)
55 A(**param.param_union(a, a))
56 self.assertTrue(self.handler.records)
57 self.handler.records.pop()
58 A(**param.param_union(a, a, warn=False))
59 self.assertFalse(self.handler.records)
60
61 def test_param_union_raises_on_unexpected_kwarg(self):
62 with self.assertRaises(TypeError):
63 param.param_union(dumbdumbface=True)
+0
-52
tests/API1/testrangeparameter.py less more
0 """
1 Unit test for Range parameters.
2 """
3 import param
4 from . import API1TestCase
5
6
7 class TestRangeParameters(API1TestCase):
8
9 def test_initialization_out_of_bounds(self):
10 try:
11 class Q(param.Parameterized):
12 q = param.Range((0, 2), bounds=(0, 1))
13 except ValueError:
14 pass
15 else:
16 raise AssertionError("No exception raised on out-of-bounds date")
17
18 def test_set_exclusive_out_of_bounds_upper(self):
19 class Q(param.Parameterized):
20 q = param.Range(bounds=(0, 10), inclusive_bounds=(True, False))
21 try:
22 Q.q = (0, 10)
23 except ValueError:
24 pass
25 else:
26 raise AssertionError("No exception raised on out-of-bounds date")
27
28 def test_set_exclusive_out_of_bounds_lower(self):
29 class Q(param.Parameterized):
30 q = param.Range(bounds=(0, 10), inclusive_bounds=(False, True))
31 try:
32 Q.q = (0, 10)
33 except ValueError:
34 pass
35 else:
36 raise AssertionError("No exception raised on out-of-bounds date")
37
38 def test_set_out_of_bounds(self):
39 class Q(param.Parameterized):
40 q = param.Range(bounds=(0, 10))
41 try:
42 Q.q = (5, 11)
43 except ValueError:
44 pass
45 else:
46 raise AssertionError("No exception raised on out-of-bounds date")
47
48 def test_get_soft_bounds(self):
49 q = param.Range((1,3), bounds=(0, 10), softbounds=(1, 9))
50 self.assertEqual(q.get_soft_bounds(), (1, 9))
51
+0
-120
tests/API1/testselector.py less more
0 """
1 Unit test for object selector parameters.
2
3 Originally implemented as doctests in Topographica in the file
4 testEnumerationParameter.txt
5 """
6
7 import param
8 from . import API1TestCase
9 from collections import OrderedDict
10
11
12 opts=dict(A=[1,2],B=[3,4],C=dict(a=1,b=2))
13
14
15 class TestSelectorParameters(API1TestCase):
16
17 def setUp(self):
18 super(TestSelectorParameters, self).setUp()
19 class P(param.Parameterized):
20 e = param.Selector([5,6,7])
21 f = param.Selector(default=10)
22 h = param.Selector(default=None)
23 g = param.Selector([7,8])
24 i = param.Selector([9],default=7, check_on_set=False)
25 s = param.Selector(OrderedDict(one=1,two=2,three=3), default=3)
26 d = param.Selector(opts, default=opts['B'])
27
28 self.P = P
29
30 def test_set_object_constructor(self):
31 p = self.P(e=6)
32 self.assertEqual(p.e, 6)
33
34 def test_get_range_list(self):
35 r = self.P.param.params("g").get_range()
36 self.assertEqual(r['7'],7)
37 self.assertEqual(r['8'],8)
38
39 def test_get_range_dict(self):
40 r = self.P.param.params("s").get_range()
41 self.assertEqual(r['one'],1)
42 self.assertEqual(r['two'],2)
43
44 def test_get_range_mutable(self):
45 r = self.P.param.params("d").get_range()
46 self.assertEqual(r['A'],opts['A'])
47 self.assertEqual(r['C'],opts['C'])
48 self.d=opts['A']
49 self.d=opts['C']
50 self.d=opts['B']
51
52 def test_set_object_outside_bounds(self):
53 p = self.P(e=6)
54 try:
55 p.e = 9
56 except ValueError:
57 pass
58 else:
59 raise AssertionError("Object set outside range.")
60
61 def test_set_object_setattr(self):
62 p = self.P(e=6)
63 p.f = 9
64 self.assertEqual(p.f, 9)
65 p.g = 7
66 self.assertEqual(p.g, 7)
67 p.i = 12
68 self.assertEqual(p.i, 12)
69
70
71 def test_set_object_not_None(self):
72 p = self.P(e=6)
73 p.g = 7
74 try:
75 p.g = None
76 except ValueError:
77 pass
78 else:
79 raise AssertionError("Object set outside range.")
80
81 def test_set_object_setattr_post_error(self):
82 p = self.P(e=6)
83 p.f = 9
84 self.assertEqual(p.f, 9)
85 p.g = 7
86 try:
87 p.g = None
88 except ValueError:
89 pass
90 else:
91 raise AssertionError("Object set outside range.")
92
93 self.assertEqual(p.g, 7)
94 p.i = 12
95 self.assertEqual(p.i, 12)
96
97 def test_initialization_out_of_bounds(self):
98 try:
99 class Q(param.Parameterized):
100 q = param.Selector([4], 5)
101 except ValueError:
102 pass
103 else:
104 raise AssertionError("Selector created outside range.")
105
106
107 def test_initialization_no_bounds(self):
108 try:
109 class Q(param.Parameterized):
110 q = param.Selector(10, default=5)
111 except TypeError:
112 pass
113 else:
114 raise AssertionError("Selector created without range.")
115
116
117 if __name__ == "__main__":
118 import nose
119 nose.runmodule()
+0
-56
tests/API1/teststringparam.py less more
0 """
1 Unit test for String parameters
2 """
3 from . import API1TestCase
4
5 import param
6
7
8 ip_regex = '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
9
10 class TestStringParameters(API1TestCase):
11
12 def test_regex_ok(self):
13 class A(param.Parameterized):
14 s = param.String('0.0.0.0', ip_regex)
15
16 a = A()
17 a.s = '123.123.0.1'
18
19 def test_reject_none(self):
20 class A(param.Parameterized):
21 s = param.String('0.0.0.0', ip_regex)
22
23 a = A()
24
25 exception = "String 's' only takes a string value."
26 with self.assertRaisesRegexp(ValueError, exception):
27 a.s = None # because allow_None should be False
28
29 def test_default_none(self):
30 class A(param.Parameterized):
31 s = param.String(None, ip_regex)
32
33 a = A()
34 a.s = '123.123.0.1'
35 a.s = None # because allow_None should be True with default of None
36
37 def test_regex_incorrect(self):
38
39 class A(param.Parameterized):
40 s = param.String('0.0.0.0', regex=ip_regex)
41
42 a = A()
43
44 exception = "String 's': '123.123.0.256' does not match regex"
45 with self.assertRaisesRegexp(ValueError, exception):
46 a.s = '123.123.0.256'
47
48 def test_regex_incorrect_default(self):
49
50 exception = "String 'None': '' does not match regex"
51 with self.assertRaisesRegexp(ValueError, exception):
52 class A(param.Parameterized):
53 s = param.String(regex=ip_regex) # default value '' does not match regular expression
54
55
+0
-302
tests/API1/testtimedependent.py less more
0 """
1 Unit tests for the param.Time class, time dependent parameters and
2 time-dependent numbergenerators.
3 """
4 import param
5 import numbergen
6 import copy
7
8 from . import API1TestCase
9 from nose.plugins.skip import SkipTest
10 import fractions
11
12 try:
13 import gmpy
14 except:
15 gmpy = None
16
17
18
19 class TestTimeClass(API1TestCase):
20
21 def test_time_init(self):
22 param.Time()
23
24 def test_time_init_int(self):
25 t = param.Time(time_type=int)
26 self.assertEqual(t(), 0)
27
28 def test_time_int_iter(self):
29 t = param.Time(time_type=int)
30 self.assertEqual(next(t), 0)
31 self.assertEqual(next(t), 1)
32
33 def test_time_init_timestep(self):
34 t = param.Time(time_type=int, timestep=2)
35 self.assertEqual(next(t), 0)
36 self.assertEqual(next(t), 2)
37
38 def test_time_int_until(self):
39 t = param.Time(time_type=int, until=3)
40 self.assertEqual(next(t), 0)
41 self.assertEqual(next(t), 1)
42 self.assertEqual(next(t), 2)
43 self.assertEqual(next(t), 3)
44 try:
45 self.assertEqual(next(t), 4)
46 raise AssertionError("StopIteration should have been raised")
47 except StopIteration:
48 pass
49
50 def test_time_int_eq(self):
51 t = param.Time(time_type=int)
52 s = param.Time(time_type=int)
53 t(3); s(3)
54 self.assertEqual(t == s, True)
55
56 def test_time_int_context(self):
57 t = param.Time(time_type=int)
58 t(3)
59 with t:
60 self.assertEqual(t(), 3)
61 t(5)
62 self.assertEqual(t(), 5)
63 self.assertEqual(t(), 3)
64
65 def test_time_int_context_iadd(self):
66
67 with param.Time(time_type=int) as t:
68 self.assertEqual(t(), 0)
69 t += 5
70 self.assertEqual(t(), 5)
71 self.assertEqual(t(), 0)
72
73 def test_time_int_change_type(self):
74 t = param.Time(time_type=int)
75 self.assertEqual(t(), 0)
76 t(1, fractions.Fraction)
77 self.assertEqual(t(), 1)
78 self.assertEqual(t.time_type, fractions.Fraction)
79
80 def test_time_init_gmpy(self):
81 if gmpy is None: raise SkipTest
82
83 t = param.Time(time_type=gmpy.mpq)
84 self.assertEqual(t(), gmpy.mpq(0))
85 t.advance(gmpy.mpq(0.25))
86 self.assertEqual(t(), gmpy.mpq(1,4))
87
88 def test_time_init_gmpy_advanced(self):
89 if gmpy is None: raise SkipTest
90 t = param.Time(time_type=gmpy.mpq,
91 timestep=gmpy.mpq(0.25),
92 until=1.5)
93 self.assertEqual(t(), gmpy.mpq(0,1))
94 t(0.5)
95 self.assertEqual(t(), gmpy.mpq(1,2))
96 with t:
97 t.advance(0.25)
98 self.assertEqual(t(), gmpy.mpq(3,4))
99 self.assertEqual(t(), gmpy.mpq(1,2))
100 tvals = [tval for tval in t]
101 self.assertEqual(tvals, [gmpy.mpq(1,2),
102 gmpy.mpq(3,4),
103 gmpy.mpq(1,1),
104 gmpy.mpq(5,4),
105 gmpy.mpq(3,2)])
106
107
108 class TestTimeDependentDynamic(API1TestCase):
109
110 def setUp(self):
111 super(TestTimeDependentDynamic, self).setUp()
112 param.Dynamic.time_dependent=None
113 self.time_fn= param.Time(time_type=int)
114
115 class Incrementer(object):
116 def __init__(self):
117 self.i = -1
118 def __call__(self):
119 self.i+=1
120 return self.i
121
122 self.Incrementer = Incrementer
123
124 class DynamicClass(param.Parameterized):
125 a = param.Number(default = self.Incrementer())
126
127 self.DynamicClass = DynamicClass
128 self._start_state = copy.copy([param.Dynamic.time_dependent,
129 numbergen.TimeAware.time_dependent,
130 param.Dynamic.time_fn,
131 numbergen.TimeAware.time_fn,
132 param.random_seed])
133
134 def tearDown(self):
135 param.Dynamic.time_dependent = self._start_state[0]
136 numbergen.TimeAware.time_dependent = self._start_state[1]
137 param.Dynamic.time_fn = self._start_state[2]
138 numbergen.TimeAware.time_fn = self._start_state[3]
139 param.random_seed = self._start_state[4]
140
141 def test_non_time_dependent(self):
142 """
143 With param.Dynamic.time_dependent=None every call should
144 increment.
145 """
146 param.Dynamic.time_dependent=None
147 param.Dynamic.time_fn = self.time_fn
148
149 dynamic = self.DynamicClass()
150 self.assertEqual(dynamic.a, 0)
151 self.assertEqual(dynamic.a, 1)
152 self.assertEqual(dynamic.a, 2)
153
154 def test_time_fixed(self):
155 """
156 With param.Dynamic.time_dependent=True the value should only
157 increment when the time value changes.
158 """
159 param.Dynamic.time_dependent=True
160 param.Dynamic.time_fn = self.time_fn
161
162 dynamic = self.DynamicClass()
163 self.assertEqual(dynamic.a, 0)
164 self.assertEqual(dynamic.a, 0)
165
166 self.time_fn += 1
167 self.assertEqual(dynamic.a, 1)
168 self.assertEqual(dynamic.a, 1)
169 param.Dynamic.time_fn -= 5
170 self.assertEqual(dynamic.a, 2)
171 self.assertEqual(dynamic.a, 2)
172
173
174 def test_time_dependent(self):
175 """
176 With param.Dynamic.time_dependent=True and param.Dynamic and
177 numbergen.TimeDependent sharing a common time_fn, the value
178 should be a function of time.
179 """
180 param.Dynamic.time_dependent=True
181 param.Dynamic.time_fn = self.time_fn
182 numbergen.TimeDependent.time_fn = self.time_fn
183
184 class DynamicClass(param.Parameterized):
185 b = param.Number(default = numbergen.ScaledTime(factor=2))
186
187 dynamic = DynamicClass()
188 self.time_fn(0)
189 self.assertEqual(dynamic.b, 0.0)
190 self.time_fn += 5
191 self.assertEqual(dynamic.b, 10.0)
192 self.assertEqual(dynamic.b, 10.0)
193 self.time_fn -= 2
194 self.assertEqual(dynamic.b, 6.0)
195 self.assertEqual(dynamic.b, 6.0)
196 self.time_fn -= 3
197 self.assertEqual(dynamic.b, 0.0)
198
199
200 def test_time_dependent_random(self):
201 """
202 When set to time_dependent=True, random number generators
203 should also be a function of time.
204 """
205 param.Dynamic.time_dependent=True
206 numbergen.TimeAware.time_dependent=True
207 param.Dynamic.time_fn = self.time_fn
208 numbergen.TimeAware.time_fn = self.time_fn
209 param.random_seed = 42
210
211 class DynamicClass(param.Parameterized):
212 c = param.Number(default = numbergen.UniformRandom(name = 'test1'))
213 d = param.Number(default = numbergen.UniformRandom(name = 'test2'))
214 e = param.Number(default = numbergen.UniformRandom(name = 'test1'))
215
216 dynamic = DynamicClass()
217
218 test1_t1 = 0.23589388250988552
219 test2_t1 = 0.12576257837158122
220 test1_t2 = 0.14117586161849593
221 test2_t2 = 0.9134917395930359
222
223 self.time_fn(0)
224 self.assertEqual(dynamic.c, test1_t1)
225 self.assertEqual(dynamic.c, dynamic.e)
226 self.assertNotEqual(dynamic.c, dynamic.d)
227 self.assertEqual(dynamic.d, test2_t1)
228 self.time_fn(1)
229 self.assertEqual(dynamic.c, test1_t2)
230 self.assertEqual(dynamic.c, test1_t2)
231 self.assertEqual(dynamic.d, test2_t2)
232 self.time_fn(0)
233 self.assertEqual(dynamic.c, test1_t1)
234 self.assertEqual(dynamic.d, test2_t1)
235
236
237 def test_time_hashing_integers(self):
238 """
239 Check that ints, fractions and strings hash to the same value
240 for integer values.
241 """
242 hashfn = numbergen.Hash("test", input_count=1)
243 hash_1 = hashfn(1)
244 hash_42 = hashfn(42)
245 hash_200001 = hashfn(200001)
246
247 self.assertEqual(hash_1, hashfn(fractions.Fraction(1)))
248 self.assertEqual(hash_1, hashfn("1"))
249
250 self.assertEqual(hash_42, hashfn(fractions.Fraction(42)))
251 self.assertEqual(hash_42, hashfn("42"))
252
253 self.assertEqual(hash_200001, hashfn(fractions.Fraction(200001)))
254 self.assertEqual(hash_200001, hashfn("200001"))
255
256
257 def test_time_hashing_rationals(self):
258 """
259 Check that hashes fractions and strings match for some
260 reasonable rational numbers.
261 """
262 hashfn = numbergen.Hash("test", input_count=1)
263 pi = "3.141592"
264 half = fractions.Fraction(0.5)
265 self.assertEqual(hashfn(0.5), hashfn(half))
266 self.assertEqual(hashfn(pi), hashfn(fractions.Fraction(pi)))
267
268
269 def test_time_hashing_integers_gmpy(self):
270 """
271 Check that hashes for gmpy values at the integers also matches
272 those of ints, fractions and strings.
273 """
274 if gmpy is None: raise SkipTest
275 hashfn = numbergen.Hash("test", input_count=1)
276 hash_1 = hashfn(1)
277 hash_42 = hashfn(42)
278
279 self.assertEqual(hash_1, hashfn(gmpy.mpq(1)))
280 self.assertEqual(hash_1, hashfn(1))
281
282 self.assertEqual(hash_42, hashfn(gmpy.mpq(42)))
283 self.assertEqual(hash_42, hashfn(42))
284
285 def test_time_hashing_rationals_gmpy(self):
286 """
287 Check that hashes of fractions and gmpy mpqs match for some
288 reasonable rational numbers.
289 """
290 if gmpy is None: raise SkipTest
291 pi = "3.141592"
292 hashfn = numbergen.Hash("test", input_count=1)
293 self.assertEqual(hashfn(0.5), hashfn(gmpy.mpq(0.5)))
294 self.assertEqual(hashfn(pi), hashfn(gmpy.mpq(3.141592)))
295
296
297
298
299 if __name__ == "__main__":
300 import nose
301 nose.runmodule()
+0
-711
tests/API1/testwatch.py less more
0 """
1 Unit test for watch mechanism
2 """
3 from . import API1TestCase
4
5 from .utils import MockLoggingHandler
6
7 import param
8
9 from param.parameterized import discard_events
10
11
12 class Accumulator(object):
13
14 def __init__(self):
15 self.args = []
16 self.kwargs = []
17
18 def __call__(self, *args, **kwargs):
19 self.args.append(args)
20 self.kwargs.append(kwargs)
21
22 def call_count(self):
23 return max(len(self.args), len(self.kwargs))
24
25 def args_for_call(self, number):
26 return self.args[number]
27
28 def kwargs_for_call(self, number):
29 return self.kwargs[number]
30
31
32
33 class SimpleWatchExample(param.Parameterized):
34 a = param.Parameter(default=0)
35 b = param.Parameter(default=0)
36 c = param.Parameter(default=0)
37 d = param.Integer(default=0)
38
39
40 class SimpleWatchSubclass(SimpleWatchExample):
41 pass
42
43
44 class WatchMethodExample(SimpleWatchSubclass):
45
46 @param.depends('a', watch='queued')
47 def _clip_a(self):
48 if self.a > 3:
49 self.a = 3
50
51 @param.depends('b', watch=True)
52 def _clip_b(self):
53 if self.b > 10:
54 self.b = 10
55
56 @param.depends('b', watch=True)
57 def _set_c(self):
58 self.c = self.b*2
59
60 @param.depends('c', watch=True)
61 def _set_d_bounds(self):
62 self.param.d.bounds = (self.c, self.c*2)
63
64
65 class WatchSubclassExample(WatchMethodExample):
66
67 pass
68
69
70
71 class TestWatch(API1TestCase):
72
73 @classmethod
74 def setUpClass(cls):
75 super(TestWatch, cls).setUpClass()
76 log = param.parameterized.get_logger()
77 cls.log_handler = MockLoggingHandler(level='DEBUG')
78 log.addHandler(cls.log_handler)
79
80
81 def setUp(self):
82 super(TestWatch, self).setUp()
83 self.accumulator = 0
84
85 def test_triggered_when_changed(self):
86 def accumulator(change):
87 self.accumulator += change.new
88
89 obj = SimpleWatchExample()
90 obj.param.watch(accumulator, 'a')
91 obj.a = 1
92 self.assertEqual(self.accumulator, 1)
93 obj.a = 2
94 self.assertEqual(self.accumulator, 3)
95
96
97 def test_discard_events_decorator(self):
98 def accumulator(change):
99 self.accumulator += change.new
100
101 obj = SimpleWatchExample()
102 obj.param.watch(accumulator, 'a')
103 with discard_events(obj):
104 obj.a = 1
105 self.assertEqual(self.accumulator, 0)
106 obj.a = 2
107 self.assertEqual(self.accumulator, 3)
108
109
110 def test_triggered_when_changed_iterator_type(self):
111 def accumulator(change):
112 self.accumulator = change.new
113
114 obj = SimpleWatchExample()
115 obj.param.watch(accumulator, 'a')
116 obj.a = []
117 self.assertEqual(self.accumulator, [])
118 obj.a = tuple()
119 self.assertEqual(self.accumulator, tuple())
120
121
122 def test_triggered_when_changed_mapping_type(self):
123 def accumulator(change):
124 self.accumulator = change.new
125
126 obj = SimpleWatchExample()
127 obj.param.watch(accumulator, 'a')
128 obj.a = []
129 self.assertEqual(self.accumulator, [])
130 obj.a = {}
131 self.assertEqual(self.accumulator, {})
132
133
134 def test_untriggered_when_unchanged(self):
135 def accumulator(change):
136 self.accumulator += change.new
137
138 obj = SimpleWatchExample()
139 obj.param.watch(accumulator, 'a')
140 obj.a = 1
141 self.assertEqual(self.accumulator, 1)
142 obj.a = 1
143 self.assertEqual(self.accumulator, 1)
144
145
146 def test_triggered_when_unchanged_complex_type(self):
147 def accumulator(change):
148 self.accumulator += 1
149
150 obj = SimpleWatchExample()
151 obj.param.watch(accumulator, 'a')
152 subobj = object()
153 obj.a = subobj
154 self.assertEqual(self.accumulator, 1)
155 obj.a = subobj
156 self.assertEqual(self.accumulator, 2)
157
158
159 def test_triggered_when_unchanged_if_not_onlychanged(self):
160 accumulator = Accumulator()
161 obj = SimpleWatchExample()
162 obj.param.watch(accumulator, 'a', onlychanged=False)
163 obj.a = 1
164
165 self.assertEqual(accumulator.call_count(), 1)
166 args = accumulator.args_for_call(0)
167 self.assertEqual(len(args), 1)
168 self.assertEqual(args[0].name, 'a')
169 self.assertEqual(args[0].old, 0)
170 self.assertEqual(args[0].new, 1)
171 self.assertEqual(args[0].type, 'set')
172
173 obj.a = 1
174 args = accumulator.args_for_call(1)
175 self.assertEqual(len(args), 1)
176 self.assertEqual(args[0].name, 'a')
177 self.assertEqual(args[0].old, 1)
178 self.assertEqual(args[0].new, 1)
179 self.assertEqual(args[0].type, 'set')
180
181
182
183 def test_untriggered_when_unwatched(self):
184 def accumulator(change):
185 self.accumulator += change.new
186
187 obj = SimpleWatchExample()
188 watcher = obj.param.watch(accumulator, 'a')
189 obj.a = 1
190 self.assertEqual(self.accumulator, 1)
191 obj.param.unwatch(watcher)
192 obj.a = 2
193 self.assertEqual(self.accumulator, 1)
194
195
196 def test_warning_unwatching_when_unwatched(self):
197 def accumulator(change):
198 self.accumulator += change.new
199
200 obj = SimpleWatchExample()
201 watcher = obj.param.watch(accumulator, 'a')
202 obj.param.unwatch(watcher)
203 obj.param.unwatch(watcher)
204 self.log_handler.assertEndsWith('WARNING',
205 ' to remove.')
206
207 def test_simple_batched_watch_setattr(self):
208
209 accumulator = Accumulator()
210
211 obj = SimpleWatchExample()
212 obj.param.watch(accumulator, ['a','b'])
213
214 obj.a = 2
215 self.assertEqual(accumulator.call_count(), 1)
216 args = accumulator.args_for_call(0)
217
218 self.assertEqual(len(args), 1)
219 self.assertEqual(args[0].name, 'a')
220 self.assertEqual(args[0].old, 0)
221 self.assertEqual(args[0].new, 2)
222 self.assertEqual(args[0].type, 'changed')
223
224 obj.b = 3
225 self.assertEqual(accumulator.call_count(), 2)
226 args = accumulator.args_for_call(1)
227
228 self.assertEqual(len(args), 1)
229 self.assertEqual(args[0].name, 'b')
230 self.assertEqual(args[0].old, 0)
231 self.assertEqual(args[0].new, 3)
232 self.assertEqual(args[0].type, 'changed')
233
234 def test_batched_watch_context_manager(self):
235
236 accumulator = Accumulator()
237
238 obj = SimpleWatchExample()
239 obj.param.watch(accumulator, ['a','b'])
240
241 with param.batch_watch(obj):
242 obj.a = 2
243 obj.b = 3
244
245 self.assertEqual(accumulator.call_count(), 1)
246 args = accumulator.args_for_call(0)
247
248 self.assertEqual(len(args), 2)
249 self.assertEqual(args[0].name, 'a')
250 self.assertEqual(args[0].old, 0)
251 self.assertEqual(args[0].new, 2)
252 self.assertEqual(args[0].type, 'changed')
253 self.assertEqual(args[1].name, 'b')
254 self.assertEqual(args[1].old, 0)
255 self.assertEqual(args[1].new, 3)
256 self.assertEqual(args[1].type, 'changed')
257
258 def test_nested_batched_watch_setattr(self):
259
260 obj = SimpleWatchExample()
261
262 accumulator = Accumulator()
263 obj.param.watch(accumulator, ['a', 'c'])
264
265 def set_c(*events):
266 obj.c = 3
267
268 obj.param.watch(set_c, ['a', 'b'])
269
270 obj.param.set_param(a=2)
271 self.assertEqual(obj.c, 3)
272
273 # Change inside watch callback should have triggered
274 # second call to accumulator
275 self.assertEqual(accumulator.call_count(), 2)
276
277
278 def test_simple_batched_watch(self):
279
280 accumulator = Accumulator()
281
282 obj = SimpleWatchExample()
283 obj.param.watch(accumulator, ['a','b'])
284 obj.param.set_param(a=23, b=42)
285
286 self.assertEqual(accumulator.call_count(), 1)
287 args = accumulator.args_for_call(0)
288 self.assertEqual(len(args), 2)
289
290 self.assertEqual(args[0].name, 'a')
291 self.assertEqual(args[0].old, 0)
292 self.assertEqual(args[0].new, 23)
293 self.assertEqual(args[0].type, 'changed')
294
295 self.assertEqual(args[1].name, 'b')
296 self.assertEqual(args[1].old, 0)
297 self.assertEqual(args[1].new, 42)
298 self.assertEqual(args[1].type, 'changed')
299
300
301 def test_simple_class_batched_watch(self):
302
303 accumulator = Accumulator()
304
305 obj = SimpleWatchSubclass
306 watcher = obj.param.watch(accumulator, ['a','b'])
307 obj.param.set_param(a=23, b=42)
308
309 self.assertEqual(accumulator.call_count(), 1)
310 args = accumulator.args_for_call(0)
311 self.assertEqual(len(args), 2)
312
313 self.assertEqual(args[0].name, 'a')
314 self.assertEqual(args[0].old, 0)
315 self.assertEqual(args[0].new, 23)
316 self.assertEqual(args[0].type, 'changed')
317
318 self.assertEqual(args[1].name, 'b')
319 self.assertEqual(args[1].old, 0)
320 self.assertEqual(args[1].new, 42)
321 self.assertEqual(args[1].type, 'changed')
322
323 SimpleWatchExample.param.unwatch(watcher)
324 obj.param.set_param(a=0, b=0)
325
326
327 def test_simple_batched_watch_callback_reuse(self):
328
329 accumulator = Accumulator()
330
331 obj = SimpleWatchExample()
332 obj.param.watch(accumulator, ['a','b'])
333 obj.param.watch(accumulator, ['c'])
334
335 obj.param.set_param(a=23, b=42, c=99)
336
337 self.assertEqual(accumulator.call_count(), 2)
338 # Order may be undefined for Python <3.6
339 for args in [accumulator.args_for_call(i) for i in [0,1]]:
340 if len(args) == 1: # ['c']
341 self.assertEqual(args[0].name, 'c')
342 self.assertEqual(args[0].old, 0)
343 self.assertEqual(args[0].new, 99)
344 self.assertEqual(args[0].type, 'changed')
345
346 elif len(args) == 2: # ['a', 'b']
347 self.assertEqual(args[0].name, 'a')
348 self.assertEqual(args[0].old, 0)
349 self.assertEqual(args[0].new, 23)
350 self.assertEqual(args[0].type, 'changed')
351
352 self.assertEqual(args[1].name, 'b')
353 self.assertEqual(args[1].old, 0)
354 self.assertEqual(args[1].new, 42)
355 self.assertEqual(args[0].type, 'changed')
356 else:
357 raise Exception('Invalid number of arguments')
358
359
360 def test_subclass_batched_watch(self):
361
362 accumulator = Accumulator()
363
364 obj = SimpleWatchSubclass()
365
366 obj.param.watch(accumulator, ['b','c'])
367 obj.param.set_param(b=23, c=42)
368
369 self.assertEqual(accumulator.call_count(), 1)
370 args = accumulator.args_for_call(0)
371 self.assertEqual(len(args), 2)
372
373 self.assertEqual(args[0].name, 'b')
374 self.assertEqual(args[0].old, 0)
375 self.assertEqual(args[0].new, 23)
376 self.assertEqual(args[0].type, 'changed')
377
378 self.assertEqual(args[1].name, 'c')
379 self.assertEqual(args[1].old, 0)
380 self.assertEqual(args[1].new, 42)
381 self.assertEqual(args[1].type, 'changed')
382
383
384 def test_nested_batched_watch(self):
385
386 accumulator = Accumulator()
387
388 obj = SimpleWatchExample()
389
390 def set_param(*changes):
391 obj.param.set_param(a=10, d=12)
392
393 obj.param.watch(accumulator, ['a', 'b','c', 'd'])
394 obj.param.watch(set_param, ['b', 'c'])
395 obj.param.set_param(b=23, c=42)
396
397 self.assertEqual(accumulator.call_count(), 2)
398 args = accumulator.args_for_call(0)
399 self.assertEqual(len(args), 2)
400
401 self.assertEqual(args[0].name, 'b')
402 self.assertEqual(args[0].old, 0)
403 self.assertEqual(args[0].new, 23)
404 self.assertEqual(args[0].type, 'changed')
405
406 self.assertEqual(args[1].name, 'c')
407 self.assertEqual(args[1].old, 0)
408 self.assertEqual(args[1].new, 42)
409 self.assertEqual(args[1].type, 'changed')
410
411 args = accumulator.args_for_call(1)
412 self.assertEqual(len(args), 2)
413
414 self.assertEqual(args[0].name, 'a')
415 self.assertEqual(args[0].old, 0)
416 self.assertEqual(args[0].new, 10)
417 self.assertEqual(args[0].type, 'changed')
418
419 self.assertEqual(args[1].name, 'd')
420 self.assertEqual(args[1].old, 0)
421 self.assertEqual(args[1].new, 12)
422 self.assertEqual(args[1].type, 'changed')
423
424
425 def test_nested_batched_watch_not_onlychanged(self):
426 accumulator = Accumulator()
427
428 obj = SimpleWatchSubclass()
429
430 obj.param.watch(accumulator, ['b','c'], onlychanged=False)
431 obj.param.set_param(b=0, c=0)
432
433 self.assertEqual(accumulator.call_count(), 1)
434
435 args = accumulator.args_for_call(0)
436 self.assertEqual(len(args), 2)
437
438 self.assertEqual(args[0].name, 'b')
439 self.assertEqual(args[0].old, 0)
440 self.assertEqual(args[0].new, 0)
441 self.assertEqual(args[0].type, 'set')
442
443 self.assertEqual(args[1].name, 'c')
444 self.assertEqual(args[1].old, 0)
445 self.assertEqual(args[1].new, 0)
446 self.assertEqual(args[1].type, 'set')
447
448
449
450 class TestWatchMethod(API1TestCase):
451
452 def test_dependent_params(self):
453 obj = WatchMethodExample()
454
455 obj.b = 3
456 self.assertEqual(obj.c, 6)
457
458 def test_multiple_watcher_dispatch_queued(self):
459 obj = WatchMethodExample()
460 obj2 = SimpleWatchExample()
461
462 def link(event):
463 obj2.a = event.new
464
465 obj.param.watch(link, 'a', queued=True)
466 obj.a = 4
467 self.assertEqual(obj.a, 3)
468 self.assertEqual(obj2.a, 3)
469
470 def test_multiple_watcher_dispatch(self):
471 obj = WatchMethodExample()
472 obj2 = SimpleWatchExample()
473
474 def link(event):
475 obj2.b = event.new
476
477 obj.param.watch(link, 'b')
478 obj.b = 11
479 self.assertEqual(obj.b, 10)
480 self.assertEqual(obj2.b, 11)
481
482 def test_multiple_watcher_dispatch_on_param_attribute(self):
483 obj = WatchMethodExample()
484 accumulator = Accumulator()
485
486 obj.param.watch(accumulator, 'd', 'bounds')
487 obj.c = 2
488 self.assertEqual(obj.param.d.bounds, (2, 4))
489 self.assertEqual(accumulator.call_count(), 1)
490
491 def test_depends_with_watch_on_subclass(self):
492 obj = WatchSubclassExample()
493
494 obj.b = 3
495 self.assertEqual(obj.c, 6)
496
497
498
499
500 class TestWatchValues(API1TestCase):
501
502 def setUp(self):
503 super(TestWatchValues, self).setUp()
504 self.accumulator = 0
505
506 def test_triggered_when_values_changed(self):
507 def accumulator(a):
508 self.accumulator += a
509
510 obj = SimpleWatchExample()
511 obj.param.watch_values(accumulator, 'a')
512 obj.a = 1
513 self.assertEqual(self.accumulator, 1)
514 obj.a = 2
515 self.assertEqual(self.accumulator, 3)
516
517
518 def test_untriggered_when_values_unchanged(self):
519 def accumulator(a):
520 self.accumulator += a
521
522 obj = SimpleWatchExample()
523 obj.param.watch_values(accumulator, 'a')
524 obj.a = 1
525 self.assertEqual(self.accumulator, 1)
526 obj.a = 1
527 self.assertEqual(self.accumulator, 1)
528
529
530 def test_untriggered_when_values_unwatched(self):
531 def accumulator(a):
532 self.accumulator += a
533
534 obj = SimpleWatchExample()
535 watcher = obj.param.watch_values(accumulator, 'a')
536 obj.a = 1
537 self.assertEqual(self.accumulator, 1)
538 obj.param.unwatch(watcher)
539 obj.a = 2
540 self.assertEqual(self.accumulator, 1)
541
542
543 def test_simple_batched_watch_values_setattr(self):
544
545 accumulator = Accumulator()
546
547 obj = SimpleWatchExample()
548 obj.param.watch_values(accumulator, ['a','b'])
549
550 obj.a = 2
551 self.assertEqual(accumulator.call_count(), 1)
552 kwargs = accumulator.kwargs_for_call(0)
553
554 self.assertEqual(len(kwargs), 1)
555 self.assertEqual(kwargs, {'a':2})
556
557 obj.b = 3
558 self.assertEqual(accumulator.call_count(), 2)
559 kwargs = accumulator.kwargs_for_call(1)
560 self.assertEqual(kwargs, {'b':3})
561
562
563 def test_simple_batched_watch_values(self):
564
565 accumulator = Accumulator()
566
567 obj = SimpleWatchExample()
568 obj.param.watch_values(accumulator, ['a','b'])
569 obj.param.set_param(a=23, b=42)
570
571 self.assertEqual(accumulator.call_count(), 1)
572 kwargs = accumulator.kwargs_for_call(0)
573 self.assertEqual(kwargs, {'a':23, 'b':42})
574
575
576 def test_simple_batched_watch_values_callback_reuse(self):
577
578 accumulator = Accumulator()
579
580 obj = SimpleWatchExample()
581 obj.param.watch_values(accumulator, ['a','b'])
582 obj.param.watch_values(accumulator, ['c'])
583
584 obj.param.set_param(a=23, b=42, c=99)
585
586 self.assertEqual(accumulator.call_count(), 2)
587 # Order may be undefined for Python <3.6
588 for kwargs in [accumulator.kwargs_for_call(i) for i in [0,1]]:
589 if len(kwargs) == 1: # ['c']
590 self.assertEqual(kwargs, {'c':99})
591 elif len(kwargs) == 2: # ['a', 'b']
592 self.assertEqual(kwargs, {'a':23, 'b':42})
593 else:
594 raise Exception('Invalid number of arguments')
595
596
597
598
599
600 class TestWatchAttributes(API1TestCase):
601
602 def setUp(self):
603 super(TestWatchAttributes, self).setUp()
604 self.accumulator = []
605
606 def tearDown(self):
607 SimpleWatchExample.param['d'].bounds = None
608
609 def test_watch_class_param_attribute(self):
610 def accumulator(a):
611 self.accumulator += [a.new]
612
613 SimpleWatchExample.param.watch(accumulator, ['d'], 'bounds')
614 SimpleWatchExample.param['d'].bounds = (0, 3)
615 assert self.accumulator == [(0, 3)]
616
617 def test_watch_instance_param_attribute(self):
618 def accumulator(a):
619 self.accumulator += [a.new]
620
621 obj = SimpleWatchExample()
622 obj.param.watch(accumulator, ['d'], 'bounds')
623
624 # Ensure watching an instance parameter makes copy
625 assert obj.param.objects('current')['d'] is not SimpleWatchExample.param['d']
626
627 obj.param['d'].bounds = (0, 3)
628 assert SimpleWatchExample.param['d'].bounds is None
629 assert self.accumulator == [(0, 3)]
630
631
632
633 class TestTrigger(API1TestCase):
634
635 def setUp(self):
636 super(TestTrigger, self).setUp()
637 self.accumulator = 0
638
639 def test_simple_trigger_one_param(self):
640 accumulator = Accumulator()
641 obj = SimpleWatchExample()
642 obj.param.watch(accumulator, ['a'])
643 obj.param.trigger('a')
644 self.assertEqual(accumulator.call_count(), 1)
645
646 args = accumulator.args_for_call(0)
647 self.assertEqual(args[0].name, 'a')
648 self.assertEqual(args[0].old, 0)
649 self.assertEqual(args[0].new, 0)
650 self.assertEqual(args[0].type, 'triggered')
651
652 def test_simple_trigger_when_batched(self):
653 accumulator = Accumulator()
654 obj = SimpleWatchExample()
655 obj.param.watch(accumulator, ['a'])
656 with param.batch_watch(obj):
657 obj.param.trigger('a')
658 self.assertEqual(accumulator.call_count(), 1)
659
660 args = accumulator.args_for_call(0)
661 self.assertEqual(args[0].name, 'a')
662 self.assertEqual(args[0].old, 0)
663 self.assertEqual(args[0].new, 0)
664 # Note: This is not strictly correct
665 self.assertEqual(args[0].type, 'changed')
666
667 def test_simple_trigger_one_param_change(self):
668 accumulator = Accumulator()
669 obj = SimpleWatchExample()
670 obj.param.watch(accumulator, ['a'])
671 obj.a = 42
672 self.assertEqual(accumulator.call_count(), 1)
673
674 obj.param.trigger('a')
675 self.assertEqual(accumulator.call_count(), 2)
676
677 args = accumulator.args_for_call(0)
678 self.assertEqual(args[0].name, 'a')
679 self.assertEqual(args[0].old, 0)
680 self.assertEqual(args[0].new, 42)
681 self.assertEqual(args[0].type, 'changed')
682
683 args = accumulator.args_for_call(1)
684 self.assertEqual(args[0].name, 'a')
685 self.assertEqual(args[0].old, 42)
686 self.assertEqual(args[0].new, 42)
687 self.assertEqual(args[0].type, 'triggered')
688
689 def test_simple_trigger_two_params(self):
690 accumulator = Accumulator()
691 obj = SimpleWatchExample()
692 obj.param.watch(accumulator, ['a','b'])
693 obj.param.trigger('a','b')
694 self.assertEqual(accumulator.call_count(), 1)
695
696 args = accumulator.args_for_call(0)
697 self.assertEqual(args[0].name, 'a')
698 self.assertEqual(args[0].old, 0)
699 self.assertEqual(args[0].new, 0)
700 self.assertEqual(args[0].type, 'triggered')
701
702 self.assertEqual(args[1].name, 'b')
703 self.assertEqual(args[1].old, 0)
704 self.assertEqual(args[1].new, 0)
705 self.assertEqual(args[1].type, 'triggered')
706
707
708 if __name__ == "__main__":
709 import nose
710 nose.runmodule()
+0
-77
tests/API1/utils.py less more
0 import logging
1
2 class MockLoggingHandler(logging.Handler):
3 """Mock logging handler to check for expected logs.
4
5 Messages are available from an instance's ``messages`` dict, in
6 order, indexed by a lowercase log level string (e.g., 'debug',
7 'info', etc.).
8
9 This is typically used by using a setUpClass classmethod and a setUp
10 method on a test case. The setUpClass classmethod can be configured
11 as follows after calling super (with cls):
12
13 log = param.parameterized.get_logger()
14 cls.log_handler = MockLoggingHandler(level='DEBUG')
15 log.addHandler(cls.log_handler)
16
17 The setUp method then just needs to call self.log_handler.reset()
18 between tests (typically after invoking super). This is necessary to
19 make the tests independent where the tests can use the
20 self.log_handler.tail and self.log_handler.assertEndsWith methods.
21 """
22
23 def __init__(self, *args, **kwargs):
24 self.messages = {'DEBUG': [], 'INFO': [], 'WARNING': [],
25 'ERROR': [], 'CRITICAL': [], 'VERBOSE':[]}
26 self.param_methods = {'WARNING':'param.warning()', 'INFO':'param.message()',
27 'VERBOSE':'param.verbose()', 'DEBUG':'param.debug()'}
28 super(MockLoggingHandler, self).__init__(*args, **kwargs)
29
30 def emit(self, record):
31 "Store a message to the instance's messages dictionary"
32 self.acquire()
33 try:
34 self.messages[record.levelname].append(record.getMessage())
35 finally:
36 self.release()
37
38 def reset(self):
39 self.acquire()
40 self.messages = {'DEBUG': [], 'INFO': [], 'WARNING': [],
41 'ERROR': [], 'CRITICAL': [], 'VERBOSE':[]}
42 self.release()
43
44 def tail(self, level, n=1):
45 "Returns the last n lines captured at the given level"
46 return [str(el) for el in self.messages[level][-n:]]
47
48 def assertEndsWith(self, level, substring):
49 """
50 Assert that the last line captured at the given level ends with
51 a particular substring.
52 """
53 msg='\n\n{method}: {last_line}\ndoes not end with:\n{substring}'
54 last_line = self.tail(level, n=1)
55 if len(last_line) == 0:
56 raise AssertionError('Missing {method} output: {substring}'.format(
57 method=self.param_methods[level], substring=repr(substring)))
58 if not last_line[0].endswith(substring):
59 raise AssertionError(msg.format(method=self.param_methods[level],
60 last_line=repr(last_line[0]),
61 substring=repr(substring)))
62
63 def assertContains(self, level, substring):
64 """
65 Assert that the last line captured at the given level contains a
66 particular substring.
67 """
68 msg='\n\n{method}: {last_line}\ndoes not contain:\n{substring}'
69 last_line = self.tail(level, n=1)
70 if len(last_line) == 0:
71 raise AssertionError('Missing {method} output: {substring}'.format(
72 method=self.param_methods[level], substring=repr(substring)))
73 if substring not in last_line[0]:
74 raise AssertionError(msg.format(method=self.param_methods[level],
75 last_line=repr(last_line[0]),
76 substring=repr(substring)))
+0
-6
tests/__init__.py less more
0 import sys
1 import unittest # noqa
2
3 if sys.version_info[0]==2 and sys.version_info[1]<7:
4 del sys.modules['unittest']
5 sys.modules['unittest'] = __import__('unittest2')
+0
-47
tox.ini less more
0 [tox]
1 envlist =
2 py37,py36,py35,py34,py27,pypy,
3 {py27,py36}-flakes,
4 {py27,py36}-with_numpy,
5 {py27,py36}-with_ipython
6 {py27,py35,py36,py37}-with_pandas
7
8 [testenv]
9 deps = .[tests]
10 commands = nosetests
11
12 [testenv:coverage]
13 # remove develop install if https://github.com/ioam/param/issues/219
14 # implemented
15 setdevelop = True
16 passenv = TRAVIS TRAVIS_*
17 deps = {[testenv]deps}
18 coveralls
19 commands = nosetests --with-coverage --cover-package=param
20 coveralls
21 # TODO missing numbergen
22
23 [testenv:with_numpy]
24 deps = {[testenv]deps}
25 numpy
26 setenv = PARAM_TEST_NUMPY = 1
27
28 [testenv:with_pandas]
29 deps = {[testenv]deps}
30 pandas
31 setenv = PARAM_TEST_PANDAS = 1
32
33
34 [testenv:with_ipython]
35 deps = {[testenv]deps}
36 ipython
37 setenv = PARAM_TEST_IPYTHON = 1
38
39 [testenv:flakes]
40 skip_install = true
41 commands = flake8
42
43 [flake8]
44 ignore = E,W,W605
45 include = *.py
46 exclude = .git,__pycache__,.tox,.eggs,*.egg,doc,dist,build,_build,.ipynb_checkpoints,run_test.py