This script in question runs as expected from powershell
:
# scripts.py
x = input("type your input: ")
print(f"your input is: {x}")
But once you wrap it into a module:
class CSV{
[string] $pythonScript
CSV([string] $pythonPath){
$this.pythonScript = $pythonPath
}
[void] Run(){
python $this.pythonScript
}
}
The interactivity is not printed. To run this class, create a new ps1 file as follows and run it
# run.ps1
using module .\module.psm1
$newCSV = [CSV]::new(".\script.py")
$newCSV.Run()
In the terminal:
PS C:\SE_temp_dir> .\run.ps1
test // typed from user input
PS C:\SE_temp_dir>
Notice that no "type your input" or "your input is test" is printed. I have tried many other alternatives:
### module.psm1:
python $this.pythonScript | Write-Output
python $this.pythonScript | Write-Host
python -v $this.pythonScript | Write-Host
### script.py:
print(f"your input is: {x}", flush=True)
import sys; sys.stdout.write(f"your input is: {x}")
But none of them worked. Why does calling a Python script with input()/print()
work interactively in plain PowerShell, but not when run inside a method of a PowerShell class? Is this a PowerShell scoping or host behavior issue?
Not sure if there will be an elegant way to provide input to your Python script in the same line from Python, however, here are some workarounds to your current issue. In both workarounds as you may note, the Run
method output type has been changed from void
to string[]
.
The first approach is what I'd personally use, instead of requesting for user input in Python, do it in PowerShell and provide that input as argument to your Python script:
class CSV {
[string] $pythonScript
CSV([string] $pythonPath) {
$this.pythonScript = $pythonPath
}
[string[]] Run() {
$in = Read-Host 'type your input'
return python $this.pythonScript $in
}
}
Then in Python you can take the second argv
:
import sys
print(f"your input is: {sys.argv[1]}")
The second approach is more complicated, instead of using input
to prompt, use print
:
print("type your input:")
x = input()
print(f"your input is: {x}")
Then in PowerShell, use a loop with logic to write directly to console the first line of your Python script's output:
class CSV {
[string] $pythonScript
CSV([string] $pythonPath) {
$this.pythonScript = $pythonPath
}
[string[]] Run() {
$firstLine = $true
$result = python $this.pythonScript | ForEach-Object {
if ($firstLine) {
# send this line directly to host
Write-Host $_
# set flag to false
$firstLine = $false
# go to next line
return
}
# else, capture in $result
$_
}
return $result
}
}
Based on comments, your Python script takes lots of user input and parsing it with sys.argv
would be a nightmare, in which case you could use argparse.ArgumentParser()
to feed multiple input parameters to your script:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-paramfoo", type = str)
parser.add_argument("-parambar", type = str)
for k, v in vars(parser.parse_args()).items():
print(f"input for parameter {k}: {v}")
Then in the PowerShell script you would be supplying these arguments as:
class CSV {
[string] $pythonScript
CSV([string] $pythonPath) {
$this.pythonScript = $pythonPath
}
[string[]] Run() {
$foo = Read-Host 'type your input for -paramfoo'
$bar = Read-Host 'type your input for -parambar'
return python $this.pythonScript -paramfoo $foo -parambar $bar
}
}