diff --git a/.travis.yml b/.travis.yml
index 4eacaa4..0886e05 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,11 @@ python:
     - "3.4"
     - "3.5"
     - "pypy"
+matrix:
+    include:
+        - python: 3.7
+          dist: xenial
+          sudo: true
 install:
     - pip install .
     - pip install -r dev-requirements.txt
diff --git a/Makefile b/Makefile
index adeb5d9..fcc01fb 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ upload-dist:
 doc:
 	rm -rf docs/build
 	rm -rf docs/source/api
-	cd docs; sphinx-apidoc -e -o source/api ../effect ../setup.py ../examples ../effect/test_*.py
+	cd docs; sphinx-apidoc -e -o source/api ../effect ../setup.py ../examples ../effect/test_*.py ../effect/async.py
 	rm docs/source/api/modules.rst
 	rm docs/source/api/effect.rst
 	# can't use sed -i on both linux and mac, so...
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 70023d4..6bc8db6 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,3 +1,5 @@
 testtools
 flake8
 pytest
+sphinx
+sphinx-rtd-theme
diff --git a/docs/source/api/effect.async.rst b/docs/source/api/effect.async.rst
deleted file mode 100644
index feaf833..0000000
--- a/docs/source/api/effect.async.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-effect.async module
-===================
-
-.. automodule:: effect.async
-    :members:
-    :undoc-members:
-    :show-inheritance:
diff --git a/docs/source/api/effect.parallel_async.rst b/docs/source/api/effect.parallel_async.rst
new file mode 100644
index 0000000..b2943df
--- /dev/null
+++ b/docs/source/api/effect.parallel_async.rst
@@ -0,0 +1,7 @@
+effect.parallel\_async module
+=============================
+
+.. automodule:: effect.parallel_async
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/effect/_base.py b/effect/_base.py
index b428032..f94d7d8 100644
--- a/effect/_base.py
+++ b/effect/_base.py
@@ -116,8 +116,8 @@ def perform(dispatcher, effect):
 
     .. [#dispatcher] The dispatcher is passed because some performers need to
        make recursive calls to :func:`perform`, because they need to perform
-       other effects (see :func:`parallel` and :func:`.perform_parallel_async`
-       for an example of this).
+       other effects (see :func:`parallel` and
+       :func:`.parallel_async.perform_parallel_async` for an example of this).
 
     .. [#box] Without using one of those decorators, the performer is actually
        passed three arguments, not two: the dispatcher, the intent, and a
diff --git a/effect/_intents.py b/effect/_intents.py
index dd78243..3c353b4 100644
--- a/effect/_intents.py
+++ b/effect/_intents.py
@@ -26,7 +26,7 @@ class ParallelEffects(object):
     An effect intent that asks for a number of effects to be run in parallel,
     and for their results to be gathered up into a sequence.
 
-    :func:`effect.async.perform_parallel_async` can perform this Intent
+    :func:`effect.parallel_async.perform_parallel_async` can perform this Intent
     assuming all child effects have asynchronous performers.
     :func:`effect.threads.perform_parallel_with_pool` can perform blocking
     performers in a thread pool.
diff --git a/effect/async.py b/effect/async.py
index 32eea71..e94a6aa 100644
--- a/effect/async.py
+++ b/effect/async.py
@@ -1,40 +1,2 @@
-"""Generic asynchronous performers."""
-
-from functools import partial
-from itertools import count
-
-from ._base import perform
-from ._intents import FirstError
-
-
-def perform_parallel_async(dispatcher, intent, box):
-    """
-    A performer for :obj:`ParallelEffects` which works if all child Effects are
-    already asynchronous. Use this for things like Twisted, asyncio, etc.
-
-    WARNING: If this is used when child Effects have blocking performers, it
-    will run them in serial, not parallel.
-    """
-    effects = list(intent.effects)
-    if not effects:
-        box.succeed([])
-        return
-    num_results = count()
-    results = [None] * len(effects)
-
-    def succeed(index, result):
-        results[index] = result
-        if next(num_results) + 1 == len(effects):
-            box.succeed(results)
-
-    def fail(index, result):
-        box.fail((FirstError,
-                  FirstError(exc_info=result, index=index),
-                  result[2]))
-
-    for index, effect in enumerate(effects):
-        perform(
-            dispatcher,
-            effect.on(
-                success=partial(succeed, index),
-                error=partial(fail, index)))
+from .parallel_async import perform_parallel_async
+__all__ = ['perform_parallel_async']
diff --git a/effect/parallel_async.py b/effect/parallel_async.py
new file mode 100644
index 0000000..32eea71
--- /dev/null
+++ b/effect/parallel_async.py
@@ -0,0 +1,40 @@
+"""Generic asynchronous performers."""
+
+from functools import partial
+from itertools import count
+
+from ._base import perform
+from ._intents import FirstError
+
+
+def perform_parallel_async(dispatcher, intent, box):
+    """
+    A performer for :obj:`ParallelEffects` which works if all child Effects are
+    already asynchronous. Use this for things like Twisted, asyncio, etc.
+
+    WARNING: If this is used when child Effects have blocking performers, it
+    will run them in serial, not parallel.
+    """
+    effects = list(intent.effects)
+    if not effects:
+        box.succeed([])
+        return
+    num_results = count()
+    results = [None] * len(effects)
+
+    def succeed(index, result):
+        results[index] = result
+        if next(num_results) + 1 == len(effects):
+            box.succeed(results)
+
+    def fail(index, result):
+        box.fail((FirstError,
+                  FirstError(exc_info=result, index=index),
+                  result[2]))
+
+    for index, effect in enumerate(effects):
+        perform(
+            dispatcher,
+            effect.on(
+                success=partial(succeed, index),
+                error=partial(fail, index)))

Tests fail on python3.7
SInce `async` is a reserved keyword since python 3.7, the tests fails on `from .async import ...` with `SyntaxError`
https://github.com/python-effect/effect/blob/9946975141af6220ed647baf2ca2367fe9152aaf/effect/test_intents.py#L24
https://github.com/python-effect/effect/blob/9946975141af6220ed647baf2ca2367fe9152aaf/effect/test_intents.py#L24

diff --git a/effect/test_async.py b/effect/test_async.py
index 2d04595..898a547 100644
--- a/effect/test_async.py
+++ b/effect/test_async.py
@@ -5,7 +5,7 @@ from testtools.testcase import TestCase
 from ._base import Effect, perform
 from ._dispatcher import ComposedDispatcher, TypeDispatcher
 from ._intents import ParallelEffects, base_dispatcher, parallel
-from .async import perform_parallel_async
+from .parallel_async import perform_parallel_async
 from .test_base import func_dispatcher
 from .test_parallel_performers import ParallelPerformerTestsMixin
 
diff --git a/effect/test_do.py b/effect/test_do.py
index a7ad88f..a097dd5 100644
--- a/effect/test_do.py
+++ b/effect/test_do.py
@@ -70,7 +70,7 @@ def test_yield_non_effect():
         perf(result)
     assert str(err_info.value).startswith(
         '@do functions must only yield Effects or results of '
-        'do_return. Got 1 from <generator object f at')
+        'do_return. Got 1 from <generator object')
 
 
 def test_raise_from_effect():
@@ -161,8 +161,13 @@ def test_stop_iteration_only_local():
         yield Effect(Constant('foo'))
 
     eff = f()
-    with raises(StopIteration):
-        perf(eff)
+    if sys.version_info > (3, 7):
+        # In Python 3.7, generators straight up aren't allowed to raise StopIteration any more
+        with raises(RuntimeError):
+            perf(eff)
+    else:
+        with raises(StopIteration):
+            perf(eff)
 
 
 @mark.skipif(not six.PY3, reason="Testing a Py3-specific feature")
diff --git a/effect/test_intents.py b/effect/test_intents.py
index 2fb2273..749c30a 100644
--- a/effect/test_intents.py
+++ b/effect/test_intents.py
@@ -21,7 +21,7 @@ from ._intents import (
     ParallelEffects, parallel_all_errors)
 from ._sync import sync_perform
 from ._test_utils import MatchesReraisedExcInfo, get_exc_info
-from .async import perform_parallel_async
+from .parallel_async import perform_parallel_async
 from .test_parallel_performers import EquitableException
 
 

pip install -e .[dev]
parse_log_pytest
Pipfile
pytest testtools flake8
3.5
dev-requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
-e git+https://github.com/python-effect/effect.git@9946975141af6220ed647baf2ca2367fe9152aaf#egg=effect
fixtures==4.0.1
flake8==5.0.4
importlib-metadata==4.2.0
iniconfig==1.1.1
mccabe==0.7.0
packaging==21.3
pbr==6.1.1
pipfile==0.0.2
pluggy==1.0.0
py==1.11.0
pycodestyle==2.9.1
pyflakes==2.5.0
pyparsing==3.1.4
pytest==7.0.1
six==1.17.0
testtools==2.6.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: effect
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - pipfile=0.0.2=py_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - fixtures==4.0.1
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - iniconfig==1.1.1
      - mccabe==0.7.0
      - packaging==21.3
      - pbr==6.1.1
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - six==1.17.0
      - testtools==2.6.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/effect

effect/test_async.py::PerformParallelAsyncTests::test_empty effect/test_async.py::PerformParallelAsyncTests::test_error effect/test_async.py::PerformParallelAsyncTests::test_error_index effect/test_async.py::PerformParallelAsyncTests::test_out_of_order effect/test_async.py::PerformParallelAsyncTests::test_parallel effect/test_do.py::test_do_non_gf effect/test_do.py::test_do_return effect/test_do.py::test_do_return_effect effect/test_do.py::test_yield_effect effect/test_do.py::test_fall_off_the_end effect/test_do.py::test_yield_non_effect effect/test_do.py::test_raise_from_effect effect/test_do.py::test_works_with_sync_perform effect/test_do.py::test_promote_metadata effect/test_do.py::test_ignore_lack_of_metadata effect/test_do.py::test_repeatable_effect effect/test_do.py::test_stop_iteration_only_local effect/test_do.py::test_py3_return effect/test_intents.py::test_perform_constant effect/test_intents.py::test_perform_error effect/test_intents.py::test_perform_func effect/test_intents.py::test_perform_func_args_kwargs effect/test_intents.py::test_first_error_str effect/test_intents.py::test_perform_delay_with_sleep effect/test_intents.py::ParallelAllErrorsTests::test_parallel_all_errors
Makefile docs/source/api/effect.async.rst effect/_intents.py effect/_base.py effect/parallel_async.py dev-requirements.txt effect/async.py .travis.yml docs/source/api/effect.parallel_async.rst
Makefile docs/source/api/effect.async.rst effect/_intents.py effect/_base.py effect/parallel_async.py dev-requirements.txt effect/async.py .travis.yml docs/source/api/effect.parallel_async.rst