Merge pull request #80 from jinnovation/patch-1

README: testcase_func_name -> name_func
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..1139fc4
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,677 @@
+# !!! WARNING !!!
+# This file is automatically generated by ../rebuild-circleci-yaml
+# !!! WARNING !!!
+version: 2.1
+
+jobs:
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-nose:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-nose2:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-pytest2:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-pytest2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-pytest3:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-unit:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py27-unit2:
+    docker:
+      - image: python:2.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py27-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-nose:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-nose2:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-pytest2:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-pytest2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-pytest3:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-unit:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py35-unit2:
+    docker:
+      - image: python:3.5
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py35-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-nose:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-nose2:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-pytest2:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-pytest2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-pytest3:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-unit:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py36-unit2:
+    docker:
+      - image: python:3.6
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py36-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-nose:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-nose2:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-pytest2:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-pytest2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-pytest3:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-unit:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-pypy-unit2:
+    docker:
+      - image: pypy:2
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e pypy-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py37-nose:
+    docker:
+      - image: python:3.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py37-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py37-nose2:
+    docker:
+      - image: python:3.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py37-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py37-pytest3:
+    docker:
+      - image: python:3.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py37-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py37-unit:
+    docker:
+      - image: python:3.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py37-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py37-unit2:
+    docker:
+      - image: python:3.7
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py37-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py38-nose:
+    docker:
+      - image: python:3.8
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py38-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py38-nose2:
+    docker:
+      - image: python:3.8
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py38-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py38-pytest3:
+    docker:
+      - image: python:3.8
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py38-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py38-unit:
+    docker:
+      - image: python:3.8
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py38-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py38-unit2:
+    docker:
+      - image: python:3.8
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py38-unit2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py39-nose:
+    docker:
+      - image: python:3.9-rc
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py39-nose
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py39-nose2:
+    docker:
+      - image: python:3.9-rc
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py39-nose2
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py39-pytest3:
+    docker:
+      - image: python:3.9-rc
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py39-pytest3
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py39-unit:
+    docker:
+      - image: python:3.9-rc
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py39-unit
+  
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-py39-unit2:
+    docker:
+      - image: python:3.9-rc
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e py39-unit2
+  
+
+workflows:
+  main:
+    jobs:
+      - test-py27-nose
+      - test-py27-nose2
+      - test-py27-pytest2
+      - test-py27-pytest3
+      - test-py27-unit
+      - test-py27-unit2
+      - test-py35-nose
+      - test-py35-nose2
+      - test-py35-pytest2
+      - test-py35-pytest3
+      - test-py35-unit
+      - test-py35-unit2
+      - test-py36-nose
+      - test-py36-nose2
+      - test-py36-pytest2
+      - test-py36-pytest3
+      - test-py36-unit
+      - test-py36-unit2
+      - test-pypy-nose
+      - test-pypy-nose2
+      - test-pypy-pytest2
+      - test-pypy-pytest3
+      - test-pypy-unit
+      - test-pypy-unit2
+      - test-py37-nose
+      - test-py37-nose2
+      - test-py37-pytest3
+      - test-py37-unit
+      - test-py37-unit2
+      - test-py38-nose
+      - test-py38-nose2
+      - test-py38-pytest3
+      - test-py38-unit
+      - test-py38-unit2
+      - test-py39-nose
+      - test-py39-nose2
+      - test-py39-pytest3
+      - test-py39-unit
+      - test-py39-unit2
+      # !!! WARNING !!!
+# This file is automatically generated by ../rebuild-circleci-yaml
+# !!! WARNING !!!
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index b277b4b..e69de29 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,46 +0,0 @@
-language: python
-sudo: false
-matrix:
-  include:
-    - env: "TOXENV=py27-nose"
-      python: "2.7"
-    - env: "TOXENV=py27-nose2"
-      python: "2.7"
-    - env: "TOXENV=py27-pytest"
-      python: "2.7"
-    - env: "TOXENV=py27-unit"
-      python: "2.7"
-    - env: "TOXENV=py27-unit2"
-      python: "2.7"
-    - env: "TOXENV=py35-nose"
-      python: "3.5"
-    - env: "TOXENV=py35-nose2"
-      python: "3.5"
-    - env: "TOXENV=py35-pytest"
-      python: "3.5"
-    - env: "TOXENV=py35-unit"
-      python: "3.5"
-    - env: "TOXENV=py35-unit2"
-      python: "3.5"
-    - env: "TOXENV=py36-nose"
-      python: "3.6"
-    - env: "TOXENV=py36-nose2"
-      python: "3.6"
-    - env: "TOXENV=py36-pytest"
-      python: "3.6"
-    - env: "TOXENV=py36-unit"
-      python: "3.6"
-    - env: "TOXENV=py36-unit2"
-      python: "3.6"
-    - env: "TOXENV=pypy-nose"
-      python: "pypy"
-    - env: "TOXENV=pypy-nose2"
-      python: "pypy"
-    - env: "TOXENV=pypy-pytest"
-      python: "pypy"
-    - env: "TOXENV=pypy-unit"
-      python: "pypy"
-    - env: "TOXENV=pypy-unit2"
-      python: "pypy"
-install: pip install tox
-script: tox
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index f471735..e1f827b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,10 @@
+0.7.2 (2020-04-12)
+    * Add `@parameterized_class` name function callback support (thanks
+      @duncwebb et al; https://github.com/wolever/parameterized/pull/74)
+
+0.7.1 (2019-11-19)
+    * Fix #73: calling `setUp` and `tearDown` in parameterized classes
+
 0.7.0 (2019-02-05)
     * Added parameterized_class feature, for parameterizing entire test
       classes (many thanks to @TobyLL for their suggestions and help testing!)
diff --git a/README.rst b/README.rst
index 6b839d1..6c06296 100644
--- a/README.rst
+++ b/README.rst
@@ -1,15 +1,14 @@
 Parameterized testing with any Python test framework
 ====================================================
 
-|pypi| |travis|
-
-.. |pypi| image:: https://img.shields.io/pypi/v/parameterized.svg
+.. image:: https://img.shields.io/pypi/v/parameterized.svg
     :alt: PyPI
     :target: https://pypi.org/project/parameterized/
 
-.. |travis| image:: https://travis-ci.org/wolever/parameterized.svg?branch=master
-    :alt: Travis CI
-    :target: https://travis-ci.org/wolever/parameterized
+.. image:: https://circleci.com/gh/wolever/parameterized.svg?style=svg
+    :alt: Circle CI
+    :target: https://circleci.com/gh/wolever/parameterized
+
 
 Parameterized testing in Python sucks.
 
@@ -174,7 +173,7 @@
 Compatibility
 -------------
 
-`Yes`__.
+`Yes`__ (mostly).
 
 __ https://travis-ci.org/wolever/parameterized
 
@@ -189,7 +188,10 @@
      - Py3.5
      - Py3.6
      - Py3.7
+     - Py3.8
+     - Py3.9
      - PyPy
+     - ``@mock.patch``
    * - nose
      - yes
      - yes
@@ -198,6 +200,9 @@
      - yes
      - yes
      - yes
+     - yes
+     - yes
+     - yes
    * - nose2
      - yes
      - yes
@@ -206,7 +211,21 @@
      - yes
      - yes
      - yes
-   * - py.test
+     - yes
+     - yes
+     - yes
+   * - py.test 2
+     - yes
+     - yes
+     - no*
+     - no*
+     - no*
+     - no*
+     - yes
+     - yes
+     - yes
+     - yes
+   * - py.test 3
      - yes
      - yes
      - yes
@@ -214,6 +233,31 @@
      - yes
      - yes
      - yes
+     - yes
+     - yes
+     - yes
+   * - py.test 4
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+     - no**
+   * - py.test fixtures
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
+     - no†
    * - | unittest
        | (``@parameterized.expand``)
      - yes
@@ -223,6 +267,9 @@
      - yes
      - yes
      - yes
+     - yes
+     - yes
+     - yes
    * - | unittest2
        | (``@parameterized.expand``)
      - yes
@@ -232,13 +279,19 @@
      - yes
      - yes
      - yes
+     - yes
+     - yes
+     - yes
 
-*Note*: py.test 4 is `not yet supported`__ (but coming!), and py.test 2 under
-Python 3 `does not appear to work`__. Please comment on the related issues if
-you are affected.
+\*: py.test 2 does `does not appear to work (#71)`__ under Python 3. Please comment on the related issues if you are affected.
 
-__ https://github.com/wolever/parameterized/issues/34
+\*\*: py.test 4 is not yet supported (but coming!) in `issue #34`__
+
+†: py.test fixture support is documented in `issue #81`__
+
 __ https://github.com/wolever/parameterized/issues/71
+__ https://github.com/wolever/parameterized/issues/34
+__ https://github.com/wolever/parameterized/issues/81
 
 Dependencies
 ------------
@@ -518,8 +571,8 @@
 trailing comma: ``(foo, )``)
 
 
