Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes Popen output to use universal newlines. #143

Merged
merged 9 commits into from
Sep 19, 2018
Merged

Conversation

FrankD412
Copy link
Member

Addition of a start_process utility method to centralize how processes are opened. Fixes #142.

@FrankD412 FrankD412 added enhancement Bugfix Discussion/Implementation of a solution to a bug (usually a PR) labels Sep 16, 2018
@FrankD412 FrankD412 self-assigned this Sep 16, 2018
@tadesautels
Copy link
Member

Something's wonky here.

When I take the dev branch and ONLY modify slurmscriptadapter.py such that the three Popen calls all have universal_newlines=True and NO OTHER CHANGES, everything runs fine for me. However, if I use this bugfix, my maestro crashes out about when it should be launching the conductor; sometimes it gets to the point where it's supposed to be calling the new start_process in maestro.py (after study setup, to start the conductor) and sometimes it doesn't.

Frank, can we take a look at this tomorrow?

Tom

Copy link
Member

@tadesautels tadesautels left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my usecase, I'm getting silent errors with this bugfix, but not if I only change the three Popen lines in slurmscriptadapter to use Popen(..., universal_newlines=True).

This needs to be debugged.

@jsemler
Copy link
Collaborator

jsemler commented Sep 18, 2018

@tadesautels, @FrankD412 - This can be reproduced with the LULESH example. The conductor call is logged (with the log level set to debug) and then the program exits.

@FrankD412
Copy link
Member Author

I just tried to execute the command in ipython and weirdly I get a return code of 127 which indicates that the specified binary doesn't exist. However, running it on the command line manually yields execution. I'm not sure what the resulting difference is yet.

@FrankD412
Copy link
Member Author

Ok, so weirdly -- joining the list items into a string causes this to work... the next weird thing is that doing that ubiquitously via the start_process method now causes the GitDependency class to error out with the following:

In [1]: from maestrowf.utils import start_process

In [2]: url = "https://github.com/LLNL/LULESH.git"

In [3]: path = "/Users/frank/Documents/Code/Python/maestrowf/sample_output/lulesh/lulesh_sample1_20180917-230533/LULESH"

In [4]: p = start_process(["git", "clone", url, path], shell=None)
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-4-bbe85a54c182> in <module>()
----> 1 p = start_process(["git", "clone", url, path], shell=None)

/Users/frank/Documents/Code/Python/maestrowf/maestrowf/utils.pyc in start_process(cmd, cwd, env, shell)
    173     return Popen(c,
    174                  shell=shell, stdout=PIPE, stderr=PIPE,
--> 175                  universal_newlines=True)

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.pyc in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
    708                                 p2cread, p2cwrite,
    709                                 c2pread, c2pwrite,
--> 710                                 errread, errwrite)
    711         except Exception:
    712             # Preserve original exception in case os.close raises.

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.pyc in _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, startupinfo, creationflags, shell, to_close, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite)
   1333                         raise
   1334                 child_exception = pickle.loads(data)
-> 1335                 raise child_exception
   1336
   1337

OSError: [Errno 2] No such file or directory

I've reproduced the exact error repeatedly in ipython, the only thing that seems to fix it is specifying shell=True. The interesting thing is that if I specify shell=False which is what its default value would be when unset, I get the error as well. The issue seems to come from joining the command, because when I pass the list like I had before it works just fine. If I join the arguments and then run it I get the above error -- it almost seems like the string version of the command is only useable when dealing with shell=True.

@FrankD412
Copy link
Member Author

Ah, I'm willing to bet it's because I now pass in the conductor command as a list which falls through because shell=True.

@FrankD412
Copy link
Member Author

Yep -- that seems to be the case... I've made a small tweak to fix the issue, but now I wonder what the appropriate convention on this issue is...

@FrankD412
Copy link
Member Author

