suned / pfun

Functional, composable, asynchronous, type-safe Python.
https://pfun.dev/
MIT License
151 stars 14 forks source link

tests take far too long to run #66

Closed thomhickey closed 3 years ago

thomhickey commented 3 years ago

The test suite takes so long to run that I probably wouldn't run it very frequently. I'm not sure if this is because of tox or if it's the tests themselves, I haven't looked into it in detail but it makes writing tests quite tedious.

suned commented 3 years ago

You are quite right, but it might not be straight forward to improve. The main reason it takes a long time is https://pypi.org/project/pytest-mypy-plugins/ since I've had to disable mypy caching when testing because the plugin tests would occasionally crash when trying to read from the mypy cache. Even with caching disabled, this error sometimes occur when running tests in parrallel using pytest xdist, which means that xdist has also been disabled making them even slower. Perhaps this issue is fixed in newer versions of pytest-mypy-plugins, in which case this would improve the situation by a margin.

Moreover, tox is setup to run tests for both python 3.7 and 3.8, which is probably not necessary to do during local development. You can run a single test environment using tox -e py37 for example which cuts the time in half. You can probably also skip the mypy plugin tests in local development and enable xdist using pytest -p no:pytest-mypy-plugins -n 4 . That command takes about 27 seconds on my machine.

If you are only changing things related to a specific type like Effect or List it should be enough to run only the tests for that type locally, and only run the entire test suite on circleci. Hope this helps.

I'll keep the issue open as a reminder to look into upgrading pytest-mypy-plugin

suned commented 3 years ago

As an update, I tried the following:

The issue with the mypy cache still persists under those versions. Specifically, what you get when using pytest-mypy-plugin together with xdist is