-Using with ``mock.patch``
-.........................
+Using with ``@mock.patch``
+..........................
 
 ``parameterized`` can be used with ``mock.patch``, but the argument ordering
 can be confusing. The ``@mock.patch(...)`` decorator must come *below* the
diff --git a/parameterized/parameterized.py b/parameterized/parameterized.py
index 96a662f..8a38796 100644
--- a/parameterized/parameterized.py
+++ b/parameterized/parameterized.py
@@ -44,7 +44,10 @@
     def make_method(func, instance, type):
         return MethodType(func, instance, type)
 
+
 CompatArgSpec = namedtuple("CompatArgSpec", "args varargs keywords defaults")
+
+
 def getargspec(func):
     if PY2:
         return CompatArgSpec(*inspect.getargspec(func))
@@ -58,11 +61,14 @@
         ) %(func, ))
     return CompatArgSpec(*args[:4])
 
+
 _param = namedtuple("param", "args kwargs")
 
+
 def skip_on_empty_helper(*a, **kw):
     raise SkipTest("parameterized input is empty")
 
+
 def reapply_patches_if_need(func):
 
     def dummy_wrapper(orgfunc):
@@ -79,6 +85,7 @@
             func = patch_obj.decorate_callable(func)
     return func
 
+
 def delete_patches_if_need(func):
     if hasattr(func, 'patchings'):
         func.patchings[:] = []
