New upstream snapshot.
Debian Janitor
2 years ago
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 | 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-2020, HoloViz team. | |
1 | 1 | All rights reserved. |
2 | 2 | |
3 | 3 | Redistribution and use in source and binary forms, with or without |
12 | 12 | documentation and/or other materials provided with the |
13 | 13 | distribution. |
14 | 14 | |
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. | |
18 | 18 | |
19 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | 20 | "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 | Description: <img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250> | |
16 | ||
17 | | | | | |
18 | | --- | --- | | |
19 | | 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) | |
20 | | Coverage | [![coveralls](https://coveralls.io/repos/github/holoviz/param/badge.svg?branch=master)](https://coveralls.io/github/holoviz/param?branch=master) | | |
21 | | 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/) | | |
22 | | 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) | | |
23 | | 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) | | |
24 | | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) | | |
25 | ||
26 | 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. | |
27 | ||
28 | 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. | |
29 | ||
30 | Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples. | |
31 | ||
32 | Platform: Windows | |
33 | Platform: Mac OS X | |
34 | Platform: Linux | |
35 | Classifier: License :: OSI Approved :: BSD License | |
36 | Classifier: Development Status :: 5 - Production/Stable | |
37 | Classifier: Programming Language :: Python :: 2 | |
38 | Classifier: Programming Language :: Python :: 2.7 | |
39 | Classifier: Programming Language :: Python :: 3 | |
40 | Classifier: Programming Language :: Python :: 3.4 | |
41 | Classifier: Programming Language :: Python :: 3.5 | |
42 | Classifier: Programming Language :: Python :: 3.6 | |
43 | Classifier: Programming Language :: Python :: 3.7 | |
44 | Classifier: Programming Language :: Python :: 3.8 | |
45 | Classifier: Operating System :: OS Independent | |
46 | Classifier: Intended Audience :: Science/Research | |
47 | Classifier: Intended Audience :: Developers | |
48 | Classifier: Natural Language :: English | |
49 | Classifier: Topic :: Scientific/Engineering | |
50 | Classifier: Topic :: Software Development :: Libraries | |
51 | Provides: param | |
52 | Provides: numbergen | |
53 | Requires-Python: >=2.7 | |
54 | Description-Content-Type: text/markdown | |
55 | Provides-Extra: all | |
56 | Provides-Extra: doc | |
57 | Provides-Extra: tests |
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 | [![coveralls](https://coveralls.io/repos/github/holoviz/param/badge.svg?branch=master)](https://coveralls.io/github/holoviz/param?branch=master) | | |
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 | | 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) | | |
9 | | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) | | |
10 | ||
11 | 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. | |
12 | ||
13 | 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. | |
14 | ||
15 | Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples. |
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 | {% 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'] }} |
0 | python-param (1.11.2a1+git20210817.1.cde53c5-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Wed, 18 Aug 2021 20:29:30 -0000 | |
5 | ||
0 | 6 | python-param (1.9.3-2) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * Source only upload for migration to testing |
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 | ************************ | |
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 | .. | |
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 | { | |
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 | { | |
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 | } |
201 | 201 | |
202 | 202 | I32 = 4294967296 # Maximum 32 bit unsigned int (i.e. 'I') value |
203 | 203 | if isinstance(val, int): |
204 | numer, denom = val, 1 | |
204 | numer, denom = val, 1 | |
205 | 205 | elif isinstance(val, fractions.Fraction): |
206 | 206 | numer, denom = val.numerator, val.denominator |
207 | 207 | elif hasattr(val, 'numer'): |
0 | {"version_string": "None"}⏎ |
0 | from __future__ import print_function | |
0 | 1 | """ |
1 | 2 | Parameters are a kind of class attribute allowing special behavior, |
2 | 3 | including dynamically generated parameter values, documentation |
28 | 29 | descendents, get_logger, instance_descriptor, basestring) |
29 | 30 | |
30 | 31 | from .parameterized import (batch_watch, depends, output, # noqa: api import |
31 | discard_events, edit_constant) | |
32 | discard_events, edit_constant, instance_descriptor) | |
32 | 33 | from .parameterized import logging_level # noqa: api import |
33 | 34 | from .parameterized import shared_parameters # noqa: api import |
34 | 35 | |
40 | 41 | # only two required files. |
41 | 42 | try: |
42 | 43 | from .version import Version |
43 | __version__ = str(Version(fpath=__file__, archive_commit="9123ba0", reponame="param")) | |
44 | __version__ = str(Version(fpath=__file__, archive_commit="$Format:%h$", reponame="param")) | |
44 | 45 | except: |
45 | 46 | __version__ = "0.0.0+unknown" |
46 | 47 | |
220 | 221 | supplied parameters, inheriting from the specified base(s). |
221 | 222 | """ |
222 | 223 | if not (isinstance(bases, list) or isinstance(bases, tuple)): |
223 | bases=[bases] | |
224 | bases=[bases] | |
224 | 225 | return type(name, tuple(bases), params) |
225 | 226 | |
226 | 227 | |
702 | 703 | |
703 | 704 | def identity_hook(obj,val): return val |
704 | 705 | |
706 | def get_soft_bounds(bounds, softbounds): | |
707 | """ | |
708 | For each soft bound (upper and lower), if there is a defined bound | |
709 | (not equal to None) and does not exceed the hard bound, then it is | |
710 | returned. Otherwise it defaults to the hard bound. The hard bound | |
711 | could still be None. | |
712 | """ | |
713 | if bounds is None: | |
714 | hl, hu = (None, None) | |
715 | else: | |
716 | hl, hu = bounds | |
717 | ||
718 | if softbounds is None: | |
719 | sl, su = (None, None) | |
720 | else: | |
721 | sl, su = softbounds | |
722 | ||
723 | if sl is None or (hl is not None and sl<hl): | |
724 | l = hl | |
725 | else: | |
726 | l = sl | |
727 | ||
728 | if su is None or (hu is not None and su>hu): | |
729 | u = hu | |
730 | else: | |
731 | u = su | |
732 | ||
733 | return (l, u) | |
705 | 734 | |
706 | 735 | |
707 | 736 | class Number(Dynamic): |
749 | 778 | |
750 | 779 | """ |
751 | 780 | |
752 | __slots__ = ['bounds','_softbounds','inclusive_bounds','set_hook', 'step'] | |
753 | ||
754 | def __init__(self,default=0.0,bounds=None,softbounds=None, | |
781 | __slots__ = ['bounds', 'softbounds', 'inclusive_bounds', 'set_hook', 'step'] | |
782 | ||
783 | def __init__(self, default=0.0, bounds=None, softbounds=None, | |
755 | 784 | inclusive_bounds=(True,True), step=None, **params): |
756 | 785 | """ |
757 | 786 | Initialize this parameter object and store the bounds. |
758 | 787 | |
759 | 788 | Non-dynamic default values are checked against the bounds. |
760 | 789 | """ |
761 | super(Number,self).__init__(default=default,**params) | |
790 | super(Number,self).__init__(default=default, **params) | |
762 | 791 | |
763 | 792 | self.set_hook = identity_hook |
764 | 793 | self.bounds = bounds |
765 | 794 | self.inclusive_bounds = inclusive_bounds |
766 | self._softbounds = softbounds | |
795 | self.softbounds = softbounds | |
767 | 796 | self.step = step |
768 | 797 | self._validate(default) |
769 | 798 | |
770 | ||
771 | def __get__(self,obj,objtype): | |
799 | def __get__(self, obj, objtype): | |
772 | 800 | """ |
773 | 801 | Same as the superclass's __get__, but if the value was |
774 | 802 | dynamically generated, check the bounds. |
775 | 803 | """ |
776 | result = super(Number,self).__get__(obj,objtype) | |
804 | result = super(Number, self).__get__(obj, objtype) | |
777 | 805 | # CEBALERT: results in extra lookups (_value_is_dynamic() is |
778 | 806 | # also looking up 'result' - should just pass it in). Note |
779 | 807 | # that this method is called often. |
780 | if self._value_is_dynamic(obj,objtype): self._validate(result) | |
808 | if self._value_is_dynamic(obj, objtype): | |
809 | self._validate(result) | |
781 | 810 | 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 | ||
791 | 811 | |
792 | 812 | def set_in_bounds(self,obj,val): |
793 | 813 | """ |
799 | 819 | bounded_val = self.crop_to_bounds(val) |
800 | 820 | else: |
801 | 821 | bounded_val = val |
802 | super(Number,self).__set__(obj,bounded_val) | |
803 | ||
822 | super(Number, self).__set__(obj, bounded_val) | |
804 | 823 | |
805 | 824 | # CEBERRORALERT: doesn't take account of exclusive bounds; see |
806 | 825 | # https://github.com/ioam/param/issues/80. |
807 | def crop_to_bounds(self,val): | |
826 | def crop_to_bounds(self, val): | |
808 | 827 | """ |
809 | 828 | Return the given value cropped to be within the hard bounds |
810 | 829 | for this parameter. |
836 | 855 | |
837 | 856 | else: |
838 | 857 | # non-numeric value sent in: reverts to default value |
839 | return self.default | |
858 | return self.default | |
840 | 859 | |
841 | 860 | return val |
842 | 861 | |
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 | ||
862 | def _validate_bounds(self, val, bounds, inclusive_bounds): | |
863 | if bounds is None or (val is None and self.allow_None) or callable(val): | |
864 | return | |
865 | vmin, vmax = bounds | |
866 | incmin, incmax = inclusive_bounds | |
867 | if vmax is not None: | |
868 | if incmax is True: | |
869 | if not val <= vmax: | |
870 | raise ValueError("Parameter %r must be at most %s, " | |
871 | "not %s." % (self.name, vmax, val)) | |
872 | else: | |
873 | if not val < vmax: | |
874 | raise ValueError("Parameter %r must be less than %s, " | |
875 | "not %s." % (self.name, vmax, val)) | |
876 | ||
877 | if vmin is not None: | |
878 | if incmin is True: | |
879 | if not val >= vmin: | |
880 | raise ValueError("Parameter %r must be at least %s, " | |
881 | "not %s." % (self.name, vmin, val)) | |
882 | else: | |
883 | if not val > vmin: | |
884 | raise ValueError("Parameter %r must be greater than %s, " | |
885 | "not %s." % (self.name, vmin, val)) | |
886 | ||
887 | def _validate_value(self, val, allow_None): | |
888 | if (allow_None and val is None) or callable(val): | |
889 | return | |
890 | ||
891 | if not _is_number(val): | |
892 | raise ValueError("Parameter %r only takes numeric values, " | |
893 | "not type %r." % (self.name, type(val))) | |
894 | ||
895 | def _validate_step(self, val, step): | |
896 | if step is not None and not _is_number(step): | |
897 | raise ValueError("Step parameter can only be None or a " | |
898 | "numeric value, not type %r." % type(step)) | |
868 | 899 | |
869 | 900 | def _validate(self, val): |
870 | 901 | """ |
871 | 902 | Checks that the value is numeric and that it is within the hard |
872 | 903 | bounds; if not, an exception is raised. |
873 | 904 | """ |
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 | ||
905 | self._validate_value(val, self.allow_None) | |
906 | self._validate_step(val, self.step) | |
907 | self._validate_bounds(val, self.bounds, self.inclusive_bounds) | |
888 | 908 | |
889 | 909 | 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 | ||
910 | return get_soft_bounds(self.bounds, self.softbounds) | |
913 | 911 | |
914 | 912 | def __setstate__(self,state): |
915 | 913 | if 'step' not in state: |
916 | 914 | state['step'] = None |
917 | 915 | |
918 | super(Number,self).__setstate__(state) | |
916 | super(Number, self).__setstate__(state) | |
919 | 917 | |
920 | 918 | |
921 | 919 | |
922 | 920 | class Integer(Number): |
923 | 921 | """Numeric Parameter required to be an Integer""" |
924 | 922 | |
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) | |
923 | def __init__(self, default=0, **params): | |
924 | Number.__init__(self, default=default, **params) | |
925 | ||
926 | def _validate_value(self, val, allow_None): | |
927 | if callable(val): | |
928 | return | |
929 | ||
930 | if allow_None and val is None: | |
931 | return | |
932 | ||
933 | if not isinstance(val, int): | |
934 | raise ValueError("Integer parameter %r must be an integer, " | |
935 | "not type %r." % (self.name, type(val))) | |
936 | ||
937 | def _validate_step(self, val, step): | |
938 | if step is not None and not isinstance(step, int): | |
939 | raise ValueError("Step parameter can only be None or an " | |
940 | "integer value, not type %r" % type(step)) | |
942 | 941 | |
943 | 942 | |
944 | 943 | |
945 | 944 | class Magnitude(Number): |
946 | 945 | """Numeric Parameter required to be in the range [0.0-1.0].""" |
947 | 946 | |
948 | def __init__(self,default=1.0,softbounds=None,**params): | |
949 | Number.__init__(self,default=default,bounds=(0.0,1.0),softbounds=softbounds,**params) | |
947 | def __init__(self, default=1.0, softbounds=None, **params): | |
948 | Number.__init__(self, default=default, bounds=(0.0,1.0), softbounds=softbounds, **params) | |
950 | 949 | |
951 | 950 | |
952 | 951 | |
955 | 954 | |
956 | 955 | __slots__ = ['bounds'] |
957 | 956 | |
958 | # CB: bounds have no effect; see https://github.com/ioam/param/issues/82 | |
959 | def __init__(self,default=False,bounds=(0,1),**params): | |
957 | # CB: bounds have no effect; see https://github.com/holoviz/param/issues/82 | |
958 | def __init__(self, default=False, bounds=(0,1), **params): | |
960 | 959 | 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)) | |
978 | 971 | |
979 | 972 | |
980 | 973 | |
983 | 976 | |
984 | 977 | __slots__ = ['length'] |
985 | 978 | |
986 | def __init__(self,default=(0,0),length=None,**params): | |
979 | def __init__(self, default=(0,0), length=None, **params): | |
987 | 980 | """ |
988 | 981 | Initialize a tuple parameter with a fixed length (number of |
989 | 982 | elements). The length is determined by the initial default |
990 | 983 | value, if any, and must be supplied explicitly otherwise. The |
991 | 984 | length is not allowed to change after instantiation. |
992 | 985 | """ |
993 | super(Tuple,self).__init__(default=default,**params) | |
986 | super(Tuple,self).__init__(default=default, **params) | |
994 | 987 | if length is None and default is not None: |
995 | 988 | self.length = len(default) |
996 | 989 | elif length is None and default is None: |
1000 | 993 | self.length = length |
1001 | 994 | self._validate(default) |
1002 | 995 | |
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)) | |
1003 | 1012 | |
1004 | 1013 | 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 | return list(value) # As JSON has no tuple representation | |
1020 | ||
1021 | @classmethod | |
1022 | def deserialize(cls, value): | |
1023 | return tuple(value) # As JSON has no tuple representation | |
1015 | 1024 | |
1016 | 1025 | |
1017 | 1026 | class NumericTuple(Tuple): |
1018 | 1027 | """A numeric tuple Parameter (e.g. (4.5,7.6,3)) with a fixed tuple length.""" |
1019 | 1028 | |
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 | ||
1029 | def _validate_value(self, val, allow_None): | |
1030 | super(NumericTuple, self)._validate_value(val, allow_None) | |
1031 | if allow_None and val is None: | |
1032 | return | |
1033 | for n in val: | |
1034 | if _is_number(n): | |
1035 | continue | |
1036 | raise ValueError("NumericTuple parameter %r only takes numeric " | |
1037 | "values, not type %r." % (self.name, type(n))) | |
1028 | 1038 | |
1029 | 1039 | |
1030 | 1040 | class XYCoordinates(NumericTuple): |
1031 | 1041 | """A NumericTuple for an X,Y coordinate.""" |
1032 | 1042 | |
1033 | def __init__(self,default=(0.0,0.0),**params): | |
1034 | super(XYCoordinates,self).__init__(default=default,length=2,**params) | |
1035 | ||
1043 | def __init__(self, default=(0.0, 0.0), **params): | |
1044 | super(XYCoordinates,self).__init__(default=default, length=2, **params) | |
1036 | 1045 | |
1037 | 1046 | |
1038 | 1047 | class Callable(Parameter): |
1045 | 1054 | 2.4, so instantiate must be False for those values. |
1046 | 1055 | """ |
1047 | 1056 | |
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 | ||
1057 | def _validate_value(self, val, allow_None): | |
1058 | if (allow_None and val is None) or callable(val): | |
1059 | return | |
1060 | ||
1061 | raise ValueError("Callable parameter %r only takes a callable object, " | |
1062 | "not objects of type %r." % (self.name, type(val))) | |
1053 | 1063 | |
1054 | 1064 | |
1055 | 1065 | class Action(Callable): |
1067 | 1077 | return False |
1068 | 1078 | |
1069 | 1079 | |
1070 | ||
1071 | 1080 | # CEBALERT: this should be a method of ClassSelector. |
1072 | 1081 | def concrete_descendents(parentclass): |
1073 | 1082 | """ |
1081 | 1090 | """ |
1082 | 1091 | return dict((c.__name__,c) for c in descendents(parentclass) |
1083 | 1092 | if not _is_abstract(c)) |
1084 | ||
1085 | 1093 | |
1086 | 1094 | |
1087 | 1095 | class Composite(Parameter): |
1094 | 1102 | in the order specified. Likewise, setting the parameter takes a |
1095 | 1103 | sequence of values and sets the value of the constituent |
1096 | 1104 | attributes. |
1097 | """ | |
1098 | ||
1099 | __slots__=['attribs','objtype'] | |
1100 | ||
1101 | def __init__(self,attribs=None,**kw): | |
1105 | ||
1106 | This Parameter type has not been tested with watchers and | |
1107 | dependencies, and may not support them properly. | |
1108 | """ | |
1109 | ||
1110 | __slots__ = ['attribs', 'objtype'] | |
1111 | ||
1112 | def __init__(self, attribs=None, **kw): | |
1102 | 1113 | if attribs is None: |
1103 | 1114 | attribs = [] |
1104 | super(Composite,self).__init__(default=None,**kw) | |
1115 | super(Composite, self).__init__(default=None, **kw) | |
1105 | 1116 | self.attribs = attribs |
1106 | 1117 | |
1107 | def __get__(self,obj,objtype): | |
1118 | def __get__(self, obj, objtype): | |
1108 | 1119 | """ |
1109 | 1120 | Return the values of all the attribs, as a list. |
1110 | 1121 | """ |
1111 | 1122 | if obj is None: |
1112 | return [getattr(objtype,a) for a in self.attribs] | |
1123 | return [getattr(objtype, a) for a in self.attribs] | |
1113 | 1124 | else: |
1114 | return [getattr(obj,a) for a in self.attribs] | |
1125 | return [getattr(obj, a) for a in self.attribs] | |
1126 | ||
1127 | def _validate_attribs(self, val, attribs): | |
1128 | if len(val) == len(attribs): | |
1129 | return | |
1130 | raise ValueError("Compound parameter %r got the wrong number " | |
1131 | "of values (needed %d, but got %d)." % | |
1132 | (self.name, len(attribs), len(val))) | |
1115 | 1133 | |
1116 | 1134 | 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)) | |
1135 | self._validate_attribs(val, self.attribs) | |
1118 | 1136 | |
1119 | 1137 | def _post_setter(self, obj, val): |
1120 | 1138 | if obj is None: |
1121 | for a,v in zip(self.attribs,val): | |
1122 | setattr(self.objtype,a,v) | |
1139 | for a, v in zip(self.attribs, val): | |
1140 | setattr(self.objtype, a, v) | |
1123 | 1141 | else: |
1124 | for a,v in zip(self.attribs,val): | |
1125 | setattr(obj,a,v) | |
1142 | for a, v in zip(self.attribs, val): | |
1143 | setattr(obj, a, v) | |
1126 | 1144 | |
1127 | 1145 | |
1128 | 1146 | class SelectorBase(Parameter): |
1138 | 1156 | raise NotImplementedError("get_range() must be implemented in subclasses.") |
1139 | 1157 | |
1140 | 1158 | |
1141 | class ObjectSelector(SelectorBase): | |
1159 | class Selector(SelectorBase): | |
1142 | 1160 | """ |
1143 | 1161 | Parameter whose value must be one object from a list of possible objects. |
1162 | ||
1163 | By default, if no default is specified, picks the first object from | |
1164 | the provided set of objects, as long as the objects are in an | |
1165 | ordered data collection. | |
1144 | 1166 | |
1145 | 1167 | check_on_set restricts the value to be among the current list of |
1146 | 1168 | objects. By default, if objects are initially supplied, |
1161 | 1183 | up from the object value. |
1162 | 1184 | """ |
1163 | 1185 | |
1164 | __slots__ = ['objects','compute_default_fn','check_on_set','names'] | |
1165 | ||
1166 | # ObjectSelector is usually used to allow selection from a list of | |
1186 | __slots__ = ['objects', 'compute_default_fn', 'check_on_set', 'names'] | |
1187 | ||
1188 | # Selector is usually used to allow selection from a list of | |
1167 | 1189 | # 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): | |
1190 | def __init__(self, objects=None, default=None, instantiate=False, | |
1191 | compute_default_fn=None, check_on_set=None, | |
1192 | allow_None=None, empty_default=False, **params): | |
1193 | ||
1194 | autodefault = None | |
1195 | if objects: | |
1196 | if is_ordered_dict(objects): | |
1197 | autodefault = list(objects.values())[0] | |
1198 | elif isinstance(objects, dict): | |
1199 | main.param.warning("Parameter default value is arbitrary due to " | |
1200 | "dictionaries prior to Python 3.6 not being " | |
1201 | "ordered; should use an ordered dict or " | |
1202 | "supply an explicit default value.") | |
1203 | autodefault = list(objects.values())[0] | |
1204 | elif isinstance(objects, list): | |
1205 | autodefault = objects[0] | |
1206 | ||
1207 | default = autodefault if (not empty_default and default is None) else default | |
1208 | ||
1170 | 1209 | if objects is None: |
1171 | 1210 | objects = [] |
1172 | 1211 | if isinstance(objects, collections_abc.Mapping): |
1178 | 1217 | self.compute_default_fn = compute_default_fn |
1179 | 1218 | |
1180 | 1219 | 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 | |
1220 | self.check_on_set = check_on_set | |
1221 | elif len(objects) == 0: | |
1222 | self.check_on_set = False | |
1184 | 1223 | else: |
1185 | self.check_on_set=True | |
1186 | ||
1187 | super(ObjectSelector,self).__init__(default=default,instantiate=instantiate, | |
1188 | **params) | |
1224 | self.check_on_set = True | |
1225 | ||
1226 | super(Selector,self).__init__( | |
1227 | default=default, instantiate=instantiate, **params) | |
1189 | 1228 | # Required as Parameter sets allow_None=True if default is None |
1190 | 1229 | self.allow_None = allow_None |
1191 | 1230 | if default is not None and self.check_on_set is True: |
1192 | 1231 | self._validate(default) |
1193 | 1232 | |
1194 | ||
1195 | 1233 | # CBNOTE: if the list of objects is changed, the current value for |
1196 | 1234 | # this parameter in existing POs could be out of the new range. |
1197 | 1235 | |
1204 | 1242 | no longer None). |
1205 | 1243 | """ |
1206 | 1244 | if self.default is None and callable(self.compute_default_fn): |
1207 | self.default=self.compute_default_fn() | |
1245 | self.default = self.compute_default_fn() | |
1208 | 1246 | if self.default not in self.objects: |
1209 | 1247 | self.objects.append(self.default) |
1210 | 1248 | |
1211 | ||
1212 | 1249 | def _validate(self, val): |
1213 | 1250 | """ |
1214 | 1251 | val must be None or one of the objects in self.objects. |
1218 | 1255 | return |
1219 | 1256 | |
1220 | 1257 | 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: | |
1258 | # This method can be called before __init__ has called | |
1259 | # super's __init__, so there may not be any name set yet. | |
1260 | if (hasattr(self, "name") and self.name): | |
1261 | attrib_name = " " + self.name | |
1262 | else: | |
1226 | 1263 | attrib_name = "" |
1227 | 1264 | |
1228 | 1265 | items = [] |
1237 | 1274 | limiter = ', ...]' |
1238 | 1275 | break |
1239 | 1276 | 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)) | |
1277 | raise ValueError("%s not in parameter%s's list of possible objects, " | |
1278 | "valid options include %s" % (val, attrib_name, items)) | |
1242 | 1279 | |
1243 | 1280 | def _ensure_value_is_in_objects(self,val): |
1244 | 1281 | """ |
1247 | 1284 | to check each item instead. |
1248 | 1285 | """ |
1249 | 1286 | if not (val in self.objects): |
1250 | self.objects.append(val) | |
1287 | self.objects.append(val) | |
1251 | 1288 | |
1252 | 1289 | def get_range(self): |
1253 | 1290 | """ |
1258 | 1295 | return named_objs(self.objects, self.names) |
1259 | 1296 | |
1260 | 1297 | |
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) | |
1298 | class ObjectSelector(Selector): | |
1299 | """ | |
1300 | Deprecated. Same as Selector, but with a different constructor for | |
1301 | historical reasons. | |
1302 | """ | |
1303 | def __init__(self, default=None, objects=None, **kwargs): | |
1304 | super(ObjectSelector,self).__init__(objects=objects, default=default, | |
1305 | empty_default=True, **kwargs) | |
1306 | ||
1291 | 1307 | |
1292 | 1308 | class ClassSelector(SelectorBase): |
1293 | 1309 | """ |
1297 | 1313 | for is_instance=True. |
1298 | 1314 | """ |
1299 | 1315 | |
1300 | __slots__ = ['class_','is_instance'] | |
1316 | __slots__ = ['class_', 'is_instance'] | |
1301 | 1317 | |
1302 | 1318 | def __init__(self,class_,default=None,instantiate=True,is_instance=True,**params): |
1303 | 1319 | self.class_ = class_ |
1305 | 1321 | super(ClassSelector,self).__init__(default=default,instantiate=instantiate,**params) |
1306 | 1322 | self._validate(default) |
1307 | 1323 | |
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_)) | |
1324 | def _validate(self, val): | |
1325 | super(ClassSelector, self)._validate(val) | |
1326 | self._validate_class_(val, self.class_, self.is_instance) | |
1327 | ||
1328 | def _validate_class_(self, val, class_, is_instance): | |
1329 | if (val is None and self.allow_None): | |
1330 | return | |
1331 | if isinstance(class_, tuple): | |
1332 | class_name = ('(%s)' % ', '.join(cl.__name__ for cl in class_)) | |
1313 | 1333 | 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): | |
1334 | class_name = class_.__name__ | |
1335 | param_cls = self.__class__.__name__ | |
1336 | if is_instance: | |
1337 | if not (isinstance(val, class_)): | |
1317 | 1338 | raise ValueError( |
1318 | "Parameter '%s' value must be an instance of %s, not '%s'" % | |
1319 | (self.name, class_name, val)) | |
1339 | "%s parameter %r value must be an instance of %s, not %r." % | |
1340 | (param_cls, self.name, class_name, val)) | |
1320 | 1341 | else: |
1321 | if not (val is None and self.allow_None) and not (issubclass(val,self.class_)): | |
1342 | if not (issubclass(val, class_)): | |
1322 | 1343 | raise ValueError( |
1323 | "Parameter '%s' must be a subclass of %s, not '%s'" % | |
1324 | (val.__name__, class_name, val.__class__.__name__)) | |
1325 | ||
1344 | "%s parameter %r must be a subclass of %s, not %r." % | |
1345 | (param_cls, self.name, class_name, val.__name__)) | |
1326 | 1346 | |
1327 | 1347 | def get_range(self): |
1328 | 1348 | """ |
1338 | 1358 | all_classes = {} |
1339 | 1359 | for cls in classes: |
1340 | 1360 | all_classes.update(concrete_descendents(cls)) |
1341 | d=OrderedDict((name,class_) for name,class_ in all_classes.items()) | |
1361 | d = OrderedDict((name, class_) for name,class_ in all_classes.items()) | |
1342 | 1362 | if self.allow_None: |
1343 | d['None']=None | |
1363 | d['None'] = None | |
1344 | 1364 | return d |
1345 | 1365 | |
1346 | 1366 | |
1349 | 1369 | Parameter whose value is a list of objects, usually of a specified type. |
1350 | 1370 | |
1351 | 1371 | The bounds allow a minimum and/or maximum length of |
1352 | list to be enforced. If the class is non-None, all | |
1372 | list to be enforced. If the item_type is non-None, all | |
1353 | 1373 | 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_ | |
1374 | ||
1375 | `class_` is accepted as an alias for `item_type`, but is | |
1376 | deprecated due to conflict with how the `class_` slot is | |
1377 | used in Selector classes. | |
1378 | """ | |
1379 | ||
1380 | __slots__ = ['bounds', 'item_type', 'class_'] | |
1381 | ||
1382 | def __init__(self, default=[], class_=None, item_type=None, | |
1383 | instantiate=True, bounds=(0, None), **params): | |
1384 | self.item_type = item_type or class_ | |
1385 | self.class_ = self.item_type | |
1361 | 1386 | self.bounds = bounds |
1362 | Parameter.__init__(self,default=default,instantiate=instantiate, | |
1387 | Parameter.__init__(self, default=default, instantiate=instantiate, | |
1363 | 1388 | **params) |
1364 | 1389 | self._validate(default) |
1365 | 1390 | |
1366 | 1391 | def _validate(self, val): |
1367 | 1392 | """ |
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 | ||
1393 | Checks that the value is numeric and that it is within the hard | |
1394 | bounds; if not, an exception is raised. | |
1395 | """ | |
1396 | self._validate_value(val, self.allow_None) | |
1397 | self._validate_bounds(val, self.bounds) | |
1398 | self._validate_item_type(val, self.item_type) | |
1399 | ||
1400 | def _validate_bounds(self, val, bounds): | |
1401 | "Checks that the list is of the right length and has the right contents." | |
1402 | if bounds is None or (val is None and self.allow_None): | |
1403 | return | |
1404 | min_length, max_length = bounds | |
1405 | l = len(val) | |
1406 | if min_length is not None and max_length is not None: | |
1407 | if not (min_length <= l <= max_length): | |
1408 | raise ValueError("%s: list length must be between %s and %s (inclusive)"%(self.name,min_length,max_length)) | |
1409 | elif min_length is not None: | |
1410 | if not min_length <= l: | |
1411 | raise ValueError("%s: list length must be at least %s." | |
1412 | % (self.name, min_length)) | |
1413 | elif max_length is not None: | |
1414 | if not l <= max_length: | |
1415 | raise ValueError("%s: list length must be at most %s." | |
1416 | % (self.name, max_length)) | |
1417 | ||
1418 | def _validate_value(self, val, allow_None): | |
1419 | if allow_None and val is None: | |
1420 | return | |
1374 | 1421 | 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 | ||
1422 | raise ValueError("List parameter %r must be a list, not an object of type %s." | |
1423 | % (self.name, type(val))) | |
1424 | ||
1425 | def _validate_item_type(self, val, item_type): | |
1426 | if item_type is None or (self.allow_None and val is None): | |
1427 | return | |
1428 | for v in val: | |
1429 | if isinstance(v, item_type): | |
1430 | continue | |
1431 | raise TypeError("List parameter %r items must be instances " | |
1432 | "of type %r, not %r." % (self.name, item_type, val)) | |
1397 | 1433 | |
1398 | 1434 | |
1399 | 1435 | class HookList(List): |
1404 | 1440 | for users to register a set of commands to be called at a |
1405 | 1441 | specified place in some sequence of processing steps. |
1406 | 1442 | """ |
1407 | __slots__ = ['class_','bounds'] | |
1408 | ||
1409 | def _check_type(self,val): | |
1443 | __slots__ = ['class_', 'bounds'] | |
1444 | ||
1445 | def _validate_value(self, val, allow_None): | |
1446 | super(HookList, self)._validate_value(val, allow_None) | |
1447 | if allow_None and val is None: | |
1448 | return | |
1410 | 1449 | for v in val: |
1411 | assert callable(v),repr(self.name)+": "+repr(v)+" is not callable." | |
1412 | ||
1450 | if callable(v): | |
1451 | continue | |
1452 | raise ValueError("HookList parameter %r items must be callable, " | |
1453 | "not %r." % (self.name, v)) | |
1413 | 1454 | |
1414 | 1455 | |
1415 | 1456 | class Dict(ClassSelector): |
1416 | 1457 | """ |
1417 | 1458 | Parameter whose value is a dictionary. |
1418 | 1459 | """ |
1460 | ||
1419 | 1461 | def __init__(self, default=None, **params): |
1420 | super(Dict,self).__init__(dict, default=default, **params) | |
1462 | super(Dict, self).__init__(dict, default=default, **params) | |
1421 | 1463 | |
1422 | 1464 | |
1423 | 1465 | class Array(ClassSelector): |
1426 | 1468 | """ |
1427 | 1469 | |
1428 | 1470 | def __init__(self, default=None, **params): |
1429 | # CEBALERT: instead use python array as default? | |
1430 | 1471 | from numpy import ndarray |
1431 | super(Array,self).__init__(ndarray, allow_None=True, default=default, **params) | |
1472 | super(Array, self).__init__(ndarray, allow_None=True, default=default, **params) | |
1473 | ||
1474 | @classmethod | |
1475 | def serialize(cls, value): | |
1476 | return value.tolist() | |
1477 | ||
1478 | @classmethod | |
1479 | def deserialize(cls, value): | |
1480 | from numpy import asarray | |
1481 | return asarray(value) | |
1432 | 1482 | |
1433 | 1483 | |
1434 | 1484 | class DataFrame(ClassSelector): |
1449 | 1499 | if a list is given, the supplied DataFrame must contain exactly the |
1450 | 1500 | same columns and in the same order and no other columns. |
1451 | 1501 | """ |
1452 | __slots__ = ['rows','columns', 'ordered'] | |
1502 | ||
1503 | __slots__ = ['rows', 'columns', 'ordered'] | |
1453 | 1504 | |
1454 | 1505 | def __init__(self, default=None, rows=None, columns=None, ordered=None, **params): |
1455 | 1506 | from pandas import DataFrame as pdDFrame |
1456 | 1507 | self.rows = rows |
1457 | 1508 | self.columns = columns |
1458 | 1509 | self.ordered = ordered |
1459 | super(DataFrame,self).__init__(pdDFrame, default=default, allow_None=True, **params) | |
1510 | super(DataFrame,self).__init__(pdDFrame, default=default, **params) | |
1460 | 1511 | self._validate(self.default) |
1461 | ||
1462 | 1512 | |
1463 | 1513 | def _length_bounds_check(self, bounds, length, name): |
1464 | 1514 | message = '{name} length {length} does not match declared bounds of {bounds}' |
1478 | 1528 | |
1479 | 1529 | if isinstance(self.columns, set) and self.ordered is True: |
1480 | 1530 | raise ValueError('Columns cannot be ordered when specified as a set') |
1531 | ||
1532 | if self.allow_None and val is None: | |
1533 | return | |
1481 | 1534 | |
1482 | 1535 | if self.columns is None: |
1483 | 1536 | pass |
1501 | 1554 | if self.rows is not None: |
1502 | 1555 | self._length_bounds_check(self.rows, len(val), 'Row') |
1503 | 1556 | |
1557 | @classmethod | |
1558 | def serialize(cls, value): | |
1559 | return value.to_dict('records') | |
1560 | ||
1561 | @classmethod | |
1562 | def deserialize(cls, value): | |
1563 | from pandas import DataFrame as pdDFrame | |
1564 | return pdDFrame(value) | |
1565 | ||
1504 | 1566 | |
1505 | 1567 | class Series(ClassSelector): |
1506 | 1568 | """ |
1510 | 1572 | which may be a number or an integer bounds tuple to constrain the |
1511 | 1573 | allowable number of rows. |
1512 | 1574 | """ |
1575 | ||
1513 | 1576 | __slots__ = ['rows'] |
1577 | ||
1578 | def __init__(self, default=None, rows=None, allow_None=False, **params): | |
1579 | from pandas import Series as pdSeries | |
1580 | self.rows = rows | |
1581 | super(Series,self).__init__(pdSeries, default=default, allow_None=allow_None, | |
1582 | **params) | |
1583 | self._validate(self.default) | |
1514 | 1584 | |
1515 | 1585 | def _length_bounds_check(self, bounds, length, name): |
1516 | 1586 | message = '{name} length {length} does not match declared bounds of {bounds}' |
1525 | 1595 | if failure: |
1526 | 1596 | raise ValueError(message.format(name=name,length=length, bounds=bounds)) |
1527 | 1597 | |
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 | ||
1534 | 1598 | def _validate(self, val): |
1535 | 1599 | super(Series, self)._validate(val) |
1600 | ||
1601 | if self.allow_None and val is None: | |
1602 | return | |
1536 | 1603 | |
1537 | 1604 | if self.rows is not None: |
1538 | 1605 | self._length_bounds_check(self.rows, len(val), 'Row') |
1566 | 1633 | Prepended to a non-relative path, in order, until a file is |
1567 | 1634 | found.""") |
1568 | 1635 | |
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'.""") | |
1636 | path_to_file = Boolean(default=True, pickle_default_value=False, | |
1637 | allow_None=True, doc=""" | |
1638 | String specifying whether the path refers to a 'File' or a | |
1639 | 'Folder'. If None, the path may point to *either* a 'File' *or* | |
1640 | a 'Folder'.""") | |
1571 | 1641 | |
1572 | 1642 | def __call__(self, path, **params): |
1573 | 1643 | p = ParamOverrides(self, params) |
1574 | ||
1575 | 1644 | path = os.path.normpath(path) |
1645 | ftype = "File" if p.path_to_file is True \ | |
1646 | else "Folder" if p.path_to_file is False else "Path" | |
1647 | ||
1648 | if not p.search_paths: | |
1649 | p.search_paths = [os.getcwd()] | |
1576 | 1650 | |
1577 | 1651 | 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) | |
1652 | if ((p.path_to_file is None and os.path.exists(path)) or | |
1653 | (p.path_to_file is True and os.path.isfile(path)) or | |
1654 | (p.path_to_file is False and os.path.isdir( path))): | |
1655 | return path | |
1656 | raise IOError("%s '%s' not found." % (ftype,path)) | |
1590 | 1657 | |
1591 | 1658 | else: |
1592 | 1659 | paths_tried = [] |
1593 | 1660 | for prefix in p.search_paths: |
1594 | 1661 | try_path = os.path.join(os.path.normpath(prefix), path) |
1595 | 1662 | |
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) | |
1663 | if ((p.path_to_file is None and os.path.exists(try_path)) or | |
1664 | (p.path_to_file is True and os.path.isfile(try_path)) or | |
1665 | (p.path_to_file is False and os.path.isdir( try_path))): | |
1666 | return try_path | |
1604 | 1667 | |
1605 | 1668 | paths_tried.append(try_path) |
1606 | 1669 | |
1607 | raise IOError(os.path.split(path)[1] + " was not found in the following place(s): " + str(paths_tried) + ".") | |
1670 | raise IOError(ftype + " " + os.path.split(path)[1] + " was not found in the following place(s): " + str(paths_tried) + ".") | |
1608 | 1671 | |
1609 | 1672 | |
1610 | 1673 | class normalize_path(ParameterizedFunction): |
1663 | 1726 | super(Path,self).__init__(default,**params) |
1664 | 1727 | |
1665 | 1728 | 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) | |
1729 | return resolve_path(path, path_to_file=None, search_paths=self.search_paths) | |
1670 | 1730 | |
1671 | 1731 | def _validate(self, val): |
1672 | 1732 | if val is None: |
1673 | 1733 | if not self.allow_None: |
1674 | Parameterized(name="%s.%s"%(self.owner.name,self.name)).warning('None is not allowed') | |
1734 | Parameterized(name="%s.%s"%(self.owner.name,self.name)).param.warning('None is not allowed') | |
1675 | 1735 | else: |
1676 | 1736 | try: |
1677 | 1737 | self._resolve(val) |
1678 | 1738 | except IOError as e: |
1679 | Parameterized(name="%s.%s"%(self.owner.name,self.name)).warning('%s',e.args[0]) | |
1739 | Parameterized(name="%s.%s"%(self.owner.name,self.name)).param.warning('%s',e.args[0]) | |
1680 | 1740 | |
1681 | 1741 | def __get__(self, obj, objtype): |
1682 | 1742 | """ |
1714 | 1774 | """ |
1715 | 1775 | |
1716 | 1776 | 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) | |
1777 | return resolve_path(path, path_to_file=True, search_paths=self.search_paths) | |
1721 | 1778 | |
1722 | 1779 | |
1723 | 1780 | class Foldername(Path): |
1738 | 1795 | """ |
1739 | 1796 | |
1740 | 1797 | 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) | |
1798 | return resolve_path(path, path_to_file=False, search_paths=self.search_paths) | |
1745 | 1799 | |
1746 | 1800 | |
1747 | 1801 | |
1757 | 1811 | |
1758 | 1812 | |
1759 | 1813 | |
1760 | class FileSelector(ObjectSelector): | |
1814 | class FileSelector(Selector): | |
1761 | 1815 | """ |
1762 | 1816 | Given a path glob, allows one file to be selected from those matching. |
1763 | 1817 | """ |
1764 | 1818 | __slots__ = ['path'] |
1765 | 1819 | |
1766 | 1820 | def __init__(self, default=None, path="", **kwargs): |
1767 | super(FileSelector, self).__init__(default, **kwargs) | |
1821 | self.default = default | |
1768 | 1822 | self.path = path |
1769 | 1823 | self.update() |
1824 | super(FileSelector, self).__init__(default=default, objects=self.objects, | |
1825 | empty_default=True, **kwargs) | |
1826 | ||
1827 | def _on_set(self, attribute, old, new): | |
1828 | super(FileSelector, self)._on_set(attribute, new, old) | |
1829 | if attribute == 'path': | |
1830 | self.update() | |
1770 | 1831 | |
1771 | 1832 | def update(self): |
1772 | 1833 | self.objects = sorted(glob.glob(self.path)) |
1778 | 1839 | return abbreviate_paths(self.path,super(FileSelector, self).get_range()) |
1779 | 1840 | |
1780 | 1841 | |
1781 | class ListSelector(ObjectSelector): | |
1782 | """ | |
1783 | Variant of ObjectSelector where the value can be multiple objects from | |
1842 | class ListSelector(Selector): | |
1843 | """ | |
1844 | Variant of Selector where the value can be multiple objects from | |
1784 | 1845 | a list of possible objects. |
1785 | 1846 | """ |
1847 | ||
1848 | def __init__(self, default=None, objects=None, **kwargs): | |
1849 | super(ListSelector,self).__init__( | |
1850 | objects=objects, default=default, empty_default=True, **kwargs) | |
1786 | 1851 | |
1787 | 1852 | def compute_default(self): |
1788 | 1853 | if self.default is None and callable(self.compute_default_fn): |
1792 | 1857 | self.objects.append(o) |
1793 | 1858 | |
1794 | 1859 | def _validate(self, val): |
1860 | if (val is None and self.allow_None): | |
1861 | return | |
1795 | 1862 | for o in val: |
1796 | 1863 | super(ListSelector, self)._validate(o) |
1797 | 1864 | |
1804 | 1871 | __slots__ = ['path'] |
1805 | 1872 | |
1806 | 1873 | def __init__(self, default=None, path="", **kwargs): |
1807 | super(MultiFileSelector, self).__init__(default, **kwargs) | |
1874 | self.default = default | |
1808 | 1875 | self.path = path |
1809 | 1876 | self.update() |
1877 | super(MultiFileSelector, self).__init__(default=default, objects=self.objects, **kwargs) | |
1878 | ||
1879 | def _on_set(self, attribute, old, new): | |
1880 | super(MultiFileSelector, self)._on_set(attribute, new, old) | |
1881 | if attribute == 'path': | |
1882 | self.update() | |
1810 | 1883 | |
1811 | 1884 | def update(self): |
1812 | 1885 | self.objects = sorted(glob.glob(self.path)) |
1826 | 1899 | def __init__(self, default=None, **kwargs): |
1827 | 1900 | super(Date, self).__init__(default=default, **kwargs) |
1828 | 1901 | |
1829 | def _validate(self, val): | |
1902 | def _validate_value(self, val, allow_None): | |
1830 | 1903 | """ |
1831 | 1904 | Checks that the value is numeric and that it is within the hard |
1832 | 1905 | bounds; if not, an exception is raised. |
1834 | 1907 | if self.allow_None and val is None: |
1835 | 1908 | return |
1836 | 1909 | |
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): | |
1910 | if not isinstance(val, dt_types) and not (allow_None and val is None): | |
1911 | raise ValueError("Date parameter %r only takes datetime and date types." % self.name) | |
1912 | ||
1913 | def _validate_step(self, val, step): | |
1914 | if step is not None and not isinstance(step, dt_types): | |
1841 | 1915 | raise ValueError("Step parameter can only be None, a datetime or datetime type") |
1842 | 1916 | |
1843 | self._checkBounds(val) | |
1917 | @classmethod | |
1918 | def serialize(cls, value): | |
1919 | if not isinstance(value, (dt.datetime, dt.date)): # i.e np.datetime64 | |
1920 | value = value.astype(dt.datetime) | |
1921 | return value.strftime("%Y-%m-%dT%H:%M:%S.%f") | |
1922 | ||
1923 | @classmethod | |
1924 | def deserialize(cls, value): | |
1925 | return dt.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") | |
1844 | 1926 | |
1845 | 1927 | |
1846 | 1928 | class CalendarDate(Number): |
1851 | 1933 | def __init__(self, default=None, **kwargs): |
1852 | 1934 | super(CalendarDate, self).__init__(default=default, **kwargs) |
1853 | 1935 | |
1854 | def _validate(self, val): | |
1936 | def _validate_value(self, val, allow_None): | |
1855 | 1937 | """ |
1856 | 1938 | Checks that the value is numeric and that it is within the hard |
1857 | 1939 | bounds; if not, an exception is raised. |
1859 | 1941 | if self.allow_None and val is None: |
1860 | 1942 | return |
1861 | 1943 | |
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) | |
1944 | if not isinstance(val, dt.date) and not (allow_None and val is None): | |
1945 | raise ValueError("CalendarDate parameter %r only takes datetime types." % self.name) | |
1946 | ||
1947 | def _validate_step(self, val, step): | |
1948 | if step is not None and not isinstance(step, dt.date): | |
1949 | raise ValueError("Step parameter can only be None or a date type.") | |
1950 | ||
1951 | @classmethod | |
1952 | def serialize(cls, value): | |
1953 | return value.strftime("%Y-%m-%d") | |
1954 | ||
1955 | @classmethod | |
1956 | def deserialize(cls, value): | |
1957 | return dt.datetime.strptime(value, "%Y-%m-%d").date() | |
1869 | 1958 | |
1870 | 1959 | |
1871 | 1960 | class Color(Parameter): |
1872 | 1961 | """ |
1873 | 1962 | 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): | |
1963 | prefix or (optionally) as a CSS3 color name. | |
1964 | """ | |
1965 | ||
1966 | # CSS3 color specification https://www.w3.org/TR/css-color-3/#svg-color | |
1967 | _named_colors = [ 'aliceblue', 'antiquewhite', 'aqua', | |
1968 | 'aquamarine', 'azure', 'beige', 'bisque', 'black', | |
1969 | 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', | |
1970 | 'cadetblue', 'chartreuse', 'chocolate', 'coral', | |
1971 | 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', | |
1972 | 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgrey', | |
1973 | 'darkgreen', 'darkkhaki', 'darkmagenta', 'darkolivegreen', | |
1974 | 'darkorange', 'darkorchid', 'darkred', 'darksalmon', | |
1975 | 'darkseagreen', 'darkslateblue', 'darkslategray', | |
1976 | 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', | |
1977 | 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', | |
1978 | 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', | |
1979 | 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', | |
1980 | 'grey', 'green', 'greenyellow', 'honeydew', 'hotpink', | |
1981 | 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', | |
1982 | 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', | |
1983 | 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', | |
1984 | 'lightgray', 'lightgrey', 'lightgreen', 'lightpink', | |
1985 | 'lightsalmon', 'lightseagreen', 'lightskyblue', | |
1986 | 'lightslategray', 'lightslategrey', 'lightsteelblue', | |
1987 | 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', | |
1988 | 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', | |
1989 | 'mediumpurple', 'mediumseagreen', 'mediumslateblue', | |
1990 | 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', | |
1991 | 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', | |
1992 | 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', | |
1993 | 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', | |
1994 | 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', | |
1995 | 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red', | |
1996 | 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', | |
1997 | 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', | |
1998 | 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', | |
1999 | 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', | |
2000 | 'tomato', 'turquoise', 'violet', 'wheat', 'white', | |
2001 | 'whitesmoke', 'yellow', 'yellowgreen'] | |
2002 | ||
2003 | __slots__ = ['allow_named'] | |
2004 | ||
2005 | def __init__(self, default=None, allow_named=True, **kwargs): | |
1878 | 2006 | super(Color, self).__init__(default=default, **kwargs) |
2007 | self.allow_named = allow_named | |
1879 | 2008 | self._validate(default) |
1880 | 2009 | |
1881 | 2010 | def _validate(self, val): |
1882 | if (self.allow_None and val is None): | |
2011 | self._validate_value(val, self.allow_None) | |
2012 | self._validate_allow_named(val, self.allow_named) | |
2013 | ||
2014 | def _validate_value(self, val, allow_None): | |
2015 | if (allow_None and val is None): | |
1883 | 2016 | return |
1884 | 2017 | 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 | ||
2018 | raise ValueError("Color parameter %r expects a string value, " | |
2019 | "not an object of type %s." % (self.name, type(val))) | |
2020 | ||
2021 | def _validate_allow_named(self, val, allow_named): | |
2022 | if (val is None and self.allow_None): | |
2023 | return | |
2024 | is_hex = re.match('^#?(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$', val) | |
2025 | if self.allow_named: | |
2026 | if not is_hex and val not in self._named_colors: | |
2027 | raise ValueError("Color '%s' only takes RGB hex codes " | |
2028 | "or named colors, received '%s'." % (self.name, val)) | |
2029 | elif not is_hex: | |
2030 | raise ValueError("Color '%s' only accepts valid RGB hex " | |
2031 | "codes, received '%s'." % (self.name, val)) | |
1890 | 2032 | |
1891 | 2033 | |
1892 | 2034 | class Range(NumericTuple): |
1893 | 2035 | "A numeric range with optional bounds and softbounds" |
1894 | 2036 | |
1895 | __slots__ = ['bounds', 'inclusive_bounds', 'softbounds'] | |
1896 | ||
2037 | __slots__ = ['bounds', 'inclusive_bounds', 'softbounds', 'step'] | |
1897 | 2038 | |
1898 | 2039 | def __init__(self,default=None, bounds=None, softbounds=None, |
1899 | inclusive_bounds=(True,True), **params): | |
2040 | inclusive_bounds=(True,True), step=None, **params): | |
1900 | 2041 | self.bounds = bounds |
1901 | 2042 | self.inclusive_bounds = inclusive_bounds |
1902 | 2043 | self.softbounds = softbounds |
2044 | self.step = step | |
1903 | 2045 | super(Range,self).__init__(default=default,length=2,**params) |
1904 | 2046 | |
1905 | ||
1906 | 2047 | 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 | |
1913 | 2048 | super(Range, self)._validate(val) |
1914 | ||
1915 | self._checkBounds(val) | |
2049 | self._validate_bounds(val, self.bounds, self.inclusive_bounds) | |
2050 | ||
2051 | def _validate_bounds(self, val, bounds, inclusive_bounds): | |
2052 | if bounds is None or (val is None and self.allow_None): | |
2053 | return | |
2054 | vmin, vmax = bounds | |
2055 | incmin, incmax = inclusive_bounds | |
2056 | for bound, v in zip(['lower', 'upper'], val): | |
2057 | too_low = (vmin is not None) and (v < vmin if incmin else v <= vmin) | |
2058 | too_high = (vmax is not None) and (v > vmax if incmax else v >= vmax) | |
2059 | if too_low or too_high: | |
2060 | raise ValueError("Range parameter %r's %s bound must be in range %s." | |
2061 | % (self.name, bound, self.rangestr())) | |
1916 | 2062 | |
1917 | 2063 | |
1918 | 2064 | 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) | |
2065 | return get_soft_bounds(self.bounds, self.softbounds) | |
1941 | 2066 | |
1942 | 2067 | |
1943 | 2068 | def rangestr(self): |
1948 | 2073 | return '%s%s, %s%s' % (incmin, vmin, vmax, incmax) |
1949 | 2074 | |
1950 | 2075 | |
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 | ||
1963 | 2076 | class DateRange(Range): |
1964 | 2077 | """ |
1965 | 2078 | A datetime or date range specified as (start, end). |
1966 | 2079 | |
1967 | 2080 | Bounds must be specified as datetime or date types (see param.dt_types). |
1968 | 2081 | """ |
1969 | def _validate(self, val): | |
1970 | if self.allow_None and val is None: | |
2082 | ||
2083 | def _validate_value(self, val, allow_None): | |
2084 | if allow_None and val is None: | |
1971 | 2085 | return |
1972 | 2086 | |
1973 | 2087 | for n in val: |
1974 | if not isinstance(n, dt_types): | |
1975 | raise ValueError("DateRange '%s' only takes datetime types: %s"%(self.name,val)) | |
2088 | if isinstance(n, dt_types): | |
2089 | continue | |
2090 | raise ValueError("DateRange parameter %r only takes datetime " | |
2091 | "types, not %s." % (self.name, val)) | |
1976 | 2092 | |
1977 | 2093 | start, end = val |
1978 | 2094 | 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) | |
2095 | raise ValueError("DateRange parameter %r's end datetime %s " | |
2096 | "is before start datetime %s." % | |
2097 | (self.name,val[1],val[0])) | |
2098 | ||
1985 | 2099 | |
1986 | 2100 | |
1987 | 2101 | class CalendarDateRange(Range): |
1988 | 2102 | """ |
1989 | 2103 | A date range specified as (start_date, end_date). |
1990 | 2104 | """ |
1991 | def _validate(self, val): | |
1992 | if self.allow_None and val is None: | |
2105 | def _validate_value(self, val, allow_None): | |
2106 | if allow_None and val is None: | |
1993 | 2107 | return |
1994 | 2108 | |
1995 | 2109 | for n in val: |
1996 | 2110 | if not isinstance(n, dt.date): |
1997 | raise ValueError("CalendarDateRange '%s' only takes date types: %s"%(self.name,val)) | |
2111 | raise ValueError("CalendarDateRange parameter %r only " | |
2112 | "takes date types, not %s." % (self.name, val)) | |
1998 | 2113 | |
1999 | 2114 | start, end = val |
2000 | 2115 | 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) | |
2116 | raise ValueError("CalendarDateRange parameter %r's end date " | |
2117 | "%s is before start date %s." % | |
2118 | (self.name, val[1], val[0])) | |
2119 | ||
2120 | ||
2121 | class Event(Boolean): | |
2122 | """ | |
2123 | An Event Parameter is one whose value is intimately linked to the | |
2124 | triggering of events for watchers to consume. Event has a Boolean | |
2125 | value, which when set to True triggers the associated watchers (as | |
2126 | any Parameter does) and then is automatically set back to | |
2127 | False. Conversely, if events are triggered directly via `.trigger`, | |
2128 | the value is transiently set to True (so that it's clear which of | |
2129 | many parameters being watched may have changed), then restored to | |
2130 | False when the triggering completes. An Event parameter is thus like | |
2131 | a momentary switch or pushbutton with a transient True value that | |
2132 | serves only to launch some other action (e.g. via a param.depends | |
2133 | decorator), rather than encapsulating the action itself as | |
2134 | param.Action does. | |
2135 | """ | |
2136 | ||
2137 | # _autotrigger_value specifies the value used to set the parameter | |
2138 | # to when the parameter is supplied to the trigger method. This | |
2139 | # value change is then what triggers the watcher callbacks. | |
2140 | __slots__ = ['_autotrigger_value', '_mode', '_autotrigger_reset_value'] | |
2141 | ||
2142 | def __init__(self,default=False,bounds=(0,1),**params): | |
2143 | self._autotrigger_value = True | |
2144 | self._autotrigger_reset_value = False | |
2145 | self._mode = 'set-reset' | |
2146 | # Mode can be one of 'set', 'set-reset' or 'reset' | |
2147 | ||
2148 | # 'set' is normal Boolean parameter behavior when set with a value. | |
2149 | # 'set-reset' temporarily sets the parameter (which triggers | |
2150 | # watching callbacks) but immediately resets the value back to | |
2151 | # False. | |
2152 | # 'reset' applies the reset from True to False without | |
2153 | # triggering watched callbacks | |
2154 | ||
2155 | # This _mode attribute is one of the few places where a specific | |
2156 | # parameter has a special behavior that is relied upon by the | |
2157 | # core functionality implemented in | |
2158 | # parameterized.py. Specifically, the set_param method | |
2159 | # temporarily sets this attribute in order to disable resetting | |
2160 | # back to False while triggered callbacks are executing | |
2161 | super(Event, self).__init__(default=default,**params) | |
2162 | ||
2163 | def _reset_event(self, obj, val): | |
2164 | val = False | |
2165 | if obj is None: | |
2166 | self.default = val | |
2167 | else: | |
2168 | obj.__dict__[self._internal_name] = val | |
2169 | self._post_setter(obj, val) | |
2170 | ||
2171 | @instance_descriptor | |
2172 | def __set__(self, obj, val): | |
2173 | if self._mode in ['set-reset', 'set']: | |
2174 | super(Event, self).__set__(obj, val) | |
2175 | if self._mode in ['set-reset', 'reset']: | |
2176 | self._reset_event(obj, val) | |
2177 | ||
2178 | ||
2179 | from contextlib import contextmanager | |
2180 | @contextmanager | |
2181 | def exceptions_summarized(): | |
2182 | """Useful utility for writing docs that need to show expected errors. | |
2183 | Shows exception only, concisely, without a traceback. | |
2184 | """ | |
2185 | try: | |
2186 | yield | |
2187 | except Exception: | |
2188 | import sys | |
2189 | etype, value, tb = sys.exc_info() | |
2190 | print("{}: {}".format(etype.__name__,value), file=sys.stderr) |
19 | 19 | __author__ = "Jean-Luc Stevens" |
20 | 20 | |
21 | 21 | import re |
22 | import sys | |
23 | import itertools | |
22 | 24 | import textwrap |
23 | 25 | import param |
24 | 26 | |
85 | 87 | |
86 | 88 | (params, val_dict, changed) = info |
87 | 89 | contents = [] |
88 | displayed_params = {} | |
89 | for name, p in params.items(): | |
90 | displayed_params = [] | |
91 | for name in self.sort_by_precedence(params): | |
90 | 92 | if only_changed and not (name in changed): |
91 | 93 | 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): | |
98 | 99 | heading = "%s: " % name |
99 | 100 | unindented = textwrap.dedent("< No docstring available >" if p.doc is None else p.doc) |
100 | 101 | |
123 | 124 | return "\n".join(contents) |
124 | 125 | |
125 | 126 | |
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 | ||
126 | 150 | def _build_table(self, info, order, max_col_len=40, only_changed=False): |
127 | 151 | """ |
128 | 152 | Collect the information about parameters needed to build a |
129 | 153 | properly formatted table and then tabulate it. |
130 | 154 | """ |
131 | 155 | |
132 | info_dict, bounds_dict = {}, {} | |
156 | info_list, bounds_dict = [], {} | |
133 | 157 | (params, val_dict, changed) = info |
134 | 158 | col_widths = dict((k,0) for k in order) |
135 | 159 | |
136 | for name, p in params.items(): | |
160 | ordering = self.sort_by_precedence(params) | |
161 | for name in ordering: | |
162 | p = params[name] | |
137 | 163 | if only_changed and not (name in changed): |
138 | 164 | continue |
139 | 165 | |
142 | 168 | allow_None = ' AN' if hasattr(p, 'allow_None') and p.allow_None else '' |
143 | 169 | |
144 | 170 | 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} | |
147 | 178 | |
148 | 179 | if hasattr(p, 'bounds'): |
149 | 180 | lbound, ubound = (None,None) if p.bounds is None else p.bounds |
161 | 192 | |
162 | 193 | if (lbound, ubound) != (None,None): |
163 | 194 | 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])]) | |
173 | 199 | col_widths[col] = max_width |
174 | 200 | |
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): | |
179 | 207 | """ |
180 | 208 | Returns the supplied information as a table suitable for |
181 | 209 | printing or paging. |
182 | 210 | |
183 | info_dict: Dictionary of the parameters name, type and mode. | |
211 | info_list: List of the parameters name, type and mode. | |
184 | 212 | col_widths: Dictionary of column widths in characters |
185 | 213 | changed: List of parameters modified from their defaults. |
186 | 214 | order: The order of the table columns |
188 | 216 | """ |
189 | 217 | |
190 | 218 | 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) | |
192 | 220 | columns = [col for col in order if col in column_set] |
193 | 221 | |
194 | 222 | title_row = [] |
201 | 229 | contents.append(blue % ''.join(title_row)+"\n") |
202 | 230 | |
203 | 231 | # Format the table rows |
204 | for row in sorted(info_dict): | |
232 | for row, info in info_list: | |
205 | 233 | row_list = [] |
206 | info = info_dict[row] | |
207 | 234 | for i,col in enumerate(columns): |
208 | 235 | width = col_widths[col]+2 |
209 | 236 | val = info[col] if (col in info) else '' |
257 | 284 | heading_text = "%s\n%s\n" % (title, heading_line) |
258 | 285 | |
259 | 286 | param_info = self.get_param_info(param_obj, include_super=True) |
260 | ||
261 | 287 | if not param_info[0]: |
262 | 288 | return "%s\n%s" % ((green % heading_text), "Object has no parameters.") |
263 | 289 | |
265 | 291 | only_changed=False) |
266 | 292 | |
267 | 293 | docstrings = self.param_docstrings(param_info, max_col_len=100, only_changed=False) |
268 | ||
269 | 294 | dflt_msg = "Parameters changed from their default values are marked in red." |
270 | 295 | top_heading = (green % heading_text) |
271 | 296 | top_heading += "\n%s" % (red % dflt_msg) |
278 | 303 | return "%s\n\n%s\n\n%s\n\n%s" % (top_heading, table, docstring_heading, docstrings) |
279 | 304 | |
280 | 305 | |
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/)""" | |
282 | 307 | message += '\nAvailable magics: %params' |
283 | 308 | |
284 | 309 | _loaded = False |
10 | 10 | import numbers |
11 | 11 | import operator |
12 | 12 | |
13 | from collections import namedtuple, OrderedDict | |
13 | # Allow this file to be used standalone if desired, albeit without JSON serialization | |
14 | try: | |
15 | from . import serializer | |
16 | except ImportError: | |
17 | serializer = None | |
18 | ||
19 | ||
20 | from collections import defaultdict, namedtuple, OrderedDict | |
21 | from functools import partial, wraps, reduce | |
14 | 22 | from operator import itemgetter,attrgetter |
15 | 23 | from types import FunctionType |
16 | from functools import partial, wraps, reduce | |
17 | 24 | |
18 | 25 | import logging |
19 | 26 | from contextlib import contextmanager |
132 | 139 | """ |
133 | 140 | batch_watch = parameterized.param._BATCH_WATCH |
134 | 141 | parameterized.param._BATCH_WATCH = True |
135 | watchers, events = parameterized.param._watchers, parameterized.param._events | |
142 | watchers, events = (list(parameterized.param._watchers), | |
143 | list(parameterized.param._events)) | |
136 | 144 | try: |
137 | 145 | yield |
138 | 146 | except: |
141 | 149 | parameterized.param._BATCH_WATCH = batch_watch |
142 | 150 | parameterized.param._watchers = watchers |
143 | 151 | parameterized.param._events = events |
152 | ||
153 | ||
154 | # External components can register an async executor which will run | |
155 | # async functions | |
156 | async_executor = None | |
144 | 157 | |
145 | 158 | |
146 | 159 | def classlist(class_): |
285 | 298 | return cls |
286 | 299 | |
287 | 300 | |
301 | def iscoroutinefunction(function): | |
302 | """ | |
303 | Whether the function is an asynchronous coroutine function. | |
304 | """ | |
305 | if not hasattr(inspect, 'iscoroutinefunction'): | |
306 | return False | |
307 | return inspect.iscoroutinefunction(function) | |
308 | ||
309 | ||
288 | 310 | def instance_descriptor(f): |
289 | 311 | # If parameter has an instance Parameter delegate setting |
290 | 312 | def _f(self, obj, val): |
294 | 316 | return |
295 | 317 | return f(self, obj, val) |
296 | 318 | return _f |
319 | ||
320 | ||
321 | def get_method_owner(method): | |
322 | """ | |
323 | Gets the instance that owns the supplied method | |
324 | """ | |
325 | if not inspect.ismethod(method): | |
326 | return None | |
327 | if isinstance(method, partial): | |
328 | method = method.func | |
329 | return method.__self__ if sys.version_info.major >= 3 else method.im_self | |
297 | 330 | |
298 | 331 | |
299 | 332 | @accept_arguments |
345 | 378 | 'or function is not supported when referencing ' |
346 | 379 | 'parameters by name.') |
347 | 380 | |
348 | if not string_specs and watch: | |
349 | def cb(event): | |
381 | if not string_specs and watch: # string_specs case handled elsewhere (later), in Parameterized.__init__ | |
382 | def cb(*events): | |
350 | 383 | args = (getattr(dep.owner, dep.name) for dep in dependencies) |
351 | 384 | dep_kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw.items()} |
352 | 385 | return func(*args, **dep_kwargs) |
353 | 386 | |
387 | grouped = defaultdict(list) | |
354 | 388 | for dep in deps: |
355 | dep.owner.param.watch(cb, dep.name) | |
389 | grouped[id(dep.owner)].append(dep) | |
390 | for group in grouped.values(): | |
391 | group[0].owner.param.watch(cb, [dep.name for dep in group]) | |
356 | 392 | |
357 | 393 | _dinfo = getattr(func, '_dinfo', {}) |
358 | 394 | _dinfo.update({'dependencies': dependencies, |
471 | 507 | return params |
472 | 508 | |
473 | 509 | |
474 | def _m_caller(self,n): | |
475 | return lambda event: getattr(self,n)() | |
510 | def _m_caller(self, n): | |
511 | function = getattr(self, n) | |
512 | if iscoroutinefunction(function): | |
513 | import asyncio | |
514 | @asyncio.coroutine | |
515 | def caller(*events): | |
516 | yield function() | |
517 | else: | |
518 | def caller(*events): | |
519 | return function() | |
520 | caller._watcher_name = n | |
521 | return caller | |
476 | 522 | |
477 | 523 | |
478 | 524 | PInfo = namedtuple("PInfo","inst cls name pobj what") |
659 | 705 | # attributes. Using __slots__ requires special support for |
660 | 706 | # operations to copy and restore Parameters (e.g. for Python |
661 | 707 | # 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', | |
708 | __slots__ = ['name', '_internal_name', 'default', 'doc', | |
709 | 'precedence', 'instantiate', 'constant', 'readonly', | |
710 | 'pickle_default_value', 'allow_None', 'per_instance', | |
665 | 711 | 'watchers', 'owner', '_label'] |
666 | 712 | |
667 | 713 | # Note: When initially created, a Parameter does not know which |
670 | 716 | # class is created, owner, name, and _internal_name are |
671 | 717 | # set. |
672 | 718 | |
673 | def __init__(self,default=None,doc=None,label=None,precedence=None, # pylint: disable-msg=R0913 | |
674 | instantiate=False,constant=False,readonly=False, | |
719 | _serializers = {'json': serializer.JSONSerialization} | |
720 | ||
721 | def __init__(self,default=None, doc=None, label=None, precedence=None, # pylint: disable-msg=R0913 | |
722 | instantiate=False, constant=False, readonly=False, | |
675 | 723 | pickle_default_value=True, allow_None=False, |
676 | 724 | 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 | |
725 | ||
726 | """Initialize a new Parameter object and store the supplied attributes: | |
727 | ||
728 | default: the owning class's value for the attribute represented | |
729 | by this Parameter, which can be overridden in an instance. | |
730 | ||
731 | doc: docstring explaining what this parameter represents. | |
732 | ||
733 | label: optional text label to be used when this Parameter is | |
734 | shown in a listing. If no label is supplied, the attribute name | |
735 | for this parameter in the owning Parameterized object is used. | |
736 | ||
737 | precedence: a numeric value, usually in the range 0.0 to 1.0, | |
738 | which allows the order of Parameters in a class to be defined in | |
739 | a listing or e.g. in GUI menus. A negative precedence indicates | |
740 | a parameter that should be hidden in such listings. | |
741 | ||
742 | instantiate: controls whether the value of this Parameter will | |
743 | be deepcopied when a Parameterized object is instantiated (if | |
744 | True), or if the single default value will be shared by all | |
745 | Parameterized instances (if False). For an immutable Parameter | |
746 | value, it is best to leave instantiate at the default of | |
747 | False, so that a user can choose to change the value at the | |
748 | Parameterized instance level (affecting only that instance) or | |
749 | at the Parameterized class or superclass level (affecting all | |
750 | existing and future instances of that class or superclass). For | |
751 | a mutable Parameter value, the default of False is also appropriate | |
752 | if you want all instances to share the same value state, e.g. if | |
753 | they are each simply referring to a single global object like | |
754 | a singleton. If instead each Parameterized should have its own | |
755 | independently mutable value, instantiate should be set to | |
756 | True, but note that there is then no simple way to change the | |
757 | value of this Parameter at the class or superclass level, | |
758 | because each instance, once created, will then have an | |
759 | independently instantiated value. | |
760 | ||
761 | constant: if true, the Parameter value can be changed only at | |
762 | the class level or in a Parameterized constructor call. The | |
763 | value is otherwise constant on the Parameterized instance, | |
764 | once it has been constructed. | |
765 | ||
766 | readonly: if true, the Parameter value cannot ordinarily be | |
767 | changed by setting the attribute at the class or instance | |
768 | levels at all. The value can still be changed in code by | |
769 | temporarily overriding the value of this slot and then | |
770 | restoring it, which is useful for reporting values that the | |
771 | _user_ should never change but which do change during code | |
772 | execution. | |
773 | ||
774 | pickle_default_value: whether the default value should be | |
775 | pickled. Usually, you would want the default value to be pickled, | |
776 | but there are rare cases where that would not be the case (e.g. | |
777 | for file search paths that are specific to a certain system). | |
778 | ||
779 | per_instance: whether a separate Parameter instance will be | |
780 | created for every Parameterized instance. True by default. | |
781 | If False, all instances of a Parameterized class will share | |
782 | the same Parameter object, including all validation | |
783 | attributes (bounds, etc.). See also instantiate, which is | |
784 | conceptually similar but affects the Parameter value rather | |
785 | than the Parameter object. | |
786 | ||
787 | allow_None: if True, None is accepted as a valid value for | |
788 | this Parameter, in addition to any other values that are | |
789 | allowed. If the default value is defined as None, allow_None | |
790 | is set to True automatically. | |
791 | ||
792 | default, doc, and precedence all default to None, which allows | |
689 | 793 | inheritance of Parameter slots (attributes) from the owning-class' |
690 | 794 | 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 | """ | |
795 | """ | |
796 | ||
701 | 797 | self.name = None |
702 | self._internal_name = None | |
703 | 798 | self.owner = None |
704 | self._label = label | |
705 | 799 | self.precedence = precedence |
706 | 800 | self.default = default |
707 | 801 | self.doc = doc |
708 | 802 | self.constant = constant or readonly # readonly => constant |
709 | 803 | self.readonly = readonly |
804 | self._label = label | |
805 | self._internal_name = None | |
710 | 806 | self._set_instantiate(instantiate) |
711 | 807 | self.pickle_default_value = pickle_default_value |
712 | 808 | self.allow_None = (default is None or allow_None) |
713 | 809 | self.watchers = {} |
714 | 810 | self.per_instance = per_instance |
715 | 811 | |
812 | @classmethod | |
813 | def serialize(cls, value): | |
814 | "Given the parameter value, return a Python value suitable for serialization" | |
815 | return value | |
816 | ||
817 | @classmethod | |
818 | def deserialize(cls, value): | |
819 | "Given a serializable Python value, return a value that the parameter can be set to" | |
820 | return value | |
821 | ||
822 | def schema(self, safe=False, subset=None, mode='json'): | |
823 | if serializer is None: | |
824 | raise ImportError('Cannot import serializer.py needed to generate schema') | |
825 | if mode not in self._serializers: | |
826 | raise KeyError('Mode %r not in available serialization formats %r' | |
827 | % (mode, list(self._serializers.keys()))) | |
828 | return self._serializers[mode].parameter_schema(self.__class__.__name__, self, | |
829 | safe=safe, subset=subset) | |
716 | 830 | |
717 | 831 | @property |
718 | 832 | def label(self): |
735 | 849 | else: |
736 | 850 | self.instantiate = instantiate or self.constant # pylint: disable-msg=W0201 |
737 | 851 | |
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) | |
852 | def __setattr__(self, attribute, value): | |
853 | implemented = (attribute != "default" and hasattr(self, 'watchers') and attribute in self.watchers) | |
854 | slot_attribute = attribute in self.__slots__ | |
747 | 855 | try: |
748 | old = getattr(self,attribute) if implemented else NotImplemented | |
856 | old = getattr(self, attribute) if implemented else NotImplemented | |
857 | if slot_attribute: | |
858 | self._on_set(attribute, old, value) | |
749 | 859 | except AttributeError as e: |
750 | if attribute in self.__slots__: | |
860 | if slot_attribute: | |
751 | 861 | # If Parameter slot is defined but an AttributeError was raised |
752 | 862 | # we are in __setstate__ and watchers should not be triggered |
753 | 863 | old = NotImplemented |
756 | 866 | |
757 | 867 | super(Parameter, self).__setattr__(attribute, value) |
758 | 868 | |
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 | |
869 | if old is NotImplemented: | |
870 | return | |
871 | ||
872 | event = Event(what=attribute,name=self.name, obj=None, cls=self.owner, | |
873 | old=old, new=value, type=None) | |
874 | for watcher in self.watchers[attribute]: | |
875 | self.owner.param._call_watcher(watcher, event) | |
876 | if not self.owner.param._BATCH_WATCH: | |
877 | self.owner.param._batch_call_watchers() | |
878 | ||
879 | def _on_set(self, attribute, old, value): | |
880 | """ | |
881 | Can be overridden on subclasses to handle changes when parameter | |
882 | attribute is set. | |
883 | """ | |
884 | ||
885 | def __get__(self, obj, objtype): # pylint: disable-msg=W0613 | |
768 | 886 | """ |
769 | 887 | Return the value for this Parameter. |
770 | 888 | |
785 | 903 | result = obj.__dict__.get(self._internal_name,self.default) |
786 | 904 | return result |
787 | 905 | |
788 | ||
789 | 906 | @instance_descriptor |
790 | def __set__(self,obj,val): | |
907 | def __set__(self, obj, val): | |
791 | 908 | """ |
792 | 909 | Set the value for this Parameter. |
793 | 910 | |
826 | 943 | # Parameterized class) |
827 | 944 | if self.constant or self.readonly: |
828 | 945 | if self.readonly: |
829 | raise TypeError("Read-only parameter '%s' cannot be modified"%self.name) | |
830 | elif obj is None: #not obj | |
946 | raise TypeError("Read-only parameter '%s' cannot be modified" % self.name) | |
947 | elif obj is None: | |
831 | 948 | _old = self.default |
832 | 949 | self.default = val |
833 | 950 | elif not obj.initialized: |
834 | _old = obj.__dict__.get(self._internal_name,self.default) | |
951 | _old = obj.__dict__.get(self._internal_name, self.default) | |
835 | 952 | obj.__dict__[self._internal_name] = val |
836 | 953 | else: |
837 | raise TypeError("Constant parameter '%s' cannot be modified"%self.name) | |
838 | ||
954 | _old = obj.__dict__.get(self._internal_name, self.default) | |
955 | if val is not _old: | |
956 | raise TypeError("Constant parameter '%s' cannot be modified"%self.name) | |
839 | 957 | else: |
840 | 958 | if obj is None: |
841 | 959 | _old = self.default |
847 | 965 | self._post_setter(obj, val) |
848 | 966 | |
849 | 967 | if obj is None: |
850 | watchers = self.watchers.get("value",[]) | |
968 | watchers = self.watchers.get("value") | |
969 | elif hasattr(obj, '_param_watchers') and self.name in obj._param_watchers: | |
970 | watchers = obj._param_watchers[self.name].get('value') | |
971 | if watchers is None: | |
972 | watchers = self.watchers.get("value") | |
851 | 973 | 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) | |
974 | watchers = None | |
975 | ||
855 | 976 | obj = self.owner if obj is None else obj |
856 | if obj is None: | |
977 | if obj is None or not watchers: | |
857 | 978 | return |
858 | 979 | |
980 | event = Event(what='value',name=self.name, obj=obj, cls=self.owner, | |
981 | old=_old, new=val, type=None) | |
859 | 982 | for watcher in watchers: |
860 | 983 | obj.param._call_watcher(watcher, event) |
861 | 984 | if not obj.param._BATCH_WATCH: |
862 | 985 | obj.param._batch_call_watchers() |
863 | 986 | |
987 | def _validate_value(self, value, allow_None): | |
988 | """Implements validation for parameter value""" | |
864 | 989 | |
865 | 990 | def _validate(self, val): |
866 | """Implements validation for the parameter""" | |
867 | ||
991 | """Implements validation for the parameter value and attributes""" | |
992 | self._validate_value(val, self.allow_None) | |
868 | 993 | |
869 | 994 | def _post_setter(self, obj, val): |
870 | 995 | """Called after the parameter value has been validated and set""" |
871 | 996 | |
872 | ||
873 | 997 | def __delete__(self,obj): |
874 | 998 | raise TypeError("Cannot delete '%s': Parameters deletion not allowed." % self.name) |
875 | ||
876 | 999 | |
877 | 1000 | def _set_names(self, attrib_name): |
878 | 1001 | if None not in (self.owner, self.name) and attrib_name != self.name: |
885 | 1008 | % (type(self).__name__, self.name, |
886 | 1009 | self.owner.name, attrib_name)) |
887 | 1010 | self.name = attrib_name |
888 | ||
889 | self._internal_name = "_%s_param_value"%attrib_name | |
890 | ||
1011 | self._internal_name = "_%s_param_value" % attrib_name | |
891 | 1012 | |
892 | 1013 | def __getstate__(self): |
893 | 1014 | """ |
931 | 1052 | 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]?)$' |
932 | 1053 | super(IPAddress, self).__init__(default=default, regex=ip_regex, **kwargs) |
933 | 1054 | |
934 | ||
935 | 1055 | """ |
936 | 1056 | |
937 | 1057 | __slots__ = ['regex'] |
942 | 1062 | self.allow_None = (default is None or allow_None) |
943 | 1063 | self._validate(default) |
944 | 1064 | |
1065 | def _validate_regex(self, val, regex): | |
1066 | if (val is None and self.allow_None): | |
1067 | return | |
1068 | if regex is not None and re.match(regex, val) is None: | |
1069 | raise ValueError("String parameter %r value %r does not match regex %r." | |
1070 | % (self.name, val, regex)) | |
1071 | ||
1072 | def _validate_value(self, val, allow_None): | |
1073 | if allow_None and val is None: | |
1074 | return | |
1075 | if not isinstance(val, basestring): | |
1076 | raise ValueError("String parameter %r only takes a string value, " | |
1077 | "not value of type %s." % (self.name, type(val))) | |
1078 | ||
945 | 1079 | 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)) | |
1080 | self._validate_value(val, self.allow_None) | |
1081 | self._validate_regex(val, self.regex) | |
954 | 1082 | |
955 | 1083 | |
956 | 1084 | class shared_parameters(object): |
985 | 1113 | @wraps(fn) |
986 | 1114 | def override_initialization(self_,*args,**kw): |
987 | 1115 | 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 | |
1116 | original_initialized = parameterized_instance.initialized | |
1117 | parameterized_instance.initialized = False | |
1118 | fn(parameterized_instance, *args, **kw) | |
1119 | parameterized_instance.initialized = original_initialized | |
992 | 1120 | return override_initialization |
993 | 1121 | |
994 | 1122 | |
1061 | 1189 | class or the instance as necessary. |
1062 | 1190 | """ |
1063 | 1191 | |
1064 | _disable_stubs = None # Flag used to disable stubs in the API1 tests | |
1192 | _disable_stubs = False # Flag used to disable stubs in the API1 tests | |
1065 | 1193 | # None for no action, True to raise and False to warn. |
1066 | 1194 | |
1067 | 1195 | def __init__(self_, cls, self=None): |
1071 | 1199 | """ |
1072 | 1200 | self_.cls = cls |
1073 | 1201 | 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 | |
1202 | ||
1203 | @property | |
1204 | def _BATCH_WATCH(self_): | |
1205 | return self_.self_or_cls._parameters_state['BATCH_WATCH'] | |
1206 | ||
1207 | @_BATCH_WATCH.setter | |
1208 | def _BATCH_WATCH(self_, value): | |
1209 | self_.self_or_cls._parameters_state['BATCH_WATCH'] = value | |
1210 | ||
1211 | @property | |
1212 | def _TRIGGER(self_): | |
1213 | return self_.self_or_cls._parameters_state['TRIGGER'] | |
1214 | ||
1215 | @_TRIGGER.setter | |
1216 | def _TRIGGER(self_, value): | |
1217 | self_.self_or_cls._parameters_state['TRIGGER'] = value | |
1218 | ||
1219 | @property | |
1220 | def _events(self_): | |
1221 | return self_.self_or_cls._parameters_state['events'] | |
1222 | ||
1223 | @_events.setter | |
1224 | def _events(self_, value): | |
1225 | self_.self_or_cls._parameters_state['events'] = value | |
1226 | ||
1227 | @property | |
1228 | def _watchers(self_): | |
1229 | return self_.self_or_cls._parameters_state['watchers'] | |
1230 | ||
1231 | @_watchers.setter | |
1232 | def _watchers(self_, value): | |
1233 | self_.self_or_cls._parameters_state['watchers'] = value | |
1078 | 1234 | |
1079 | 1235 | @property |
1080 | 1236 | def self_or_cls(self_): |
1081 | 1237 | return self_.cls if self_.self is None else self_.self |
1082 | 1238 | |
1239 | def __setstate__(self, state): | |
1240 | # Set old parameters state on Parameterized._parameters_state | |
1241 | self_or_cls = state.get('self', state.get('cls')) | |
1242 | for k in self_or_cls._parameters_state: | |
1243 | key = '_'+k | |
1244 | if key in state: | |
1245 | self_or_cls._parameters_state[k] = state.pop(key) | |
1246 | for k, v in state.items(): | |
1247 | setattr(self, k, v) | |
1083 | 1248 | |
1084 | 1249 | def __getitem__(self_, key): |
1085 | 1250 | """ |
1088 | 1253 | inst = self_.self |
1089 | 1254 | parameters = self_.objects(False) if inst is None else inst.param.objects(False) |
1090 | 1255 | p = parameters[key] |
1091 | if (inst is not None and p.per_instance and | |
1256 | if (inst is not None and getattr(inst, 'initialized', False) and p.per_instance and | |
1092 | 1257 | not getattr(inst, '_disable_instance__params', False)): |
1093 | 1258 | if key not in inst._instance__params: |
1094 | 1259 | try: |
1099 | 1264 | except: |
1100 | 1265 | raise |
1101 | 1266 | finally: |
1102 | p.watchers = watchers | |
1267 | p.watchers = {k: list(v) for k, v in watchers.items()} | |
1103 | 1268 | p.owner = inst |
1104 | 1269 | inst._instance__params[key] = p |
1105 | 1270 | else: |
1170 | 1335 | First, ensures that all Parameters with 'instantiate=True' |
1171 | 1336 | (typically used for mutable Parameters) are copied directly |
1172 | 1337 | into each object, to ensure that there is an independent copy |
1173 | (to avoid suprising aliasing errors). Then sets each of the | |
1338 | (to avoid surprising aliasing errors). Then sets each of the | |
1174 | 1339 | keyword arguments, warning when any of them are not defined as |
1175 | 1340 | parameters. |
1176 | 1341 | |
1184 | 1349 | for class_ in classlist(type(self)): |
1185 | 1350 | if not issubclass(class_, Parameterized): |
1186 | 1351 | continue |
1187 | for (k,v) in class_.__dict__.items(): | |
1352 | for (k, v) in class_.param._parameters.items(): | |
1188 | 1353 | # (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 | |
1354 | if v.instantiate and k != "name": | |
1355 | params_to_instantiate[k] = v | |
1191 | 1356 | |
1192 | 1357 | for p in params_to_instantiate.values(): |
1193 | 1358 | self.param._instantiate_param(p) |
1194 | 1359 | |
1195 | 1360 | ## keyword arg setting |
1196 | for name,val in params.items(): | |
1361 | for name, val in params.items(): | |
1197 | 1362 | desc = self.__class__.get_param_descriptor(name)[0] # pylint: disable-msg=E1101 |
1198 | 1363 | if not desc: |
1199 | self.param.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters",name,val) | |
1364 | self.param.warning("Setting non-parameter attribute %s=%s using a mechanism intended only for parameters", name, val) | |
1200 | 1365 | # i.e. if not desc it's setting an attribute in __dict__, not a Parameter |
1201 | setattr(self,name,val) | |
1366 | setattr(self, name, val) | |
1202 | 1367 | |
1203 | 1368 | @classmethod |
1204 | 1369 | def deprecate(cls, fn): |
1231 | 1396 | |
1232 | 1397 | |
1233 | 1398 | # CEBALERT: this is a bit ugly |
1234 | def _instantiate_param(self_,param_obj,dict_=None,key=None): | |
1399 | def _instantiate_param(self_, param_obj, dict_=None, key=None): | |
1235 | 1400 | # deepcopy param_obj.default into self.__dict__ (or dict_ if supplied) |
1236 | 1401 | # under the parameter's _internal_name (or key if supplied) |
1237 | 1402 | self = self_.self |
1238 | 1403 | dict_ = dict_ or self.__dict__ |
1239 | 1404 | key = key or param_obj._internal_name |
1240 | param_key = (str(type(self)), param_obj.name) | |
1241 | 1405 | if shared_parameters._share: |
1406 | param_key = (str(type(self)), param_obj.name) | |
1242 | 1407 | if param_key in shared_parameters._shared_cache: |
1243 | 1408 | new_object = shared_parameters._shared_cache[param_key] |
1244 | 1409 | else: |
1246 | 1411 | shared_parameters._shared_cache[param_key] = new_object |
1247 | 1412 | else: |
1248 | 1413 | new_object = copy.deepcopy(param_obj.default) |
1249 | dict_[key]=new_object | |
1250 | ||
1251 | if isinstance(new_object,Parameterized): | |
1414 | ||
1415 | dict_[key] = new_object | |
1416 | ||
1417 | if isinstance(new_object, Parameterized): | |
1252 | 1418 | global object_count |
1253 | object_count+=1 | |
1419 | object_count += 1 | |
1254 | 1420 | # CB: writes over name given to the original object; |
1255 | 1421 | # should it instead keep the same name? |
1256 | 1422 | new_object.param._generate_name() |
1308 | 1474 | Includes Parameters from this class and its |
1309 | 1475 | superclasses. |
1310 | 1476 | """ |
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 | ||
1323 | 1477 | pdict = self_.objects(instance='existing') |
1324 | 1478 | if parameter_name is None: |
1325 | 1479 | return pdict |
1349 | 1503 | raise ValueError("Invalid positional arguments for %s.set_param" % |
1350 | 1504 | (self_or_cls.name)) |
1351 | 1505 | |
1506 | trigger_params = [k for k in kwargs | |
1507 | if ((k in self_.self_or_cls.param) and | |
1508 | hasattr(self_.self_or_cls.param[k], '_autotrigger_value'))] | |
1509 | ||
1510 | for tp in trigger_params: | |
1511 | self_.self_or_cls.param[tp]._mode = 'set' | |
1512 | ||
1352 | 1513 | for (k, v) in kwargs.items(): |
1353 | 1514 | if k not in self_or_cls.param: |
1354 | 1515 | self_.self_or_cls.param._BATCH_WATCH = False |
1363 | 1524 | if not BATCH_WATCH: |
1364 | 1525 | self_._batch_call_watchers() |
1365 | 1526 | |
1527 | for tp in trigger_params: | |
1528 | p = self_.self_or_cls.param[tp] | |
1529 | p._mode = 'reset' | |
1530 | setattr(self_or_cls, tp, p._autotrigger_reset_value) | |
1531 | p._mode = 'set-reset' | |
1366 | 1532 | |
1367 | 1533 | def objects(self_, instance=True): |
1368 | 1534 | """ |
1397 | 1563 | |
1398 | 1564 | if instance and self_.self is not None: |
1399 | 1565 | if instance == 'existing': |
1400 | if self_.self._instance__params: | |
1566 | if getattr(self_.self, 'initialized', False) and self_.self._instance__params: | |
1401 | 1567 | return dict(pdict, **self_.self._instance__params) |
1402 | 1568 | return pdict |
1403 | 1569 | else: |
1409 | 1575 | """ |
1410 | 1576 | Trigger watchers for the given set of parameter names. Watchers |
1411 | 1577 | will be triggered whether or not the parameter values have |
1412 | actually changed. | |
1413 | """ | |
1578 | actually changed. As a special case, the value will actually be | |
1579 | changed for a Parameter of type Event, setting it to True so | |
1580 | that it is clear which Event parameter has been triggered. | |
1581 | """ | |
1582 | trigger_params = [p for p in self_.self_or_cls.param | |
1583 | if hasattr(self_.self_or_cls.param[p], '_autotrigger_value')] | |
1584 | triggers = {p:self_.self_or_cls.param[p]._autotrigger_value | |
1585 | for p in trigger_params if p in param_names} | |
1586 | ||
1414 | 1587 | events = self_.self_or_cls.param._events |
1415 | 1588 | watchers = self_.self_or_cls.param._watchers |
1416 | 1589 | self_.self_or_cls.param._events = [] |
1418 | 1591 | param_values = dict(self_.get_param_values()) |
1419 | 1592 | params = {name: param_values[name] for name in param_names} |
1420 | 1593 | self_.self_or_cls.param._TRIGGER = True |
1421 | self_.set_param(**params) | |
1594 | self_.set_param(**dict(params, **triggers)) | |
1422 | 1595 | self_.self_or_cls.param._TRIGGER = False |
1423 | 1596 | self_.self_or_cls.param._events += events |
1424 | 1597 | self_.self_or_cls.param._watchers += watchers |
1434 | 1607 | event_type = 'changed' if watcher.onlychanged else 'set' |
1435 | 1608 | return Event(what=event.what, name=event.name, obj=event.obj, cls=event.cls, |
1436 | 1609 | old=event.old, new=event.new, type=event_type) |
1610 | ||
1611 | def _execute_watcher(self, watcher, events): | |
1612 | if watcher.mode == 'args': | |
1613 | args, kwargs = events, {} | |
1614 | else: | |
1615 | args, kwargs = (), {event.name: event.new for event in events} | |
1616 | ||
1617 | if iscoroutinefunction(watcher.fn): | |
1618 | if async_executor is None: | |
1619 | raise RuntimeError("Could not execute %s coroutine function. " | |
1620 | "Please register a asynchronous executor on " | |
1621 | "param.parameterized.async_executor, which " | |
1622 | "schedules the function on an event loop." % | |
1623 | watcher.fn) | |
1624 | async_executor(partial(watcher.fn, *args, **kwargs)) | |
1625 | else: | |
1626 | watcher.fn(*args, **kwargs) | |
1437 | 1627 | |
1438 | 1628 | def _call_watcher(self_, watcher, event): |
1439 | 1629 | """ |
1451 | 1641 | else: |
1452 | 1642 | event = self_._update_event_type(watcher, event, self_.self_or_cls.param._TRIGGER) |
1453 | 1643 | 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 | ||
1644 | self_._execute_watcher(watcher, (event,)) | |
1459 | 1645 | |
1460 | 1646 | def _batch_call_watchers(self_): |
1461 | 1647 | """ |
1475 | 1661 | for name in watcher.parameter_names |
1476 | 1662 | if (name, watcher.what) in event_dict] |
1477 | 1663 | 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 | ||
1664 | self_._execute_watcher(watcher, events) | |
1483 | 1665 | |
1484 | 1666 | def set_dynamic_time_fn(self_,time_fn,sublistattr=None): |
1485 | 1667 | """ |
1522 | 1704 | for obj in sublist: |
1523 | 1705 | obj.param.set_dynamic_time_fn(time_fn,sublistattr) |
1524 | 1706 | |
1525 | def get_param_values(self_,onlychanged=False): | |
1707 | def serialize_parameters(self_, subset=None, mode='json'): | |
1708 | self_or_cls = self_.self_or_cls | |
1709 | if mode not in Parameter._serializers: | |
1710 | raise ValueError('Mode %r not in available serialization formats %r' | |
1711 | % (mode, list(Parameter._serializers.keys()))) | |
1712 | serializer = Parameter._serializers[mode] | |
1713 | return serializer.serialize_parameters(self_or_cls, subset=subset) | |
1714 | ||
1715 | def serialize_value(self_, pname, mode='json'): | |
1716 | self_or_cls = self_.self_or_cls | |
1717 | if mode not in Parameter._serializers: | |
1718 | raise ValueError('Mode %r not in available serialization formats %r' | |
1719 | % (mode, list(Parameter._serializers.keys()))) | |
1720 | serializer = Parameter._serializers[mode] | |
1721 | return serializer.serialize_parameter_value(self_or_cls, pname) | |
1722 | ||
1723 | def deserialize_parameters(self_, serialization, subset=None, mode='json'): | |
1724 | self_or_cls = self_.self_or_cls | |
1725 | serializer = Parameter._serializers[mode] | |
1726 | return serializer.deserialize_parameters(self_or_cls, serialization, subset=subset) | |
1727 | ||
1728 | def deserialize_value(self_, pname, value, mode='json'): | |
1729 | self_or_cls = self_.self_or_cls | |
1730 | if mode not in Parameter._serializers: | |
1731 | raise ValueError('Mode %r not in available serialization formats %r' | |
1732 | % (mode, list(Parameter._serializers.keys()))) | |
1733 | serializer = Parameter._serializers[mode] | |
1734 | return serializer.deserialize_parameter_value(self_or_cls, pname, value) | |
1735 | ||
1736 | def schema(self_, safe=False, subset=None, mode='json'): | |
1737 | """ | |
1738 | Returns a schema for the parameters on this Parameterized object. | |
1739 | """ | |
1740 | self_or_cls = self_.self_or_cls | |
1741 | if mode not in Parameter._serializers: | |
1742 | raise ValueError('Mode %r not in available serialization formats %r' | |
1743 | % (mode, list(Parameter._serializers.keys()))) | |
1744 | serializer = Parameter._serializers[mode] | |
1745 | return serializer.schema(self_or_cls, safe=safe, subset=subset) | |
1746 | ||
1747 | def get_param_values(self_, onlychanged=False): | |
1526 | 1748 | """ |
1527 | 1749 | Return a list of name,value pairs for all Parameters of this |
1528 | 1750 | object. |
1537 | 1759 | # (would need to distinguish instantiation of default from |
1538 | 1760 | # user setting of value). |
1539 | 1761 | vals = [] |
1540 | for name,val in self_or_cls.param.objects('existing').items(): | |
1762 | for name, val in self_or_cls.param.objects('existing').items(): | |
1541 | 1763 | value = self_or_cls.param.get_value_generator(name) |
1542 | 1764 | # (this is pointless for cls) |
1543 | if not onlychanged or not all_equal(value,val.default): | |
1544 | vals.append((name,value)) | |
1765 | if not onlychanged or not all_equal(value, val.default): | |
1766 | vals.append((name, value)) | |
1545 | 1767 | |
1546 | 1768 | vals.sort(key=itemgetter(0)) |
1547 | 1769 | return vals |
1758 | 1980 | """ |
1759 | 1981 | self = self_.self |
1760 | 1982 | d = {} |
1761 | for param_name,param in self.param.objects('existing').items(): | |
1983 | for param_name, param in self.param.objects('existing').items(): | |
1762 | 1984 | if param.constant: |
1763 | 1985 | pass |
1764 | elif param.instantiate: | |
1765 | self.param._instantiate_param(param,dict_=d,key=param_name) | |
1766 | else: | |
1767 | d[param_name]=param.default | |
1986 | if param.instantiate: | |
1987 | self.param._instantiate_param(param, dict_=d, key=param_name) | |
1988 | d[param_name] = param.default | |
1768 | 1989 | return d |
1769 | 1990 | |
1770 | 1991 | # CEBALERT: designed to avoid any processing unless the print |
1883 | 2104 | # 'name' to '__name__'?) |
1884 | 2105 | mcs.name = name |
1885 | 2106 | |
1886 | mcs.param = Parameters(mcs) | |
2107 | mcs._parameters_state = { | |
2108 | "BATCH_WATCH": False, # If true, Event and watcher objects are queued. | |
2109 | "TRIGGER": False, | |
2110 | "events": [], # Queue of batched events | |
2111 | "watchers": [] # Queue of batched watchers | |
2112 | } | |
2113 | mcs._param = Parameters(mcs) | |
1887 | 2114 | |
1888 | 2115 | # All objects (with their names) of type Parameter that are |
1889 | 2116 | # defined in this class |
1890 | 2117 | parameters = [(n,o) for (n,o) in dict_.items() |
1891 | if isinstance(o,Parameter)] | |
2118 | if isinstance(o, Parameter)] | |
2119 | ||
2120 | mcs._param._parameters = dict(parameters) | |
1892 | 2121 | |
1893 | 2122 | for param_name,param in parameters: |
1894 | 2123 | mcs._initialize_parameter(param_name,param) |
1960 | 2189 | # _ParameterizedMetaclass__abstract before running, but |
1961 | 2190 | # the actual class object will have an attribute |
1962 | 2191 | # _ClassName__abstract. So, we have to mangle it ourselves at |
1963 | # runtime. | |
2192 | # runtime. Mangling follows description in https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references | |
1964 | 2193 | try: |
1965 | return getattr(mcs,'_%s__abstract'%mcs.__name__) | |
2194 | return getattr(mcs,'_%s__abstract'%mcs.__name__.lstrip("_")) | |
1966 | 2195 | except AttributeError: |
1967 | 2196 | return False |
1968 | 2197 | |
1969 | 2198 | abstract = property(__is_abstract) |
1970 | 2199 | |
1971 | ||
2200 | def _get_param(mcs): | |
2201 | return mcs._param | |
2202 | ||
2203 | param = property(_get_param) | |
1972 | 2204 | |
1973 | 2205 | def __setattr__(mcs,attribute_name,value): |
1974 | 2206 | """ |
2048 | 2280 | Note that instantiate is handled differently: if there is a |
2049 | 2281 | parameter with the same name in one of the superclasses with |
2050 | 2282 | instantiate set to True, this parameter will inherit |
2051 | instatiate=True. | |
2283 | instantiate=True. | |
2052 | 2284 | """ |
2053 | 2285 | # get all relevant slots (i.e. slots defined in all |
2054 | 2286 | # superclasses of this parameter) |
2271 | 2503 | dbprint_prefix=None |
2272 | 2504 | |
2273 | 2505 | |
2274 | ||
2275 | ||
2276 | ||
2506 | # Copy of Python 3.2 reprlib's recursive_repr but allowing extra arguments | |
2507 | if sys.version_info.major >= 3: | |
2508 | from threading import get_ident | |
2509 | def recursive_repr(fillvalue='...'): | |
2510 | 'Decorator to make a repr function return fillvalue for a recursive call' | |
2511 | ||
2512 | def decorating_function(user_function): | |
2513 | repr_running = set() | |
2514 | ||
2515 | def wrapper(self, *args, **kwargs): | |
2516 | key = id(self), get_ident() | |
2517 | if key in repr_running: | |
2518 | return fillvalue | |
2519 | repr_running.add(key) | |
2520 | try: | |
2521 | result = user_function(self, *args, **kwargs) | |
2522 | finally: | |
2523 | repr_running.discard(key) | |
2524 | return result | |
2525 | return wrapper | |
2526 | ||
2527 | return decorating_function | |
2528 | else: | |
2529 | def recursive_repr(fillvalue='...'): | |
2530 | def decorating_function(user_function): | |
2531 | return user_function | |
2532 | return decorating_function | |
2277 | 2533 | |
2278 | 2534 | |
2279 | 2535 | @add_metaclass(ParameterizedMetaclass) |
2320 | 2576 | see documentation for the 'logging' module. |
2321 | 2577 | """ |
2322 | 2578 | |
2323 | name = String(default=None,constant=True,doc=""" | |
2324 | String identifier for this object.""") | |
2325 | ||
2326 | ||
2327 | def __init__(self,**params): | |
2579 | name = String(default=None, constant=True, doc=""" | |
2580 | String identifier for this object.""") | |
2581 | ||
2582 | def __init__(self, **params): | |
2328 | 2583 | global object_count |
2329 | 2584 | |
2330 | 2585 | # Flag that can be tested to see if e.g. constant Parameters |
2331 | 2586 | # 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) | |
2587 | self.initialized = False | |
2588 | self._parameters_state = { | |
2589 | "BATCH_WATCH": False, # If true, Event and watcher objects are queued. | |
2590 | "TRIGGER": False, | |
2591 | "events": [], # Queue of batched events | |
2592 | "watchers": [] # Queue of batched watchers | |
2593 | } | |
2335 | 2594 | self._instance__params = {} |
2336 | 2595 | self._param_watchers = {} |
2337 | 2596 | |
2348 | 2607 | # instantiation of Parameterized with watched deps. Will |
2349 | 2608 | # probably store expanded deps on class - see metaclass |
2350 | 2609 | # 'dependers'. |
2351 | for p in self.param.params_depended_on(n): | |
2610 | grouped = defaultdict(list) | |
2611 | for dep in self.param.params_depended_on(n): | |
2612 | grouped[(id(dep.inst),id(dep.cls),dep.what)].append(dep) | |
2613 | for group in grouped.values(): | |
2352 | 2614 | # 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 | |
2615 | gdep = group[0] # Need to grab representative dep from this group | |
2616 | (gdep.inst or gdep.cls).param.watch(_m_caller(self, n), [d.name for d in group], gdep.what, queued=queued) | |
2617 | ||
2618 | self.initialized = True | |
2619 | ||
2620 | @property | |
2621 | def param(self): | |
2622 | return Parameters(self.__class__, self=self) | |
2356 | 2623 | |
2357 | 2624 | # 'Special' methods |
2358 | 2625 | |
2364 | 2631 | """ |
2365 | 2632 | # remind me, why is it a copy? why not just state.update(self.__dict__)? |
2366 | 2633 | state = self.__dict__.copy() |
2367 | ||
2368 | 2634 | for slot in get_occupied_slots(self): |
2369 | 2635 | state[slot] = getattr(self,slot) |
2370 | 2636 | |
2385 | 2651 | """ |
2386 | 2652 | self.initialized=False |
2387 | 2653 | |
2654 | # When making a copy the internal watchers have to be | |
2655 | # recreated and point to the new instance | |
2656 | if '_param_watchers' in state: | |
2657 | param_watchers = state['_param_watchers'] | |
2658 | for p, attrs in param_watchers.items(): | |
2659 | for attr, watchers in attrs.items(): | |
2660 | new_watchers = [] | |
2661 | for watcher in watchers: | |
2662 | watcher_args = list(watcher) | |
2663 | if watcher.inst is not None: | |
2664 | watcher_args[0] = self | |
2665 | fn = watcher.fn | |
2666 | if hasattr(fn, '_watcher_name'): | |
2667 | watcher_args[2] = _m_caller(self, fn._watcher_name) | |
2668 | elif get_method_owner(fn) is watcher.inst: | |
2669 | watcher_args[2] = getattr(self, fn.__name__) | |
2670 | new_watchers.append(Watcher(*watcher_args)) | |
2671 | param_watchers[p][attr] = new_watchers | |
2672 | ||
2388 | 2673 | if '_instance__params' not in state: |
2389 | 2674 | state['_instance__params'] = {} |
2390 | 2675 | if '_param_watchers' not in state: |
2391 | 2676 | state['_param_watchers'] = {} |
2677 | state.pop('param', None) | |
2392 | 2678 | |
2393 | 2679 | for name,value in state.items(): |
2394 | 2680 | setattr(self,name,value) |
2395 | 2681 | self.initialized=True |
2396 | 2682 | |
2683 | @recursive_repr() | |
2397 | 2684 | def __repr__(self): |
2398 | 2685 | """ |
2399 | 2686 | Provide a nearly valid Python representation that could be used to recreate |
2421 | 2708 | return self.pprint(imports,prefix, unknown_value=None, qualify=True, |
2422 | 2709 | separator="\n") |
2423 | 2710 | |
2711 | @recursive_repr() | |
2424 | 2712 | # CEBALERT: not yet properly documented |
2425 | 2713 | def pprint(self, imports=None, prefix=" ", unknown_value='<?>', |
2426 | 2714 | qualify=False, separator=""): |
2793 | 3081 | cls = self_or_cls |
2794 | 3082 | else: |
2795 | 3083 | p = params |
2796 | params = dict(self_or_cls.get_param_values()) | |
3084 | params = dict(self_or_cls.param.get_param_values()) | |
2797 | 3085 | params.update(p) |
2798 | 3086 | params.pop('name') |
2799 | 3087 | cls = self_or_cls.__class__ |
0 | """ | |
1 | Classes used to support string serialization of Parameters and | |
2 | Parameterized objects. | |
3 | """ | |
4 | ||
5 | import json | |
6 | ||
7 | class UnserializableException(Exception): | |
8 | pass | |
9 | ||
10 | class UnsafeserializableException(Exception): | |
11 | pass | |
12 | ||
13 | def JSONNullable(json_type): | |
14 | "Express a JSON schema type as nullable to easily support Parameters that allow_None" | |
15 | return { "anyOf": [ json_type, { "type": "null"}] } | |
16 | ||
17 | ||
18 | ||
19 | class Serialization(object): | |
20 | """ | |
21 | Base class used to implement different types of serialization. | |
22 | """ | |
23 | ||
24 | @classmethod | |
25 | def schema(cls, pobj, subset=None): | |
26 | raise NotImplementedError # noqa: unimplemented method | |
27 | ||
28 | @classmethod | |
29 | def serialize_parameters(cls, pobj, subset=None): | |
30 | """ | |
31 | Serialize the parameters on a Parameterized object into a | |
32 | single serialized object, e.g. a JSON string. | |
33 | """ | |
34 | raise NotImplementedError # noqa: unimplemented method | |
35 | ||
36 | @classmethod | |
37 | def deserialize_parameters(cls, pobj, serialized, subset=None): | |
38 | """ | |
39 | Deserialize a serialized object representing one or | |
40 | more Parameters into a dictionary of parameter values. | |
41 | """ | |
42 | raise NotImplementedError # noqa: unimplemented method | |
43 | ||
44 | @classmethod | |
45 | def serialize_parameter_value(cls, pobj, pname): | |
46 | """ | |
47 | Serialize a single parameter value. | |
48 | """ | |
49 | raise NotImplementedError # noqa: unimplemented method | |
50 | ||
51 | @classmethod | |
52 | def deserialize_parameter_value(cls, pobj, pname, value): | |
53 | """ | |
54 | Deserialize a single parameter value. | |
55 | """ | |
56 | raise NotImplementedError # noqa: unimplemented method | |
57 | ||
58 | ||
59 | class JSONSerialization(Serialization): | |
60 | """ | |
61 | Class responsible for specifying JSON serialization, deserialization | |
62 | and JSON schemas for Parameters and Parameterized classes and | |
63 | objects. | |
64 | """ | |
65 | ||
66 | unserializable_parameter_types = ['Callable'] | |
67 | ||
68 | json_schema_literal_types = {int:'integer', float:'number', str:'string', | |
69 | type(None):'null'} | |
70 | ||
71 | @classmethod | |
72 | def loads(cls, serialized): | |
73 | return json.loads(serialized) | |
74 | ||
75 | @classmethod | |
76 | def dumps(cls, obj): | |
77 | return json.dumps(obj) | |
78 | ||
79 | @classmethod | |
80 | def schema(cls, pobj, safe=False, subset=None): | |
81 | schema = {} | |
82 | for name, p in pobj.param.objects('existing').items(): | |
83 | if subset is not None and name not in subset: | |
84 | continue | |
85 | schema[name] = p.schema(safe=safe) | |
86 | if p.doc: | |
87 | schema[name]["description"] = p.doc.strip() | |
88 | if p.label: | |
89 | schema[name]["title"] = p.label | |
90 | return schema | |
91 | ||
92 | @classmethod | |
93 | def serialize_parameters(cls, pobj, subset=None): | |
94 | components = {} | |
95 | for name, p in pobj.param.objects('existing').items(): | |
96 | if subset is not None and name not in subset: | |
97 | continue | |
98 | value = pobj.param.get_value_generator(name) | |
99 | components[name] = p.serialize(value) | |
100 | return cls.dumps(components) | |
101 | ||
102 | @classmethod | |
103 | def deserialize_parameters(cls, pobj, serialization, subset=None): | |
104 | deserialized = cls.loads(serialization) | |
105 | components = {} | |
106 | for name, value in deserialized.items(): | |
107 | if subset is not None and name not in subset: | |
108 | continue | |
109 | deserialized = pobj.param[name].deserialize(value) | |
110 | components[name] = deserialized | |
111 | return components | |
112 | ||
113 | # Parameter level methods | |
114 | ||
115 | @classmethod | |
116 | def _get_method(cls, ptype, suffix): | |
117 | "Returns specialized method if available, otherwise None" | |
118 | method_name = ptype.lower()+'_' + suffix | |
119 | return getattr(cls, method_name, None) | |
120 | ||
121 | @classmethod | |
122 | def parameter_schema(cls, ptype, p, safe=False, subset=None): | |
123 | if ptype in cls.unserializable_parameter_types: | |
124 | raise UnserializableException | |
125 | dispatch_method = cls._get_method(ptype, 'schema') | |
126 | if dispatch_method: | |
127 | schema = dispatch_method(p, safe=safe) | |
128 | else: | |
129 | schema = { "type": ptype.lower()} | |
130 | ||
131 | return JSONNullable(schema) if p.allow_None else schema | |
132 | ||
133 | @classmethod | |
134 | def serialize_parameter_value(cls, pobj, pname): | |
135 | value = pobj.param.get_value_generator(pname) | |
136 | return cls.dumps(pobj.param[pname].serialize(value)) | |
137 | ||
138 | @classmethod | |
139 | def deserialize_parameter_value(cls, pobj, pname, value): | |
140 | value = cls.loads(value) | |
141 | return pobj.param[pname].deserialize(value) | |
142 | ||
143 | # Custom Schemas | |
144 | ||
145 | @classmethod | |
146 | def array_schema(cls, p, safe=False): | |
147 | if safe is True: | |
148 | msg = ('Array is not guaranteed to be safe for ' | |
149 | 'serialization as the dtype is unknown') | |
150 | raise UnsafeserializableException(msg) | |
151 | return { "type": "array"} | |
152 | ||
153 | @classmethod | |
154 | def dict_schema(cls, p, safe=False): | |
155 | if safe is True: | |
156 | msg = ('Dict is not guaranteed to be safe for ' | |
157 | 'serialization as the key and value types are unknown') | |
158 | raise UnsafeserializableException(msg) | |
159 | return { "type": "object"} | |
160 | ||
161 | @classmethod | |
162 | def date_schema(cls, p, safe=False): | |
163 | return { "type": "string", "format": "date-time"} | |
164 | ||
165 | @classmethod | |
166 | def calendardate_schema(cls, p, safe=False): | |
167 | return { "type": "string", "format": "date"} | |
168 | ||
169 | @classmethod | |
170 | def tuple_schema(cls, p, safe=False): | |
171 | schema = { "type": "array"} | |
172 | if p.length is not None: | |
173 | schema['minItems'] = p.length | |
174 | schema['maxItems'] = p.length | |
175 | return schema | |
176 | ||
177 | @classmethod | |
178 | def number_schema(cls, p, safe=False): | |
179 | schema = { "type": p.__class__.__name__.lower() } | |
180 | return cls.declare_numeric_bounds(schema, p.bounds, p.inclusive_bounds) | |
181 | ||
182 | @classmethod | |
183 | def declare_numeric_bounds(cls, schema, bounds, inclusive_bounds): | |
184 | "Given an applicable numeric schema, augment with bounds information" | |
185 | if bounds is not None: | |
186 | (low, high) = bounds | |
187 | if low is not None: | |
188 | key = 'minimum' if inclusive_bounds[0] else 'exclusiveMinimum' | |
189 | schema[key] = low | |
190 | if high is not None: | |
191 | key = 'maximum' if inclusive_bounds[1] else 'exclusiveMaximum' | |
192 | schema[key] = high | |
193 | return schema | |
194 | ||
195 | @classmethod | |
196 | def integer_schema(cls, p, safe=False): | |
197 | return cls.number_schema(p) | |
198 | ||
199 | @classmethod | |
200 | def numerictuple_schema(cls, p, safe=False): | |
201 | schema = cls.tuple_schema(p, safe=safe) | |
202 | schema["additionalItems"] = { "type": "number" } | |
203 | return schema | |
204 | ||
205 | @classmethod | |
206 | def xycoordinates_schema(cls, p, safe=False): | |
207 | return cls.numerictuple_schema(p, safe=safe) | |
208 | ||
209 | @classmethod | |
210 | def range_schema(cls, p, safe=False): | |
211 | schema = cls.tuple_schema(p, safe=safe) | |
212 | bounded_number = cls.declare_numeric_bounds({ "type": "number" }, | |
213 | p.bounds, p.inclusive_bounds) | |
214 | schema["additionalItems"] = bounded_number | |
215 | return schema | |
216 | ||
217 | @classmethod | |
218 | def list_schema(cls, p, safe=False): | |
219 | schema = { "type": "array"} | |
220 | if safe is True and p.item_type is None: | |
221 | msg = ('List without a class specified cannot be guaranteed ' | |
222 | 'to be safe for serialization') | |
223 | raise UnsafeserializableException(msg) | |
224 | if p.item_type is not None and p.item_type in cls.json_schema_literal_types: | |
225 | schema['items'] = {"type": cls.json_schema_literal_types[p.item_type]} | |
226 | return schema | |
227 | ||
228 | @classmethod | |
229 | def objectselector_schema(cls, p, safe=False): | |
230 | try: | |
231 | allowed_types = [{'type': cls.json_schema_literal_types[type(obj)]} | |
232 | for obj in p.objects] | |
233 | schema = { "anyOf": allowed_types} | |
234 | schema['enum'] = p.objects | |
235 | return schema | |
236 | except: | |
237 | if safe is True: | |
238 | msg = ('ObjectSelector cannot be guaranteed to be safe for ' | |
239 | 'serialization due to unserializable type in objects') | |
240 | raise UnsafeserializableException(msg) | |
241 | return {} | |
242 | ||
243 | @classmethod | |
244 | def listselector_schema(cls, p, safe=False): | |
245 | if p.objects is None: | |
246 | if safe is True: | |
247 | msg = ('ListSelector cannot be guaranteed to be safe for ' | |
248 | 'serialization as allowed objects unspecified') | |
249 | return {'type': 'array'} | |
250 | for obj in p.objects: | |
251 | if type(obj) not in cls.json_schema_literal_types: | |
252 | msg = "ListSelector cannot serialize type %s" % type(obj) | |
253 | raise UnserializableException(msg) | |
254 | return {'type': 'array', 'items':{'enum':p.objects}} | |
255 | ||
256 | ||
257 | @classmethod | |
258 | def dataframe_schema(cls, p, safe=False): | |
259 | schema = {'type': 'array'} | |
260 | if safe is True: | |
261 | msg = ('DataFrame is not guaranteed to be safe for ' | |
262 | 'serialization as the column dtypes are unknown') | |
263 | raise UnsafeserializableException(msg) | |
264 | if p.columns is None: | |
265 | schema['items'] = {'type': 'object'} | |
266 | return schema | |
267 | ||
268 | mincols, maxcols = None, None | |
269 | if isinstance(p.columns, int): | |
270 | mincols, maxcols = p.columns, p.columns | |
271 | elif isinstance(p.columns, tuple): | |
272 | mincols, maxcols = p.columns | |
273 | ||
274 | if isinstance(p.columns, int) or isinstance(p.columns, tuple): | |
275 | schema['items'] = {'type': 'object', | |
276 | 'minItems': mincols, | |
277 | 'maxItems': maxcols} | |
278 | ||
279 | if isinstance(p.columns, list) or isinstance(p.columns, set): | |
280 | literal_types = [{'type':el} for el in cls.json_schema_literal_types.values()] | |
281 | allowable_types = {"anyOf": literal_types} | |
282 | properties = {name: allowable_types for name in p.columns} | |
283 | schema['items'] = {'type': 'object', | |
284 | 'properties' : properties } | |
285 | ||
286 | ||
287 | minrows, maxrows = None, None | |
288 | if isinstance(p.rows, int): | |
289 | minrows, maxrows = p.rows, p.rows | |
290 | elif isinstance(p.rows, tuple): | |
291 | minrows, maxrows = p.rows | |
292 | ||
293 | if minrows is not None: | |
294 | schema['minItems'] = minrows | |
295 | if maxrows is not None: | |
296 | schema['maxItems'] = maxrows | |
297 | ||
298 | return schema |
1 | 1 | Provide consistent and up-to-date ``__version__`` strings for |
2 | 2 | Python packages. |
3 | 3 | |
4 | See https://github.com/pyviz/autover for more information. | |
4 | See https://github.com/holoviz/autover for more information. | |
5 | 5 | """ |
6 | 6 | |
7 | 7 | # The Version class is a copy of autover.version.Version v0.2.5, |
25 | 25 | cwd=cwd) |
26 | 26 | output, error = (str(s.decode()).strip() for s in proc.communicate()) |
27 | 27 | |
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: | |
29 | 32 | raise Exception(proc.returncode, error) |
30 | 33 | return output |
31 | 34 | |
173 | 176 | # Verify this is the correct repository (since fpath could |
174 | 177 | # be an unrelated git repository, and autover could just have |
175 | 178 | # 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' , | |
180 | 182 | # A remote 'server:reponame.git' can also be referred |
181 | 183 | # to (i.e. cloned) as `server:reponame`. |
182 | 184 | '/' + 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 | output = run_cmd([cmd, 'describe', '--long', '--match', | |
193 | "v[0-9]*.[0-9]*.[0-9]*", '--dirty'], | |
194 | cwd=os.path.dirname(self.fpath)) | |
189 | 195 | if as_string: return output |
190 | 196 | except Exception as e1: |
191 | 197 | 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 | Description: <img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250> | |
16 | ||
17 | | | | | |
18 | | --- | --- | | |
19 | | 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) | |
20 | | Coverage | [![coveralls](https://coveralls.io/repos/github/holoviz/param/badge.svg?branch=master)](https://coveralls.io/github/holoviz/param?branch=master) | | |
21 | | 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/) | | |
22 | | 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) | | |
23 | | 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) | | |
24 | | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) | | |
25 | ||
26 | 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. | |
27 | ||
28 | 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. | |
29 | ||
30 | Please see [param's website](https://param.holoviz.org) for official releases, installation instructions, documentation, and examples. | |
31 | ||
32 | Platform: Windows | |
33 | Platform: Mac OS X | |
34 | Platform: Linux | |
35 | Classifier: License :: OSI Approved :: BSD License | |
36 | Classifier: Development Status :: 5 - Production/Stable | |
37 | Classifier: Programming Language :: Python :: 2 | |
38 | Classifier: Programming Language :: Python :: 2.7 | |
39 | Classifier: Programming Language :: Python :: 3 | |
40 | Classifier: Programming Language :: Python :: 3.4 | |
41 | Classifier: Programming Language :: Python :: 3.5 | |
42 | Classifier: Programming Language :: Python :: 3.6 | |
43 | Classifier: Programming Language :: Python :: 3.7 | |
44 | Classifier: Programming Language :: Python :: 3.8 | |
45 | Classifier: Operating System :: OS Independent | |
46 | Classifier: Intended Audience :: Science/Research | |
47 | Classifier: Intended Audience :: Developers | |
48 | Classifier: Natural Language :: English | |
49 | Classifier: Topic :: Scientific/Engineering | |
50 | Classifier: Topic :: Software Development :: Libraries | |
51 | Provides: param | |
52 | Provides: numbergen | |
53 | Requires-Python: >=2.7 | |
54 | Description-Content-Type: text/markdown | |
55 | Provides-Extra: all | |
56 | Provides-Extra: doc | |
57 | Provides-Extra: tests |
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 | flake8 | |
3 | graphviz | |
4 | myst-parser | |
5 | myst_nb==0.12.2 | |
6 | nbconvert<6.0 | |
7 | nbsite>=0.6.1 | |
8 | pydata-sphinx-theme | |
9 | pygraphviz | |
10 | pytest | |
11 | pytest-cov | |
12 | ||
13 | [doc] | |
14 | graphviz | |
15 | myst-parser | |
16 | myst_nb==0.12.2 | |
17 | nbconvert<6.0 | |
18 | nbsite>=0.6.1 | |
19 | pydata-sphinx-theme | |
20 | pygraphviz | |
21 | ||
22 | [tests] | |
23 | flake8 | |
24 | pytest | |
25 | pytest-cov |
1 | 1 | universal = 1 |
2 | 2 | |
3 | 3 | [flake8] |
4 | # TODO tests (one day) | |
5 | 4 | 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, | |
26 | W605 | |
21 | 27 | |
22 | [nosetests] | |
23 | verbosity = 2 | |
24 | with-doctest = 1 | |
25 | nologcapture = 1 | |
28 | [tool:pytest] | |
29 | python_files = test*.py | |
30 | addopts = --doctest-modules param numbergen | |
31 | ||
32 | [egg_info] | |
33 | tag_build = | |
34 | tag_date = 0 | |
35 |
18 | 18 | # pip doesn't support tests_require |
19 | 19 | # (https://github.com/pypa/pip/issues/1197) |
20 | 20 | 'tests': [ |
21 | 'nose', | |
22 | 'flake8' | |
21 | 'pytest', | |
22 | 'pytest-cov', | |
23 | 'flake8', | |
24 | ], | |
25 | 'doc': [ | |
26 | 'pygraphviz', | |
27 | 'nbsite >=0.6.1', | |
28 | 'pydata-sphinx-theme', | |
29 | 'myst-parser', | |
30 | 'nbconvert <6.0', | |
31 | 'graphviz', | |
32 | 'myst_nb ==0.12.2' | |
23 | 33 | ] |
24 | 34 | } |
25 | 35 | |
31 | 41 | setup_args = dict( |
32 | 42 | name='param', |
33 | 43 | 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", | |
44 | description='Make your Python code clearer and more reliable by declaring Parameters.', | |
45 | long_description=open('README.md').read() if os.path.isfile('README.md') else 'Consult README.md', | |
46 | long_description_content_type="text/markdown", | |
47 | author="HoloViz", | |
48 | author_email="developers@holoviz.org", | |
49 | maintainer="HoloViz", | |
50 | maintainer_email="developers@holoviz.org", | |
40 | 51 | platforms=['Windows', 'Mac OS X', 'Linux'], |
41 | 52 | license='BSD', |
42 | url='http://ioam.github.com/param/', | |
53 | url='http://param.holoviz.org/', | |
43 | 54 | packages=["param","numbergen"], |
44 | 55 | provides=["param","numbergen"], |
45 | 56 | include_package_data = True, |
47 | 58 | install_requires=[], |
48 | 59 | extras_require=extras_require, |
49 | 60 | tests_require=extras_require['tests'], |
61 | project_urls={ | |
62 | "Documentation": "https://param.holoviz.org/", | |
63 | "Releases": "https://github.com/holoviz/param/releases", | |
64 | "Bug Tracker": "https://github.com/holoviz/param/issues", | |
65 | "Source Code": "https://github.com/holoviz/param", | |
66 | "Panel Examples": "https://panel.holoviz.org/user_guide/Param.html", | |
67 | }, | |
50 | 68 | classifiers=[ |
51 | 69 | "License :: OSI Approved :: BSD License", |
52 | 70 | "Development Status :: 5 - Production/Stable", |
56 | 74 | "Programming Language :: Python :: 3.4", |
57 | 75 | "Programming Language :: Python :: 3.5", |
58 | 76 | "Programming Language :: Python :: 3.6", |
77 | "Programming Language :: Python :: 3.7", | |
78 | "Programming Language :: Python :: 3.8", | |
59 | 79 | "Operating System :: OS Independent", |
60 | 80 | "Intended Audience :: Science/Research", |
61 | 81 | "Intended Audience :: Developers", |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | 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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | 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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | 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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | """ | |
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 | 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 | 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 | [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 |