Hi Jonathan,
I see: you may I not seen the second part of my email from this morning :).
Please find below the proposed new code for the module python function:
import os, sys
import re, subprocess
from __future__ import print_function
def module(*arguments):
ns = {}
out, err = subprocess.Popen(['/usr/bin/tclsh',
'/usr/share/Modules/libexec/modulecmd.tcl', 'python'] + list(arguments),
stdout=subprocess.PIPE, stderr=susprocess.PIPE).communicate()
exec(out, ns)
if '_mlstatus' in ns:
_mlstatus = ns['_mlstatus']
else:
_mlstatus = True
print(err.decode(), file=sys.stderr)
return _mlstatus
Regards,
Xavier
Le mer. 23 mars 2022 à 17:20, Jonathan Buck <jon...@gm...> a
écrit :
> Hi Xavier,
>
> Thanks for the additional information about _mlstatus. I tried to
> implement the function as suggested, and I did not have the same success as
> you. I may have done this completely wrong. I placed your function inside
> of my own script, so that it's defined locally. I'm assuming that may be
> the only difference between us, however I'm not sure why that would be a
> problem. For what it's worth, my environment is:
> bash version 4.4.23
> python version 3.6.15
> module version 4.7.1
>
> #!/usr/bin/python3
>
> import os
> import sys
> from io import StringIO
> import re, subprocess
> def module(*arguments):
> ns = {}
> exec(subprocess.Popen(['/usr/bin/tclsh',
> '/usr/share/Modules/libexec/modulecmd.tcl', 'python'] + list(arguments),
> stdout=subprocess.PIPE).communicate()[0], ns)
> if '_mlstatus' in ns:
> _mlstatus = ns['_mlstatus']
> else:
> _mlstatus = True
> return _mlstatus
>
> # Setup variables to hold stdout/stderr
> result_stdout = StringIO()
> result_stderr = StringIO()
> sys.stdout = result_stdout
> sys.stderr = result_stderr
>
> # run
> print("----- TRY RUNNING MODULE COMMAND -----")
> module('--version')
> print("----- DONE RUNNING MODULE COMMAND -----")
> module_stdout = result_stdout.getvalue()
> module_stderr = result_stderr.getvalue()
>
> # reset stdout/stderr
> sys.stdout = sys.__stdout__
> sys.stderr = sys.__stderr__
>
> # print out what was captured to the variables
> print("===== TRY PRINTING STDOUT =====")
> sys.stdout.write(module_stdout)
> print("===== DONE PRINTING STDOUT =====")
>
> print("***** TRY PRINTING STDERR *****")
> sys.stdout.write(module_stderr)
> print("***** DONE PRINTING STDERR *****")
>
> The result of that script is:
> ~> python3 module_test.py
> Modules Release 4.7.1 (2021-04-06)
> ===== TRY PRINTING STDOUT =====
> ----- TRY RUNNING MODULE COMMAND -----
> ----- DONE RUNNING MODULE COMMAND -----
> ===== DONE PRINTING STDOUT =====
> ***** TRY PRINTING STDERR *****
> ***** DONE PRINTING STDERR *****
>
> On Wed, Mar 23, 2022 at 11:47 AM Xavier Delaruelle <
> xav...@gm...> wrote:
>
>>
>> Le mer. 23 mars 2022 à 16:31, Jonathan Buck <jon...@gm...> a
>> écrit :
>>
>>> Hi Xavier,
>>>
>>> This is great! Thanks for showing me how to expose the underlying
>>> function! One thing to note is that everything returned comes through
>>> stderr. So, as you were saying, I wasn't able to trap anything because
>>> everything was coming through stderr.
>>>
>>> In my testing, _mlstatus is returned by stdout. Are there other values
>>> that might be returned by stdout other than "_mlstatus = False" ?
>>>
>>> Am I right in assuming '_mlstatus' is the return code of the tcl
>>> execution?
>>>
>>
>> _mlstatus is the variable that hold the return code of the evaluation. On
>> stdout also goes all the environment changes implied by modulefile
>> evaluation.
>>
>>
>>> The only problem I still have is that I can't seem to capture that
>>> stderr output to a variable. Currently, it goes to the console, no matter
>>> what I do. I'm going to play around with it and see if this is because of
>>> the exec() or the popen() statements. Ideally, I would like to capture the
>>> output when the _mlstatus is False. For my use case, This feature would
>>> allow me to compile a list of my 'bad' modules and their associated output.
>>>
>>> I've been playing with your initial code this morning, teasing it apart
>>> and designing a return object containing the output and the return code.
>>> This might not be the preferred methodology, but either way I'll report
>>> back with what I find.
>>>
>>
>> With the code I proposed this morning, you should be able to get the
>> content from sys.stderr. On my side, I have run this proposed code in the
>> python script you provided and I correctly got that stderr output between
>> the TRY/DONE PRINTING STDERR lines.
>>
>> Regards,
>> Xavier
>>
>>
>>
>>> Thanks again and with all respect,
>>> Jonathan
>>>
>>>
>>> On Wed, Mar 23, 2022 at 11:14 AM Xavier Delaruelle <
>>> xav...@gm...> wrote:
>>>
>>>> Hi Jonathan,
>>>>
>>>> The exit status should be easy to grab from the module() python
>>>> function. This function basically returns this exit status as a boolean. I
>>>> paste below the current python code of the module() definition in python.
>>>> It has not changed a lot since Modules v4.1.
>>>>
>>>> $ $MODULES_CMD python autoinit
>>>> import os
>>>> import re, subprocess
>>>> def module(*arguments):
>>>> ns = {}
>>>> exec(subprocess.Popen(['/usr/bin/tclsh',
>>>> '/usr/share/Modules/libexec/modulecmd.tcl', 'python'] + list(arguments),
>>>> stdout=subprocess.PIPE).communicate()[0], ns)
>>>> if '_mlstatus' in ns:
>>>> _mlstatus = ns['_mlstatus']
>>>> else:
>>>> _mlstatus = True
>>>> return _mlstatus
>>>>
>>>> With the code example you gave, the module function used is the one
>>>> defined in a sh shell session (due to the shell=True parameter of
>>>> subprocess.run). Usually the module shell function returns the output on
>>>> stdout (stderr messages are redirected to stdout after the evaluation of
>>>> the shell code generated on stdout).
>>>>
>>>> Regards,
>>>> Xavier
>>>>
>>>> Le mer. 23 mars 2022 à 14:26, Jonathan Buck <jon...@gm...>
>>>> a écrit :
>>>>
>>>>> Hi Paul,
>>>>>
>>>>> Thanks for the response! I took a look at this and spoke with a
>>>>> colleague, but exit status is hard to grab from the module() method within
>>>>> python. The only way we were able to pull this off was to ignore the
>>>>> suggested python exec() statement found in the documentation and rather
>>>>> create our own popen() statement. This allowed us to then check the status
>>>>> of that process. What I don't like about this solution is that we're
>>>>> depending on the originating shell to have properly sourced the module
>>>>> environment, so that we can execute these 'os' commands. Here's a snippet
>>>>> of the code my colleague came up with:
>>>>>
>>>>> for mod in modules_to_test: proc = subprocess.run(f"module use {mod_path};module load {mod}", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, universal_newlines=True)
>>>>> print("", "########", f"Test: {mod}", "########", sep="\n") # Just FYI to see the attributes of the object # print(proc) print(f"stdout: {proc.stdout}") print(f"stderr: {proc.stderr}") print(f"return code: {proc.returncode}")
>>>>>
>>>>> We Found that all output goes to proc.stdout rather than any errors
>>>>> going to proc.stderr, regardless of a return code being 1 or 0. The return
>>>>> code is the winner here. So, this works but I'm calling it a dirty solution
>>>>> because I think that once I automate this and try to execute remotely on
>>>>> our clusters, we'll lose the properly sourced environment.
>>>>>
>>>>> On Tue, Mar 22, 2022 at 4:19 PM Paul Markfort <pau...@gm...>
>>>>> wrote:
>>>>>
>>>>>> If you are just trying to, determine if there was an error thrown by
>>>>>> the module command,
>>>>>> Just test the exit status:
>>>>>>
>>>>>> EXIT STATUS
>>>>>> The module command exits with 0 if its execution succeed.
>>>>>> Otherwise 1
>>>>>> is returned.
>>>>>>
>>>>>>
>>>>>> Keep in mind that every call to the module command redirects
>>>>>> sys.stdout so that the output is evaluated by python (so you probably can't
>>>>>> capture that).
>>>>>> And sys.stderr redirection by a module command might depend on what
>>>>>> stderr is connected to.
>>>>>>
>>>>>> Last - stderr and stdout are buffered IO, so you may also have to do
>>>>>> a flush before reading them.
>>>>>>
>>>>>>
>>>>>> I should preface the below with, "I haven't coded in python":
>>>>>>
>>>>>> Maybe you need to test your code with no modules involved first.
>>>>>> You might want to read this (seems like stringIO() may have some
>>>>>> limitations).
>>>>>>
>>>>>> https://stackoverflow.com/questions/1218933/can-i-redirect-the-stdout-into-some-sort-of-string-buffer
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> On 2022-03-22 09:18 AM, Jonathan Buck wrote:
>>>>>> > Hi Jacques,
>>>>>> >
>>>>>> > Thanks for the quick reply! I have successfully use the module()
>>>>>> command
>>>>>> > to setup environments without issue. What I'm hoping to do is
>>>>>> capture any
>>>>>> > stderr messages to a variable. I have several use cases for this,
>>>>>> most
>>>>>> > importantly I'm attempting to automate checks against our hundreds
>>>>>> of
>>>>>> > modules to determine if any of them are broken when loaded. I'm
>>>>>> unable to
>>>>>> > redirect stdout/stderr in a manner that I've used before which I
>>>>>> believe is
>>>>>> > due to the command being a subprocess. I'm not a python guru, but
>>>>>> the
>>>>>> > popen statement declares " stdout=subprocess.PIPE" which I thought
>>>>>> was the
>>>>>> > right way to get the stdout returned to the originating process.
>>>>>> >
>>>>>> > All that being said, here's my test script:
>>>>>> >
>>>>>> > import os
>>>>>> > import sys
>>>>>> > from io import StringIO
>>>>>> >
>>>>>> > # Setup variables to hold stdout/stderr
>>>>>> > result_stdout = StringIO()
>>>>>> > result_stderr = StringIO()
>>>>>> > sys.stdout = result_stdout
>>>>>> > sys.stderr = result_stderr
>>>>>> >
>>>>>> > # setup environment module
>>>>>> > exec(open('/p/app/Modules/4.7.1/init/python.py').read())
>>>>>> >
>>>>>> > # run
>>>>>> > print("----- TRY RUNNING MODULE COMMAND -----")
>>>>>> > module('--version')
>>>>>> > print("----- DONE RUNNING MODULE COMMAND -----")
>>>>>> > module_stdout = result_stdout.getvalue()
>>>>>> > module_stderr = result_stderr.getvalue()
>>>>>> >
>>>>>> > # reset stdout/stderr to defaults
>>>>>> > sys.stdout = sys.__stdout__
>>>>>> > sys.stderr = sys.__stderr__
>>>>>> >
>>>>>> > # print out what was captured to the variables
>>>>>> > print("===== TRY PRINTING STDOUT =====")
>>>>>> > sys.stdout.write(module_stdout)
>>>>>> > print("===== DONE PRINTING STDOUT =====")
>>>>>> >
>>>>>> > print("***** TRY PRINTING STDERR *****")
>>>>>> > sys.stderr.write(module_stderr)
>>>>>> > print("***** DONE PRINTING STDERR *****")
>>>>>> >
>>>>>> >
>>>>>> > As you can see, it's simple. I make sure to reset stdout/stderr to
>>>>>> > defaults, however these are my results:
>>>>>> >
>>>>>> > ~> python3 module_test.py
>>>>>> > Modules Release 4.7.1 (2021-04-06)
>>>>>> > ===== TRY PRINTING JSON STDOUT =====
>>>>>> > ----- TRY RUNNING MODULE COMMAND -----
>>>>>> > ----- DONE RUNNING MODULE COMMAND -----
>>>>>> > ===== DONE PRINTING JSON STDOUT =====
>>>>>> > ***** TRY PRINTING JSON STDERR *****
>>>>>> > ***** DONE PRINTING JSON STDERR *****
>>>>>> > ~>
>>>>>> >
>>>>>> > This shows that my stdout redirect is capturing the 'print'
>>>>>> statements
>>>>>> > for running the module command. However the output for the module
>>>>>> > command prints at the very beginning and doesn't show up. Any
>>>>>> > thoughts? I found a fancy way to do the same thing in bash, and I
>>>>>> > proved that in bash the same output is going to stdout.
>>>>>> >
>>>>>> >
>>>>>> > Very Respectfully,
>>>>>> >
>>>>>> > Jonathan
>>>>>> >
>>>>>> >
>>>>>> > On Tue, Mar 22, 2022 at 8:06 AM Contact <dv...@cr...> wrote:
>>>>>> >
>>>>>> >> Hello,
>>>>>> >>
>>>>>> >> In my case I used modules from python with the following logic:
>>>>>> >>
>>>>>> >> import os
>>>>>> >> exec(open(/home/jrp/modules/init/python.py')).read())
>>>>>> >> module('avail')
>>>>>> >> module('load', 'gcc/7')
>>>>>> >> module('list')
>>>>>> >>
>>>>>> >> From the above example,
>>>>>> >>
>>>>>> >> - exec call will allow me to use modules from python itself
>>>>>> and define
>>>>>> >> module function
>>>>>> >> - Each module function invocation will be redirected to module
>>>>>> to
>>>>>> >> perform required action
>>>>>> >>
>>>>>> >> As reminder, the content of stdout is used by modules to setup all
>>>>>> >> required environment functions, functions/alias (if supported by
>>>>>> current
>>>>>> >> shell), ...
>>>>>> >>
>>>>>> >> I hope it will help you.
>>>>>> >>
>>>>>> >> Jacques
>>>>>> >>
>>>>>> >>
>>>>>> >> Le 21/03/2022 à 20:09, Jonathan Buck a écrit :
>>>>>> >>
>>>>>> >> Hi,
>>>>>> >>
>>>>>> >> I'm attempting to capture and act on the stdout and stderr of the
>>>>>> module
>>>>>> >> command. The python implementation appears to be a subprocess for
>>>>>> which I
>>>>>> >> wasn't able to redirect with io.StringIO() as I have with other
>>>>>> output
>>>>>> >> capture.
>>>>>> >>
>>>>>> >> I attempted to look through the code, which I admit I don't fully
>>>>>> >> understand and appears to have a one-liner style of executing the
>>>>>> >> subprocess. I'm writing code that integrates with a production
>>>>>> system, so
>>>>>> >> I'm not able to modify the python.py code.
>>>>>> >>
>>>>>> >> Does anyone know how else this might be accomplished? Or should I
>>>>>> make
>>>>>> >> this a feature request?
>>>>>> >>
>>>>>> >> Thanks for your help,
>>>>>> >> Jonathan
>>>>>>
>>>>> _______________________________________________
>> Modules-interest mailing list
>> Mod...@li...
>> https://lists.sourceforge.net/lists/listinfo/modules-interest
>>
> _______________________________________________
> Modules-interest mailing list
> Mod...@li...
> https://lists.sourceforge.net/lists/listinfo/modules-interest
>
|