@@ -217,6 +224,7 @@
 
     return result
 
+
 def short_repr(x, n=64):
     """ A shortened repr of ``x`` which is guaranteed to be ``unicode``::
 
@@ -236,6 +244,7 @@
         x_repr = x_repr[:n//2] + "..." + x_repr[len(x_repr) - n//2:]
     return x_repr
 
+
 def default_doc_func(func, num, p):
     if func.__doc__ is None:
         return None
@@ -256,9 +265,11 @@
     args = "%s[with %s]" %(len(first) and " " or "", ", ".join(descs))
     return "".join([first.rstrip(), args, suffix, nl, rest])
 
+
 def default_name_func(func, num, p):
     base_name = func.__name__
     name_suffix = "_%s" %(num, )
+
     if len(p.args) > 0 and isinstance(p.args[0], string_types):
         name_suffix += "_" + parameterized.to_safe_name(p.args[0])
     return base_name + name_suffix
@@ -271,6 +282,7 @@
     "_pytest": "pytest",
 }
 
+
 def set_test_runner(name):
     global _test_runner_override
     if name not in _test_runners:
@@ -280,6 +292,7 @@
         )
     _test_runner_override = name
 
+
 def detect_runner():
     """ Guess which test runner we're using by traversing the stack and looking
         for the first matching module. This *should* be reasonably safe, as
@@ -482,9 +495,9 @@
             frame = stack[1]
             frame_locals = frame[0].f_locals
 
-            paramters = cls.input_as_callable(input)()
+            parameters = cls.input_as_callable(input)()
 
-            if not paramters:
+            if not parameters:
                 if not skip_on_empty:
                     raise ValueError(
                         "Parameters iterable is empty (hint: use "
@@ -493,8 +506,8 @@
                     )
                 return wraps(f)(lambda: skip_on_empty_helper())
 
-            digits = len(str(len(paramters) - 1))
-            for num, p in enumerate(paramters):
+            digits = len(str(len(parameters) - 1))
+            for num, p in enumerate(parameters):
                 name = name_func(f, "{num:0>{digits}}".format(digits=digits, num=num), p)
                 # If the original function has patches applied by 'mock.patch',
                 # re-construct all patches on the just former decoration layer
