Skip to content

Commit

Permalink
Merge pull request #52 from paulromano/nonblocking-pipe
Browse files Browse the repository at this point in the history
Avoid hanging by using non-blocking pipe
  • Loading branch information
nstauff authored Jul 12, 2022
2 parents 8f9c180 + 739af8b commit a9f07c8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 8 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

* RELAP-5 Plugin
* MCNP Plugin
* Serpent Plugin

### Changes

* The `Plugin.__call__` method now allows arbitrary keyword arguments to be
passed on to the `Plugin.run` method
* The `Database` class now acts like a sequence
* Database directory names use random strings to avoid clashes when multiple
instances of WATTS are running simulataneously
instances of WATTS are running simultaneously
* File template-based plugins now accept an `extra_template_inputs` argument
indicating extra template files that should be rendered
* The `PluginOpenMC` class now takes an optional `function` argument that
specifies an arbitrary execution sequence
* All plugins consistently use an attribute `executable` for specifying the path
to an executable

### Fixed

* Use non-blocking pipe when capturing output to avoid some plugins stalling.
* Avoid use of Unix-specific features in the Python standard library when
running on Windows

## [0.2.0]

### Added
Expand Down
39 changes: 32 additions & 7 deletions src/watts/fileutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: MIT

from contextlib import contextmanager
import errno
import os
import platform
import select
Expand All @@ -10,6 +11,9 @@
import tempfile
from typing import Union

if sys.platform != 'win32':
import fcntl

# Type for arguments that accept file paths
PathLike = Union[str, bytes, os.PathLike]

Expand Down Expand Up @@ -91,18 +95,39 @@ def run(args):
Based on https://stackoverflow.com/a/12272262 and
https://stackoverflow.com/a/7730201
"""
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
# Windows doesn't support select.select and fcntl module so just default to
# using subprocess.run. In this case, show_stdout/show_stderr won't work.
if sys.platform == 'win32':
subprocess.run(args)
return

# Helper function to add the O_NONBLOCK flag to a file descriptor
def make_async(fd):
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)

# Helper function to read some data from a file descriptor, ignoring EAGAIN errors
def read_async(fd):
try:
return fd.read()
except IOError as e:
if e.errno != errno.EAGAIN:
raise e
else:
return ''

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
make_async(p.stdout)
make_async(p.stderr)

while True:
select.select([p.stdout, p.stderr], [], [])
select.select([p.stdout, p.stderr], [], [], 0)

stdout_data = p.stdout.read()
stderr_data = p.stderr.read()
stdout_data = read_async(p.stdout)
stderr_data = read_async(p.stderr)
if stdout_data:
sys.stdout.write(stdout_data)
sys.stdout.write(stdout_data.decode())
if stderr_data:
sys.stderr.write(stderr_data)
sys.stderr.write(stderr_data.decode())

if p.poll() is not None:
break

0 comments on commit a9f07c8

Please sign in to comment.