How to use pytest with pyclip inside a tox python env?

165 views Asked by At

I'm using Pyperclip to put something into the clipboard and testing it with Pytest. I'm using GitHub actions for CI/CD that runs tox. Running pytest on my local system works just fine, but doing the same in tox fails because of the following display errors

    @pytest.mark.parametrize(
        ("rs", "ip", "port", "expected"),
        (("Something {} {}", "192", "1234", "Something 192 1234"),),
    )
    def test_rs_clipper(rs, ip, port, expected):
>       provide_rs(rs, ip, port)

tests/test_langhandler.py:39: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/python3.6/lib/python3.9/site-packages/rst/lang_handler.py:130: in provide_rs
    pyperclip.copy(f"{rs.format(ip,port)}")
.tox/python3.6/lib/python3.9/site-packages/pyperclip/__init__.py:618: in lazy_load_stub_copy
    return copy(text)

I'm assuming it's because of the virtual environment tox is creating. How can I simulate a display environment so at least the pyperclip functionality works?

Output for tox -rvv

setting PATH=/opt/rst/.tox/python3.6/bin:/opt/rst/venv/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/share/games:/usr/local/sbin:/usr/sbin:/sbin:/root/.local/bin:/snap/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/share/games:/usr/local/sbin:/usr/sbin:/sbin:/root/.local/bin:/snap/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[1512278] /opt/rst$ /opt/rst/.tox/python3.6/bin/python -m pip freeze >.tox/python3.6/log/python3.6-0.log
python3.6 finish: envreport  after 0.57 seconds
python3.6 installed: attrs==21.4.0,black==22.3.0,certifi==2021.10.8,charset-normalizer==2.0.12,click==8.1.2,commonmark==0.9.1,coverage==6.3.2,coverage-badge==1.1.0,distlib==0.3.4,filelock==3.6.0,flake8==4.0.1,idna==3.3,iniconfig==1.1.1,keyboard==0.13.5,mccabe==0.6.1,mypy==0.942,mypy-extensions==0.4.3,netifaces==0.10.9,packaging==21.3,pathspec==0.9.0,platformdirs==2.5.2,pluggy==1.0.0,py==1.11.0,pycodestyle==2.8.0,pyflakes==2.4.0,Pygments==2.12.0,pyngrok==5.1.0,pyparsing==3.0.8,pyperclip==1.8.0,pytest==7.1.1,pytest-cov==3.0.0,python-coveralls==2.9.3,PyYAML==6.0,requests==2.27.1,reverse-shell-tool @ file:///opt/rst/.tox/.tmp/package/1/reverse-shell-tool-1.0.2.tar.gz,rich==12.2.0,simple-term-menu==1.4.1,six==1.16.0,toml==0.10.2,tomli==2.0.1,tox==3.25.0,typing_extensions==4.2.0,urllib3==1.26.9,virtualenv==20.14.1
python3.6 start: run-test-pre 
python3.6 run-test-pre: PYTHONHASHSEED='2092568237'
python3.6 finish: run-test-pre  after 0.00 seconds
python3.6 start: run-test 
python3.6 run-test: commands[0] | pytest --cov=/opt/rst/.tox/python3.6/tmp/src --basetemp=/opt/rst/.tox/python3.6/tmp
setting PATH=/opt/rst/.tox/python3.6/bin:/opt/rst/venv/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/share/games:/usr/local/sbin:/usr/sbin:/sbin:/root/.local/bin:/snap/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/share/games:/usr/local/sbin:/usr/sbin:/sbin:/root/.local/bin:/snap/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[1512279] /opt/rst$ /opt/rst/.tox/python3.6/bin/pytest --cov=/opt/rst/.tox/python3.6/tmp/src --basetemp=/opt/rst/.tox/python3.6/tmp
============================================================== test session starts ===============================================================
platform linux -- Python 3.9.2, pytest-7.1.1, pluggy-1.0.0
cachedir: .tox/python3.6/.pytest_cache
rootdir: /opt/rst, configfile: pyproject.toml, testpaths: tests
plugins: cov-3.0.0
collected 2 items                                                                                                                                

