New Upstream Release - python-param

Ready changes

Summary

Merged new upstream version: 1.13.0 (was: 1.12.3).

Resulting package

Built on 2023-04-02T20:56 (took 5m29s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python3-param

Lintian Result

Diff

diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index e42e8a4..ea51ba4 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -81,7 +81,7 @@ jobs:
           python setup.py sdist bdist_wheel
       - name: Publish package to PyPI
         if: github.event_name == 'push'
-        uses: pypa/gh-action-pypi-publish@master
+        uses: pypa/gh-action-pypi-publish@release/v1
         with:
           user: ${{ secrets.PPU }}
           password: ${{ secrets.PPP }}
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 69007f2..dfc741c 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -2,7 +2,7 @@ name: tests
 on:
   push:
     branches:
-      - master
+      - main
   pull_request:
     branches:
     - '*'
@@ -30,6 +30,8 @@ jobs:
       - uses: actions/checkout@v3
         with:
           fetch-depth: "100"
+      - name: Fetch unshallow
+        run: git fetch --prune --tags --unshallow -f
       - name: Set up Python ${{ matrix.python-version }}
         uses: actions/setup-python@v4
         with:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..458ad16
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,5 @@
+# Code of Conduct
+
+For the code of conduct, see [HoloViz/HoloViz - CODE_OF_CONDUCT.md](https://github.com/holoviz/holoviz/blob/param-gov/CODE_OF_CONDUCT.md).
+
+The Param Project’s equivalently named documents take precedence over any external materials referenced within this linked document above.
\ No newline at end of file
diff --git a/README.md b/README.md
index 54f78c4..1488fe8 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,14 @@
-<img src="https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png" width=250>
+<img src="https://raw.githubusercontent.com/holoviz/param/main/doc/_static/logo_horizontal.png" width=250>
 
 |    |    |
 | --- | --- |
 | 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)
-| Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/master/graph/badge.svg)](https://codecov.io/gh/holoviz/param) ||
+| Coverage | [![codecov](https://codecov.io/gh/holoviz/param/branch/main/graph/badge.svg)](https://codecov.io/gh/holoviz/param) ||
 | 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/) |
 | 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) |
 | Python | [![Python support](https://img.shields.io/pypi/pyversions/param.svg)](https://pypi.org/project/param/)
 | 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) |
-| Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/master?labpath=examples) |
+| Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/holoviz/param/main?labpath=examples) |
 | Support | [![Discourse](https://img.shields.io/discourse/status?server=https%3A%2F%2Fdiscourse.holoviz.org)](https://discourse.holoviz.org/) |
 
 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.
diff --git a/debian/changelog b/debian/changelog
index 73c94cd..ba8d5a3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-param (1.13.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 02 Apr 2023 20:51:55 -0000
+
 python-param (1.12.3-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/fix_setup.py b/debian/patches/fix_setup.py
index 0fe6ae5..5c84e85 100644
--- a/debian/patches/fix_setup.py
+++ b/debian/patches/fix_setup.py
@@ -4,8 +4,10 @@ Origin: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1026514#37
 Bug-Debian: https://bugs.debian.org/1026514
 Description: Fix setup.py
 
---- a/setup.py
-+++ b/setup.py
+Index: python-param.git/setup.py
+===================================================================
+--- python-param.git.orig/setup.py
++++ python-param.git/setup.py
 @@ -7,6 +7,8 @@ from setuptools import setup
  
  def get_setup_version(reponame):
diff --git a/doc/about.md b/doc/about.md
index 5db98f5..509861b 100644
--- a/doc/about.md
+++ b/doc/about.md
@@ -1,6 +1,6 @@
 # About
 
-Param is completely open source, available under a [BSD license](https://github.com/holoviz/param/blob/master/LICENSE.txt), freely for both commercial and non-commercial use. Param was originally developed at the University of Texas at Austin and the University of Edinburgh with funding from the US National Institutes of Health grant 1R01-MH66991. Param is now maintained by [Anaconda Inc.](https://anaconda.com) and by community contributors.
+Param is completely open source, available under a [BSD license](https://github.com/holoviz/param/blob/main/LICENSE.txt), freely for both commercial and non-commercial use. Param was originally developed at the University of Texas at Austin and the University of Edinburgh with funding from the US National Institutes of Health grant 1R01-MH66991. Param is now maintained by [Anaconda Inc.](https://anaconda.com) and by community contributors.
 
 Param is maintained as part of the [HoloViz](https://holoviz.org) family of tools. The [holoviz.org](https://holoviz.org) website shows how to use Param together with other libraries to solve complex problems, with detailed tutorials and examples. Each of the HoloViz tools builds on Param, as do many of the example projects at [examples.pyviz.org](https://examples.pyviz.org).
 
diff --git a/doc/governance/project-docs/CONTRIBUTING.md b/doc/governance/project-docs/CONTRIBUTING.md
new file mode 100644
index 0000000..f61f629
--- /dev/null
+++ b/doc/governance/project-docs/CONTRIBUTING.md
@@ -0,0 +1,5 @@
+# Contributing
+
+For the contributing policy, see [HoloViz/HoloViz - CONTRIBUTING.md](https://github.com/holoviz/holoviz/blob/param-gov/doc/governance/project-docs/CONTRIBUTING.md).
+
+The Param Project’s equivalently named documents take precedence over any external materials referenced within this linked document above.
\ No newline at end of file
diff --git a/doc/governance/project-docs/GOVERNANCE.md b/doc/governance/project-docs/GOVERNANCE.md
new file mode 100644
index 0000000..18c7068
--- /dev/null
+++ b/doc/governance/project-docs/GOVERNANCE.md
@@ -0,0 +1,7 @@
+# Governance Policy
+
+The "Project" is herein defined as the activities related to this specific GitHub repository [`Param`](https://github.com/holoviz/param), within the `HoloViz` GitHub Organization.
+
+This Project adopts the governance specified by all of the numbered sections of [HoloViz/HoloViz - GOVERNANCE.md](https://github.com/holoviz/holoviz/blob/param-gov/doc/governance/project-docs/GOVERNANCE.md).
+
+The Param Project’s equivalently named documents take precedence over any external materials referenced within this linked document above.
\ No newline at end of file
diff --git a/doc/governance/project-docs/LICENSE.md b/doc/governance/project-docs/LICENSE.md
new file mode 100644
index 0000000..fe47ca9
--- /dev/null
+++ b/doc/governance/project-docs/LICENSE.md
@@ -0,0 +1,3 @@
+# License
+
+For the license, see [HoloViz/Param - LICENSE.txt](https://github.com/holoviz/param/blob/main/LICENSE.txt).
\ No newline at end of file
diff --git a/doc/governance/project-docs/MEMBERS.md b/doc/governance/project-docs/MEMBERS.md
new file mode 100644
index 0000000..3b45e15
--- /dev/null
+++ b/doc/governance/project-docs/MEMBERS.md
@@ -0,0 +1,11 @@
+# Maintainers
+
+For member policy, see the description at the top of [HoloViz/HoloViz - MEMBERS.md](https://github.com/holoviz/holoviz/blob/param-gov/doc/governance/project-docs/MEMBERS.md)
+
+The Param Project’s equivalently named documents take precedence over any external materials referenced within this linked document above.
+
+| **NAME** | **Role** | **GitHub Handle** |
+| --- | --- | --- |
+| James Bednar | Project Director | [jbednar](https://github.com/jbednar) |
+| Jean-Luc Stevens | Maintainer | [jlstevens](https://github.com/jlstevens) |
+| Philipp Rudiger | Maintainer | [philippjfr](https://github.com/philippjfr) |
\ No newline at end of file
diff --git a/doc/releases.md b/doc/releases.md
index 28ce298..fceaf52 100644
--- a/doc/releases.md
+++ b/doc/releases.md
@@ -1,5 +1,39 @@
 # Releases
 
+## Version 1.13.0
+
+Date: 2023-03-14
+
+The `1.13.0` is the last release of Param before the 2.0 release. However, Param 1.13 is meant to receive long-term support; security patches and fixes to critical bugs are planned to be backported to the 1.13.x series.
+
+This release includes a new `Bytes` *Parameter* and a few important bug fixes. This release is also marked by the adoption of a formal project governance, ensuring Param's future as a healthy open-source project. Many thanks to @ovidner and @droumis for their first contributions! And to @maximlt, @Hoxbro, @jlstevens, @philippjfr and @jbednar for their continuing support to fixing and improving Param.
+
+Bug fixes:
+
+* Fix copying when having watchers on e.g. bounds on inherited Parameter types ([#675](https://github.com/holoviz/param/pull/675))
+* Allow JSON serialization to work with `json.dumps` ([#655](https://github.com/holoviz/param/pull/655))
+* `ListSelector` restricted to `list` type objects ([#531](https://github.com/holoviz/param/pull/531))
+* Fix `depends` async wrapper ([#684](https://github.com/holoviz/param/pull/684))
+* Allow named colors to be any case ([#711](https://github.com/holoviz/param/pull/711))
+
+New features:
+
+* Add Bytes parameter ([#542](https://github.com/holoviz/param/pull/542))
+
+Documentation:
+
+* Fix param module link ([#682](https://github.com/holoviz/param/pull/682))
+
+Project governance:
+
+* Create initial project governance docs ([#674](https://github.com/holoviz/param/pull/674))
+
+Maintenance:
+
+* Rename `master` branch to `main` ([#672](https://github.com/holoviz/param/pull/672))
+* Add more tests ([#710](https://github.com/holoviz/param/pull/710))
+* Various CI related fixes ([#680](https://github.com/holoviz/param/pull/680), [#683](https://github.com/holoviz/param/pull/683) and [#709](https://github.com/holoviz/param/pull/709))
+
 ## Version 1.12.3
 
 Date: 2022-12-06
diff --git a/examples/Promo.ipynb b/examples/Promo.ipynb
index 3bd7b05..aaf1a00 100644
--- a/examples/Promo.ipynb
+++ b/examples/Promo.ipynb
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "<img src=\"https://raw.githubusercontent.com/holoviz/param/master/doc/_static/logo_horizontal.png\" style=\"display:block;margin-left:auto;margin-right:auto;width:50%;max-width:500px\">"
+    "<img src=\"https://raw.githubusercontent.com/holoviz/param/main/doc/_static/logo_horizontal.png\" style=\"display:block;margin-left:auto;margin-right:auto;width:50%;max-width:500px\">"
    ]
   },
   {
diff --git a/examples/user_guide/Dynamic_Parameters.ipynb b/examples/user_guide/Dynamic_Parameters.ipynb
index dab8f7f..a6356f3 100644
--- a/examples/user_guide/Dynamic_Parameters.ipynb
+++ b/examples/user_guide/Dynamic_Parameters.ipynb
@@ -275,7 +275,7 @@
     "- named functions don't support internal state and need to be stored in a named module somewhere for them to be picklable, potentially resulting in a large number of one-off functions to keep track of\n",
     "- making a new object with a `__call__` method is verbose and error-prone, and again needs to be stored in a formal module if it is to be picklable.\n",
     "\n",
-    "To make using Dynamic parameters more convenient, Param includes a separate module [Numbergen](https://github.com/holoviz/param/blob/master/numbergen/__init__.py) that provides ready-to-use, picklable, composable, and interchangeable callable objects producing numeric values. Numbergen relies only on Param and the Python standard library, so it should be easy to add to any project. \n",
+    "To make using Dynamic parameters more convenient, Param includes a separate module [Numbergen](https://github.com/holoviz/param/blob/main/numbergen/__init__.py) that provides ready-to-use, picklable, composable, and interchangeable callable objects producing numeric values. Numbergen relies only on Param and the Python standard library, so it should be easy to add to any project. \n",
     "\n",
     "Numbergen objects are designed to work seamlessly as Dynamic parameter values, providing easy access to various temporal distributions, along with tools for combining and configuring number generators without having to write custom functions or classes. Moreover, because all of these objects are Parameterized objects sharing the same usage interface (each provides a numeric value when called, regardless of how many or which parameters are required to configure that distribution), using them together with Param's Dynamic support provides a huge amount of power over the values parameters take over time, without requiring any extra complexity in your program. Without Dynamic support and numbergen, your Parameterized classes could of course provide their own support for e.g. a normal random distribution by accepting a mean and variance, but it would then be limited to that specific random distribution, whereas Dynamic parameters can accept _any_ current or future number generator object as configured by a user for their own purposes, neatly separating your Parameterized's requirements (\"a positive integer value\") from the user's requirements (\"let's see what happens when the value starts at 1 and doubles every iteration\").\n",
     "\n",
@@ -371,7 +371,7 @@
     "\n",
     "A TimeAware object also has access to a `time_fn` and has a `time_dependent` parameter, but either sets `time_dependent=False` (indicating that values are never a strict function of time) or allows either True or False (switching into and out of a time dependent mode). All current `TimeAware` NumberGenerator objects are random number generators that support both possible values of `time_dependent`. For `time_dependent=False` (the default), they return a new value on each call, while for `time_dependent=True`, they return pseudorandom values that follow the indicated distribution but are also a strict function of the time, in that the same number will be returned for a given time value even if time skips ahead or backwards. \n",
     "\n",
-    "These random values are thus very tightly controlled to allow reproducible, repeatable results, with values determined by both a seed value (to choose the overall set of random values) and by the current time. Effectively, when `time_dependent=True`, these numbers provide a random value seeded by the generator's `name` parameter, the global `param.random_seed`, the `seed` parameter of the NumberGenerator, _and_ the NumberGenerator's current `time_fn()` value.  The resulting generated values should be the same for a given object and a given `time_fn` value, even across platforms and machine-word sizes (see the [Hash](https://github.com/holoviz/param/blob/master/numbergen/__init__.py#L176), TimeAwareRandomState, and RandomDistribution classes for more details). \n",
+    "These random values are thus very tightly controlled to allow reproducible, repeatable results, with values determined by both a seed value (to choose the overall set of random values) and by the current time. Effectively, when `time_dependent=True`, these numbers provide a random value seeded by the generator's `name` parameter, the global `param.random_seed`, the `seed` parameter of the NumberGenerator, _and_ the NumberGenerator's current `time_fn()` value.  The resulting generated values should be the same for a given object and a given `time_fn` value, even across platforms and machine-word sizes (see the [Hash](https://github.com/holoviz/param/blob/main/numbergen/__init__.py#L176), TimeAwareRandomState, and RandomDistribution classes for more details). \n",
     "\n",
     "For best results, you should provide an explicit unique name to any such generator and preserve that name over time, so that results will be reproducible across program runs. By default, the underlying random numbers are generated using Python's [random](https://docs.python.org/3/library/random.html) module (which see for details of the number generation), but you can substitute an instance of `numpy.random.RandomState` or similar compatible object for `self.random_generator` for higher performance or to generate time-dependent array values.\n",
     "\n",
diff --git a/examples/user_guide/Parameter_Types.ipynb b/examples/user_guide/Parameter_Types.ipynb
index fd2ba8a..3804c85 100644
--- a/examples/user_guide/Parameter_Types.ipynb
+++ b/examples/user_guide/Parameter_Types.ipynb
@@ -50,7 +50,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The full behavior of these types is covered in the [Reference Manual](https://param.holoviz.org/Reference_Manual/param.html). Here we will discuss the major categories of Parameter type and how to use them, including examples of what each type does _not_ allow (labeled `with param.exceptions_summarized():`).  Each of these classes is also suitable for subclassing to create more specialized types enforcing your own specific constraints.  After reading about Parameters in general, feel free to skip around in this page and only look at the Parameter types of interest to you!"
+    "The full behavior of these types is covered in the [Reference Manual](https://param.holoviz.org/reference.html#param-module). Here we will discuss the major categories of Parameter type and how to use them, including examples of what each type does _not_ allow (labeled `with param.exceptions_summarized():`).  Each of these classes is also suitable for subclassing to create more specialized types enforcing your own specific constraints.  After reading about Parameters in general, feel free to skip around in this page and only look at the Parameter types of interest to you!"
    ]
   },
   {
diff --git a/param/__init__.py b/param/__init__.py
index cbe200b..002ede1 100644
--- a/param/__init__.py
+++ b/param/__init__.py
@@ -43,7 +43,7 @@ from numbers import Real
 # only two required files.
 try:
     from .version import Version
-    __version__ = str(Version(fpath=__file__, archive_commit="897687f71", reponame="param"))
+    __version__ = str(Version(fpath=__file__, archive_commit="782a03c4", reponame="param"))
 except:
     __version__ = "0.0.0+unknown"
 
@@ -735,6 +735,42 @@ def get_soft_bounds(bounds, softbounds):
     return (l, u)
 
 
+class Bytes(Parameter):
+    """
+    A Bytes Parameter, with a default value and optional regular
+    expression (regex) matching.
+
+    Similar to the String parameter, but instead of type basestring
+    this parameter only allows objects of type bytes (e.g. b'bytes').
+    """
+
+    __slots__ = ['regex']
+
+    def __init__(self, default=b"", regex=None, allow_None=False, **kwargs):
+        super(Bytes, self).__init__(default=default, allow_None=allow_None, **kwargs)
+        self.regex = regex
+        self.allow_None = (default is None or allow_None)
+        self._validate(default)
+
+    def _validate_regex(self, val, regex):
+        if (val is None and self.allow_None):
+            return
+        if regex is not None and re.match(regex, val) is None:
+            raise ValueError("Bytes parameter %r value %r does not match regex %r."
+                             % (self.name, val, regex))
+
+    def _validate_value(self, val, allow_None):
+        if allow_None and val is None:
+            return
+        if not isinstance(val, bytes):
+            raise ValueError("Bytes parameter %r only takes a byte string value, "
+                             "not value of type %s." % (self.name, type(val)))
+
+    def _validate(self, val):
+        self._validate_value(val, self.allow_None)
+        self._validate_regex(val, self.regex)
+
+
 class Number(Dynamic):
     """
     A numeric Dynamic Parameter, with a default value and optional bounds.
@@ -1022,12 +1058,12 @@ class Tuple(Parameter):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         return list(value) # As JSON has no tuple representation
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         return tuple(value) # As JSON has no tuple representation
 
@@ -1483,12 +1519,12 @@ class Array(ClassSelector):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         return value.tolist()
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         from numpy import asarray
         return asarray(value)
@@ -1570,12 +1606,12 @@ class DataFrame(ClassSelector):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         return value.to_dict('records')
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         from pandas import DataFrame as pdDFrame
         return pdDFrame(value)
@@ -1878,6 +1914,9 @@ class ListSelector(Selector):
     def _validate(self, val):
         if (val is None and self.allow_None):
             return
+        if not isinstance(val, list):
+            raise ValueError("ListSelector parameter %r only takes list "
+                             "types, not %r." % (self.name, val))
         for o in val:
             super(ListSelector, self)._validate(o)
 
@@ -1942,14 +1981,14 @@ class Date(Number):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         if not isinstance(value, (dt.datetime, dt.date)): # i.e np.datetime64
             value = value.astype(dt.datetime)
         return value.strftime("%Y-%m-%dT%H:%M:%S.%f")
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         return dt.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
 
@@ -1980,12 +2019,12 @@ class CalendarDate(Number):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         return value.strftime("%Y-%m-%d")
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         return dt.datetime.strptime(value, "%Y-%m-%d").date()
 
@@ -2056,7 +2095,7 @@ class Color(Parameter):
             return
         is_hex = re.match('^#?(([0-9a-fA-F]{2}){3}|([0-9a-fA-F]){3})$', val)
         if self.allow_named:
-            if not is_hex and val not in self._named_colors:
+            if not is_hex and val.lower() not in self._named_colors:
                 raise ValueError("Color '%s' only takes RGB hex codes "
                                  "or named colors, received '%s'." % (self.name, val))
         elif not is_hex:
@@ -2140,7 +2179,7 @@ class DateRange(Range):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         # List as JSON has no tuple representation
         serialized = []
         for v in value:
@@ -2155,7 +2194,7 @@ class DateRange(Range):
         return serialized
 
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         deserialized = []
         for v in value:
@@ -2191,13 +2230,13 @@ class CalendarDateRange(Range):
     @classmethod
     def serialize(cls, value):
         if value is None:
-            return 'null'
+            return None
         # As JSON has no tuple representation
         return [v.strftime("%Y-%m-%d") for v in value]
 
     @classmethod
     def deserialize(cls, value):
-        if value == 'null':
+        if value == 'null' or value is None:
             return None
         # As JSON has no tuple representation
         return tuple([dt.datetime.strptime(v, "%Y-%m-%d").date() for v in value])
diff --git a/param/_async.py b/param/_async.py
index b5139b5..03d2446 100644
--- a/param/_async.py
+++ b/param/_async.py
@@ -3,10 +3,12 @@ Module that implements async/def function wrappers to be used
 by param internal callbacks. These are defined in a separate file due
 to py2 incompatibility with both `async/await` and `yield from` syntax.
 """
+from functools import wraps
 
 def generate_depends(func):
+    @wraps(func)
     async def _depends(*args, **kw):  # noqa: E999
-        await func(*args, **kw)  # noqa: E999
+        return await func(*args, **kw)  # noqa: E999
     return _depends
 
 def generate_caller(function, what='value', changed=None, callback=None, skip_event=None):
diff --git a/param/parameterized.py b/param/parameterized.py
index eb844fd..607e01f 100644
--- a/param/parameterized.py
+++ b/param/parameterized.py
@@ -1117,7 +1117,7 @@ class Parameter(object):
                                  "it has been bound to a Parameterized.")
 
         implemented = (attribute != "default" and hasattr(self, 'watchers') and attribute in self.watchers)
-        slot_attribute = attribute in self.__slots__
+        slot_attribute = attribute in get_all_slots(type(self))
         try:
             old = getattr(self, attribute) if implemented else NotImplemented
             if slot_attribute:
diff --git a/param/version.py b/param/version.py
index 0534ae0..1d6559d 100644
--- a/param/version.py
+++ b/param/version.py
@@ -400,8 +400,8 @@ class Version(object):
         prefix = reponame + '-' # Prefix to match
         if setup_dir.startswith(prefix):
             tag = setup_dir[len(prefix):]
-            # Assuming the tag is a version if it isn't empty, 'master' and has a dot in it
-            if tag not in ['', 'master'] and ('.' in tag):
+            # Assuming the tag is a version if it isn't empty, 'master' or 'main' and has a dot in it
+            if tag not in ['', 'master', 'main'] and ('.' in tag):
                 return tag
         return None
 
diff --git a/setup.py b/setup.py
index 8ae0e99..0de3590 100644
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@ extras_require = {
     # (https://github.com/pypa/pip/issues/1197)
     'tests': [
         'pytest',
-        'pytest-cov',
+        'coverage',
         'flake8',
     ],
     'doc': [
diff --git a/tests/API0/testlistselector.py b/tests/API0/testlistselector.py
index e532f85..a55d775 100644
--- a/tests/API0/testlistselector.py
+++ b/tests/API0/testlistselector.py
@@ -53,7 +53,7 @@ class TestListSelectorParameters(unittest.TestCase):
         p.g = [7]
         try:
             p.g = None
-        except TypeError:
+        except ValueError:
             pass
         else:
             raise AssertionError("Object set outside range.")
@@ -110,12 +110,12 @@ class TestListSelectorParameters(unittest.TestCase):
     ### new tests (not copied from testobjectselector)
 
     def test_bad_default(self):
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             class Q(param.Parameterized):
                 r = param.ListSelector(default=6,check_on_set=True)
 
     def test_implied_check_on_set(self):
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             class Q(param.Parameterized):
                 r = param.ListSelector(default=7,objects=[7,8])
 
@@ -135,7 +135,7 @@ class TestListSelectorParameters(unittest.TestCase):
         class Q(param.Parameterized):
             r = param.ListSelector(default=6,check_on_set=False)
 
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             Q.r = 6
     ##########################
 
diff --git a/tests/API1/testbooleanparam.py b/tests/API1/testbooleanparam.py
new file mode 100644
index 0000000..221bac1
--- /dev/null
+++ b/tests/API1/testbooleanparam.py
@@ -0,0 +1,155 @@
+"""
+Unit test for Boolean parameters.
+"""
+import datetime as dt
+
+import param
+
+from . import API1TestCase
+from .utils import check_defaults
+
+
+class TestBooleanParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestBooleanParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.Boolean()
+            f = param.Boolean(default=None)
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default is False
+        assert p.allow_None is False
+        assert p.bounds == (0, 1)
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            b = param.Boolean()
+
+        check_defaults(A.param.b, label='B')
+        self._check_defaults(A.param.b)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            b = param.Boolean()
+
+        a = A()
+
+        check_defaults(a.param.b, label='B')
+        self._check_defaults(a.param.b)
+
+    def test_defaults_unbound(self):
+        b = param.Boolean()
+
+        check_defaults(b, label=None)
+        self._check_defaults(b)
+
+    def test_default_is_None(self):
+        p = self.P()
+        assert p.f is None
+        assert p.param.f.allow_None is True
+
+        p.f = True
+        p.f = None
+        assert p.f is None
+
+    def test_raise_None_when_not_allowed(self):
+        p = self.P()
+
+        msg = r"Boolean parameter 'e' must be True or False, not None"
+        with self.assertRaisesRegex(ValueError, msg):
+                p.e = None
+
+        with self.assertRaisesRegex(ValueError, msg):
+                self.P.e = None
+
+    def test_bad_type(self):
+        msg = r"Boolean parameter 'e' must be True or False, not test"
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
+
+
+class TestEventParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestEventParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.Event()
+            f = param.Event(default=None)
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default is False
+        assert p.allow_None is False
+        assert p.bounds == (0, 1)
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            b = param.Event()
+
+        check_defaults(A.param.b, label='B')
+        self._check_defaults(A.param.b)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            b = param.Event()
+
+        a = A()
+
+        check_defaults(a.param.b, label='B')
+        self._check_defaults(a.param.b)
+
+    def test_defaults_unbound(self):
+        b = param.Event()
+
+        check_defaults(b, label=None)
+        self._check_defaults(b)
+
+    def test_resets_to_false(self):
+        p = self.P()
+        p.e = True
+        assert p.e is False
+
+    def test_default_is_None(self):
+        p = self.P()
+        assert p.f is None
+        assert p.param.f.allow_None is True
+
+        p.f = None
+        assert p.f is False
+
+    def test_raise_None_when_not_allowed(self):
+        p = self.P()
+
+        msg = r"Boolean parameter 'e' must be True or False, not None"
+        with self.assertRaisesRegex(ValueError, msg):
+                p.e = None
+
+        with self.assertRaisesRegex(ValueError, msg):
+                self.P.e = None
+
+    def test_bad_type(self):
+        msg = r"Boolean parameter 'e' must be True or False, not test"
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
diff --git a/tests/API1/testbytesparam.py b/tests/API1/testbytesparam.py
new file mode 100644
index 0000000..efbac4e
--- /dev/null
+++ b/tests/API1/testbytesparam.py
@@ -0,0 +1,108 @@
+"""
+Unit test for Bytes parameters
+"""
+import sys
+
+import pytest
+
+from . import API1TestCase
+from .utils import check_defaults
+
+import param
+
+
+ip_regex = br'^((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]?)$'
+
+class TestBytesParameters(API1TestCase):
+
+    def _check_defaults(self, p):
+        assert p.default == b''
+        assert p.allow_None is False
+        assert p.regex is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            b = param.Bytes()
+
+        check_defaults(A.param.b, label='B')
+        self._check_defaults(A.param.b)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            b = param.Bytes()
+
+        a = A()
+
+        check_defaults(a.param.b, label='B')
+        self._check_defaults(a.param.b)
+
+    def test_defaults_unbound(self):
+        b = param.Bytes()
+
+        check_defaults(b, label=None)
+        self._check_defaults(b)
+
+    def test_bytes_default_type(self):
+        if sys.version_info.major < 3:
+            pytest.skip()
+
+        with pytest.raises(ValueError):
+            class A(param.Parameterized):
+                s = param.Bytes('abc')
+
+    def test_bytes_value_type(self):
+        if sys.version_info.major < 3:
+            pytest.skip()
+
+        class A(param.Parameterized):
+            s = param.Bytes()
+
+        with pytest.raises(ValueError):
+            A(s='abc')
+        
+            
+    def test_regex_ok(self):
+        class A(param.Parameterized):
+            s = param.Bytes(b'0.0.0.0', ip_regex)
+
+        a = A()
+        a.s = b'123.123.0.1'
+
+    def test_reject_none(self):
+        class A(param.Parameterized):
+            s = param.Bytes(b'0.0.0.0', ip_regex)
+
+        a = A()
+
+        cls = 'class' if sys.version_info.major > 2 else 'type'
+        exception = "Bytes parameter 's' only takes a byte string value, not value of type <%s 'NoneType'>." % cls
+        with self.assertRaisesRegex(ValueError, exception):
+            a.s = None  # because allow_None should be False
+
+    def test_default_none(self):
+        class A(param.Parameterized):
+            s = param.Bytes(None, ip_regex)
+
+        a = A()
+        a.s = b'123.123.0.1'
+        a.s = None  # because allow_None should be True with default of None
+
+    def test_regex_incorrect(self):
+        class A(param.Parameterized):
+            s = param.Bytes(b'0.0.0.0', regex=ip_regex)
+
+        a = A()
+
+        b = 'b' if sys.version_info.major > 2 else ''
+        exception = "Bytes parameter 's' value %s'123.123.0.256' does not match regex %r."  % (b, ip_regex)
+        with self.assertRaises(ValueError) as e:
+            a.s = b'123.123.0.256'
+        self.assertEqual(str(e.exception), exception)
+
+    def test_regex_incorrect_default(self):
+        b = 'b' if sys.version_info.major > 2 else ''
+        exception = "Bytes parameter None value %s'' does not match regex %r." % (b, ip_regex)
+        with self.assertRaises(ValueError) as e:
+            class A(param.Parameterized):
+                s = param.Bytes(regex=ip_regex)  # default value '' does not match regular expression
+        self.assertEqual(str(e.exception), exception)
diff --git a/tests/API1/testcalendardateparam.py b/tests/API1/testcalendardateparam.py
index 6eb9dcb..166d2e7 100644
--- a/tests/API1/testcalendardateparam.py
+++ b/tests/API1/testcalendardateparam.py
@@ -7,10 +7,41 @@ import datetime as dt
 import pytest
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 
 class TestDateTimeParameters(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            d = param.CalendarDate()
+
+        check_defaults(A.param.d, label='D')
+        self._check_defaults(A.param.d)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            d = param.CalendarDate()
+
+        a = A()
+
+        check_defaults(a.param.d, label='D')
+        self._check_defaults(a.param.d)
+
+    def test_defaults_unbound(self):
+        d = param.CalendarDate()
+
+        check_defaults(d, label=None)
+        self._check_defaults(d)
+
     def test_initialization_out_of_bounds(self):
         try:
             class Q(param.Parameterized):
@@ -57,3 +88,8 @@ class TestDateTimeParameters(API1TestCase):
     def test_datetime_not_accepted(self):
         with pytest.raises(ValueError):
             param.CalendarDate(dt.datetime(2021, 8, 16, 10))
+
+    def test_step_invalid_type_parameter(self):
+        exception = "Step can only be None or a date type"
+        with self.assertRaisesRegex(ValueError, exception):
+            param.CalendarDate(dt.date(2017,2,27), step=3.2)
diff --git a/tests/API1/testcalendardaterangeparam.py b/tests/API1/testcalendardaterangeparam.py
index 01984ee..3bb005e 100644
--- a/tests/API1/testcalendardaterangeparam.py
+++ b/tests/API1/testcalendardaterangeparam.py
@@ -11,6 +11,34 @@ from . import API1TestCase
 
 class TestDateTimeRange(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.length == 2
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            r = param.CalendarDateRange()
+        
+        self._check_defaults(P.param.r)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            r = param.CalendarDateRange()
+
+        p = P()
+
+        self._check_defaults(p.param.r)
+
+    def test_defaults_unbound(self):
+        r = param.CalendarDateRange()
+
+        self._check_defaults(r)
+
     bad_range = (dt.date(2017,2,27),dt.date(2017,2,26))
 
     def test_wrong_type_default(self):
diff --git a/tests/API1/testclassselector.py b/tests/API1/testclassselector.py
index 69382df..e477f86 100644
--- a/tests/API1/testclassselector.py
+++ b/tests/API1/testclassselector.py
@@ -7,6 +7,7 @@ from numbers import Number
 
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 class TestClassSelectorParameters(API1TestCase):
 
@@ -20,6 +21,37 @@ class TestClassSelectorParameters(API1TestCase):
 
         self.P = P
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.instantiate is True
+        assert p.is_instance is True
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.ClassSelector(int)
+
+        check_defaults(P.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(P.param.s)
+        assert P.param.s.class_ is int
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.ClassSelector(int)
+
+        p = P()
+
+        check_defaults(p.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(p.param.s)
+        assert p.param.s.class_ is int
+
+    def test_defaults_unbound(self):
+        s = param.ClassSelector(int)
+
+        check_defaults(s, label=None, skip=['instantiate'])
+        self._check_defaults(s)
+        assert s.class_ is int
+
     def test_single_class_instance_constructor(self):
         p = self.P(e=6)
         self.assertEqual(p.e, 6)
@@ -73,6 +105,35 @@ class TestClassSelectorParameters(API1TestCase):
 
 class TestDictParameters(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.instantiate is True
+        assert p.is_instance is True
+        assert p.class_ == dict
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.Dict()
+
+        check_defaults(P.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.Dict()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.Dict()
+
+        check_defaults(s, label=None, skip=['instantiate'])
+        self._check_defaults(s)
+
     def test_valid_dict_parameter(self):
         valid_dict = {1:2, 3:3}
 
diff --git a/tests/API1/testcolorparameter.py b/tests/API1/testcolorparameter.py
index 233694c..e47868c 100644
--- a/tests/API1/testcolorparameter.py
+++ b/tests/API1/testcolorparameter.py
@@ -3,9 +3,37 @@ Unit test for Color parameters.
 """
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 class TestColorParameters(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.allow_named is True
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            c = param.Color()
+
+        check_defaults(A.param.c, label='C')
+        self._check_defaults(A.param.c)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            c = param.Color()
+
+        a = A()
+
+        check_defaults(a.param.c, label='C')
+        self._check_defaults(a.param.c)
+
+    def test_defaults_unbound(self):
+        c = param.Color()
+
+        check_defaults(c, label=None)
+        self._check_defaults(c)
+
     def test_initialization_invalid_string(self):
         try:
             class Q(param.Parameterized):
@@ -62,3 +90,9 @@ class TestColorParameters(API1TestCase):
             q = param.Color(allow_named=True)
         Q.q = 'indianred'
         self.assertEqual(Q.q, 'indianred')
+
+    def test_valid_named_color_mixed_case(self):
+        class Q(param.Parameterized):
+            q = param.Color(allow_named=True)
+        Q.q = 'WhiteSmoke'
+        self.assertEqual(Q.q, 'WhiteSmoke')
diff --git a/tests/API1/testcompositeparams.py b/tests/API1/testcompositeparams.py
index 5cf3aac..0e8662d 100644
--- a/tests/API1/testcompositeparams.py
+++ b/tests/API1/testcompositeparams.py
@@ -7,6 +7,7 @@ testCompositeParameter.txt
 
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 class TestCompositeParameters(API1TestCase):
 
@@ -33,13 +34,42 @@ class TestCompositeParameters(API1TestCase):
 
         self.SomeSequence = SomeSequence
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.attribs == []
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            c = param.Composite()
+
+        check_defaults(P.param.c, label='C')
+        self._check_defaults(P.param.c)
+        assert P.param.c.objtype is P
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            c = param.Composite()
+
+        p = P()
+
+        check_defaults(p.param.c, label='C')
+        self._check_defaults(p.param.c)
+        assert p.param.c.objtype is P
+
+    def test_defaults_unbound(self):
+        c = param.Composite()
+
+        check_defaults(c, label=None)
+        self._check_defaults(c)
+        assert not hasattr(c, 'objtype')
+
     def test_initialization(self):
         "Make an instance and do default checks"
         self.assertEqual(self.a.x, 0)
         self.assertEqual(self.a.y, 0)
         self.assertEqual(self.a.xy, [0,0])
 
-
     def test_set_component(self):
         self.a.x = 1
         self.assertEqual(self.a.xy, [1,0])
diff --git a/tests/API1/testdateparam.py b/tests/API1/testdateparam.py
index a060a4b..dc81e71 100644
--- a/tests/API1/testdateparam.py
+++ b/tests/API1/testdateparam.py
@@ -1,14 +1,45 @@
 """
 Unit test for Date parameters.
 """
-
-
+import sys
+import json
 import datetime as dt
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 class TestDateParameters(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            d = param.Date()
+
+        check_defaults(A.param.d, label='D')
+        self._check_defaults(A.param.d)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            d = param.Date()
+
+        a = A()
+
+        check_defaults(a.param.d, label='D')
+        self._check_defaults(a.param.d)
+
+    def test_defaults_unbound(self):
+        d = param.Date()
+
+        check_defaults(d, label=None)
+        self._check_defaults(d)
+
     def test_initialization_out_of_bounds(self):
         try:
             class Q(param.Parameterized):
@@ -52,3 +83,25 @@ class TestDateParameters(API1TestCase):
         self.assertEqual(q.get_soft_bounds(), (dt.datetime(2017,2,1),
                                                dt.datetime(2017,2,25)))
 
+    def test_step_invalid_type_datetime_parameter(self):
+        exception = "Step can only be None, a datetime or datetime type"
+        with self.assertRaisesRegex(ValueError, exception):
+            param.Date(dt.datetime(2017,2,27), step=3.2)
+
+
+def test_date_serialization():
+    class User(param.Parameterized):
+        A = param.Date(default=None)
+
+    # Validate round is possible
+    User.param.deserialize_parameters(User.param.serialize_parameters())
+
+    if sys.version_info.major == 2:
+        return
+
+    serialized_data = '{"name": "User", "A": null}'
+    deserialized_data = {"name": "User", "A": None}
+
+    assert serialized_data == json.dumps(deserialized_data)
+    assert serialized_data == User.param.serialize_parameters()
+    assert deserialized_data == User.param.deserialize_parameters(serialized_data)
diff --git a/tests/API1/testdaterangeparam.py b/tests/API1/testdaterangeparam.py
index 01a805f..94a56bd 100644
--- a/tests/API1/testdaterangeparam.py
+++ b/tests/API1/testdaterangeparam.py
@@ -8,6 +8,7 @@ import param
 import pytest
 
 from . import API1TestCase
+from .utils import check_defaults
 
 try:
     import numpy as np
@@ -19,6 +20,34 @@ except:
 
 class TestDateRange(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.length == 2
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            r = param.DateRange()
+        
+        self._check_defaults(P.param.r)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            r = param.DateRange()
+
+        p = P()
+
+        self._check_defaults(p.param.r)
+
+    def test_defaults_unbound(self):
+        r = param.DateRange()
+
+        self._check_defaults(r)
+
     bad_range = (dt.datetime(2017,2,27),dt.datetime(2017,2,26))
 
     def test_wrong_type_default(self):
diff --git a/tests/API1/testdefaults.py b/tests/API1/testdefaults.py
index 7c62256..2fff701 100644
--- a/tests/API1/testdefaults.py
+++ b/tests/API1/testdefaults.py
@@ -3,6 +3,8 @@ Do all subclasses of Parameter supply a valid default?
 """
 import pytest
 
+import param
+
 from param.parameterized import add_metaclass
 from param import concrete_descendents, Parameter
 
@@ -10,6 +12,7 @@ from param import concrete_descendents, Parameter
 from param import * # noqa
 from param import ClassSelector
 from . import API1TestCase
+from .utils import check_defaults
 
 positional_args = {
 #    ClassSelector: (object,)
@@ -50,3 +53,80 @@ class DefaultsMetaclassTest(type):
 @add_metaclass(DefaultsMetaclassTest)
 class TestDefaults(API1TestCase):
     pass
+
+
+def test_defaults_parameter_inst():
+    class A(param.Parameterized):
+        s = param.Parameter()
+    
+    a = A()
+
+    check_defaults(a.param.s, label='S')
+    assert a.param.s.default is None
+    assert a.param.s.allow_None is True
+
+def test_defaults_parameter_class():
+    class A(param.Parameterized):
+        s = param.Parameter()
+    
+    check_defaults(A.param.s, label='S')
+    assert A.param.s.default is None
+    assert A.param.s.allow_None is True
+
+def test_defaults_parameter_unbound():
+    s = param.Parameter()
+
+    check_defaults(s, label=None)
+    assert s.default is None
+    assert s.allow_None is True
+
+def test_defaults_parameter_inst_allow_None():
+    class A(param.Parameterized):
+        s1 = param.Parameter(default='not None')
+        s2 = param.Parameter(default='not None', allow_None=False)
+        s3 = param.Parameter(default='not None', allow_None=True)
+        s4 = param.Parameter(default=None)
+        s5 = param.Parameter(default=None, allow_None=False)
+        s6 = param.Parameter(default=None, allow_None=True)
+    
+    a = A()
+
+    assert a.param.s1.allow_None is False
+    assert a.param.s2.allow_None is False
+    assert a.param.s3.allow_None is True
+    assert a.param.s4.allow_None is True
+    assert a.param.s5.allow_None is True
+    assert a.param.s6.allow_None is True
+
+
+def test_defaults_parameter_class_allow_None():
+    class A(param.Parameterized):
+        s1 = param.Parameter(default='not None')
+        s2 = param.Parameter(default='not None', allow_None=False)
+        s3 = param.Parameter(default='not None', allow_None=True)
+        s4 = param.Parameter(default=None)
+        s5 = param.Parameter(default=None, allow_None=False)
+        s6 = param.Parameter(default=None, allow_None=True)
+    
+    assert A.param.s1.allow_None is False
+    assert A.param.s2.allow_None is False
+    assert A.param.s3.allow_None is True
+    assert A.param.s4.allow_None is True
+    assert A.param.s5.allow_None is True
+    assert A.param.s6.allow_None is True
+
+
+def test_defaults_parameter_unbound_allow_None():
+    s1 = param.Parameter(default='not None')
+    s2 = param.Parameter(default='not None', allow_None=False)
+    s3 = param.Parameter(default='not None', allow_None=True)
+    s4 = param.Parameter(default=None)
+    s5 = param.Parameter(default=None, allow_None=False)
+    s6 = param.Parameter(default=None, allow_None=True)
+
+    assert s1.allow_None is False
+    assert s2.allow_None is False
+    assert s3.allow_None is True
+    assert s4.allow_None is True
+    assert s5.allow_None is True
+    assert s6.allow_None is True
diff --git a/tests/API1/testfileselector.py b/tests/API1/testfileselector.py
new file mode 100644
index 0000000..792911e
--- /dev/null
+++ b/tests/API1/testfileselector.py
@@ -0,0 +1,138 @@
+import os
+import shutil
+import tempfile
+
+import param
+
+from . import API1TestCase
+from .utils import check_defaults
+
+
+class TestFileSelectorParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestFileSelectorParameters, self).setUp()
+
+        tmpdir1 = tempfile.mkdtemp()
+        fa = os.path.join(tmpdir1, 'a.txt')
+        fb = os.path.join(tmpdir1, 'b.txt')
+        glob1 = os.path.join(tmpdir1, '*')
+        open(fa, 'w').close()
+        open(fb, 'w').close()
+        tmpdir2 = tempfile.mkdtemp()
+        fc = os.path.join(tmpdir2, 'c.txt')
+        fd = os.path.join(tmpdir2, 'd.txt')
+        glob2 = os.path.join(tmpdir2, '*')
+        open(fc, 'w').close()
+        open(fd, 'w').close()
+
+        self.tmpdir1 = tmpdir1
+        self.tmpdir2 = tmpdir2
+        self.fa = fa
+        self.fb = fb
+        self.fc = fc
+        self.fd = fd
+        self.glob1 = glob1
+        self.glob2 = glob2
+
+        class P(param.Parameterized):
+            a = param.FileSelector(path=glob1)
+            b = param.FileSelector(default=fa, path=glob1)
+
+        self.P = P
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir1)
+        shutil.rmtree(self.tmpdir2)
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is None
+        assert p.objects == []
+        assert p.compute_default_fn is None
+        assert p.check_on_set is False
+        assert p.names is None
+        assert p.path == ""
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.FileSelector()
+
+        check_defaults(P.param.s, label='S')
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.FileSelector()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S')
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.FileSelector()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
+    def test_default_is_None(self):
+        p = self.P()
+        assert p.a is None
+        assert p.param.a.default is None
+
+    def test_default_is_honored(self):
+        p = self.P()
+        assert p.b == self.fa
+        assert p.param.b.default in [self.fa, self.fb]
+
+    def test_allow_default_None(self):
+        class P(param.Parameterized):
+            a = param.FileSelector(default=None)
+
+    def test_default_not_in_glob(self):
+        with self.assertRaises(ValueError):
+            class P(param.Parameterized):
+                a = param.FileSelector(default='not/in/glob', path=self.glob1)
+
+    def test_objects_auto_set(self):
+        p = self.P()
+        assert p.param.a.objects == [self.fa, self.fb]
+
+    def test_set_object_constructor(self):
+        p = self.P(a=self.fb)
+        assert p.a == self.fb
+
+    def test_set_object_outside_bounds(self):
+        p = self.P()
+        with self.assertRaises(ValueError):
+            p.a = '/not/in/glob'
+
+    def test_set_path_and_update(self):
+        p = self.P()
+        p.param.b.path = self.glob2
+        p.param.b.update()
+        assert p.param.b.objects == [self.fc, self.fd]
+        assert p.param.b.default in [self.fc, self.fd]
+        # Default updated but not the value itself
+        assert p.b == self.fa
+
+    def test_get_range(self):
+        p = self.P()
+        r = p.param.a.get_range()
+        assert r['a.txt'] == self.fa
+        assert r['b.txt'] == self.fb
+        p.param.a.path = self.glob2
+        p.param.a.update()
+        r = p.param.a.get_range()
+        assert r['c.txt'] == self.fc
+        assert r['d.txt'] == self.fd
+
+    def test_update_file_removed(self):
+        p = self.P()
+        assert p.param.b.objects == [self.fa, self.fb]
+        assert p.param.b.default in [self.fa, self.fb]
+        os.remove(self.fa)
+        p.param.b.update()
+        assert p.param.b.objects == [self.fb]
+        assert p.param.b.default == self.fb
diff --git a/tests/API1/testlist.py b/tests/API1/testlist.py
index 17dcab7..3692b72 100644
--- a/tests/API1/testlist.py
+++ b/tests/API1/testlist.py
@@ -1,5 +1,6 @@
 import param
 from . import API1TestCase
+from .utils import check_defaults
 # TODO: I copied the tests from testobjectselector, although I
 # struggled to understand some of them. Both files should be reviewed
 # and cleaned up together.
@@ -17,6 +18,36 @@ class TestListParameters(API1TestCase):
 
         self.P = P
 
+    def _check_defaults(self, p):
+        assert p.default == []
+        assert p.allow_None is False
+        assert p.class_ is None
+        assert p.item_type is None
+        assert p.bounds == (0, None)
+        assert p.instantiate is True
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            l = param.List()
+
+        check_defaults(P.param.l, label='L', skip=['instantiate'])
+        self._check_defaults(P.param.l)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            l = param.List()
+
+        p = P()
+
+        check_defaults(p.param.l, label='L', skip=['instantiate'])
+        self._check_defaults(p.param.l)
+
+    def test_defaults_unbound(self):
+        l = param.List()
+
+        check_defaults(l, label=None, skip=['instantiate'])
+        self._check_defaults(l)
+
     def test_default_None(self):
         class Q(param.Parameterized):
             r = param.List(default=[])  #  Also check None)
@@ -51,3 +82,79 @@ class TestListParameters(API1TestCase):
             pass
         else:
             raise AssertionError("Object set outside range.")
+
+
+class TestHookListParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestHookListParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.HookList([abs])
+            l = param.HookList(bounds=(0,10))
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default == []
+        assert p.allow_None is False
+        assert p.class_ is None
+        assert p.item_type is None
+        assert p.bounds == (0, None)
+        assert p.instantiate is True
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            l = param.HookList()
+
+        check_defaults(P.param.l, label='L', skip=['instantiate'])
+        self._check_defaults(P.param.l)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            l = param.HookList()
+
+        p = P()
+
+        check_defaults(p.param.l, label='L', skip=['instantiate'])
+        self._check_defaults(p.param.l)
+
+    def test_defaults_unbound(self):
+        l = param.HookList()
+
+        check_defaults(l, label=None, skip=['instantiate'])
+        self._check_defaults(l)
+
+    def test_default_None(self):
+        class Q(param.Parameterized):
+            r = param.HookList(default=[])  #  Also check None)
+
+    def test_set_object_constructor(self):
+        p = self.P(e=[abs])
+        self.assertEqual(p.e, [abs])
+
+    def test_set_object_outside_bounds(self):
+        p = self.P()
+        try:
+            p.l = [abs]*11
+        except ValueError:
+            pass
+        else:
+            raise AssertionError("Object set outside range.")
+
+    def test_set_object_wrong_type_foo(self):
+        p = self.P()
+        try:
+            p.e = ['s']
+        except ValueError:
+            pass
+        else:
+            raise AssertionError("Object allowed of wrong type.")
+
+    def test_set_object_not_None(self):
+        p = self.P()
+        try:
+            p.e = None
+        except ValueError:
+            pass
+        else:
+            raise AssertionError("Object set outside range.")
diff --git a/tests/API1/testlistselector.py b/tests/API1/testlistselector.py
index 37b8317..cb382ed 100644
--- a/tests/API1/testlistselector.py
+++ b/tests/API1/testlistselector.py
@@ -1,5 +1,6 @@
 import param
 from . import API1TestCase
+from .utils import check_defaults
 # TODO: I copied the tests from testobjectselector, although I
 # struggled to understand some of them. Both files should be reviewed
 # and cleaned up together.
@@ -20,6 +21,36 @@ class TestListSelectorParameters(API1TestCase):
 
         self.P = P
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is None
+        assert p.objects == []
+        assert p.compute_default_fn is None
+        assert p.check_on_set is False
+        assert p.names is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.ListSelector()
+
+        check_defaults(P.param.s, label='S')
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.ListSelector()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S')
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.ListSelector()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
     def test_default_None(self):
         class Q(param.Parameterized):
             r = param.ListSelector(default=None)
@@ -28,6 +59,15 @@ class TestListSelectorParameters(API1TestCase):
         p = self.P(e=[6])
         self.assertEqual(p.e, [6])
 
+    def test_allow_None_is_None(self):
+        p = self.P()
+        assert p.param.e.allow_None is None
+        assert p.param.f.allow_None is None
+        assert p.param.g.allow_None is None
+        assert p.param.h.allow_None is None
+        assert p.param.i.allow_None is None
+
+
     def test_set_object_outside_bounds(self):
         p = self.P(e=[6])
         try:
@@ -52,7 +92,7 @@ class TestListSelectorParameters(API1TestCase):
         p.g = [7]
         try:
             p.g = None
-        except TypeError:
+        except ValueError:
             pass
         else:
             raise AssertionError("Object set outside range.")
@@ -109,12 +149,12 @@ class TestListSelectorParameters(API1TestCase):
     ### new tests (not copied from testobjectselector)
 
     def test_bad_default(self):
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             class Q(param.Parameterized):
                 r = param.ListSelector(default=6,check_on_set=True)
 
     def test_implied_check_on_set(self):
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             class Q(param.Parameterized):
                 r = param.ListSelector(default=7,objects=[7,8])
 
@@ -134,7 +174,7 @@ class TestListSelectorParameters(API1TestCase):
         class Q(param.Parameterized):
             r = param.ListSelector(default=6,check_on_set=False)
 
-        with self.assertRaises(TypeError):
+        with self.assertRaises(ValueError):
             Q.r = 6
     ##########################
 
@@ -154,3 +194,16 @@ class TestListSelectorParameters(API1TestCase):
 
         with self.assertRaises(TypeError):
             Q.param.params('r').compute_default()
+
+    def test_initialization_bad_iterable(self):
+        with self.assertRaises(ValueError):
+            class Q(param.Parameterized):
+                j = param.ListSelector('ab', ['a', 'b', 'c', 'd'])
+
+    def test_set_bad_iterable(self):
+        class Q(param.Parameterized):
+            r = param.ListSelector(objects=['a', 'b', 'c', 'd'])
+
+        q = Q()
+        with self.assertRaises(ValueError):
+            q.r = 'ab'
diff --git a/tests/API1/testmultifileselector.py b/tests/API1/testmultifileselector.py
new file mode 100644
index 0000000..59e482d
--- /dev/null
+++ b/tests/API1/testmultifileselector.py
@@ -0,0 +1,142 @@
+import os
+import shutil
+import tempfile
+
+import param
+
+from . import API1TestCase
+from .utils import check_defaults
+
+
+class TestMultiFileSelectorParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestMultiFileSelectorParameters, self).setUp()
+
+        tmpdir1 = tempfile.mkdtemp()
+        fa = os.path.join(tmpdir1, 'a.txt')
+        fb = os.path.join(tmpdir1, 'b.txt')
+        glob1 = os.path.join(tmpdir1, '*')
+        open(fa, 'w').close()
+        open(fb, 'w').close()
+        tmpdir2 = tempfile.mkdtemp()
+        fc = os.path.join(tmpdir2, 'c.txt')
+        fd = os.path.join(tmpdir2, 'd.txt')
+        glob2 = os.path.join(tmpdir2, '*')
+        open(fc, 'w').close()
+        open(fd, 'w').close()
+
+        self.tmpdir1 = tmpdir1
+        self.tmpdir2 = tmpdir2
+        self.fa = fa
+        self.fb = fb
+        self.fc = fc
+        self.fd = fd
+        self.glob1 = glob1
+        self.glob2 = glob2
+
+        class P(param.Parameterized):
+            a = param.MultiFileSelector(path=glob1)
+            b = param.MultiFileSelector(default=[fa], path=glob1)
+
+        self.P = P
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir1)
+        shutil.rmtree(self.tmpdir2)
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is None
+        assert p.objects == []
+        assert p.compute_default_fn is None
+        assert p.check_on_set is False
+        assert p.names is None
+        assert p.path == ''
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.MultiFileSelector()
+
+        check_defaults(P.param.s, label='S')
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.MultiFileSelector()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S')
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.MultiFileSelector()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
+    def test_default_is_None(self):
+        p = self.P()
+        assert p.a is None
+        assert p.param.a.default is None
+
+    def test_default_is_honored(self):
+        p = self.P()
+        assert p.b == [self.fa]
+        assert p.param.b.default ==[self.fa]
+
+    def test_allow_default_None(self):
+        class P(param.Parameterized):
+            a = param.MultiFileSelector(default=None)
+
+    def test_objects_auto_set(self):
+        p = self.P()
+        assert p.param.a.objects == [self.fa, self.fb]
+
+    def test_default_not_in_glob(self):
+        with self.assertRaises(ValueError):
+            class P(param.Parameterized):
+                a = param.MultiFileSelector(default=['not/in/glob'], path=self.glob1)
+
+    def test_objects_auto_set(self):
+        p = self.P()
+        assert sorted(p.param.a.objects) == sorted([self.fa, self.fb])
+
+    def test_set_object_constructor(self):
+        p = self.P(a=[self.fb])
+        assert p.a == [self.fb]
+
+    def test_set_object_outside_bounds(self):
+        p = self.P()
+        with self.assertRaises(ValueError):
+            p.a = ['/not/in/glob']
+
+    def test_set_path_and_update(self):
+        p = self.P()
+        p.param.b.path = self.glob2
+        p.param.b.update()
+        assert sorted(p.param.b.objects) == sorted([self.fc, self.fd])
+        assert sorted(p.param.b.default) == sorted([self.fc, self.fd])
+        # Default updated but not the value itself
+        assert p.b == [self.fa]
+
+    def test_get_range(self):
+        p = self.P()
+        r = p.param.a.get_range()
+        assert r['a.txt'] == self.fa
+        assert r['b.txt'] == self.fb
+        p.param.a.path = self.glob2
+        p.param.a.update()
+        r = p.param.a.get_range()
+        assert r['c.txt'] == self.fc
+        assert r['d.txt'] == self.fd
+
+    def test_update_file_removed(self):
+        p = self.P()
+        assert p.param.b.objects == [self.fa, self.fb]
+        assert p.param.b.default == [self.fa]
+        os.remove(self.fa)
+        p.param.b.update()
+        assert p.param.b.objects == [self.fb]
+        assert p.param.b.default == [self.fb]
diff --git a/tests/API1/testnumberparameter.py b/tests/API1/testnumberparameter.py
index 62a28fd..85f02c2 100644
--- a/tests/API1/testnumberparameter.py
+++ b/tests/API1/testnumberparameter.py
@@ -2,54 +2,575 @@
 Unit test for Number parameters and their subclasses.
 """
 import param
-import datetime as dt
 from . import API1TestCase
+from .utils import check_defaults
 
 
 class TestNumberParameters(API1TestCase):
 
-    def test_initialization_without_step_class(self):
-        class Q(param.Parameterized):
-            q = param.Number(default=1)
+    def setUp(self):
+        super(TestNumberParameters, self).setUp()
+        class P(param.Parameterized):
+            b = param.Number(allow_None=False)
+            c = param.Number(default=1, allow_None=True)
+            d = param.Number(default=None)
+            e = param.Number(default=1)
+            f = param.Number(default=1, step=0.5)
+            g = param.Number(default=lambda: 1)
+            h = param.Number(default=1, bounds=(0, 2))
+            i = param.Number(bounds=(-1, 1))
+            j = param.Number(bounds=(-1, 1), inclusive_bounds=(False, True))
+            k = param.Number(bounds=(-1, 1), inclusive_bounds=(True, False))
+            l = param.Number(bounds=(-1, 1), inclusive_bounds=(False, False))
+            m = param.Number(bounds=(-1, None))
+            n = param.Number(bounds=(None, 1))
 
-        self.assertEqual(Q.param['q'].step, None)
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default == 0.0
+        assert p.allow_None is False
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            n = param.Number()
+
+        check_defaults(A.param.n, label='N')
+        self._check_defaults(A.param.n)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            n = param.Number()
+
+        a = A()
+
+        check_defaults(a.param.n, label='N')
+        self._check_defaults(a.param.n)
+
+    def test_defaults_unbound(self):
+        n = param.Number()
+
+        check_defaults(n, label=None)
+        self._check_defaults(n)
+
+    def test_allow_None_class(self):
+        self.P.c = None
+        assert self.P.c is None
+        self.P.d = None
+        assert self.P.d is None
+
+        exception = "Parameter 'b' only takes numeric values, not type <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.b = None
+
+    def test_allow_None_inst(self):
+        p = self.P()
+        p.c = None
+        assert p.c is None
+        p.d = None
+        assert p.d is None
+
+        exception = "Parameter 'b' only takes numeric values, not type <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.b = None
+
+    def test_initialization_without_step_class(self):
+        self.assertEqual(self.P.param['e'].step, None)
 
     def test_initialization_with_step_class(self):
+        self.assertEqual(self.P.param['f'].step, 0.5)
+
+    def test_initialization_without_step_instance(self):
+        p = self.P()
+        self.assertEqual(p.param['e'].step, None)
+
+    def test_initialization_with_step_instance(self):
+        p = self.P()
+        self.assertEqual(p.param['f'].step, 0.5)
+
+    def test_step_invalid_type_number_parameter(self):
+        exception = "Step can only be None or a numeric value"
+        with self.assertRaisesRegex(ValueError, exception):
+            param.Number(step='invalid value')
+
+    def test_outside_bounds(self):
+        exception = "Parameter 'h' must be at most 2, not 10."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.h = 10
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            p.h = 10
+
+    def test_unbounded_side_class(self):
+        self.P.m = 10
+        assert self.P.m == 10
+
+        exception = "Parameter 'm' must be at least -1, not -10."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.m = -10
+
+        self.P.n = -10
+        assert self.P.n == -10
+
+        exception = "Parameter 'n' must be at most 1, not 10."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.n = 10
+
+    def test_unbounded_side_inst(self):
+        p = self.P()
+
+        p.m = 10
+        assert p.m == 10
+
+        exception = "Parameter 'm' must be at least -1, not -10."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.m = -10
+
+        p.n = -10
+        assert p.n == -10
+
+        exception = "Parameter 'n' must be at most 1, not 10."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.n = 10
+
+    def test_inclusive_bounds_no_error_class(self):
+        self.P.i = -1
+        assert self.P.i == -1
+        self.P.i = 1
+        assert self.P.i == 1
+
+        self.P.j = 1
+        assert self.P.j == 1
+
+        self.P.k = -1
+        assert self.P.k == -1
+
+    def test_inclusive_bounds_no_error_inst(self):
+        p = self.P()
+        p.i = -1
+        assert p.i == -1
+        p.i = 1
+        assert p.i == 1
+
+        p.j = 1
+        assert p.j == 1
+
+        p.k = -1
+        assert p.k == -1
+
+    def test_inclusive_bounds_error_on_bounds(self):
+        p = self.P()
+        exception = "Parameter 'j' must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.j = -1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.j = -1
+
+        exception = "Parameter 'k' must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.k = 1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.k = 1
+
+        exception = "Parameter 'l' must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.l = -1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.l = -1
+        exception = "Parameter 'l' must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.l = 1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.l = 1
+
+    def test_inclusive_bounds_error_on_bounds_post(self):
+        exception = "Parameter None must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Number(default=-1, bounds=(-1, 1), inclusive_bounds=(False, True))
+
+        exception = "Parameter None must be less than 1, not 1"
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Number(default=1, bounds=(-1, 1), inclusive_bounds=(True, False))
+
+        exception = "Parameter None must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Number(default=-1, bounds=(-1, 1), inclusive_bounds=(False, False))
+
+        exception = "Parameter None must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Number(default=1, bounds=(-1, 1), inclusive_bounds=(False, False))
+
+    def test_invalid_default_for_bounds(self):
+        exception = "Parameter None must be at least 10, not 0.0."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                n = param.Number(bounds=(10, 20))
+
+    def test_callable(self):
+        assert self.P.g == 1
+        p = self.P()
+        assert p.g == 1
+
+    def test_callable_wrong_type(self):
         class Q(param.Parameterized):
-            q = param.Number(default=1, step=0.5)
+            q = param.Number(default=lambda: 'test')
 
-        self.assertEqual(Q.param['q'].step, 0.5)
+        exception = "Parameter 'q' only takes numeric values, not type <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            Q.q
 
-    def test_initialization_without_step_instance(self):
+        q = Q()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            q.q
+
+    def test_callable_outside_bounds(self):
         class Q(param.Parameterized):
-            q = param.Number(default=1)
+            q = param.Number(default=lambda: 2, bounds=(0, 1))
 
-        qobj = Q()
-        self.assertEqual(qobj.param['q'].step, None)
+        exception = "Parameter 'q' must be at most 1, not 2."
+        with self.assertRaisesRegex(ValueError, exception):
+            Q.q
 
-    def test_initialization_with_step_instance(self):
+        q = Q()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            q.q
+
+    def test_crop_to_bounds(self):
+        p = self.P()
+
+        # when allow_None is True
+        assert p.param.d.crop_to_bounds(None) is None
+
+        # no bounds
+        assert p.param.e.crop_to_bounds(10000) == 10000
+
+        # with concrete bounds
+        assert p.param.h.crop_to_bounds(10) == 2
+        assert p.param.h.crop_to_bounds(-10) == 0
+
+        # return default if non numerical
+        assert p.param.e.crop_to_bounds('test') == 1
+
+        # Unbound
+        assert p.param.m.crop_to_bounds(10) == 10
+        assert p.param.n.crop_to_bounds(-10) == -10
+
+
+class TestIntegerParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestIntegerParameters, self).setUp()
+        class P(param.Parameterized):
+            b = param.Integer(allow_None=False)
+            c = param.Integer(default=1, allow_None=True)
+            d = param.Integer(default=None)
+            e = param.Integer(default=1)
+            f = param.Integer(default=1, step=1)
+            g = param.Integer(default=lambda: 1)
+            h = param.Integer(default=1, bounds=(0, 2))
+            i = param.Integer(bounds=(-1, 1))
+            j = param.Integer(bounds=(-1, 1), inclusive_bounds=(False, True))
+            k = param.Integer(bounds=(-1, 1), inclusive_bounds=(True, False))
+            l = param.Integer(bounds=(-1, 1), inclusive_bounds=(False, False))
+            m = param.Integer(bounds=(-1, None))
+            n = param.Integer(bounds=(None, 1))
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert isinstance(p.default, int)
+        assert p.default == 0
+        assert p.allow_None is False
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            n = param.Integer()
+
+        check_defaults(A.param.n, label='N')
+        self._check_defaults(A.param.n)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            n = param.Integer()
+
+        a = A()
+
+        check_defaults(a.param.n, label='N')
+        self._check_defaults(a.param.n)
+
+    def test_defaults_unbound(self):
+        n = param.Integer()
+
+        check_defaults(n, label=None)
+        self._check_defaults(n)
+
+    def test_allow_None_class(self):
+        self.P.c = None
+        assert self.P.c is None
+        self.P.d = None
+        assert self.P.d is None
+
+        exception = "Integer parameter 'b' must be an integer, not type <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.b = None
+
+    def test_allow_None_inst(self):
+        p = self.P()
+        p.c = None
+        assert p.c is None
+        p.d = None
+        assert p.d is None
+
+        exception = "Integer parameter 'b' must be an integer, not type <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.b = None
+
+    def test_initialization_without_step_class(self):
         class Q(param.Parameterized):
-            q = param.Number(default=1, step=0.5)
+            q = param.Integer(default=1)
 
-        qobj = Q()
-        self.assertEqual(qobj.param['q'].step, 0.5)
+        self.assertEqual(Q.param['q'].step, None)
+
+
+    def test_initialization_without_step_class(self):
+        self.assertEqual(self.P.param['e'].step, None)
+
+    def test_initialization_with_step_class(self):
+        self.assertEqual(self.P.param['f'].step, 1)
+
+    def test_initialization_without_step_instance(self):
+        p = self.P()
+        self.assertEqual(p.param['e'].step, None)
+
+    def test_initialization_with_step_instance(self):
+        p = self.P()
+        self.assertEqual(p.param['f'].step, 1)
 
     def test_step_invalid_type_number_parameter(self):
-        exception = "Step can only be None or a numeric value"
+        exception = "Step can only be None or an integer value"
         with self.assertRaisesRegex(ValueError, exception):
-            param.Number(step='invalid value')
+            param.Integer(step='invalid value')
 
     def test_step_invalid_type_integer_parameter(self):
         exception = "Step can only be None or an integer value"
         with self.assertRaisesRegex(ValueError, exception):
             param.Integer(step=3.4)
 
-    def test_step_invalid_type_datetime_parameter(self):
-        exception = "Step can only be None, a datetime or datetime type"
+    def test_outside_bounds(self):
+        exception = "Parameter 'h' must be at most 2, not 10."
         with self.assertRaisesRegex(ValueError, exception):
-            param.Date(dt.datetime(2017,2,27), step=3.2)
+            self.P.h = 10
 
-    def test_step_invalid_type_date_parameter(self):
-        exception = "Step can only be None or a date type"
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            p.h = 10
+
+    def test_unbounded_side_class(self):
+        self.P.m = 10
+        assert self.P.m == 10
+
+        exception = "Parameter 'm' must be at least -1, not -10."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.m = -10
+
+        self.P.n = -10
+        assert self.P.n == -10
+
+        exception = "Parameter 'n' must be at most 1, not 10."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.n = 10
+
+    def test_unbounded_side_inst(self):
+        p = self.P()
+
+        p.m = 10
+        assert p.m == 10
+
+        exception = "Parameter 'm' must be at least -1, not -10."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.m = -10
+
+        p.n = -10
+        assert p.n == -10
+
+        exception = "Parameter 'n' must be at most 1, not 10."
+        with self.assertRaisesRegex(ValueError, exception):
+            p.n = 10
+
+    def test_inclusive_bounds_no_error_class(self):
+        self.P.i = -1
+        assert self.P.i == -1
+        self.P.i = 1
+        assert self.P.i == 1
+
+        self.P.j = 1
+        assert self.P.j == 1
+
+        self.P.k = -1
+        assert self.P.k == -1
+
+    def test_inclusive_bounds_no_error_inst(self):
+        p = self.P()
+        p.i = -1
+        assert p.i == -1
+        p.i = 1
+        assert p.i == 1
+
+        p.j = 1
+        assert p.j == 1
+
+        p.k = -1
+        assert p.k == -1
+
+    def test_inclusive_bounds_error_on_bounds(self):
+        p = self.P()
+        exception = "Parameter 'j' must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.j = -1
         with self.assertRaisesRegex(ValueError, exception):
-            param.CalendarDate(dt.date(2017,2,27), step=3.2)
+            p.j = -1
+
+        exception = "Parameter 'k' must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.k = 1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.k = 1
+
+        exception = "Parameter 'l' must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.l = -1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.l = -1
+        exception = "Parameter 'l' must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            self.P.l = 1
+        with self.assertRaisesRegex(ValueError, exception):
+            p.l = 1
+
+    def test_inclusive_bounds_error_on_bounds_post(self):
+        exception = "Parameter None must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Integer(default=-1, bounds=(-1, 1), inclusive_bounds=(False, True))
+
+        exception = "Parameter None must be less than 1, not 1"
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Integer(default=1, bounds=(-1, 1), inclusive_bounds=(True, False))
+
+        exception = "Parameter None must be greater than -1, not -1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Integer(default=-1, bounds=(-1, 1), inclusive_bounds=(False, False))
+
+        exception = "Parameter None must be less than 1, not 1."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                j = param.Integer(default=1, bounds=(-1, 1), inclusive_bounds=(False, False))
+
+    def test_invalid_default_for_bounds(self):
+        exception = "Parameter None must be at least 10, not 0."
+        with self.assertRaisesRegex(ValueError, exception):
+            class P(param.Parameterized):
+                n = param.Integer(bounds=(10, 20))
+
+    def test_callable(self):
+        assert self.P.g == 1
+        p = self.P()
+        assert p.g == 1
+
+    def test_callable_wrong_type(self):
+        class Q(param.Parameterized):
+            q = param.Integer(default=lambda: 'test')
+
+        exception = "Integer parameter 'q' must be an integer, not type <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, exception):
+            Q.q
+
+        q = Q()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            q.q
+
+    def test_callable_outside_bounds(self):
+        class Q(param.Parameterized):
+            q = param.Integer(default=lambda: 2, bounds=(0, 1))
+
+        exception = "Parameter 'q' must be at most 1, not 2."
+        with self.assertRaisesRegex(ValueError, exception):
+            Q.q
+
+        q = Q()
+
+        with self.assertRaisesRegex(ValueError, exception):
+            q.q
+
+    def test_crop_to_bounds(self):
+        p = self.P()
+
+        # when allow_None is True
+        assert p.param.d.crop_to_bounds(None) is None
+
+        # no bounds
+        assert p.param.e.crop_to_bounds(10000) == 10000
+
+        # with concrete bounds
+        assert p.param.h.crop_to_bounds(10) == 2
+        assert p.param.h.crop_to_bounds(-10) == 0
+
+        # return default if non numerical
+        assert p.param.e.crop_to_bounds('test') == 1
+
+        # Unbound
+        assert p.param.m.crop_to_bounds(10) == 10
+        assert p.param.n.crop_to_bounds(-10) == -10
+
+
+class TestMagnitudeParameters(API1TestCase):
+
+    def _check_defaults(self, p):
+        assert p.default == 1.0
+        assert p.allow_None is False
+        assert p.bounds == (0.0, 1.0)
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            n = param.Magnitude()
+
+        check_defaults(A.param.n, label='N')
+        self._check_defaults(A.param.n)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            n = param.Magnitude()
+
+        a = A()
+
+        check_defaults(a.param.n, label='N')
+        self._check_defaults(a.param.n)
+
+    def test_defaults_unbound(self):
+        n = param.Magnitude()
+
+        check_defaults(n, label=None)
+        self._check_defaults(n)
diff --git a/tests/API1/testnumpy.py b/tests/API1/testnumpy.py
index 98b3d1f..f351bbc 100644
--- a/tests/API1/testnumpy.py
+++ b/tests/API1/testnumpy.py
@@ -6,6 +6,7 @@ import os
 
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 try:
     import numpy
@@ -24,6 +25,36 @@ def _is_array_and_equal(test,ref):
 
 # TODO: incomplete
 class TestNumpy(API1TestCase):
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.instantiate is True
+        assert p.is_instance is True
+        assert p.class_ == numpy.ndarray
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.Array()
+
+        check_defaults(P.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.Array()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.Array()
+
+        check_defaults(s, label=None, skip=['instantiate'])
+        self._check_defaults(s)
+
     def test_array_param(self):
         class Z(param.Parameterized):
             z = param.Array(default=numpy.array([1]))
diff --git a/tests/API1/testobjectselector.py b/tests/API1/testobjectselector.py
index a4a3c8c..cd6c086 100644
--- a/tests/API1/testobjectselector.py
+++ b/tests/API1/testobjectselector.py
@@ -7,6 +7,7 @@ testEnumerationParameter.txt
 
 import param
 from . import API1TestCase
+from .utils import check_defaults
 from collections import OrderedDict
 
 
@@ -28,10 +29,50 @@ class TestObjectSelectorParameters(API1TestCase):
 
         self.P = P
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is None
+        assert p.objects == []
+        assert p.compute_default_fn is None
+        assert p.check_on_set is False
+        assert p.names is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.ObjectSelector()
+
+        check_defaults(P.param.s, label='S')
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.ObjectSelector()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S')
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.ObjectSelector()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
     def test_set_object_constructor(self):
         p = self.P(e=6)
         self.assertEqual(p.e, 6)
 
+    def test_allow_None_is_None(self):
+        p = self.P()
+        assert p.param.e.allow_None is None
+        assert p.param.f.allow_None is None
+        assert p.param.g.allow_None is None
+        assert p.param.h.allow_None is None
+        assert p.param.i.allow_None is None
+        assert p.param.s.allow_None is None
+        assert p.param.d.allow_None is None
+
     def test_get_range_list(self):
         r = self.P.param.params("g").get_range()
         self.assertEqual(r['7'],7)
diff --git a/tests/API1/testpandas.py b/tests/API1/testpandas.py
index e31fc96..aa24d10 100644
--- a/tests/API1/testpandas.py
+++ b/tests/API1/testpandas.py
@@ -6,6 +6,7 @@ import os
 
 import param
 from . import API1TestCase
+from .utils import check_defaults
 
 try:
     import pandas
@@ -18,6 +19,38 @@ except ImportError:
 
 class TestDataFrame(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.instantiate is True
+        assert p.is_instance is True
+        assert p.rows is None
+        assert p.columns is None
+        assert p.ordered is None
+        assert p.class_ == pandas.DataFrame
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.DataFrame()
+
+        check_defaults(P.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.DataFrame()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.DataFrame()
+
+        check_defaults(s, label=None, skip=['instantiate'])
+        self._check_defaults(s)
+
     def test_dataframe_positional_argument(self):
         valid_df = pandas.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]},
                                     columns=['b', 'a', 'c'])
@@ -163,6 +196,36 @@ class TestDataFrame(API1TestCase):
 
 class TestSeries(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.instantiate is True
+        assert p.is_instance is True
+        assert p.rows is None
+        assert p.class_ == pandas.Series
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.Series()
+
+        check_defaults(P.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.Series()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S', skip=['instantiate'])
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.Series()
+
+        check_defaults(s, label=None, skip=['instantiate'])
+        self._check_defaults(s)
+
     def test_series_positional_argument(self):
         valid_series = pandas.Series([1,2])
         class Test(param.Parameterized):
diff --git a/tests/API1/testpathparam.py b/tests/API1/testpathparam.py
new file mode 100644
index 0000000..637eaad
--- /dev/null
+++ b/tests/API1/testpathparam.py
@@ -0,0 +1,251 @@
+import os
+import shutil
+import tempfile
+
+import param
+
+from . import API1TestCase
+from .utils import check_defaults
+
+
+class TestPathParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestPathParameters, self).setUp()
+
+        tmpdir1 = tempfile.mkdtemp()
+        fa = os.path.join(tmpdir1, 'a.txt')
+        fb = os.path.join(tmpdir1, 'b.txt')
+        open(fa, 'w').close()
+        open(fb, 'w').close()
+
+        self.tmpdir1 = tmpdir1
+        self.fa = fa
+        self.fb = fb
+
+        class P(param.Parameterized):
+            a = param.Path()
+            b = param.Path(self.fb)
+            c = param.Path('a.txt', search_paths=[tmpdir1])
+
+        self.P = P
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir1)
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.search_paths == []
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            p = param.Path()
+
+        check_defaults(P.param.p, label='P')
+        self._check_defaults(P.param.p)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            p = param.Path()
+
+        p = P()
+
+        check_defaults(p.param.p, label='P')
+        self._check_defaults(p.param.p)
+
+    def test_defaults_unbound(self):
+        p = param.Path()
+
+        check_defaults(p, label=None)
+        self._check_defaults(p)
+
+    def test_no_path_class(self):
+        assert self.P.a is None
+
+    def test_no_path_class(self):
+        p = self.P()
+        assert p.a is None
+
+    def test_inst_with_path(self):
+        p = self.P(a=self.fa)
+        assert isinstance(p.a, str)
+        assert os.path.isfile(p.a)
+        assert os.path.isabs(p.a)
+        assert p.a == self.fa
+
+    def test_set_to_None_allowed(self):
+        p = self.P()
+
+        assert p.param.b.allow_None is False
+        # This should probably raise an error (#708)
+        p.b = None
+
+    def test_search_paths(self):
+        p = self.P()
+        
+        assert isinstance(p.c, str)
+        assert os.path.isfile(p.c)
+        assert os.path.isabs(p.c)
+        assert p.c == self.fa
+
+
+class TestFilenameParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestFilenameParameters, self).setUp()
+
+        tmpdir1 = tempfile.mkdtemp()
+        fa = os.path.join(tmpdir1, 'a.txt')
+        fb = os.path.join(tmpdir1, 'b.txt')
+        open(fa, 'w').close()
+        open(fb, 'w').close()
+
+        self.tmpdir1 = tmpdir1
+        self.fa = fa
+        self.fb = fb
+
+        class P(param.Parameterized):
+            a = param.Filename()
+            b = param.Filename(self.fb)
+            c = param.Filename('a.txt', search_paths=[tmpdir1])
+
+        self.P = P
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir1)
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.search_paths == []
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            p = param.Filename()
+
+        check_defaults(P.param.p, label='P')
+        self._check_defaults(P.param.p)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            p = param.Filename()
+
+        p = P()
+
+        check_defaults(p.param.p, label='P')
+        self._check_defaults(p.param.p)
+
+    def test_defaults_unbound(self):
+        p = param.Filename()
+
+        check_defaults(p, label=None)
+        self._check_defaults(p)
+
+    def test_no_path_class(self):
+        assert self.P.a is None
+
+    def test_no_path_class(self):
+        p = self.P()
+        assert p.a is None
+
+    def test_inst_with_path(self):
+        p = self.P(a=self.fa)
+        assert isinstance(p.a, str)
+        assert os.path.isfile(p.a)
+        assert os.path.isabs(p.a)
+        assert p.a == self.fa
+
+    def test_set_to_None_allowed(self):
+        p = self.P()
+
+        assert p.param.b.allow_None is False
+        # This should probably raise an error (#708)
+        p.b = None
+
+    def test_search_paths(self):
+        p = self.P()
+        
+        assert isinstance(p.c, str)
+        assert os.path.isfile(p.c)
+        assert os.path.isabs(p.c)
+        assert p.c == self.fa
+
+
+class TestFoldernameParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestFoldernameParameters, self).setUp()
+
+        tmpdir1 = tempfile.mkdtemp()
+        da = os.path.join(tmpdir1, 'da')
+        os.mkdir(da)
+
+        self.tmpdir1 = tmpdir1
+        self.da = da
+
+        class P(param.Parameterized):
+            a = param.Foldername()
+            b = param.Foldername(tmpdir1)
+            c = param.Path('da', search_paths=[tmpdir1])
+
+        self.P = P
+
+    def tearDown(self):
+        shutil.rmtree(self.tmpdir1)
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.search_paths == []
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            p = param.Foldername()
+
+        check_defaults(P.param.p, label='P')
+        self._check_defaults(P.param.p)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            p = param.Foldername()
+
+        p = P()
+
+        check_defaults(p.param.p, label='P')
+        self._check_defaults(p.param.p)
+
+    def test_defaults_unbound(self):
+        p = param.Foldername()
+
+        check_defaults(p, label=None)
+        self._check_defaults(p)
+
+    def test_no_path_class(self):
+        assert self.P.a is None
+
+    def test_no_path_class(self):
+        p = self.P()
+        assert p.a is None
+
+    def test_inst_with_path(self):
+        p = self.P(a=self.da)
+        assert isinstance(p.a, str)
+        assert os.path.isdir(p.a)
+        assert os.path.isabs(p.a)
+        assert p.a == self.da
+
+    def test_set_to_None_allowed(self):
+        p = self.P()
+
+        assert p.param.b.allow_None is False
+        # This should probably raise an error (#708)
+        p.b = None
+
+    def test_search_paths(self):
+        p = self.P()
+        
+        assert isinstance(p.c, str)
+        assert os.path.isdir(p.c)
+        assert os.path.isabs(p.c)
+        assert p.c == self.da
diff --git a/tests/API1/testrangeparameter.py b/tests/API1/testrangeparameter.py
index 45ecaeb..9e98547 100644
--- a/tests/API1/testrangeparameter.py
+++ b/tests/API1/testrangeparameter.py
@@ -7,6 +7,111 @@ from . import API1TestCase
 
 class TestRangeParameters(API1TestCase):
 
+    def setUp(self):
+        super(TestRangeParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.Range()
+            f = param.Range(default=(0, 1), allow_None=True)
+            g = param.Range(default=(0, 1))
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is True
+        assert p.length == 2
+        assert p.bounds is None
+        assert p.softbounds is None
+        assert p.inclusive_bounds == (True, True)
+        assert p.step is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            r = param.Range()
+        
+        self._check_defaults(P.param.r)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            r = param.Range()
+
+        p = P()
+
+        self._check_defaults(p.param.r)
+
+    def test_defaults_unbound(self):
+        r = param.Range()
+
+        self._check_defaults(r)
+
+    def test_set_object_constructor(self):
+        p = self.P(e=(0, 20))
+        assert p.e == (0, 20)
+
+    def test_raise_not_2_tuple(self):
+        p = self.P()
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = (1, 2, 3)
+
+    def test_raise_if_value_bad_length_constructor(self):
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=(1, 1, 1))
+
+    def test_raise_if_value_bad_length_setattr(self):
+        p = self.P()
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = (1, 1, 1)
+
+    def test_raise_if_default_is_None_and_no_length(self):
+        msg = "length must be specified if no default is supplied"
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                t = param.NumericTuple(default=None)
+
+    def test_bad_type(self):
+        msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+        
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+        
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
+        
+        msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.NumericTuple(default='test')        
+
+    def test_support_allow_None_True(self):
+        p = self.P()
+        assert p.f == (0, 1)
+        p.f = None
+        assert p.f is None
+
+        class P(param.Parameterized):
+            f = param.Range(default=(0, 1), allow_None=True)
+        
+        P.f = None
+        assert P.f is None
+
+    def test_support_allow_None_False(self):
+        p = self.P()
+        msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            p.g = None
+
+        msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.g = None
+
     def test_initialization_out_of_bounds(self):
         try:
             class Q(param.Parameterized):
diff --git a/tests/API1/testselector.py b/tests/API1/testselector.py
index dd6d85b..bffae37 100644
--- a/tests/API1/testselector.py
+++ b/tests/API1/testselector.py
@@ -7,6 +7,7 @@ testEnumerationParameter.txt
 
 import param
 from . import API1TestCase
+from.utils import check_defaults
 from collections import OrderedDict
 
 
@@ -28,10 +29,50 @@ class TestSelectorParameters(API1TestCase):
 
         self.P = P
 
+    def _check_defaults(self, p):
+        assert p.default is None
+        assert p.allow_None is None
+        assert p.objects == []
+        assert p.compute_default_fn is None
+        assert p.check_on_set is False
+        assert p.names is None
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            s = param.Selector()
+
+        check_defaults(P.param.s, label='S')
+        self._check_defaults(P.param.s)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            s = param.Selector()
+
+        p = P()
+
+        check_defaults(p.param.s, label='S')
+        self._check_defaults(p.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.Selector()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
     def test_set_object_constructor(self):
         p = self.P(e=6)
         self.assertEqual(p.e, 6)
 
+    def test_allow_None_is_None(self):
+        p = self.P()
+        assert p.param.e.allow_None is None
+        assert p.param.f.allow_None is None
+        assert p.param.g.allow_None is None
+        assert p.param.h.allow_None is None
+        assert p.param.i.allow_None is None
+        assert p.param.s.allow_None is None
+        assert p.param.d.allow_None is None
+
     def test_get_range_list(self):
         r = self.P.param.params("g").get_range()
         self.assertEqual(r['7'],7)
diff --git a/tests/API1/teststringparam.py b/tests/API1/teststringparam.py
index 06864c3..ce12561 100644
--- a/tests/API1/teststringparam.py
+++ b/tests/API1/teststringparam.py
@@ -4,6 +4,7 @@ Unit test for String parameters
 import sys
 
 from . import API1TestCase
+from .utils import check_defaults
 
 import param
 
@@ -12,6 +13,33 @@ ip_regex = r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|
 
 class TestStringParameters(API1TestCase):
 
+    def _check_defaults(self, p):
+        assert p.default == ''
+        assert p.allow_None is False
+        assert p.regex is None
+
+    def test_defaults_class(self):
+        class A(param.Parameterized):
+            s = param.String()
+
+        check_defaults(A.param.s, label='S')
+        self._check_defaults(A.param.s)
+
+    def test_defaults_inst(self):
+        class A(param.Parameterized):
+            s = param.String()
+
+        a = A()
+
+        check_defaults(a.param.s, label='S')
+        self._check_defaults(a.param.s)
+
+    def test_defaults_unbound(self):
+        s = param.String()
+
+        check_defaults(s, label=None)
+        self._check_defaults(s)
+
     def test_regex_ok(self):
         class A(param.Parameterized):
             s = param.String('0.0.0.0', ip_regex)
diff --git a/tests/API1/testtupleparam.py b/tests/API1/testtupleparam.py
new file mode 100644
index 0000000..33589d6
--- /dev/null
+++ b/tests/API1/testtupleparam.py
@@ -0,0 +1,374 @@
+from . import API1TestCase
+from .utils import check_defaults
+
+import param
+import pytest
+
+try:
+    import numpy as np
+except:
+    np = None
+
+
+class TestTupleParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestTupleParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.Tuple(default=(1, 1))
+            f = param.Tuple(default=(0, 0, 0))
+            g = param.Tuple(default=None, length=3)
+            h = param.Tuple(length=2, allow_None=True)
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default == (0, 0)
+        assert p.length == 2
+        assert p.allow_None is False
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            t = param.Tuple()
+
+        check_defaults(P.param.t, label='T')
+        self._check_defaults(P.param.t)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            t = param.Tuple()
+
+        p = P()
+
+        check_defaults(p.param.t, label='T')
+        self._check_defaults(p.param.t)
+
+    def test_defaults_unbound(self):
+        t = param.Tuple()
+
+        check_defaults(t, label=None)
+        self._check_defaults(t)
+
+    def test_set_object_constructor(self):
+        p = self.P(e=(2, 2))
+        self.assertEqual(p.e, (2, 2))
+
+    def test_length_inferred_from_default(self):
+        p = self.P()
+        assert p.param.f.length == 3
+
+    def test_raise_if_value_bad_length_constructor(self):
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=(1, 1, 'extra'))
+
+    def test_raise_if_value_bad_length_setattr(self):
+        p = self.P()
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = (1, 1, 'extra')
+
+    def test_raise_if_default_is_None_and_no_length(self):
+        msg = "length must be specified if no default is supplied"
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                t = param.Tuple(default=None)
+
+    def test_None_default(self):
+        p = self.P()
+        assert p.g is None
+        assert p.param.g.length == 3
+        assert p.param.g.allow_None
+
+    def test_raise_if_default_is_None_and_bad_length(self):
+        msg = r"Tuple parameter 'g' is not of the correct length \(2 instead of 3\)."
+        with self.assertRaisesRegex(ValueError, msg):
+            p = self.P(g=(0, 0))
+
+        p = self.P()
+        with self.assertRaisesRegex(ValueError, msg):
+            p.g = (0, 0)
+
+    def test_bad_type(self):
+        msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
+
+        msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.Tuple(default='test')
+
+    def test_support_allow_None(self):
+        p = self.P()
+        assert p.h == (0, 0)
+        p.h = None
+        p.h = (1, 1)
+        assert p.h == (1, 1)
+
+        class P(param.Parameterized):
+            h = param.Tuple(length=2, allow_None=True)
+
+        P.h = None
+        P.h = (1, 1)
+        assert P.h == (1, 1)
+
+
+class TestNumericTupleParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestNumericTupleParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.NumericTuple(default=(1, 1))
+            f = param.NumericTuple(default=(0, 0, 0))
+            g = param.NumericTuple(default=None, length=3)
+            h = param.NumericTuple(length=2, allow_None=True)
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default == (0, 0)
+        assert p.length == 2
+        assert p.allow_None is False
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            t = param.NumericTuple()
+
+        check_defaults(P.param.t, label='T')
+        self._check_defaults(P.param.t)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            t = param.NumericTuple()
+
+        p = P()
+
+        check_defaults(p.param.t, label='T')
+        self._check_defaults(p.param.t)
+
+    def test_defaults_unbound(self):
+        t = param.NumericTuple()
+
+        check_defaults(t, label=None)
+        self._check_defaults(t)
+
+    def test_set_object_constructor(self):
+        p = self.P(e=(2, 2))
+        self.assertEqual(p.e, (2, 2))
+
+    def test_length_inferred_from_default(self):
+        p = self.P()
+        assert p.param.f.length == 3
+
+    def test_raise_if_value_bad_length_constructor(self):
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=(1, 1, 1))
+
+    def test_raise_if_value_bad_length_setattr(self):
+        p = self.P()
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = (1, 1, 1)
+
+    def test_raise_if_default_is_None_and_no_length(self):
+        msg = "length must be specified if no default is supplied"
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                t = param.NumericTuple(default=None)
+
+    def test_None_default(self):
+        p = self.P()
+        assert p.g is None
+        assert p.param.g.length == 3
+        assert p.param.g.allow_None
+
+    def test_raise_if_default_is_None_and_bad_length(self):
+        msg = r"Tuple parameter 'g' is not of the correct length \(2 instead of 3\)."
+        with self.assertRaisesRegex(ValueError, msg):
+            p = self.P(g=(0, 0))
+
+        p = self.P()
+        with self.assertRaisesRegex(ValueError, msg):
+            p.g = (0, 0)
+
+    def test_bad_type(self):
+        msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
+
+        msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.NumericTuple(default='test')
+
+    def test_support_allow_None(self):
+        p = self.P()
+        assert p.h == (0, 0)
+        p.h = None
+        p.h = (1, 1)
+        assert p.h == (1, 1)
+
+        class P(param.Parameterized):
+            h = param.NumericTuple(length=2, allow_None=True)
+
+        P.h = None
+        P.h = (1, 1)
+        assert P.h == (1, 1)
+
+    def test_raise_on_non_numeric_values(self):
+        msg = r"NumericTuple parameter 'e' only takes numeric values, not type <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = ('bad', 1)
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=('bad', 1))
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = ('bad', 1)
+
+        msg = r"NumericTuple parameter None only takes numeric values, not type <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.NumericTuple(default=('bad', 1))
+
+    @pytest.mark.skipif(np is None, reason='NumPy is not available')
+    def test_support_numpy_values(self):
+        self.P(e=(np.int64(1), np.float32(2)))
+
+
+class TestXYCoordinatesParameters(API1TestCase):
+
+    def setUp(self):
+        super(TestXYCoordinatesParameters, self).setUp()
+        class P(param.Parameterized):
+            e = param.XYCoordinates(default=(1, 1))
+            f = param.XYCoordinates(default=(0, 1), allow_None=True)
+            g = param.XYCoordinates(default=(1, 2))
+
+        self.P = P
+
+    def _check_defaults(self, p):
+        assert p.default == (0.0, 0.0)
+        assert p.length == 2
+        assert p.allow_None is False
+
+    def test_defaults_class(self):
+        class P(param.Parameterized):
+            t = param.XYCoordinates()
+
+        self._check_defaults(P.param.t)
+
+    def test_defaults_inst(self):
+        class P(param.Parameterized):
+            t = param.XYCoordinates()
+
+        p = P()
+
+        self._check_defaults(p.param.t)
+
+    def test_defaults_unbound(self):
+        t = param.XYCoordinates()
+
+        self._check_defaults(t)
+
+    def test_set_object_constructor(self):
+        p = self.P(e=(2, 2))
+        self.assertEqual(p.e, (2, 2))
+
+    def test_raise_if_value_bad_length_constructor(self):
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=(1, 1, 1))
+
+    def test_raise_if_value_bad_length_setattr(self):
+        p = self.P()
+        msg = r"Tuple parameter 'e' is not of the correct length \(3 instead of 2\)"
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = (1, 1, 1)
+
+    def test_bad_type(self):
+        msg = r"Tuple parameter 'e' only takes a tuple value, not <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = 'test'
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e='test')
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = 'test'
+
+        msg = r"Tuple parameter None only takes a tuple value, not <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.NumericTuple(default='test')
+
+    def test_support_allow_None_True(self):
+        p = self.P()
+        assert p.f == (0, 1)
+        p.f = None
+        assert p.f is None
+
+        class P(param.Parameterized):
+            f = param.Range(default=(0, 1), allow_None=True)
+
+        P.f = None
+        assert P.f is None
+
+    def test_support_allow_None_False(self):
+        p = self.P()
+        msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            p.g = None
+
+        msg = "Tuple parameter 'g' only takes a tuple value, not <(class|type) 'NoneType'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.g = None
+
+    def test_raise_on_non_numeric_values(self):
+        msg = r"NumericTuple parameter 'e' only takes numeric values, not type <(class|type) 'str'>."
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P.e = ('bad', 1)
+
+        with self.assertRaisesRegex(ValueError, msg):
+            self.P(e=('bad', 1))
+
+        p = self.P()
+
+        with self.assertRaisesRegex(ValueError, msg):
+            p.e = ('bad', 1)
+
+        msg = r"NumericTuple parameter None only takes numeric values, not type <(class|type) 'str'>."
+        with self.assertRaisesRegex(ValueError, msg):
+            class P(param.Parameterized):
+                e = param.NumericTuple(default=('bad', 1))
+
+    @pytest.mark.skipif(np is None, reason='NumPy is not available')
+    def test_support_numpy_values(self):
+        self.P(e=(np.int64(1), np.float32(2)))
diff --git a/tests/API1/testutils.py b/tests/API1/testutils.py
index ace822f..7e2c707 100644
--- a/tests/API1/testutils.py
+++ b/tests/API1/testutils.py
@@ -1,9 +1,10 @@
 import datetime as dt
+import os
 
 import param
 import pytest
 
-from param import guess_param_types
+from param import guess_param_types, resolve_path
 
 try:
     import numpy as np
@@ -56,3 +57,265 @@ def test_guess_param_types(val, p):
     if not type(out_param) == param.Parameter:
         assert out_param.default is val
         assert out_param.constant
+
+@pytest.fixture
+def reset_search_paths():
+    # The default is [os.getcwd()] which doesn't play well with the testing
+    # framework where every test creates a new temporary directory.
+    # This fixture sets it temporarily to [].
+    original = resolve_path.search_paths
+    try:
+        resolve_path.search_paths = []
+        yield
+    finally:
+        resolve_path.search_paths = original
+
+
+def test_resolve_path_file_default():
+    assert resolve_path.path_to_file is True
+    assert resolve_path.search_paths == [os.getcwd()]
+
+
+def test_resolve_path_file_not_found():
+    with pytest.raises(IOError, match='File surelyyoudontexist was not found in the following'):
+        resolve_path('surelyyoudontexist')
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_file_not_found(tmpdir):
+    cdir = os.getcwd()
+    os.chdir(str(tmpdir))
+    try:
+        with pytest.raises(IOError, match='File notthere was not found in the following'):
+            resolve_path('notthere')
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_folder_not_found(tmpdir):
+    cdir = os.getcwd()
+    os.chdir(str(tmpdir))
+    try:
+        with pytest.raises(IOError, match='Folder notthere was not found in the following'):
+            resolve_path('notthere', path_to_file=False)
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_either_not_found(tmpdir):
+    cdir = os.getcwd()
+    os.chdir(str(tmpdir))
+    try:
+        with pytest.raises(IOError, match='Path notthere was not found in the following'):
+            resolve_path('notthere', path_to_file=None)
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+@pytest.mark.parametrize('path_to_file', [True, False, None])
+def test_resolve_path_abs_not_found(tmpdir, path_to_file):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.chdir(str(tmpdir))
+    try:
+        with pytest.raises(IOError, match='not found'):
+            resolve_path(fp, path_to_file=path_to_file)
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_cwd_file(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path('foo')
+        assert os.path.isfile(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_cwd_folder(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path('foo', path_to_file=False)
+        assert os.path.isdir(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_cwd_either_file(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path('foo', path_to_file=None)
+        assert os.path.isfile(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_cwd_either_folder(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path('foo', path_to_file=None)
+        assert os.path.isdir(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_abs_file(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path(fp)
+        assert os.path.isfile(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_abs_folder(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path(fp, path_to_file=False)
+        assert os.path.isdir(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_abs_either_file(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path(fp, path_to_file=None)
+        assert os.path.isfile(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_abs_either_folder(tmpdir):
+    cdir = os.getcwd()
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    os.chdir(str(tmpdir))
+    try:
+        p = resolve_path(fp, path_to_file=None)
+        assert os.path.isdir(p)
+        assert os.path.basename(p) == 'foo'
+        assert os.path.isabs(p)
+        assert p == fp
+    finally:
+        os.chdir(cdir)
+
+
+def test_resolve_path_search_paths_file(tmpdir):
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    p = resolve_path('foo', search_paths=[str(tmpdir)])
+    assert os.path.isfile(p)
+    assert os.path.basename(p) == 'foo'
+    assert os.path.isabs(p)
+    assert p == fp
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_search_paths_folder(tmpdir):
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=False)
+    assert os.path.isdir(p)
+    assert os.path.basename(p) == 'foo'
+    assert os.path.isabs(p)
+    assert p == fp
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_search_paths_either_file(tmpdir):
+    fp = os.path.join(str(tmpdir), 'foo')
+    open(fp, 'w').close()
+    p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=None)
+    assert os.path.isfile(p)
+    assert os.path.basename(p) == 'foo'
+    assert os.path.isabs(p)
+    assert p == fp
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_search_paths_either_folder(tmpdir):
+    fp = os.path.join(str(tmpdir), 'foo')
+    os.mkdir(fp)
+    p = resolve_path('foo', search_paths=[str(tmpdir)], path_to_file=None)
+    assert os.path.isdir(p)
+    assert os.path.basename(p) == 'foo'
+    assert os.path.isabs(p)
+    assert p == fp
+
+
+@pytest.mark.usefixtures('reset_search_paths')
+def test_resolve_path_search_paths_multiple_file(tmpdir):
+    d1 = os.path.join(str(tmpdir), 'd1')
+    d2 = os.path.join(str(tmpdir), 'd2')
+    os.mkdir(d1)
+    os.mkdir(d2)
+    fp1 = os.path.join(d1, 'foo1')
+    open(fp1, 'w').close()
+    fp2 = os.path.join(d2, 'foo2')
+    open(fp2, 'w').close()
+    p = resolve_path('foo1', search_paths=[d1, d2])
+    assert os.path.isfile(p)
+    assert os.path.basename(p) == 'foo1'
+    assert os.path.isabs(p)
+    assert p == fp1
+
+    p = resolve_path('foo2', search_paths=[d1, d2])
+    assert os.path.isfile(p)
+    assert os.path.basename(p) == 'foo2'
+    assert os.path.isabs(p)
+    assert p == fp2
diff --git a/tests/API1/testwatch.py b/tests/API1/testwatch.py
index 184ba5a..d0220b0 100644
--- a/tests/API1/testwatch.py
+++ b/tests/API1/testwatch.py
@@ -512,6 +512,7 @@ class TestWatch(API1TestCase):
         obj = SimpleWatchExample()
 
         obj.param.watch(obj.method, ['a'])
+        obj.param.watch(lambda x: None, 'd', what='bounds')
 
         copied = copy.deepcopy(obj)
 
diff --git a/tests/API1/utils.py b/tests/API1/utils.py
index 8380c3d..3000f68 100644
--- a/tests/API1/utils.py
+++ b/tests/API1/utils.py
@@ -73,3 +73,23 @@ class MockLoggingHandler(logging.Handler):
             raise AssertionError(msg.format(level=level,
                                             last_line=repr(last_line[0]),
                                             substring=repr(substring)))
+
+
+def check_defaults(parameter, label, skip=[]):
+    # ! Not testing default and allow_None
+    if 'doc' not in skip:
+        assert parameter.doc is None
+    if 'precedence' not in skip:
+        assert parameter.precedence is None
+    if 'instantiate' not in skip:
+        assert parameter.instantiate is False
+    if 'constant' not in skip:
+        assert parameter.constant is False
+    if 'readonly' not in skip:
+        assert parameter.readonly is False
+    if 'pickle_default_value' not in skip:
+        assert parameter.pickle_default_value is True
+    if 'per_instance' not in skip:
+        assert parameter.per_instance is True
+    if 'label' not in skip:
+        assert parameter.label == label
diff --git a/tox.ini b/tox.ini
index 2f69812..8169cbb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,7 +16,9 @@ python =
 description = run test suite under {basepython}
 deps = .[tests]
 commands =
-    pytest tests --cov=numbergen --cov=param --cov-append --cov-report xml
+    coverage run --source=numbergen,param -m pytest tests
+    coverage report
+    coverage xml
 
 [testenv:with_numpy]
 description = run test suite with numpy under {basepython}
diff --git a/tox27.ini b/tox27.ini
index 57e3dac..1c6079b 100644
--- a/tox27.ini
+++ b/tox27.ini
@@ -10,7 +10,9 @@ python =
 description = run test suite under {basepython}
 deps = .[tests]
 commands =
-    pytest tests --ignore tests/API1/testparamdepends_py3.py --cov=numbergen --cov=param --cov-append --cov-report xml
+    coverage run --source=numbergen,param -m pytest tests --ignore tests/API1/testparamdepends_py3.py
+    coverage report --omit param/_async.py
+    coverage xml --omit param/_async.py
 
 [testenv:with_numpy]
 description = run test suite with numpy under {basepython}

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.13.0.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.13.0.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.13.0.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.13.0.egg-info/top_level.txt

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.12.3.egg-info/PKG-INFO
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.12.3.egg-info/dependency_links.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.12.3.egg-info/requires.txt
-rw-r--r--  root/root   /usr/lib/python3/dist-packages/param-1.12.3.egg-info/top_level.txt

No differences were encountered in the control files

More details

Full run details