diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7811478..cef097c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - pyver: ["3.8", "3.9", "3.10", "3.11", "3.12"] + pyver: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] compiler: [gcc] include: - os: macos-latest @@ -26,6 +26,12 @@ jobs: - os: macos-latest pyver: "3.12" compiler: clang + - os: macos-latest + pyver: "3.13" + compiler: clang + - os: macos-latest + pyver: "3.14" + compiler: clang runs-on: ${{ matrix.os }} diff --git a/RELEASE_NOTES b/RELEASE_NOTES index a768eec..76d830b 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -4,6 +4,7 @@ Enhancements - numpy_util.ahelp can now show dict of arrays + - ostools.DirStack can be used as a context Bug Fixes diff --git a/esutil/ostools.py b/esutil/ostools.py index 1c3c672..1cf0e22 100644 --- a/esutil/ostools.py +++ b/esutil/ostools.py @@ -12,42 +12,17 @@ directory stack in Unix shells. See the documentation for the DirStack class for more details. - Example: - >>> ds=esutil.ostools.DirStack(verbose=True) - >>> ds.push('~/data') - ~/data ~ - >>> ds.push('/usr/bin') - /usr/bin ~/data ~ - >>> ds.pop() - ~/data ~ - >>> ds.pop() - ~ - Class Name: StagedOutFile Purpose: A context manager for staging files from temporary directories to a final destination. - Example: - >>> fname = "/home/jill/output.dat" - >>> tmpdir = "/tmp" - >>> with StagedOutFile(fname, tmpdir=tmpdir) as sf: - ... with open(sf.path, 'w') as fobj: - ... fobj.write("some data") - Class Name: StagedInFile Purpose: A class to stage a file in to local disk for reading. - Example: - >>> fname="/home/jill/output.dat" - >>> tmpdir="/tmp" - >>> with StagedInFile(fname,tmpdir=tmpdir) as sf: - ... with open(sf.path) as fobj: - ... # read some data from fobj - Functions: See docs for the individual functions for more info. @@ -78,6 +53,7 @@ Extract the directory from a file name and create it if it doesn't exist. """ + from __future__ import print_function import os @@ -113,8 +89,19 @@ class DirStack(object): ~/data ~ >>> ds.pop() ~ - + # + # you can use as a context as well. On exiting the + # context, all directories are popped + >>> print(os.getcwd()) + '/home/username' + >>> with DirStack() as ds: + ds.push('/tmp') + print(os.getcwd()) + '/tmp' + >>> print(os.getcwd()) + '/home/username' """ + def __init__(self, verbose=False): self.verbose = verbose self._home = os.path.expanduser('~') @@ -143,7 +130,6 @@ def pop(self): that directory. """ if len(self._dirs) == 0: - stderr.write("Directory stack is empty\n") return dir = self._dirs.pop() @@ -160,7 +146,7 @@ def getstack(self): def print_stack(self): self.print_dir(os.getcwd()) - for i in range(len(self._dirs)-1, -1, -1): + for i in range(len(self._dirs) - 1, -1, -1): d = self._dirs[i] self.print_dir(d) stdout.write('\n') @@ -169,6 +155,13 @@ def print_dir(self, dir): dir = dir.replace(self._home, '~') stdout.write('%s ' % dir) + def __enter__(self): + return self + + def __exit__(self, exception_type, exception_value, traceback): + while len(self._dirs) > 0: + self.pop() + def path_join(*paths): """ @@ -258,13 +251,15 @@ def expand_path(filename): expand_filename = expand_path -def exec_process(command, - timeout=None, - poll=1, - stdout_file=subprocess.PIPE, - stderr_file=subprocess.PIPE, - shell=True, - verbose=False): +def exec_process( + command, + timeout=None, + poll=1, + stdout_file=subprocess.PIPE, + stderr_file=subprocess.PIPE, + shell=True, + verbose=False, +): """ Name: exec_process @@ -324,7 +319,7 @@ def exec_process(command, if verbose: print(command[0], ' \\', file=stderr) for c in command[1:]: - print(' '+c+' \\', file=stderr) + print(' ' + c + ' \\', file=stderr) else: cmd = command if verbose: @@ -374,7 +369,7 @@ def _poll_subprocess(pobj, timeout, poll): exit_status = pobj.poll() if exit_status is not None: break - tm = time.time()-tm0 + tm = time.time() - tm0 if tm > timeout: break except KeyboardInterrupt: @@ -385,8 +380,10 @@ def _poll_subprocess(pobj, timeout, poll): # exit status will not be None upon completion. If we passed # the timeout we want to kill the process. if exit_status is None: - stderr.write("Process is taking longer than %s seconds. " - "Ending process\n" % timeout) + stderr.write( + "Process is taking longer than %s seconds. " + "Ending process\n" % timeout + ) os.kill(pobj.pid, signal.SIGTERM) exit_status = 1024 stdout_ret, stderr_ret = None, None @@ -460,6 +457,7 @@ class StagedOutFile(object): ... with open(sf.path, 'w') as fobj: ... fobj.write("some data") """ + def __init__(self, fname, tmpdir=None, must_exist=False): self.must_exist = must_exist self.was_staged_out = False @@ -514,8 +512,7 @@ def stage_out(self): makedirs_fromfile(self.final_path) - print( - "staging out '%s' -> '%s'" % (self.path, self.final_path)) + print("staging out '%s' -> '%s'" % (self.path, self.final_path)) shutil.move(self.path, self.final_path) self.was_staged_out = True @@ -549,6 +546,7 @@ class StagedInFile(object): # read some data """ + def __init__(self, fname, tmpdir=None): self._set_paths(fname, tmpdir=tmpdir) diff --git a/esutil/tests/test_dirstack.py b/esutil/tests/test_dirstack.py new file mode 100644 index 0000000..d170cef --- /dev/null +++ b/esutil/tests/test_dirstack.py @@ -0,0 +1,42 @@ +def test_dirstack_context(): + import tempfile + import os + from esutil.ostools import DirStack + + with tempfile.TemporaryDirectory() as tmpdir: + + # on macos CI the actual directory has a different name + # than tmpdir! Do a first run to get that name + orig_dir = os.getcwd() + os.chdir(tmpdir) + tmpdir_actual_name = os.getcwd() + os.chdir(orig_dir) + + with DirStack() as ds: + ds.push(tmpdir) + assert os.getcwd() == tmpdir_actual_name + + assert os.getcwd() == orig_dir + + +def test_dirstack_nocontext(): + import tempfile + import os + from esutil.ostools import DirStack + + with tempfile.TemporaryDirectory() as tmpdir: + + # on macos CI the actual directory has a different name + # than tmpdir! Do a first run to get that name + + orig_dir = os.getcwd() + os.chdir(tmpdir) + tmpdir_actual_name = os.getcwd() + os.chdir(orig_dir) + + ds = DirStack() + ds.push(tmpdir) + assert os.getcwd() == tmpdir_actual_name + ds.pop() + + assert os.getcwd() == orig_dir