#!/usr/bin/env python # # __init__.py - Miscellaneous functions used throughout fsl_ci. # # Author: Paul McCarthy # import os.path as op import os import errno import shlex import string import time import tempfile import contextlib as ctxlib import subprocess as sp __version__ = '0.1.0' """Current version of the fsl-ci-rules.""" USERNAME = 'fsl-ci-rules' """Username to be used for all git interactions which require one. """ EMAIL = 'fsl-ci-rules@git.fmrib.ox.ac.uk' """Password to be used for all git interactions which require one. """ def fprint(*args, **kwargs): """Print with flush=True. """ print(*args, **kwargs, flush=True) @ctxlib.contextmanager def tempdir(): """Context manager to create, and change into, a temporary directory, and then afterwards delete it and change back to the original working directory. """ with tempfile.TemporaryDirectory() as td: prevdir = os.getcwd() os.chdir(td) try: yield td finally: os.chdir(prevdir) @ctxlib.contextmanager def indir(dirname): """Context manager to change into a directory, and then afterwards change back to the original working directory. """ prevdir = os.getcwd() os.chdir(dirname) try: yield finally: os.chdir(prevdir) def sprun(cmd, **kwargs): """Runs the given command with subprocess.run. """ fprint(f'Running {cmd}') cmd = shlex.split(cmd) return sp.run(cmd, check=True, **kwargs) def is_valid_project_version(version): """Return True if the given version/tag is "valid" - it must be a sequence of integers, separated by periods, with an optional leading 'v'. """ if version.lower().startswith('v'): version = version[1:] for part in version.split('.'): if not all([c in string.digits for c in part]): return False return True @ctxlib.contextmanager def lockdir(dirname): """Primitive mechanism by which concurrent access to a directory can be prevented. Attempts to create a semaphore file in the directory, but waits if that file already exists. Removes the file when finished. """ delay = 10 lockfile = op.join(dirname, '.fsl_ci.lockdir') while True: try: fprint(f'Attempting to lock {dirname} for exclusive access.') fd = os.open(os.O_CREAT | os.O_EXCL | os.O_RDWR) break except OSError as e: if e.errno != errno.EEXIST: raise e fprint(f'{dirname} is already locked - ' f'trying again in {delay} seconds ...') time.sleep(10) fprint(f'Exclusive access acquired for {dirname} ...') try: yield finally: fprint(f'Relinquishing lock on {dirname}') os.close( fd) os.unlink(lockfile)