Module rsyscall.stdlib.mktemp

Functions for making temporary directories.

Expand source code Browse git
"Functions for making temporary directories."
import random
import string
from rsyscall.thread import Thread
from rsyscall.path import Path
from rsyscall.handle import WrittenPointer
import os
import typing as t

def random_string(k: int=8) -> str:
    "Return a random string - useful for making files that don't conflict with others."
    return ''.join(random.choices(string.ascii_letters + string.digits, k=k))

async def update_symlink(thr: Thread, path: WrittenPointer[Path],
                         target: t.Union[str, os.PathLike]) -> WrittenPointer[Path]:
    "Atomically update this path to contain a symlink pointing at this target."
    tmpname = path.value.name + ".updating." + random_string(k=8)
    tmppath = await thr.ram.ptr(path.value.parent/tmpname)
    await thr.task.symlink(await thr.ram.ptr(target), tmppath)
    await thr.task.rename(tmppath, path)
    return path

async def mkdtemp(thr: Thread, prefix: str="mkdtemp") -> 'TemporaryDirectory':
    "Make a temporary directory in thr.environ.tmpdir."
    parent = thr.environ.tmpdir
    name = prefix+"."+random_string(k=8)
    await thr.task.mkdir(await thr.ram.ptr(parent/name), 0o700)
    return TemporaryDirectory(thr, parent, name)

class TemporaryDirectory:
    "A temporary directory we've created and are responsible for cleaning up."
    def __init__(self, thr: Thread, parent: Path, name: str) -> None:
        "Don't directly instantiate, use rsyscall.mktemp.mkdtemp to create this class."
        self.thr = thr
        self.parent = parent
        self.name = name
        self.path = parent/name

    async def cleanup(self) -> None:
        """Delete this temporary directory and everything inside it.

        We do this cleanup by execing sh; that's the cheapest way to do it.  We have to
        chmod -R +w the directory before we rm -rf it, because the directory might contain
        files without the writable bit set, which would prevent us from deleting it.

        """
        # TODO would be nice if not sharing the fs information gave us a cap to chdir
        cleanup = await self.thr.clone()
        await cleanup.task.chdir(await cleanup.ram.ptr(self.parent))
        child = await cleanup.exec(self.thr.environ.sh.args(
            '-c', f"chmod -R +w -- {self.name} && rm -rf -- {self.name}"))
        await child.check()

    async def __aenter__(self) -> Path:
        return self.path

    async def __aexit__(self, *args, **kwargs):
        await self.cleanup()

Functions

def random_string(k: int = 8) ‑> str

Return a random string - useful for making files that don't conflict with others.

Expand source code Browse git
def random_string(k: int=8) -> str:
    "Return a random string - useful for making files that don't conflict with others."
    return ''.join(random.choices(string.ascii_letters + string.digits, k=k))

Atomically update this path to contain a symlink pointing at this target.

Expand source code Browse git
async def update_symlink(thr: Thread, path: WrittenPointer[Path],
                         target: t.Union[str, os.PathLike]) -> WrittenPointer[Path]:
    "Atomically update this path to contain a symlink pointing at this target."
    tmpname = path.value.name + ".updating." + random_string(k=8)
    tmppath = await thr.ram.ptr(path.value.parent/tmpname)
    await thr.task.symlink(await thr.ram.ptr(target), tmppath)
    await thr.task.rename(tmppath, path)
    return path
async def mkdtemp(thr: Thread, prefix: str = 'mkdtemp') ‑> TemporaryDirectory

Make a temporary directory in thr.environ.tmpdir.

Expand source code Browse git
async def mkdtemp(thr: Thread, prefix: str="mkdtemp") -> 'TemporaryDirectory':
    "Make a temporary directory in thr.environ.tmpdir."
    parent = thr.environ.tmpdir
    name = prefix+"."+random_string(k=8)
    await thr.task.mkdir(await thr.ram.ptr(parent/name), 0o700)
    return TemporaryDirectory(thr, parent, name)

Classes

class TemporaryDirectory (thr: Thread, parent: Path, name: str)

A temporary directory we've created and are responsible for cleaning up.

Don't directly instantiate, use rsyscall.mktemp.mkdtemp to create this class.

Expand source code Browse git
class TemporaryDirectory:
    "A temporary directory we've created and are responsible for cleaning up."
    def __init__(self, thr: Thread, parent: Path, name: str) -> None:
        "Don't directly instantiate, use rsyscall.mktemp.mkdtemp to create this class."
        self.thr = thr
        self.parent = parent
        self.name = name
        self.path = parent/name

    async def cleanup(self) -> None:
        """Delete this temporary directory and everything inside it.

        We do this cleanup by execing sh; that's the cheapest way to do it.  We have to
        chmod -R +w the directory before we rm -rf it, because the directory might contain
        files without the writable bit set, which would prevent us from deleting it.

        """
        # TODO would be nice if not sharing the fs information gave us a cap to chdir
        cleanup = await self.thr.clone()
        await cleanup.task.chdir(await cleanup.ram.ptr(self.parent))
        child = await cleanup.exec(self.thr.environ.sh.args(
            '-c', f"chmod -R +w -- {self.name} && rm -rf -- {self.name}"))
        await child.check()

    async def __aenter__(self) -> Path:
        return self.path

    async def __aexit__(self, *args, **kwargs):
        await self.cleanup()

Methods

async def cleanup(self) ‑> NoneType

Delete this temporary directory and everything inside it.

We do this cleanup by execing sh; that's the cheapest way to do it. We have to chmod -R +w the directory before we rm -rf it, because the directory might contain files without the writable bit set, which would prevent us from deleting it.

Expand source code Browse git
async def cleanup(self) -> None:
    """Delete this temporary directory and everything inside it.

    We do this cleanup by execing sh; that's the cheapest way to do it.  We have to
    chmod -R +w the directory before we rm -rf it, because the directory might contain
    files without the writable bit set, which would prevent us from deleting it.

    """
    # TODO would be nice if not sharing the fs information gave us a cap to chdir
    cleanup = await self.thr.clone()
    await cleanup.task.chdir(await cleanup.ram.ptr(self.parent))
    child = await cleanup.exec(self.thr.environ.sh.args(
        '-c', f"chmod -R +w -- {self.name} && rm -rf -- {self.name}"))
    await child.check()