tests/test_langhandler.py .F                                                                                                               [100%]/opt/rst/.tox/python3.6/lib/python3.9/site-packages/coverage/inorout.py:519: CoverageWarning: Module /opt/rst/.tox/python3.6/tmp/src was never imported. (module-not-imported)
  self.warn(f"Module {pkg} was never imported.", slug="module-not-imported")
/opt/rst/.tox/python3.6/lib/python3.9/site-packages/coverage/control.py:793: CoverageWarning: No data was collected. (no-data-collected)
  self._warn("No data was collected.", slug="no-data-collected")
WARNING: Failed to generate report: No data to report.

/opt/rst/.tox/python3.6/lib/python3.9/site-packages/pytest_cov/plugin.py:294: CovReportWarning: Failed to generate report: No data to report.

  self.cov_controller.finish()


==================================================================== FAILURES ====================================================================
__________________________________________ test_rs_clipper[Something {} {}-192-1234-Something 192 1234] __________________________________________

rs = 'Something {} {}', ip = '192', port = '1234', expected = 'Something 192 1234'

    @pytest.mark.parametrize(
        ("rs", "ip", "port", "expected"),
        (("Something {} {}", "192", "1234", "Something 192 1234"),),
    )
    def test_rs_clipper(rs, ip, port, expected):
>       provide_rs(rs, ip, port)

tests/test_langhandler.py:39: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.tox/python3.6/lib/python3.9/site-packages/rst/lang_handler.py:130: in provide_rs
    pyperclip.copy(f"{rs.format(ip,port)}")
.tox/python3.6/lib/python3.9/site-packages/pyperclip/__init__.py:618: in lazy_load_stub_copy
    return copy(text)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pyperclip.init_no_clipboard.<locals>.ClipboardUnavailable object at 0x7f35938c1970>, args = ('Something 192 1234',), kwargs = {}

    def __call__(self, *args, **kwargs):
>       raise PyperclipException(EXCEPT_MSG)
E       pyperclip.PyperclipException: 
E           Pyperclip could not find a copy/paste mechanism for your system.
E           For more information, please visit https://pyperclip.readthedocs.io/en/latest/index.html#not-implemented-error

.tox/python3.6/lib/python3.9/site-packages/pyperclip/__init__.py:303: PyperclipException

----------- coverage: platform linux, python 3.9.2-final-0 -----------

============================================================ short test summary info =============================================================
FAILED tests/test_langhandler.py::test_rs_clipper[Something {} {}-192-1234-Something 192 1234] - pyperclip.PyperclipException: 
========================================================== 1 failed, 1 passed in 0.19s ===========================================================
ERROR: InvocationError for command /opt/rst/.tox/python3.6/bin/pytest --cov=/opt/rst/.tox/python3.6/tmp/src --basetemp=/opt/rst/.tox/python3.6/tmp (exited with code 1)
python3.6 finish: run-test  after 0.59 seconds
python3.6 start: run-test-post 
python3.6 finish: run-test-post  after 0.00 seconds
1

There are 1 answers

3
Kale Kundert On BEST ANSWER

I haven't used pyperclip in CI/CD before, so I might be totally wrong, but my guess is that you need to start an X server within a virtual frame buffer. Here's a link to one of my projects that does this, but I've copied the important part below:

# .github/workflows/test_and_release.yml
...
jobs:
  test:
    ...
    steps:
      - name: Run test suite
        uses: GabrielBB/[email protected]
        with:
          run: |
            pytest tests --cov glooey
    ...
...

Basically, I just use GabrielBB/xvfb-action and it sets everything up for me. Again, I'm not sure that this will work for you, but I figured I'd put it out there just in case.


Edit: Starting an X server is just the first step, you also need to provide pyperclip with a way to access the clipboard, i.e. by installing xsel, xclip, gtk, or qt. See this stack overflow post for more details, but basically you need to add something like this to your CI workflow:

jobs:
  test:
    ...
    steps:
      - name: Install test dependencies
        run: sudo apt-get install xclip
    ...
...