!!!!!!!!!!!!!!!!!!!! <ExceptionInfo RuntimeError('\'----------------------------------------------------------------------------------------------------\'.../issues\'\n\'----------------------------------------------------------------------------------------------------\'\n') tblen=14>
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/xdist/workermanage.py", line 338, in process_from_remote
INTERNALERROR>     config=self.config, data=kwargs["data"]
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/manager.py", line 87, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 412, in pytest_report_from_serializable
INTERNALERROR>     return TestReport._from_json(data)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 211, in _from_json
INTERNALERROR>     kwargs = _report_kwargs_from_json(reportdict)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 547, in _report_kwargs_from_json
INTERNALERROR>     reportdict["longrepr"]["reprtraceback"]
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 530, in deserialize_repr_traceback
INTERNALERROR>     deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"]
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 530, in <listcomp>
INTERNALERROR>     deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"]
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 525, in deserialize_repr_entry
INTERNALERROR>     _report_unserialization_failure(entry_type, TestReport, reportdict)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/reports.py", line 226, in _report_unserialization_failure
INTERNALERROR>     raise RuntimeError(stream.getvalue())
INTERNALERROR> RuntimeError: '----------------------------------------------------------------------------------------------------'
INTERNALERROR> 'INTERNALERROR: Unknown entry type returned: TraceLastReprEntry'
INTERNALERROR> "report_name: <class '_pytest.reports.TestReport'>"
INTERNALERROR> {'$report_type': 'TestReport',
INTERNALERROR>  'duration': 0.7022934830000018,
INTERNALERROR>  'item_index': 168,
INTERNALERROR>  'keywords': {'immutable_has_correct_constructor': 1,
INTERNALERROR>               'pfun': 1,
INTERNALERROR>               'test_mypy_plugin.yaml': 1,
INTERNALERROR>               'tests/__init__.py': 1},
INTERNALERROR>  'location': ('tests/test_mypy_plugin.yaml',
INTERNALERROR>               None,
INTERNALERROR>               'immutable_has_correct_constructor'),
INTERNALERROR>  'longrepr': {'chain': [({'extraline': None,
INTERNALERROR>                           'reprentries': [{'data': {'lines': ['E   '
INTERNALERROR>                                                               'pytest_mypy_plugins.utils.TypecheckAssertionError: '
INTERNALERROR>                                                               'Invalid '
INTERNALERROR>                                                               'output: ',
INTERNALERROR>                                                               'E   Expected:',
INTERNALERROR>                                                               'E     main:6: '
INTERNALERROR>                                                               'error: Missing '
INTERNALERROR>                                                               'positional '
INTERNALERROR>                                                               'argument '
INTERNALERROR>                                                               '"field" in call '
INTERNALERROR>                                                               'to "C" (diff)',
INTERNALERROR>                                                               'E   Actual:',
INTERNALERROR>                                                               'E     '
INTERNALERROR>                                                               '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/pytest-mypy-ow52mbzo/Traceback '
INTERNALERROR>                                                               '(most recent '
INTERNALERROR>                                                               'call last): '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"/Users/sune/pfun/.venv/bin/mypy", '
INTERNALERROR>                                                               'line 10, in '
INTERNALERROR>                                                               '<module> (diff)',
INTERNALERROR>                                                               'E         '
INTERNALERROR>                                                               'sys.exit(console_entry())                 '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"/Users/sune/pfun/.venv/lib/python3.7/site-packages/mypy/__main__.py", '
INTERNALERROR>                                                               'line 11, in '
INTERNALERROR>                                                               'console_entry '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E         '
INTERNALERROR>                                                               'main(None, '
INTERNALERROR>                                                               'sys.stdout, '
INTERNALERROR>                                                               'sys.stderr)        '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/main.py", '
INTERNALERROR>                                                               'line 90, in '
INTERNALERROR>                                                               'main       '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 180, in '
INTERNALERROR>                                                               'build    (diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 254, in '
INTERNALERROR>                                                               '_build   (diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 2638, in '
INTERNALERROR>                                                               'dispatch (diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 2957, in '
INTERNALERROR>                                                               'process_graph '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 3032, in '
INTERNALERROR>                                                               'process_fresh_modules '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/build.py", '
INTERNALERROR>                                                               'line 1960, in '
INTERNALERROR>                                                               'load_tree '
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E       File '
INTERNALERROR>                                                               '"mypy/metastore.py", '
INTERNALERROR>                                                               'line 98, in '
INTERNALERROR>                                                               'read  (diff)',
INTERNALERROR>                                                               'E     '
INTERNALERROR>                                                               '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/pytest-mypy-ow52mbzo/FileNotFoundError: '
INTERNALERROR>                                                               '[Errno 2] No '
INTERNALERROR>                                                               'such file or '
INTERNALERROR>                                                               'directory: '
INTERNALERROR>                                                               "'/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/.mypy_cache/3.7/asyncio/runners.data.json' "
INTERNALERROR>                                                               '(diff)',
INTERNALERROR>                                                               'E   ',
INTERNALERROR>                                                               'E   Alignment '
INTERNALERROR>                                                               'of first line '
INTERNALERROR>                                                               'difference:',
INTERNALERROR>                                                               'E     E: '
INTERNALERROR>                                                               'main:6: error: '
INTERNALERROR>                                                               'Missing '
INTERNALERROR>                                                               'positional '
INTERNALERROR>                                                               'argument '
INTERNALERROR>                                                               '"field" in call '
INTERNALERROR>                                                               'to "C"...',
INTERNALERROR>                                                               'E     A: '
INTERNALERROR>                                                               '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/...',
INTERNALERROR>                                                               'E        ^'],
INTERNALERROR>                                                     'reprfileloc': {'lineno': 129,
INTERNALERROR>                                                                     'message': '',
INTERNALERROR>                                                                     'path': '/Users/sune/pfun/tests/test_mypy_plugin.yaml'},
INTERNALERROR>                                                     'reprfuncargs': None,
INTERNALERROR>                                                     'reprlocals': None,
INTERNALERROR>                                                     'style': 'short'},
INTERNALERROR>                                            'type': 'TraceLastReprEntry'}],
INTERNALERROR>                           'style': 'short'},
INTERNALERROR>                          {'lineno': 251,
INTERNALERROR>                           'message': '',
INTERNALERROR>                           'path': '/Users/sune/pfun/.venv/lib/python3.7/site-packages/pytest_mypy_plugins/utils.py'},
INTERNALERROR>                          None)],
INTERNALERROR>               'reprcrash': {'lineno': 251,
INTERNALERROR>                             'message': '',
INTERNALERROR>                             'path': '/Users/sune/pfun/.venv/lib/python3.7/site-packages/pytest_mypy_plugins/utils.py'},
INTERNALERROR>               'reprtraceback': {'extraline': None,
INTERNALERROR>                                 'reprentries': [{'data': {'lines': ['E   '
INTERNALERROR>                                                                     'pytest_mypy_plugins.utils.TypecheckAssertionError: '
INTERNALERROR>                                                                     'Invalid '
INTERNALERROR>                                                                     'output: ',
INTERNALERROR>                                                                     'E   '
INTERNALERROR>                                                                     'Expected:',
INTERNALERROR>                                                                     'E     '
INTERNALERROR>                                                                     'main:6: '
INTERNALERROR>                                                                     'error: '
INTERNALERROR>                                                                     'Missing '
INTERNALERROR>                                                                     'positional '
INTERNALERROR>                                                                     'argument '
INTERNALERROR>                                                                     '"field" '
INTERNALERROR>                                                                     'in call '
INTERNALERROR>                                                                     'to "C" '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E   '
INTERNALERROR>                                                                     'Actual:',
INTERNALERROR>                                                                     'E     '
INTERNALERROR>                                                                     '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/pytest-mypy-ow52mbzo/Traceback '
INTERNALERROR>                                                                     '(most '
INTERNALERROR>                                                                     'recent '
INTERNALERROR>                                                                     'call '
INTERNALERROR>                                                                     'last): '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"/Users/sune/pfun/.venv/bin/mypy", '
INTERNALERROR>                                                                     'line 10, '
INTERNALERROR>                                                                     'in '
INTERNALERROR>                                                                     '<module> '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E         '
INTERNALERROR>                                                                     'sys.exit(console_entry())                 '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"/Users/sune/pfun/.venv/lib/python3.7/site-packages/mypy/__main__.py", '
INTERNALERROR>                                                                     'line 11, '
INTERNALERROR>                                                                     'in '
INTERNALERROR>                                                                     'console_entry '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E         '
INTERNALERROR>                                                                     'main(None, '
INTERNALERROR>                                                                     'sys.stdout, '
INTERNALERROR>                                                                     'sys.stderr)        '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/main.py", '
INTERNALERROR>                                                                     'line 90, '
INTERNALERROR>                                                                     'in '
INTERNALERROR>                                                                     'main       '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line 180, '
INTERNALERROR>                                                                     'in '
INTERNALERROR>                                                                     'build    '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line 254, '
INTERNALERROR>                                                                     'in '
INTERNALERROR>                                                                     '_build   '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line '
INTERNALERROR>                                                                     '2638, in '
INTERNALERROR>                                                                     'dispatch '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line '
INTERNALERROR>                                                                     '2957, in '
INTERNALERROR>                                                                     'process_graph '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line '
INTERNALERROR>                                                                     '3032, in '
INTERNALERROR>                                                                     'process_fresh_modules '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/build.py", '
INTERNALERROR>                                                                     'line '
INTERNALERROR>                                                                     '1960, in '
INTERNALERROR>                                                                     'load_tree '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E       '
INTERNALERROR>                                                                     'File '
INTERNALERROR>                                                                     '"mypy/metastore.py", '
INTERNALERROR>                                                                     'line 98, '
INTERNALERROR>                                                                     'in read  '
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E     '
INTERNALERROR>                                                                     '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/pytest-mypy-ow52mbzo/FileNotFoundError: '
INTERNALERROR>                                                                     '[Errno 2] '
INTERNALERROR>                                                                     'No such '
INTERNALERROR>                                                                     'file or '
INTERNALERROR>                                                                     'directory: '
INTERNALERROR>                                                                     "'/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/.mypy_cache/3.7/asyncio/runners.data.json' "
INTERNALERROR>                                                                     '(diff)',
INTERNALERROR>                                                                     'E   ',
INTERNALERROR>                                                                     'E   '
INTERNALERROR>                                                                     'Alignment '
INTERNALERROR>                                                                     'of first '
INTERNALERROR>                                                                     'line '
INTERNALERROR>                                                                     'difference:',
INTERNALERROR>                                                                     'E     E: '
INTERNALERROR>                                                                     'main:6: '
INTERNALERROR>                                                                     'error: '
INTERNALERROR>                                                                     'Missing '
INTERNALERROR>                                                                     'positional '
INTERNALERROR>                                                                     'argument '
INTERNALERROR>                                                                     '"field" '
INTERNALERROR>                                                                     'in call '
INTERNALERROR>                                                                     'to "C"...',
INTERNALERROR>                                                                     'E     A: '
INTERNALERROR>                                                                     '../../../../../../private/var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/...',
INTERNALERROR>                                                                     'E        '
INTERNALERROR>                                                                     '^'],
INTERNALERROR>                                                           'reprfileloc': {'lineno': 129,
INTERNALERROR>                                                                           'message': '',
INTERNALERROR>                                                                           'path': '/Users/sune/pfun/tests/test_mypy_plugin.yaml'},
INTERNALERROR>                                                           'reprfuncargs': None,
INTERNALERROR>                                                           'reprlocals': None,
INTERNALERROR>                                                           'style': 'short'},
INTERNALERROR>                                                  'type': 'TraceLastReprEntry'}],
INTERNALERROR>                                 'style': 'short'},
INTERNALERROR>               'sections': []},
INTERNALERROR>  'nodeid': 'tests/test_mypy_plugin.yaml::immutable_has_correct_constructor',
INTERNALERROR>  'outcome': 'failed',
INTERNALERROR>  'sections': [],
INTERNALERROR>  'testrun_uid': 'a2270c80e91740cfa0b77a59eb58ebf7',
INTERNALERROR>  'user_properties': [],
INTERNALERROR>  'when': 'call',
INTERNALERROR>  'worker_id': 'gw3'}
INTERNALERROR> 'Please report this bug at https://github.com/pytest-dev/pytest/issues'
INTERNALERROR> '----------------------------------------------------------------------------------------------------'
[gw3] node down: <ExceptionInfo RuntimeError('\'----------------------------------------------------------------------------------------------------\'.../issues\'\n\'----------------------------------------------------------------------------------------------------\'\n') tblen=14>
F
replacing crashed worker gw3
gw0 [201] / gw1 [201] / gw2 [201] / gw4 C    INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/main.py", line 269, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/_pytest/main.py", line 323, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/hooks.py", line 286, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/manager.py", line 87, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/xdist/dsession.py", line 112, in pytest_runtestloop
INTERNALERROR>     self.loop_once()
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/xdist/dsession.py", line 135, in loop_once
INTERNALERROR>     call(**kwargs)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/xdist/dsession.py", line 278, in worker_runtest_protocol_complete
INTERNALERROR>     self.sched.mark_test_complete(node, item_index, duration)
INTERNALERROR>   File "/Users/sune/pfun/.venv/lib/python3.7/site-packages/xdist/scheduler/load.py", line 151, in mark_test_complete
INTERNALERROR>     self.node2pending[node].remove(item_index)
INTERNALERROR> KeyError: <WorkerController gw3>

