Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Miscellaneous operating system functions """
from posix.unistd cimport dup, dup2, close from cpython.exc cimport PyErr_SetFromErrno
import os import contextlib
def have_program(program, path=None): """ Return ``True`` if a ``program`` executable is found in the path given by ``path``.
INPUT:
- ``program`` - a string, the name of the program to check.
- ``path`` - string or None. Paths to search for ``program``, separated by ``os.pathsep``. If ``None``, use the :envvar:`PATH` environment variable.
OUTPUT: bool
EXAMPLES::
sage: from sage.misc.sage_ostools import have_program sage: have_program('ls') True sage: have_program('there_is_not_a_program_with_this_name') False sage: have_program('sage', path=SAGE_ROOT) True sage: have_program('ls', path=SAGE_ROOT) False """ except OSError: pass
@contextlib.contextmanager def restore_cwd(chdir=None): """ Context manager that restores the original working directory upon exiting.
INPUT:
- ``chdir`` -- optionally change directories to the given directory upon entering the context manager
EXAMPLES:
sage: import os sage: from sage.misc.sage_ostools import restore_cwd sage: from sage.misc.misc import SAGE_TMP sage: cwd = os.getcwd() sage: with restore_cwd(str(SAGE_TMP)): ....: print(os.getcwd() == SAGE_TMP) True sage: cwd == os.getcwd() True """ finally:
cdef file_and_fd(x, int* fd): """ If ``x`` is a file, return ``x`` and set ``*fd`` to its file descriptor. If ``x`` is an integer, return ``None`` and set ``*fd`` to ``x``. Otherwise, set ``*fd = -1`` and raise a ``TypeError``. """
cdef class redirection: r""" Context to implement redirection of files, analogous to the ``>file`` or ``1>&2`` syntax in POSIX shells.
Unlike the ``redirect_stdout`` and ``redirect_stderr`` contexts in the Python 3.4 standard library, this acts on the OS level, not on the Python level. This implies that it only works for true files, not duck-type file objects such as ``StringIO``.
INPUT:
- ``source`` -- the file to be redirected
- ``dest`` -- where the source file should be redirected to
- ``close`` -- (boolean, default: ``True``) whether to close the destination file upon exiting the context. This is only supported if ``dest`` is a Python file.
The ``source`` and ``dest`` arguments can be either Python files or file descriptors.
EXAMPLES::
sage: from sage.misc.sage_ostools import redirection sage: fn = tmp_filename()
::
sage: with redirection(sys.stdout, open(fn, 'w')): ....: print("hello world!") sage: with open(fn) as f: ....: sys.stdout.write(f.read()) hello world!
We can do the same using a file descriptor as source::
sage: fd = sys.stdout.fileno() sage: with redirection(fd, open(fn, 'w')): ....: _ = os.write(fd, "hello world!\n") sage: with open(fn) as f: ....: sys.stdout.write(f.read()) hello world!
The converse also works::
sage: with open(fn, 'w') as f: ....: f.write("This goes to the file\n") ....: with redirection(f, sys.stdout, close=False): ....: f.write("This goes to stdout\n") ....: f.write("This goes to the file again\n") This goes to stdout sage: with open(fn) as f: ....: sys.stdout.write(f.read()) This goes to the file This goes to the file again
The same :class:`redirection` instance can be reused multiple times, provided that ``close=False``::
sage: f = open(fn, 'w+') sage: r = redirection(sys.stdout, f, close=False) sage: with r: ....: print("Line 1") sage: with r: ....: print("Line 2") sage: with f: ....: f.seek(0) ....: sys.stdout.write(f.read()) Line 1 Line 2
The redirection also works for subprocesses::
sage: import subprocess sage: with redirection(sys.stdout, open(fn, 'w')): ....: _ = subprocess.call(["echo", "hello world"]) sage: with open(fn) as f: ....: sys.stdout.write(f.read()) hello world
TESTS::
sage: from six.moves import cStringIO as StringIO sage: redirection(sys.stdout, StringIO()) Traceback (most recent call last): ... TypeError: <...> must be a Python file or an integer
The redirection is removed and the destination file is closed even in the case of errors::
sage: f = open(os.devnull, 'w') sage: with redirection(sys.stdout, f): ....: raise KeyboardInterrupt Traceback (most recent call last): ... KeyboardInterrupt sage: f.closed True
Reusing a :class:`redirection` instance with ``close=True``::
sage: f = open(fn, 'w+') sage: r = redirection(sys.stdout, f, close=True) sage: with r: ....: print("Line 1") sage: with r: ....: print("Line 2") Traceback (most recent call last): ... ValueError: invalid destination file
Using ``close=True`` on a non-file::
sage: redirection(sys.stdout, 0, close=True) Traceback (most recent call last): ... ValueError: close=True requires a Python destination file
Passing invalid file descriptors::
sage: with redirection(-123, open(os.devnull, 'w')): ....: pass Traceback (most recent call last): ... OSError: [Errno 9] Bad file descriptor sage: with redirection(sys.stdout, -123): ....: pass Traceback (most recent call last): ... OSError: [Errno 9] Bad file descriptor
Nesting the same :class:`redirection` object is not allowed::
sage: with redirection(sys.stdout, open(os.devnull, 'w')) as r: ....: with r: ....: pass Traceback (most recent call last): ... ValueError: source already redirected """ cdef readonly source_file, dest_file cdef readonly int source_fd, dest_fd, dup_source_fd cdef bint close_dest
def __cinit__(self):
def __init__(self, source, dest, close=None):
cdef int flush(self) except -1:
def __enter__(self): # Basic sanity checks raise ValueError("invalid source file")
finally: close(dupsrc)
def __exit__(self, *args): finally: PyErr_SetFromErrno(OSError) |