Files
pipenv/pipenv/cmdparse.py
T
Tzu-ping Chung 483bf175cf Rework cmdparse.Script API
The API now requires a command argument from signature. Parsing errors
(no command from input) is thrown as a custom exception, and caught by
the outmost possible invocation to emit a message from click.
2018-04-06 23:26:36 -04:00

66 lines
1.8 KiB
Python

import re
import shlex
import six
class ScriptEmptyError(ValueError):
pass
class Script(object):
"""Parse a script line (in Pipfile's [scripts] section).
This always works in POSIX mode, even on Windows.
"""
def __init__(self, command, args=None):
self._parts = [command]
if args:
self._parts.extend(args)
@classmethod
def parse(cls, value):
if isinstance(value, six.string_types):
value = shlex.split(value)
if not value:
raise ScriptEmptyError(value)
return cls(value[0], value[1:])
def __repr__(self):
return 'Script({0!r})'.format(self._parts)
@property
def command(self):
return self._parts[0]
@property
def args(self):
return self._parts[1:]
def extend(self, extra_args):
self._parts.extend(extra_args)
def cmdify(self):
"""Encode into a cmd-executable string.
This re-implements CreateProcess's quoting logic to turn a list of
arguments into one single string for the shell to interpret.
* All double quotes are escaped with a backslash.
* Existing backslashes before a quote are doubled, so they are all
escaped properly.
* Backslashes elsewhere are left as-is; cmd will interpret them
literally.
The result is then quoted into a pair of double quotes to be grouped.
The intended use of this function is to pre-process an argument list
before passing it into ``subprocess.Popen(..., shell=True)``.
See also: https://docs.python.org/3/library/subprocess.html#converting-argument-sequence
"""
return ' '.join(
'"{0}"'.format(re.sub(r'(\\*)"', r'\1\1\\"', arg))
for arg in self._parts
)