It also looks like we can't avoid shell=True for launching the conductor. If I make the command a list and force shell=False, the Popen call returns 127 (executable not found). This implies to me that the shell is required because it's the loading of the shell environment that tells the system where to point for Python packages. I'm trying to think of a good middle group here to make it clear instead of overloading the start_process to account for every single case.

@tadesautels
Copy link
Member

tadesautels commented Sep 18, 2018

There's a bug in this one too; start_process isn't passing down cwd or env correctly. One possible method:

` kwargs_to_Popen = {"shell": shell}
if cwd is not None:
kwargs_to_Popen["cwd"] = cwd
if env is not None:
kwargs_to_Popen["env"] = env

return Popen(cmd,
             stdout=PIPE, stderr=PIPE,
             universal_newlines=True,
             **kwargs_to_Popen)`

@FrankD412
Copy link
Member Author

Oh, you're right. I don't know if you can do the mixed declaration for calling Popen like that. I'll give it a shot -- but if that works I think that's the best path forward.

@tadesautels
Copy link
Member

The mixed call worked for me.

@FrankD412
Copy link
Member Author

Cool -- I'll go ahead and implement it. Learned something today!

The shell=False flag was not being passed, nor were the env or cwd
arguments being used in the actual function.
@FrankD412
Copy link
Member Author

So one thought that came up -- I've already flagged that if the specified cmd to Popen is a list we automatically set shell = False (not sure on the justification for it, but Popen raises an error). Generally, it's a no-no to silently change things under the hood without a user's knowledge. It sounds reasonable to me to throw an exception if there's a mismatch. Just wanted to field that as a thought for feedback.

@@ -168,6 +168,13 @@ def start_process(cmd, cwd=None, env=None, shell=True):
if isinstance(cmd, list):
shell = False

kwargs_to_Popen = {"shell": shell}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My take on this:

def start_process(cmd, cwd=None, env=None, shell=True):
    """
    Starts a new process using a specified command.

    :param cmd: A string or a list representing the command to be run.
    :param cwd: Current working path that the process will be started in.
    :param env: A dictionary containing the environment the process will use.
    :param shell: Boolean that determines if the process will run a shell.
    """
    if isinstance(cmd, list):
        shell = False

    # Define kwargs for the upcoming Popen call.
    kwargs = {
        "shell":                shell,
        "cwd":                  cwd,
        "env":                  env,
        "universal_newlines":   True,
        "stdout":               PIPE,
        "stderr":               PIPE,
    }

    return Popen(cmd, **kwargs)

Though your code has the benefit of not passing in extra parameters.

@FrankD412 FrankD412 merged commit 2ac39fd into develop Sep 19, 2018
@FrankD412 FrankD412 deleted the bugfix/popen_bytes branch September 19, 2018 05:08
Copy link
Member

@kcathey kcathey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling start_process is a good move to consolidate and standardize issues along these lines.

FrankD412 added a commit that referenced this pull request Sep 30, 2018
* Update setup.py to 1.1.3dev

* Updated the getting started documentation

* Added examples for running both lulesh specs

