pytestでpytest-profilingを使っているときにでる ValueError: Plugin already registered

概要

$ make pytest
if [ -z 1588034257 ] || [ 1587982138 -ge 1588034257 ]; then \
        docker build . -t test; \
else \
        echo "Docker container image is ready up to date."; \
fi
Docker container image is ready up to date.
docker run --rm -it test pytest --color=no tests/
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 495, in _importconftest
    return self._conftestpath2mod[key]
KeyError: PosixPath('/test/conftest.py')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/bin/pytest", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 105, in main
    config = _prepareconfig(args, plugins)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 257, in _prepareconfig
    return pluginmanager.hook.pytest_cmdline_parse(
  File "/usr/local/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 203, in _multicall
    gen.send(outcome)
  File "/usr/local/lib/python3.8/site-packages/_pytest/helpconfig.py", line 90, in pytest_cmdline_parse
    config = outcome.get_result()
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 836, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1044, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 1001, in _preparse
    self.hook.pytest_load_initial_conftests(
  File "/usr/local/lib/python3.8/site-packages/pluggy/hooks.py", line 286, in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
    self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
    return outcome.get_result()
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
    raise ex[1].with_traceback(ex[2])
  File "/usr/local/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
    res = hook_impl.function(*args)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 899, in pytest_load_initial_conftests
    self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 441, in _set_initial_conftests
    self._try_load_conftest(anchor)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 447, in _try_load_conftest
    self._getconftestmodules(anchor)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 473, in _getconftestmodules
    mod = self._importconftest(conftestpath)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 520, in _importconftest
    self.consider_conftest(mod)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 575, in consider_conftest
    self.register(conftestmodule, name=conftestmodule.__file__)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 386, in register
    self.consider_module(plugin)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 581, in consider_module
    self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 586, in _import_plugin_specs
    self.import_plugin(import_spec)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 629, in import_plugin
    self.register(mod, modname)
  File "/usr/local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 379, in register
    ret = super().register(plugin, name)
  File "/usr/local/lib/python3.8/site-packages/pluggy/manager.py", line 104, in register
    raise ValueError(
ValueError: Plugin already registered: pytest_profiling=<module 'pytest_profiling' from '/usr/local/lib/python3.8/site-packages/pytest_profiling.py'>
{'140074314182224': <_pytest.config.PytestPluginManager object at 0x7f6597bd2e50>, 'pytestconfig': <_pytest.config.Config object at 0x7f6597170220>, 'mark': <module '_pytest.mark' from '/usr/local/lib/python3.8/site-packages/_pytest/mark/__init__.py'>, 'main': <module '_pytest.main' from '/usr/local/lib/python3.8/site-packages/_pytest/main.py'>, 'runner': <module '_pytest.runner' from '/usr/local/lib/python3.8/site-packages/_pytest/runner.py'>, 'fixtures': <module '_pytest.fixtures' from '/usr/local/lib/python3.8/site-packages/_pytest/fixtures.py'>, 'helpconfig': <module '_pytest.helpconfig' from '/usr/local/lib/python3.8/site-packages/_pytest/helpconfig.py'>, 'python': <module '_pytest.python' from '/usr/local/lib/python3.8/site-packages/_pytest/python.py'>, 'terminal': <module '_pytest.terminal' from '/usr/local/lib/python3.8/site-packages/_pytest/terminal.py'>, 'debugging': <module '_pytest.debugging' from '/usr/local/lib/python3.8/site-packages/_pytest/debugging.py'>, 'unittest': <module '_pytest.unittest' from '/usr/local/lib/python3.8/site-packages/_pytest/unittest.py'>, 'capture': <module '_pytest.capture' from '/usr/local/lib/python3.8/site-packages/_pytest/capture.py'>, 'skipping': <module '_pytest.skipping' from '/usr/local/lib/python3.8/site-packages/_pytest/skipping.py'>, 'tmpdir': <module '_pytest.tmpdir' from '/usr/local/lib/python3.8/site-packages/_pytest/tmpdir.py'>, 'monkeypatch': <module '_pytest.monkeypatch' from '/usr/local/lib/python3.8/site-packages/_pytest/monkeypatch.py'>, 'recwarn': <module '_pytest.recwarn' from '/usr/local/lib/python3.8/site-packages/_pytest/recwarn.py'>, 'pastebin': <module '_pytest.pastebin' from '/usr/local/lib/python3.8/site-packages/_pytest/pastebin.py'>, 'nose': <module '_pytest.nose' from '/usr/local/lib/python3.8/site-packages/_pytest/nose.py'>, 'assertion': <module '_pytest.assertion' from '/usr/local/lib/python3.8/site-packages/_pytest/assertion/__init__.py'>, 'junitxml': <module '_pytest.junitxml' from '/usr/local/lib/python3.8/site-packages/_pytest/junitxml.py'>, 'resultlog': <module '_pytest.resultlog' from '/usr/local/lib/python3.8/site-packages/_pytest/resultlog.py'>, 'doctest': <module '_pytest.doctest' from '/usr/local/lib/python3.8/site-packages/_pytest/doctest.py'>, 'cacheprovider': <module '_pytest.cacheprovider' from '/usr/local/lib/python3.8/site-packages/_pytest/cacheprovider.py'>, 'freeze_support': <module '_pytest.freeze_support' from '/usr/local/lib/python3.8/site-packages/_pytest/freeze_support.py'>, 'setuponly': <module '_pytest.setuponly' from '/usr/local/lib/python3.8/site-packages/_pytest/setuponly.py'>, 'setupplan': <module '_pytest.setupplan' from '/usr/local/lib/python3.8/site-packages/_pytest/setupplan.py'>, 'stepwise': <module '_pytest.stepwise' from '/usr/local/lib/python3.8/site-packages/_pytest/stepwise.py'>, 'warnings': <module '_pytest.warnings' from '/usr/local/lib/python3.8/site-packages/_pytest/warnings.py'>, 'logging': <module '_pytest.logging' from '/usr/local/lib/python3.8/site-packages/_pytest/logging.py'>, 'reports': <module '_pytest.reports' from '/usr/local/lib/python3.8/site-packages/_pytest/reports.py'>, 'faulthandler': <module '_pytest.faulthandler' from '/usr/local/lib/python3.8/site-packages/_pytest/faulthandler.py'>, 'profiling': <module 'pytest_profiling' from '/usr/local/lib/python3.8/site-packages/pytest_profiling.py'>, 'capturemanager': <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=5 _state='started' tmpfile=<_pytest.capture.EncodedFile object at 0x7f6596ed0220>> err=<FDCapture 2 oldfd=6 _state='started' tmpfile=<_pytest.capture.EncodedFile object at 0x7f6596ed02b0>> in_=<FDCapture 0 oldfd=3 _state='started' tmpfile=<_io.TextIOWrapper name='/dev/null' mode='r' encoding='UTF-8'>> _state='started' _in_suspended=False> _capture_fixture=None>, '/test/conftest.py': <module 'conftest' from '/test/conftest.py'>}
make: *** [Makefile:26: pytest] Error 1/conftest.py'>}
make: *** [Makefile:26: pytest] Error 1

とりあえずpytest-profilingを無効にして、testを行えるようにしようと思ったが、conftest.pyを消してdockerイメージをビルドしても直らなかった。

.pyc をすべて消す方法は効かなかった https://github.com/pytest-dev/pytest/issues/3112#issuecomment-404979798

Docker imageを消してから作り直してみても効かなかった

dangling image を消して再度pytestを動かしたら治った

$ docker image prune

疑問

  • docker rmi では解決せず、 docker image prune では解決したことから、原因は中間イメージレイヤの変更であると考えられる

    • docker rmi では中間イメージレイヤは削除されないと仮定するとこういう演繹ができる
  • しかし、中間レイヤは読み込み専用のはず

  • ファイルを変更するたびに毎回イメージをビルドし直す方法をとっていたが、Dockerfileの COPY コマンド以降には ENVコマンドしかないのでファイルシステムに変更はないはず
  • なぜ中間イメージレイヤが変更されたのか?
  • 再度conftest.pyを配置してイメージをビルドしても同じエラーが出た。以前は使用可能だった。

2020-04-28 13:40 追記

  • conftest.pyを消去してもprofileは動作したので、

If a plugin is installed, pytest automatically finds and integrates it, there is no need to activate it.

Requiring plugins using a pytest_plugins variable in non-root conftest.py files is deprecated. See full explanation in the Writing plugins section.

が関係しそう。conftest.pyはrootに配置しているはずではあるが。

docs.pytest.org