I've been wrapping my head around this problem and can't find a solution.
I have a pretty simple code:
import argparse
import json
def main():
parser = argparse.ArgumentParser(description="Process a JSON argument.")
parser.add_argument(
"--json",
type=str,
required=True,
help="JSON string or path to a JSON file."
)
args = parser.parse_args()
# Try to parse the argument as a JSON string or file path
try:
# First, attempt to parse it as a JSON string
data = json.loads(args.json)
print("Parsed JSON string successfully:", data)
except json.JSONDecodeError:
# If that fails, treat it as a file path
try:
with open(args.json, 'r') as file:
data = json.load(file)
print("Parsed JSON file successfully:", data)
except FileNotFoundError:
print("Error: Input is not a valid JSON string or file path.")
return
# Use the parsed dictionary (data)
print("JSON as dictionary:", data)
if __name__ == "__main__":
main()
saved as script.py When I run it from Powershell with
python script.py --json '{"key": "value", "number": 42}'
I receive:
Traceback (most recent call last):
File "C:\mypath\script.py", line 17, in main
data = json.loads(args.json)
File "C:\Users\myuser\miniconda3\envs\ti_nlp\lib\json\__init__.py", line 346, in loads
return _default_decoder.decode(s)
File "C:\Users\myuser\miniconda3\envs\ti_nlp\lib\json\decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Users\myuser\miniconda3\envs\ti_nlp\lib\json\decoder.py", line 353, in raw_decode
obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\mypath\script.py", line 33, in <module>
main()
File "C:\mypath\script.py", line 22, in main
with open(args.json, 'r') as file:
OSError: [Errno 22] Invalid argument: '{key: value, number: 42}'
When i run it with cmd (Anaconda prompt):
python script.py --json "{\"key\": \"value\", \"number\": 42}"
it works just fine:
Parsed JSON string successfully: {'key': 'value', 'number': 42}
JSON as dictionary: {'key': 'value', 'number': 42}
So... what is the issue with Powershell and how can I solve this problem?
This is an issue prior to the introduction of PSNativeCommandArgumentPassing
, before that the double-quotes are consumed when passed as arguments. For comparison, you can change your Python script to:
import sys
def main():
print(sys.argv)
if __name__ == "__main__":
main()
Then when called from PowerShell 7.2 and below you would see:
PS ..\pwsh> python script.py --json '{"key": "value", "number": 42}'
['script.py', '--json', '{key: value, number: 42}']
And in 7.3+ you will see that the "
are preserved:
PS ..\pwsh> python script.py --json '{"key": "value", "number": 42}'
['script.py', '--json', '{"key": "value", "number": 42}']
That's the cause of your issue. To solve this you will need to escape the "
with \
. A way to handle this dynamically could be via .Replace('"', '\"')
:
$json = '{"key": "value", "number": 42}'
if ($PSVersionTable.PSVersion -lt '7.3' -or $PSNativeCommandArgumentPassing -eq 'Legacy') {
$json = $json.Replace('"', '\"')
}
python script.py --json $json
There are other alternatives too, for example encoding your Json to Base64 on PowerShell side and decoding it in Python, so in PowerShell:
$json = '{"key": "value", "number": 42}'
$b64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($json))
python script.py --json $b64
And in Python:
# First, attempt to b64decode and parse it as a JSON string
data = json.loads(b64decode(args.json))
Then you code should be compatible with all versions.