* Flux for Spectrum bugfixes (#116)

* Additional arguments can be passed through batch.

* Correction of the use of extend to append.

* Removal of OMPI vars from env.

* Reversal of env altering.

* Addition of mpi exe to batch.

* Removal of -gpu flag

* Addition of "allocated" to PENDING set.

* Correction of the check_status method call.

* Correction of the Flux import

* Addition of the EnvironmentError to try/catch for check_status.

* Addition of jobid to submission INFO logging.

* Workflow setup fix (#117)

* Fixed a workflow setup issue caused by spaces in the spec name

* Additional dirname formatting

* Refactor of the Study class to breakdown complex APIs (#118)

* Minor docstring correction.

* Change to study setup API to break out workspace creation.

* Update Maestro frontend to use new Study method.

* Renamed Study.setup to be better communicate its functionality.

* Correction to style for configure_study method.

* Moved environment application to add_step.

* Split out acquiring environment elements to its own method.

* Updates to staging checks.

* Renamed _setup_linear and _setup_parameterized from setup to stage for clarity.

* Tweaks and addition of starting to SpectrumFluxAdapter. (#119)

* Updated the LULESH examples to use the LULESH git repository for cloning the repo (#121)

* Add the generation of metadata to Study construction. (#120)

* Addition of method and tweaks to support metadata.

* Addition of call to the method for generating metadata.

* Correction to encode YAML string for py3 compatibility.

* Another attempt at py2-py3 compatible encoding.

* Addition of load_metadata method.

* Additional debug logging for the Flux Spectrum Adapter (#122)

* Additional logging for debugging of the adapter.

* Additional logging.

* Updated Exception.message references to Exception.args (#125)

* Fix status.csv 'State' column writeout. (#127)

* #107 Enhance/return codes (#128)

* #107 Added StudyStatus enumeration
Changed DAG execute_ready_steps to return StudyStatus enum instead of a boolean
conductor and maestro also accept StudyStatus and convert to return value

* #128 fixed issues found in pull request

* #107 added inline comments per PC

* #107 pulling out _check_study_completion
to avoid initial implementaion copy and paste error

* #107 fixed line length issue

* Bugfix that would cause the ExecutionGraph to not update when cancelled. (#131)

* Addition of custom parameter generation when running studies. (#129)

* Command line parameter for custom ParameterGenerators.

* Addition of sample custom parameter generation.

* Correction to missing parameters in SIZE.

* Addition of utility method for importing custom gen py files.

* Correction of syntax.

* Conversion of DOS newlines to Unix.

* Addition of call to check for custom code.

* Correction of a bad docstring.

* Addition of parameter metadata to Maestro.

* Addition of parameters to study metadata.

* Addition of copying of the parameter file to study workspace.

* Correction to a bug for attempting a restart when a restart isn't specified. (#133)

* Initial fix for steps that cannot restart.

* Comments to clarify logic.

* More flexible ExecutionGraph description API and logging of description. (#137)

* Tweaks to how the ExecutionGraph adds descriptions. Added logging.

* Updated study to use the new API and some minor refactor.

* Fixed #134

* Fix to Variable verification. (#138)

 Fixes #123

* Updates to Maestro documentation (#114)

* Updates to the README.

* Added initial getting started example documentation

* Addition of Quick Start documentation. (#130)

* Addition of Quick Start.

* Addition of cancellation status example.

* Corrections to quick start and fix for build errors.

* Addition of a LULESH study specification with heavier comments. (#132)

* Start of a LULESH spec that's commented.

* Addition of explanation of step structure.

* Additional comments about steps and dependencies.

* Minor tweaks to comments.

* Addition of parameter block comments.

* Addition of reference to launcher_token YAML

* Tweaks and edits based on feedback.

* Throttled workflows push steps into the ready queue multiple times. (#136)

* Moved 'mark_submitted' into ExecutionGraph 'execute'

* Conversion of ready_steps deque to use step names.

* Capping the available slots to the length of ready_steps.

* Fixes Popen output to use universal newlines. (#143)

* Addition of a shell util to centralize process creation settings.

* Switch to start_process method.

* Addition of docstring for start_process.

* Update to fix universal newline from false to true.

* Addition of a check for a list and a hard set of shell to False.

* Made conductor launch command a string.

* Fix behavior of start_process for localscriptadapter.

The shell=False flag was not being passed, nor were the env or cwd
arguments being used in the actual function.

* Clean up of start_process

* Cleans up GitDependency errors to not rely on return codes. (#144)

* Addition of a ping method.

* Tweaks to error logging.

* Moved specific existence exception out of return code check.

* Cleanup of some merge characters.

* Tweak for connectivity checking.

* Addition of info to the error message.

* Removal of git return codes since they don't mean anything.

* Update version number to 1.1.3.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bugfix Discussion/Implementation of a solution to a bug (usually a PR) enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants