mirror of
https://github.com/httpie/cli.git
synced 2024-11-08 00:44:45 +01:00
'\' only escapes separator characters in req-items
It makes easier to work with Windows paths. Closes #253, #254
This commit is contained in:
parent
1035710956
commit
5084f18568
@ -1277,6 +1277,9 @@ Changelog
|
|||||||
* Added `CONTRIBUTING`_.
|
* Added `CONTRIBUTING`_.
|
||||||
* Fixed ``User-Agent`` overwriting when used within a session.
|
* Fixed ``User-Agent`` overwriting when used within a session.
|
||||||
* Fixed handling of empty passwords in URL credentials.
|
* Fixed handling of empty passwords in URL credentials.
|
||||||
|
* To make it easier to deal with Windows paths in request items, ``\``
|
||||||
|
now only special characters (the ones that are used as key-value
|
||||||
|
separators).
|
||||||
* `0.8.0`_ (2014-01-25)
|
* `0.8.0`_ (2014-01-25)
|
||||||
* Added ``field=@file.txt`` and ``field:=@file.json`` for embedding
|
* Added ``field=@file.txt`` and ``field:=@file.json`` for embedding
|
||||||
the contents of text and JSON files into request data.
|
the contents of text and JSON files into request data.
|
||||||
|
@ -398,6 +398,9 @@ class KeyValue(object):
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.__dict__ == other.__dict__
|
return self.__dict__ == other.__dict__
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
class SessionNameValidator(object):
|
class SessionNameValidator(object):
|
||||||
|
|
||||||
@ -424,6 +427,9 @@ class KeyValueArgType(object):
|
|||||||
|
|
||||||
def __init__(self, *separators):
|
def __init__(self, *separators):
|
||||||
self.separators = separators
|
self.separators = separators
|
||||||
|
self.special_characters = set('\\')
|
||||||
|
for separator in separators:
|
||||||
|
self.special_characters.update(separator)
|
||||||
|
|
||||||
def __call__(self, string):
|
def __call__(self, string):
|
||||||
"""Parse `string` and return `self.key_value_class()` instance.
|
"""Parse `string` and return `self.key_value_class()` instance.
|
||||||
@ -446,17 +452,18 @@ class KeyValueArgType(object):
|
|||||||
=> ['foo', Escaped('='), 'bar', Escaped('\\'), 'baz']
|
=> ['foo', Escaped('='), 'bar', Escaped('\\'), 'baz']
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
backslash = '\\'
|
||||||
tokens = ['']
|
tokens = ['']
|
||||||
esc = False
|
s = iter(s)
|
||||||
for c in s:
|
for c in s:
|
||||||
if esc:
|
if c == backslash:
|
||||||
tokens.extend([Escaped(c), ''])
|
nc = next(s, '')
|
||||||
esc = False
|
if nc in self.special_characters:
|
||||||
else:
|
tokens.extend([Escaped(nc), ''])
|
||||||
if c == '\\':
|
|
||||||
esc = True
|
|
||||||
else:
|
else:
|
||||||
tokens[-1] += c
|
tokens[-1] += c + nc
|
||||||
|
else:
|
||||||
|
tokens[-1] += c
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
tokens = tokenize(string)
|
tokens = tokenize(string)
|
||||||
|
@ -18,26 +18,28 @@ from fixtures import (
|
|||||||
|
|
||||||
class TestItemParsing:
|
class TestItemParsing:
|
||||||
|
|
||||||
key_value_type = KeyValueArgType(*input.SEP_GROUP_ALL_ITEMS)
|
key_value = KeyValueArgType(*input.SEP_GROUP_ALL_ITEMS)
|
||||||
|
|
||||||
def test_invalid_items(self):
|
def test_invalid_items(self):
|
||||||
items = ['no-separator']
|
items = ['no-separator']
|
||||||
for item in items:
|
for item in items:
|
||||||
pytest.raises(argparse.ArgumentTypeError,
|
pytest.raises(argparse.ArgumentTypeError, self.key_value, item)
|
||||||
self.key_value_type, item)
|
|
||||||
|
|
||||||
def test_escape(self):
|
def test_escape_separator(self):
|
||||||
items = input.parse_items([
|
items = input.parse_items([
|
||||||
# headers
|
# headers
|
||||||
self.key_value_type(r'foo\:bar:baz'),
|
self.key_value(r'foo\:bar:baz'),
|
||||||
self.key_value_type(r'jack\@jill:hill'),
|
self.key_value(r'jack\@jill:hill'),
|
||||||
|
|
||||||
# data
|
# data
|
||||||
self.key_value_type(r'baz\=bar=foo'),
|
self.key_value(r'baz\=bar=foo'),
|
||||||
|
|
||||||
# files
|
# files
|
||||||
self.key_value_type(r'bar\@baz@%s' % FILE_PATH_ARG)
|
self.key_value(r'bar\@baz@%s' % FILE_PATH_ARG),
|
||||||
])
|
])
|
||||||
# `requests.structures.CaseInsensitiveDict` => `dict`
|
# `requests.structures.CaseInsensitiveDict` => `dict`
|
||||||
headers = dict(items.headers._store.values())
|
headers = dict(items.headers._store.values())
|
||||||
|
|
||||||
assert headers == {
|
assert headers == {
|
||||||
'foo:bar': 'baz',
|
'foo:bar': 'baz',
|
||||||
'jack@jill': 'hill',
|
'jack@jill': 'hill',
|
||||||
@ -45,25 +47,36 @@ class TestItemParsing:
|
|||||||
assert items.data == {'baz=bar': 'foo'}
|
assert items.data == {'baz=bar': 'foo'}
|
||||||
assert 'bar@baz' in items.files
|
assert 'bar@baz' in items.files
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(('string', 'key', 'sep', 'value'), [
|
||||||
|
('path=c:\windows', 'path', '=', 'c:\windows'),
|
||||||
|
('path=c:\windows\\', 'path', '=', 'c:\windows\\'),
|
||||||
|
('path\==c:\windows', 'path=', '=', 'c:\windows'),
|
||||||
|
])
|
||||||
|
def test_backslash_before_non_special_character_does_not_escape(
|
||||||
|
self, string, key, sep, value):
|
||||||
|
expected = KeyValue(orig=string, key=key, sep=sep, value=value)
|
||||||
|
actual = self.key_value(string)
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
def test_escape_longsep(self):
|
def test_escape_longsep(self):
|
||||||
items = input.parse_items([
|
items = input.parse_items([
|
||||||
self.key_value_type(r'bob\:==foo'),
|
self.key_value(r'bob\:==foo'),
|
||||||
])
|
])
|
||||||
assert items.params == {'bob:': 'foo'}
|
assert items.params == {'bob:': 'foo'}
|
||||||
|
|
||||||
def test_valid_items(self):
|
def test_valid_items(self):
|
||||||
items = input.parse_items([
|
items = input.parse_items([
|
||||||
self.key_value_type('string=value'),
|
self.key_value('string=value'),
|
||||||
self.key_value_type('header:value'),
|
self.key_value('header:value'),
|
||||||
self.key_value_type('list:=["a", 1, {}, false]'),
|
self.key_value('list:=["a", 1, {}, false]'),
|
||||||
self.key_value_type('obj:={"a": "b"}'),
|
self.key_value('obj:={"a": "b"}'),
|
||||||
self.key_value_type('eh:'),
|
self.key_value('eh:'),
|
||||||
self.key_value_type('ed='),
|
self.key_value('ed='),
|
||||||
self.key_value_type('bool:=true'),
|
self.key_value('bool:=true'),
|
||||||
self.key_value_type('file@' + FILE_PATH_ARG),
|
self.key_value('file@' + FILE_PATH_ARG),
|
||||||
self.key_value_type('query==value'),
|
self.key_value('query==value'),
|
||||||
self.key_value_type('string-embed=@' + FILE_PATH_ARG),
|
self.key_value('string-embed=@' + FILE_PATH_ARG),
|
||||||
self.key_value_type('raw-json-embed:=@' + JSON_FILE_PATH_ARG),
|
self.key_value('raw-json-embed:=@' + JSON_FILE_PATH_ARG),
|
||||||
])
|
])
|
||||||
|
|
||||||
# Parsed headers
|
# Parsed headers
|
||||||
|
Loading…
Reference in New Issue
Block a user