Which ultimately boils down to the test failing because mypy is crashing with FileNotFoundError when trying to read e.g /var/folders/6c/nr1b_04d1335cl1f68ksy2q00000gn/T/.mypy_cache/3.7/asyncio/runners.data.json. Most annoyingly, the error only happens sometimes.

suned commented 3 years ago

@thomhickey TLDR: The tests should be much faster now. I still think its a good idea to just run the tests for one python environment locally (e.g with poetry run tox -e py38) and only run both environments on circleci.

I managed to get pytest-mypy-plugin to play nicely with xdist which improved the test speeds by a margin. Turns out they are ignoring some configuration in the mypy.ini file regarding caching, but its possible to turn off the cache by adding a configuration option to each test in the yml. But pytest-mypy-plugin is still the bottleneck for test speed, and I think they could be even faster than they are now. As far as I can tell, they write each test case to a temp file (!) and invoke mypy as a subprocess. I think it might be worth having a look at this alternative pytest-mypy-plugin, that uses the mypy python api instead, which should be faster.

On a side note, I also improved the circleci test speed quite substantially.

thomhickey commented 3 years ago

Wonderful, thanks so much @suned ... I will get the latest and try it out. I've been buried in Javascript front-end projects lately, but I'm backing them with FastApi so I can definitely start to use pfun in these projects.