@@ -536,7 +549,7 @@
         return str(re.sub("[^a-zA-Z0-9_]+", "_", s))
 
 
-def parameterized_class(attrs, input_values=None):
+def parameterized_class(attrs, input_values=None, classname_func=None):
     """ Parameterizes a test class by setting attributes on the class.
 
         Can be used in two ways:
@@ -569,26 +582,46 @@
         [dict(zip(attrs, vals)) for vals in input_values]
     )
 
+    classname_func = classname_func or default_classname_func
+
     def decorator(base_class):
         test_class_module = sys.modules[base_class.__module__].__dict__
         for idx, input_dict in enumerate(input_dicts):
             test_class_dict = dict(base_class.__dict__)
             test_class_dict.update(input_dict)
 
-            name_suffix = input_values and input_values[idx]
-            if isinstance(name_suffix, (list, tuple)) and len(input_values) > 0:
-                name_suffix = name_suffix[0]
-            name_suffix = (
-                "_%s" %(name_suffix, ) if isinstance(name_suffix, string_types) else
-                ""
-            )
-
-            name = "%s_%s%s" %(
-                base_class.__name__,
-                idx,
-                name_suffix,
-            )
+            name = classname_func(base_class, idx, input_dicts)
 
             test_class_module[name] = type(name, (base_class, ), test_class_dict)
 
+        # We need to leave the base class in place (see issue #73), but if we
+        # leave the test_ methods in place, the test runner will try to pick
+        # them up and run them... which doesn't make sense, since no parameters
+        # will have been applied.
+        # Address this by iterating over the base class and remove all test
+        # methods.
+        for method_name in list(base_class.__dict__):
+            if method_name.startswith("test_"):
+                delattr(base_class, method_name)
+        return base_class
+
     return decorator
+
+
+def default_classname_func(cls, num, p):
+
+    name_suffix = p and p[num]
+    if isinstance(name_suffix, (list, tuple)) and len(p) > 0:
+        name_suffix = name_suffix[0]
+    name_suffix = (
+        "_%s" %(name_suffix, ) if isinstance(name_suffix, string_types) else
+        ""
+    )
+
+    name = "%s_%s%s" %(
+        cls.__name__,
+        num,
+        name_suffix,
+    )
+
+    return name
\ No newline at end of file
diff --git a/parameterized/test.py b/parameterized/test.py
index 5e506f5..770cab8 100644
--- a/parameterized/test.py
+++ b/parameterized/test.py
@@ -166,12 +166,9 @@
                              (foo, mock_umask._mock_name))
 
     expect([
-        "test_multiple_function_patch_decorator"
-        "(42, 51, 'umask', 'fdopen')",
-        "test_multiple_function_patch_decorator"
-        "('foo0', 'bar0', 'umask', 'fdopen')",
-        "test_multiple_function_patch_decorator"
-        "('foo1', 'bar1', 'umask', 'fdopen')",
+        "test_multiple_function_patch_decorator(42, 51, 'umask', 'fdopen')",
+        "test_multiple_function_patch_decorator('foo0', 'bar0', 'umask', 'fdopen')",
+        "test_multiple_function_patch_decorator('foo1', 'bar1', 'umask', 'fdopen')",
     ])
 
     @parameterized.expand([(42, 51), ("foo0", "bar0"), param("foo1", "bar1")])
@@ -252,7 +249,9 @@
         f_locals = stack[3][0].f_locals
         test_method = (
             f_locals.get("testMethod") or # Py27
-            f_locals.get("function") # Py33
+            f_locals.get("function") or # Py33
+            f_locals.get("method") or # Py38
+            None
         )
         if test_method is None:
             raise AssertionError("uh oh, unittest changed a local variable name")
@@ -452,10 +451,10 @@
 ])
 class TestParameterizedClass(TestCase):
     expect([
-        "TestParameterizedClass_0_foo:test_method_a('foo', 1, 2)",
-        "TestParameterizedClass_0_foo:test_method_b('foo', 1, 2)",
-        "TestParameterizedClass_1_bar:test_method_a('bar', 3, 0)",
-        "TestParameterizedClass_1_bar:test_method_b('bar', 3, 0)",
+        "TestParameterizedClass_0:test_method_a('foo', 1, 2)",
+        "TestParameterizedClass_0:test_method_b('foo', 1, 2)",
+        "TestParameterizedClass_1:test_method_a('bar', 3, 0)",
+        "TestParameterizedClass_1:test_method_b('bar', 3, 0)",
         "TestParameterizedClass_2:test_method_a(0, 1, 2)",
         "TestParameterizedClass_2:test_method_b(0, 1, 2)",
     ])
@@ -478,14 +477,115 @@
         self._assertions("test_method_b")
 
 
+def custom_cls_naming_func(clsname, idx, attrs):
+    """
+    Custom class naming function for the form of parameterized_class that
+    takes a tuple of attributes, then a list of tuples of values
+    :param clsname: The original class that the decorator specialises
+    :param idx: the test index (starts at 0)
+    :param attrs: A list of dicts of attribute values
+    :return:
+    """
+    return "%s_%s_%s_%s" % (clsname.__name__, str(attrs[idx]['a']), str(attrs[idx]['b']), str(attrs[idx]['c']))
+
+
+@parameterized_class(("a", "b", "c"), [
+    ("foo", 1, 2),
+    ("bar", 3, 0),
+    (0, 1, 2),
+], classname_func=custom_cls_naming_func)
+class TestNamedParameterizedClass(TestCase):
+    expect([
+        "TestNamedParameterizedClass_foo_1_2:test_method_a('foo', 1, 2)",
+        "TestNamedParameterizedClass_foo_1_2:test_method_b('foo', 1, 2)",
+        "TestNamedParameterizedClass_bar_3_0:test_method_a('bar', 3, 0)",
+        "TestNamedParameterizedClass_bar_3_0:test_method_b('bar', 3, 0)",
+        "TestNamedParameterizedClass_0_1_2:test_method_a(0, 1, 2)",
+        "TestNamedParameterizedClass_0_1_2:test_method_b(0, 1, 2)",
+    ])
+
+    def _assertions(self, test_name):
+        assert hasattr(self, "a")
+        assert_equal(self.b + self.c, 3)
+        missing_tests.remove("%s:%s(%r, %r, %r)" %(
+            self.__class__.__name__,
+            test_name,
+            self.a,
+            self.b,
+            self.c,
+        ))
+
+    def test_method_a(self):
+        self._assertions("test_method_a")
+
+    def test_method_b(self):
+        self._assertions("test_method_b")
+
+
 @parameterized_class([
     {"foo": 1},
     {"bar": 1},
 ])
 class TestParameterizedClassDict(TestCase):
     expect([
+        "TestParameterizedClassDict_0:setUp(1, 0)",
+        "TestParameterizedClassDict_0:tearDown(1, 0)",
         "TestParameterizedClassDict_0:test_method(1, 0)",
         "TestParameterizedClassDict_1:test_method(0, 1)",
+        "TestParameterizedClassDict_1:setUp(0, 1)",
+        "TestParameterizedClassDict_1:tearDown(0, 1)",
+    ])
+
+    foo = 0
+    bar = 0
+
+    def setUp(self):
+        # Ensure that super() works (issue #73)
+        super(TestParameterizedClassDict, self).setUp()
+        missing_tests.remove("%s:setUp(%r, %r)" %(
+            self.__class__.__name__,
+            self.foo,
+            self.bar,
+        ))
+
+    def tearDown(self):
+        # Ensure that super() works (issue #73)
+        super(TestParameterizedClassDict, self).tearDown()
+        missing_tests.remove("%s:tearDown(%r, %r)" %(
+            self.__class__.__name__,
+            self.foo,
+            self.bar,
+        ))
+
+    def test_method(self):
+        missing_tests.remove("%s:test_method(%r, %r)" %(
+            self.__class__.__name__,
+            self.foo,
+            self.bar,
+        ))
+
+
+def custom_cls_naming_dict_func(clsname, idx, attrs):
+    """
+    Custom class naming function for the form of parameterized_class that
+    takes a list of dictionaries containing attributes to override.
+    :param clsname: The original class that the decorator specialises
+    :param idx: the test index (starts at 0)
+    :param attrs: A list of dicts of attribute values
+    :return:
+    """
+    return "%s_%s_%s" % (clsname.__name__, str(attrs[idx].get('foo', 0)), str(attrs[idx].get('bar', 0)))
+
+
+@parameterized_class([
+    {"foo": 1},
+    {"bar": 1},
+], classname_func=custom_cls_naming_dict_func
+)
+class TestNamedParameterizedClassDict(TestCase):
+    expect([
+        "TestNamedParameterizedClassDict_1_0:test_method(1, 0)",
+        "TestNamedParameterizedClassDict_0_1:test_method(0, 1)",
     ])
 
     foo = 0
diff --git a/rebuild-circleci-yaml b/rebuild-circleci-yaml
new file mode 100755
index 0000000..5f5224b
--- /dev/null
+++ b/rebuild-circleci-yaml
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+"""
+Rebuilds the .circleci/config.yml file based on the output of ``tox --listenvs``
+"""
+
+import jinja2
+import subprocess as sp
+
+template = jinja2.Template("""\
+# !!! WARNING !!!
+# This file is automatically generated by ../rebuild-circleci-yaml
+# !!! WARNING !!!
+version: 2.1
+
+jobs:
+  {% for (tox_env, python_ver) in env_list: %}
+  # !!! WARNING !!!
+  # This file is automatically generated by ../rebuild-circleci-yaml
+  # !!! WARNING !!!
+
+  test-{{ tox_env }}:
+    docker:
+      {% if python_ver == "pypy" -%}
+      - image: pypy:2
+      {%- else -%}
+      - image: python:{{ python_ver }}
+      {%- endif %}
+    steps:
+      - checkout
+      - run:
+          name: install tox
+          command: pip install tox==3.7.0
+      - run:
+          name: run tests
+          command: tox -e {{ tox_env }}
+  {% endfor %}
+
+workflows:
+  main:
+    jobs:
+      {% for (tox_env, _) in env_list: -%}
+      - test-{{ tox_env }}
+      {% endfor -%}
+
+# !!! WARNING !!!
+# This file is automatically generated by ../rebuild-circleci-yaml
+# !!! WARNING !!!
+""")
+
+# Maps tox python versions (ex, "py27") to Travis Python versions (ex, "2.7")
+py_version_map = {
+    "py26": "2.6",
+    "py27": "2.7",
+    "py35": "3.5",
+    "py36": "3.6",
+    "py37": "3.7",
+    "py38": "3.8",
+    "py39": "3.9-rc",
+    "pypy": "pypy",
+}
+
+def main():
+    env_list_str = sp.check_output("tox --listenvs", shell=True).splitlines()
+    env_list = []
+    for env in env_list_str:
+        if not env.strip():
+            continue
+        py_ver, _, _ = env.partition("-")
+        env_list.append((env, py_version_map[py_ver]))
+
+    with open(".circleci/config.yml", "w") as f:
+        f.write(template.render(env_list=env_list))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/setup.py b/setup.py
index 0082729..64b7f3b 100644
--- a/setup.py
+++ b/setup.py
@@ -8,13 +8,13 @@
 os.chdir(os.path.dirname(sys.argv[0]) or ".")
 
 try:
-    long_description = open("README.rst", "U").read()
+    long_description = open("README.rst").read()
 except IOError:
     long_description = "See https://github.com/wolever/parameterized"
 
 setup(
     name="parameterized",
-    version="0.7.0",
+    version="0.7.2",
     url="https://github.com/wolever/parameterized",
     license="FreeBSD",
     author="David Wolever",
@@ -26,5 +26,10 @@
         'License :: OSI Approved :: BSD License',
     ],
     packages=find_packages(),
+    extras_require={
+        'dev': [
+            'jinja2',
+        ]
+    },
     long_description=long_description,
 )
diff --git a/tox.ini b/tox.ini
index 93c0656..67a3ccf 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist=py{27,35,36,py}-{nose,nose2,pytest2,pytest3,unit,unit2},py37-{nose,nose2,pytest3,unit,unit2}
+envlist=py{27,35,36,py}-{nose,nose2,pytest2,pytest3,unit,unit2},py{37,38,39}-{nose,nose2,pytest3,unit,unit2}
 [testenv]
 deps=
     nose