Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 }}

Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Enhancements

- numpy_util.ahelp can now show dict of arrays
- ostools.DirStack can be used as a context

Bug Fixes

Expand Down
80 changes: 39 additions & 41 deletions esutil/ostools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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('~')
Expand Down Expand Up @@ -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()
Expand All @@ -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')
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -549,6 +546,7 @@ class StagedInFile(object):
# read some data

"""

def __init__(self, fname, tmpdir=None):

self._set_paths(fname, tmpdir=tmpdir)
Expand Down
42 changes: 42 additions & 0 deletions esutil/tests/test_dirstack.py
Original file line number Diff line number Diff line change
@@ -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
Loading