2
\$\begingroup\$

I have a use case, where I want to take screenshots via scrot without leaving behind residual files:

"""Takes screenshots."""

from pathlib import Path
from subprocess import check_call
from tempfile import TemporaryDirectory

from digsigclt.os.posix.common import SCROT
from digsigclt.types import Screenshot


__all__ = ['screenshot']


JPEG = 'image/jpeg'
FORMATS = {
    'jpe': JPEG,
    'jpeg': JPEG,
    'jpg': JPEG,
    'png': 'image/png',
    'gif': 'image/gif'
}


def screenshot(filetype: str = 'jpg', display: str = ':0',
               quality: int = None, multidisp: bool = False,
               pointer: bool = False) -> Screenshot:
    """Takes a screenshot."""

    try:
        content_type = FORMATS[filetype]
    except KeyError:
        raise ValueError('Invalid image file type.') from None

    command = [SCROT, '--silent', '--display', display]

    if quality is not None:
        command += ['--quality', str(quality)]

    if multidisp:
        command.append('--multidisp')

    if pointer:
        command.append('--pointer')

    with TemporaryDirectory() as tmpd:
        tmpfile = Path(tmpd).joinpath(f'digsigclt-screenshot.{filetype}')
        command.append(str(tmpfile))
        check_call(command)

        with tmpfile.open('rb') as file:
            return Screenshot(file.read(), content_type)

The code works as intended. What bothers me, however, is the workaround that I had to take using TemporaryDirectory. All tests using a ǸamedTemporaryFile directly failed due to the fact, that python opens the target file before the subprocess writes to it, so that a subsequent read() would return an empty bytes object. Is there a way to improve the intermediate file handling without the TemporaryDirectory workaround and without opening and closing the intermediate file more than once?

\$\endgroup\$
2
  • 2
    \$\begingroup\$ The problem is that scrot silently doesn't write to an existing file, not that you can't read the contents. You could just run it in the temporary directory and do -e 'echo $n' to get the file it wrote to ... or you keep doing what you are doing right now. Consider just generating a random filename in /tmp though, that's probably easier. Or you know, use a different tool or even a Python library for the screenshot if you really want to avoid the temporary files altogether. \$\endgroup\$
    – ferada
    Commented Apr 7, 2021 at 10:31
  • 2
    \$\begingroup\$ Aaah. Thanks for the hint. I just realized that scrot has the --overwrite option for that. I think my solution lies there. \$\endgroup\$ Commented Apr 7, 2021 at 20:17

1 Answer 1

2
\$\begingroup\$

All tests using a NamedTemporaryFile directly failed

The semantics for NamedTemporaryFile are a bit involved; you might want to read the fine manual, especially the per-OS behavior around the delete flag.

Given that by default scrot won't write to a file that exists, you may wish to unlink() prior to forking off a child.

annotation

def screenshot( ... ,
               quality: int = None, ...

Please prefer quality: int | None = None for this optional parameter.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.