mirror of
https://github.com/httpie/cli.git
synced 2025-06-24 19:41:23 +02:00
commit
29e594daaf
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from . import pretty
|
from . import pretty
|
||||||
from . import __doc__ as doc
|
from . import __doc__ as doc
|
||||||
@ -31,23 +32,44 @@ class ParseError(Exception):
|
|||||||
|
|
||||||
KeyValue = namedtuple('KeyValue', ['key', 'value', 'sep', 'orig'])
|
KeyValue = namedtuple('KeyValue', ['key', 'value', 'sep', 'orig'])
|
||||||
|
|
||||||
|
|
||||||
class KeyValueType(object):
|
class KeyValueType(object):
|
||||||
"""A type used with `argparse`."""
|
"""A type used with `argparse`."""
|
||||||
def __init__(self, *separators):
|
def __init__(self, *separators):
|
||||||
self.separators = separators
|
self.separators = separators
|
||||||
|
self.escapes = ['\\\\' + sep for sep in separators]
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
found = dict((string.find(sep), sep)
|
found = {}
|
||||||
for sep in self.separators
|
found_escapes = []
|
||||||
if string.find(sep) != -1)
|
for esc in self.escapes:
|
||||||
|
found_escapes += [m.span() for m in re.finditer(esc, string)]
|
||||||
|
for sep in self.separators:
|
||||||
|
matches = re.finditer(sep, string)
|
||||||
|
for match in matches:
|
||||||
|
start, end = match.span()
|
||||||
|
inside_escape = False
|
||||||
|
for estart, eend in found_escapes:
|
||||||
|
if start >= estart and end <= eend:
|
||||||
|
inside_escape = True
|
||||||
|
break
|
||||||
|
if not inside_escape:
|
||||||
|
found[start] = sep
|
||||||
|
|
||||||
if not found:
|
if not found:
|
||||||
#noinspection PyExceptionInherit
|
#noinspection PyExceptionInherit
|
||||||
raise argparse.ArgumentTypeError(
|
raise argparse.ArgumentTypeError(
|
||||||
'"%s" is not a valid value' % string)
|
'"%s" is not a valid value' % string)
|
||||||
sep = found[min(found.keys())]
|
|
||||||
key, value = string.split(sep, 1)
|
# split the string at the earliest non-escaped separator.
|
||||||
|
seploc = min(found.keys())
|
||||||
|
sep = found[seploc]
|
||||||
|
key = string[:seploc]
|
||||||
|
value = string[seploc + len(sep):]
|
||||||
|
|
||||||
|
# remove escape chars
|
||||||
|
for sepstr in self.separators:
|
||||||
|
key = key.replace('\\' + sepstr, sepstr)
|
||||||
|
value = value.replace('\\' + sepstr, sepstr)
|
||||||
return KeyValue(key=key, value=value, sep=sep, orig=string)
|
return KeyValue(key=key, value=value, sep=sep, orig=string)
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +57,33 @@ class TestItemParsing(BaseTest):
|
|||||||
self.assertRaises(argparse.ArgumentTypeError,
|
self.assertRaises(argparse.ArgumentTypeError,
|
||||||
lambda: self.kv(item))
|
lambda: self.kv(item))
|
||||||
|
|
||||||
|
def test_escape(self):
|
||||||
|
headers, data, files = cli.parse_items([
|
||||||
|
# headers
|
||||||
|
self.kv('foo\\:bar:baz'),
|
||||||
|
self.kv('jack\\@jill:hill'),
|
||||||
|
# data
|
||||||
|
self.kv('baz\\=bar=foo'),
|
||||||
|
# files
|
||||||
|
self.kv('bar\\@baz@%s' % TEST_FILE)
|
||||||
|
])
|
||||||
|
self.assertDictEqual(headers, {
|
||||||
|
'foo:bar': 'baz',
|
||||||
|
'jack@jill': 'hill',
|
||||||
|
})
|
||||||
|
self.assertDictEqual(data, {
|
||||||
|
'baz=bar': 'foo',
|
||||||
|
})
|
||||||
|
self.assertIn('bar@baz', files)
|
||||||
|
|
||||||
|
def test_escape_longsep(self):
|
||||||
|
headers, data, files = cli.parse_items([
|
||||||
|
self.kv('bob\\:==foo'),
|
||||||
|
])
|
||||||
|
self.assertDictEqual(data, {
|
||||||
|
'bob:=': 'foo',
|
||||||
|
})
|
||||||
|
|
||||||
def test_valid_items(self):
|
def test_valid_items(self):
|
||||||
headers, data, files = cli.parse_items([
|
headers, data, files = cli.parse_items([
|
||||||
self.kv('string=value'),
|
self.kv('string=value'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user