xonsh / xonsh

:shell: Python-powered shell. Full-featured and cross-platform.
http://xon.sh
Other
8.31k stars 634 forks source link

Why captured subprocess `!()` is not evaluated immediately? #3394

Closed skirkham closed 3 months ago

skirkham commented 4 years ago

When capturing a sub-process with !(<command>) the attributes of the storage variable appear empty until the variable itself is evaluated on its own. This is better shown by example:

ls
# one  three  two

x = !(ls)
print(x.output)
# 

x
# CommandPipeline(stdin=<_io.BytesIO object at 0x7fbc5889b650>, stdout=<_io.BytesIO object at 0x7fbc5889b5f0>, stderr=<_io.BytesIO object at 0x7fbc5889b170>, pid=16798, returncode=0, args=['ls'], alias=['ls', '--color=auto', '-v'], stdin_redirect=['<stdin>', 'r'], stdout_redirect=[6, 'wb'], stderr_redirect=[8, 'w'], timestamps=[1574779875.0330954, 1574779879.5113409], executed_cmd=['/bin/ls', '--color=auto', '-v'], input=, output=one
# three
# two
# , errors=None)

print(x.output)
# one
# three
# two

For community

⬇️ Please click the 👍 reaction instead of leaving a +1 or 👍 comment

gforsyth commented 4 years ago

Thanks for reporting, @skirkham ! I can reproduce this. This is definitely a regression -- can you post the output of the xonfig command, please? Thanks!

gforsyth commented 4 years ago

Ahh, weird -- so the output of a CommandPipeline object is lazily evaluated -- depending on whether or not the command has "ended" or not.

This is not the expected result:

(base) gil@badcatredux ~ $ x = !(ls)                                                          
(base) gil@badcatredux ~ $ x.ended                                                            
False

but evaluating the variable is leading it to be marked as ended, so then output is correctly teed up.

skirkham commented 4 years ago

Output of xonfig if it's still helpful

+------------------+-----------------+
| xonsh            | 0.9.8           |
| Python           | 3.7.5           |
| PLY              | 3.11            |
| have readline    | True            |
| prompt toolkit   | 1.0.15          |
| shell type       | prompt_toolkit1 |
| pygments         | 2.3.1           |
| on posix         | True            |
| on linux         | True            |
| distro           | ubuntu          |
| on darwin        | False           |
| on windows       | False           |
| on cygwin        | False           |
| on msys2         | False           |
| is superuser     | False           |
| default encoding | utf-8           |
| xonsh encoding   | utf-8           |
| encoding errors  | surrogateescape |
+------------------+-----------------+
daniel-shimon commented 3 years ago

@shahinism as I understand it, this issue is with .output specifically since it's supposed to be lazy evaluated

Could you try the same with .out?

scopatz commented 3 years ago

Yeah, this should be the difference between .out and .output, one of them forces the process to end

scopatz commented 3 years ago

Maybe this distinction is too subtle for public APIs...

mehow-ves commented 6 months ago

Ok so this issue remains unresolved right? Why is !() lazy evaluating the output whereas it doesn't say so anywhere in the docs? Furthremore none of the other subprocess types does the same, all of them evaluate right away and block the program until the command isn't evaluated.

What's worse is that calling .output doesn't actually force the command to end or even start so that attribute is useless unless you force the command to evaluet somehow else.

I found that doing any of the following will force the command to finish (non-exhaustive):

mehow-ves commented 6 months ago

Sure, I might give it a go. Question is what is the outcome that we want? Do we just want to prevent any lazy behaviour or just improve what triggers the execution? I reckon removing the lazines might mess up performance/execution order of some scripts and hence would be a breaking change. However if that would be the case then the script wasn't utilizing the subprocess correctly as they were using a bug rather than a feature.

anki-code commented 6 months ago

improve what triggers the execution

I prefer this way for now. User just need to have what expected by calling .output. Thanks for working on this!