This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, paul.moore, steve.dower, tim.golden, vikram.invincible.pal4, zach.ware
Date 2020-11-29.07:28:07
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1606634888.02.0.939709621685.issue42499@roundup.psfhosted.org>
In-reply-to
Content
Python uses the Windows C runtime to parse the command line into an argument array. By the CRT rules [1], you need to escape the embedded double quotes using backslashes. For example:

    PS C:\> python -c 's = \"fn main() {\"; s += \"\n\".join(\"let x = 0;\" for _ in range(2)); s+= \"}\"; print(s)'
    fn main() {let x = 0;
    let x = 0;}

In case you don't know already, PowerShell magically converts the single quotes in your command line to double quotes. It recognizes that most Windows programs parse the command line using the C runtime or WinAPI CommandLineToArgvW, which doesn't have any special handling for single quotes.

You can use ctypes to see how your original command line gets parsed via CommandLineToArgvW, which is functionally the same as what the C runtime does. For example (note the -i option to enter interactive mode):

    PS C:\> python -ic 's = "fn main() {"; s += "\n".join("let x = 0;" for _ in range(2)); s+= "}"; print(s)'
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    NameError: name 'fn' is not defined

    >>> import ctypes
    >>> kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
    >>> shell32 = ctypes.WinDLL('shell32', use_last_error=True)
    >>> kernel32.GetCommandLineW.restype = ctypes.c_wchar_p
    >>> shell32.CommandLineToArgvW.restype = ctypes.POINTER(ctypes.c_wchar_p)

    >>> cmd = kernel32.GetCommandLineW()
    >>> num = ctypes.c_int()
    >>> args = shell32.CommandLineToArgvW(cmd, ctypes.byref(num))

    >>> print(*zip(range(num.value), args[:num.value]), sep='\n')
    (0, 'C:\\Program Files\\Python39\\python.exe')
    (1, '-ic')
    (2, 's = fn')
    (3, 'main()')
    (4, '{; s += \\n.join(let')
    (5, 'x')
    (6, '=')
    (7, '0; for _ in range(2)); s+= }; print(s)')

As you can see, because the double quotes aren't escaped, they get consumed, combined with the rule for white space, to parse the command line after "-ic" into six arguments instead of just one argument.

---

[1] https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160#parsing-c-command-line-arguments
History
Date User Action Args
2020-11-29 07:28:08eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, vikram.invincible.pal4
2020-11-29 07:28:08eryksunsetmessageid: <1606634888.02.0.939709621685.issue42499@roundup.psfhosted.org>
2020-11-29 07:28:07eryksunlinkissue42499 messages
2020-11-29 07:28:07eryksuncreate