mirror of
https://github.com/httpie/cli.git
synced 2024-11-28 08:38:44 +02:00
parent
493e98c833
commit
c2a0cef76e
@ -9,7 +9,8 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
|
|||||||
`2.2.0-dev`_ (unreleased)
|
`2.2.0-dev`_ (unreleased)
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
* Added support for ``--ciphers`` (`#870`_).
|
* Added ``--format-options`` to allow disabling sorting, etc. (`#128`_)
|
||||||
|
* Added ``--ciphers`` to allow configuring OpenSSL ciphers (`#870`_).
|
||||||
* Added support for ``$XDG_CONFIG_HOME`` (`#920`_).
|
* Added support for ``$XDG_CONFIG_HOME`` (`#920`_).
|
||||||
* Fixed built-in plugins-related circular imports (`#925`_).
|
* Fixed built-in plugins-related circular imports (`#925`_).
|
||||||
|
|
||||||
@ -433,6 +434,7 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
|
|||||||
.. _2.2.0-dev: https://github.com/jakubroztocil/httpie/compare/2.1.0...master
|
.. _2.2.0-dev: https://github.com/jakubroztocil/httpie/compare/2.1.0...master
|
||||||
|
|
||||||
|
|
||||||
|
.. _#128: https://github.com/jakubroztocil/httpie/issues/128
|
||||||
.. _#488: https://github.com/jakubroztocil/httpie/issues/488
|
.. _#488: https://github.com/jakubroztocil/httpie/issues/488
|
||||||
.. _#840: https://github.com/jakubroztocil/httpie/issues/840
|
.. _#840: https://github.com/jakubroztocil/httpie/issues/840
|
||||||
.. _#870: https://github.com/jakubroztocil/httpie/issues/870
|
.. _#870: https://github.com/jakubroztocil/httpie/issues/870
|
||||||
|
14
README.rst
14
README.rst
@ -1395,6 +1395,20 @@ One of these options can be used to control output processing:
|
|||||||
Default for redirected output.
|
Default for redirected output.
|
||||||
==================== ========================================================
|
==================== ========================================================
|
||||||
|
|
||||||
|
|
||||||
|
You can control the applied formatting via the ``--format-options`` option.
|
||||||
|
For example, this is how you would disable the default header and JSON key
|
||||||
|
sorting, and specify a custom JSON indent size:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http --format-options headers.sort=false,json.sort_keys=false,json.indent=2 httpbin.org/get
|
||||||
|
|
||||||
|
This is something you will typically store as one of the default options in your
|
||||||
|
`config`_ file. See ``http --help`` for all the available formatting options.
|
||||||
|
|
||||||
|
|
||||||
Binary data
|
Binary data
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -2,9 +2,10 @@ import argparse
|
|||||||
import getpass
|
import getpass
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Union, List, Optional
|
from copy import deepcopy
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from httpie.cli.constants import SEPARATOR_CREDENTIALS
|
from httpie.cli.constants import DEFAULT_FORMAT_OPTIONS, SEPARATOR_CREDENTIALS
|
||||||
from httpie.sessions import VALID_SESSION_NAME_PATTERN
|
from httpie.sessions import VALID_SESSION_NAME_PATTERN
|
||||||
|
|
||||||
|
|
||||||
@ -181,3 +182,65 @@ def readable_file_arg(filename):
|
|||||||
return filename
|
return filename
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
raise argparse.ArgumentTypeError(f'{filename}: {ex.args[1]}')
|
raise argparse.ArgumentTypeError(f'{filename}: {ex.args[1]}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def parse_format_options(s: str, defaults: Optional[dict]) -> dict:
|
||||||
|
"""
|
||||||
|
Parse `s` and update `defaults` with the parsed values.
|
||||||
|
|
||||||
|
>>> parse_format_options(
|
||||||
|
... defaults={'json': {'indent': 4, 'sort_keys': True}},
|
||||||
|
... s='json.indent=2,json.sort_keys=False',
|
||||||
|
... )
|
||||||
|
{'json': {'indent': 2, 'sort_keys': False}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
value_map = {
|
||||||
|
'true': True,
|
||||||
|
'false': False,
|
||||||
|
}
|
||||||
|
options = deepcopy(defaults or {})
|
||||||
|
for option in s.split(','):
|
||||||
|
try:
|
||||||
|
path, value = option.lower().split('=')
|
||||||
|
section, key = path.split('.')
|
||||||
|
except ValueError:
|
||||||
|
raise argparse.ArgumentTypeError(
|
||||||
|
f'--format-options: invalid option: {option!r}')
|
||||||
|
|
||||||
|
if value in value_map:
|
||||||
|
parsed_value = value_map[value]
|
||||||
|
else:
|
||||||
|
if value.isnumeric():
|
||||||
|
parsed_value = int(value)
|
||||||
|
else:
|
||||||
|
parsed_value = value
|
||||||
|
|
||||||
|
if defaults is None:
|
||||||
|
options.setdefault(section, {})
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
default_value = defaults[section][key]
|
||||||
|
except KeyError:
|
||||||
|
raise argparse.ArgumentTypeError(
|
||||||
|
f'--format-options: invalid key: {path!r} in {option!r}')
|
||||||
|
|
||||||
|
default_type, parsed_type = type(default_value), type(parsed_value)
|
||||||
|
if parsed_type is not default_type:
|
||||||
|
raise argparse.ArgumentTypeError(
|
||||||
|
'--format-options: invalid value type:'
|
||||||
|
f' {value!r} in {option!r}'
|
||||||
|
f' (expected {default_type.__name__}'
|
||||||
|
f' got {parsed_type.__name__})'
|
||||||
|
)
|
||||||
|
|
||||||
|
options[section][key] = parsed_value
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
PARSED_DEFAULT_FORMAT_OPTIONS = parse_format_options(
|
||||||
|
s=','.join(DEFAULT_FORMAT_OPTIONS),
|
||||||
|
defaults=None,
|
||||||
|
)
|
||||||
|
@ -83,6 +83,15 @@ PRETTY_MAP = {
|
|||||||
}
|
}
|
||||||
PRETTY_STDOUT_TTY_ONLY = object()
|
PRETTY_STDOUT_TTY_ONLY = object()
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_FORMAT_OPTIONS = [
|
||||||
|
'headers.sort=true',
|
||||||
|
'json.format=true',
|
||||||
|
'json.indent=4',
|
||||||
|
'json.sort_keys=true',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
OUTPUT_OPTIONS_DEFAULT = OUT_RESP_HEAD + OUT_RESP_BODY
|
OUTPUT_OPTIONS_DEFAULT = OUT_RESP_HEAD + OUT_RESP_BODY
|
||||||
OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED = OUT_RESP_BODY
|
OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED = OUT_RESP_BODY
|
||||||
|
@ -8,20 +8,23 @@ from textwrap import dedent, wrap
|
|||||||
from httpie import __doc__, __version__
|
from httpie import __doc__, __version__
|
||||||
from httpie.cli.argparser import HTTPieArgumentParser
|
from httpie.cli.argparser import HTTPieArgumentParser
|
||||||
from httpie.cli.argtypes import (
|
from httpie.cli.argtypes import (
|
||||||
KeyValueArgType, SessionNameValidator, readable_file_arg,
|
KeyValueArgType, PARSED_DEFAULT_FORMAT_OPTIONS, SessionNameValidator,
|
||||||
|
parse_format_options,
|
||||||
|
readable_file_arg,
|
||||||
)
|
)
|
||||||
from httpie.cli.constants import (
|
from httpie.cli.constants import (
|
||||||
OUTPUT_OPTIONS, OUTPUT_OPTIONS_DEFAULT, OUT_REQ_BODY, OUT_REQ_HEAD,
|
DEFAULT_FORMAT_OPTIONS, OUTPUT_OPTIONS,
|
||||||
|
OUTPUT_OPTIONS_DEFAULT, OUT_REQ_BODY, OUT_REQ_HEAD,
|
||||||
OUT_RESP_BODY, OUT_RESP_HEAD, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY,
|
OUT_RESP_BODY, OUT_RESP_HEAD, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY,
|
||||||
SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY,
|
SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY,
|
||||||
)
|
)
|
||||||
from httpie.output.formatters.colors import (
|
from httpie.output.formatters.colors import (
|
||||||
AUTO_STYLE, AVAILABLE_STYLES, DEFAULT_STYLE,
|
AUTO_STYLE, AVAILABLE_STYLES, DEFAULT_STYLE,
|
||||||
)
|
)
|
||||||
from httpie.plugins.registry import plugin_manager
|
|
||||||
from httpie.plugins.builtin import BuiltinAuthPlugin
|
from httpie.plugins.builtin import BuiltinAuthPlugin
|
||||||
|
from httpie.plugins.registry import plugin_manager
|
||||||
from httpie.sessions import DEFAULT_SESSIONS_DIR
|
from httpie.sessions import DEFAULT_SESSIONS_DIR
|
||||||
from httpie.ssl import DEFAULT_SSL_CIPHERS, AVAILABLE_SSL_VERSION_ARG_MAPPING
|
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS
|
||||||
|
|
||||||
|
|
||||||
parser = HTTPieArgumentParser(
|
parser = HTTPieArgumentParser(
|
||||||
@ -206,9 +209,9 @@ output_processing.add_argument(
|
|||||||
default=DEFAULT_STYLE,
|
default=DEFAULT_STYLE,
|
||||||
choices=AVAILABLE_STYLES,
|
choices=AVAILABLE_STYLES,
|
||||||
help="""
|
help="""
|
||||||
Output coloring style (default is "{default}"). One of:
|
Output coloring style (default is "{default}"). It can be One of:
|
||||||
|
|
||||||
{available_styles}
|
{available_styles}
|
||||||
|
|
||||||
The "{auto_style}" style follows your terminal's ANSI color styles.
|
The "{auto_style}" style follows your terminal's ANSI color styles.
|
||||||
|
|
||||||
@ -221,11 +224,38 @@ output_processing.add_argument(
|
|||||||
available_styles='\n'.join(
|
available_styles='\n'.join(
|
||||||
'{0}{1}'.format(8 * ' ', line.strip())
|
'{0}{1}'.format(8 * ' ', line.strip())
|
||||||
for line in wrap(', '.join(sorted(AVAILABLE_STYLES)), 60)
|
for line in wrap(', '.join(sorted(AVAILABLE_STYLES)), 60)
|
||||||
).rstrip(),
|
).strip(),
|
||||||
auto_style=AUTO_STYLE,
|
auto_style=AUTO_STYLE,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
output_processing.add_argument(
|
||||||
|
'--format-options',
|
||||||
|
type=lambda s: parse_format_options(
|
||||||
|
s=s,
|
||||||
|
defaults=PARSED_DEFAULT_FORMAT_OPTIONS
|
||||||
|
),
|
||||||
|
default=PARSED_DEFAULT_FORMAT_OPTIONS,
|
||||||
|
help="""
|
||||||
|
Controls output formatting. Only relevant when formatting is enabled
|
||||||
|
through (explicit or implied) --pretty=all or --pretty=format.
|
||||||
|
The following are the default options:
|
||||||
|
|
||||||
|
{option_list}
|
||||||
|
|
||||||
|
You can specify multiple comma-separated options. For example, this modifies
|
||||||
|
the settings to disable the sorting of JSON keys and headers:
|
||||||
|
|
||||||
|
--format-options json.sort_keys=false,headers.sort=false
|
||||||
|
|
||||||
|
This is something you will typically put into your config file.
|
||||||
|
|
||||||
|
""".format(
|
||||||
|
option_list='\n'.join(
|
||||||
|
(8 * ' ') + option for option in DEFAULT_FORMAT_OPTIONS).strip()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Output options
|
# Output options
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -3,6 +3,10 @@ from httpie.plugins import FormatterPlugin
|
|||||||
|
|
||||||
class HeadersFormatter(FormatterPlugin):
|
class HeadersFormatter(FormatterPlugin):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.enabled = self.format_options['headers']['sort']
|
||||||
|
|
||||||
def format_headers(self, headers: str) -> str:
|
def format_headers(self, headers: str) -> str:
|
||||||
"""
|
"""
|
||||||
Sorts headers by name while retaining relative
|
Sorts headers by name while retaining relative
|
||||||
|
@ -4,11 +4,12 @@ import json
|
|||||||
from httpie.plugins import FormatterPlugin
|
from httpie.plugins import FormatterPlugin
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_INDENT = 4
|
|
||||||
|
|
||||||
|
|
||||||
class JSONFormatter(FormatterPlugin):
|
class JSONFormatter(FormatterPlugin):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.enabled = self.format_options['json']['format']
|
||||||
|
|
||||||
def format_body(self, body: str, mime: str) -> str:
|
def format_body(self, body: str, mime: str) -> str:
|
||||||
maybe_json = [
|
maybe_json = [
|
||||||
'json',
|
'json',
|
||||||
@ -26,8 +27,8 @@ class JSONFormatter(FormatterPlugin):
|
|||||||
# unicode escapes to improve readability.
|
# unicode escapes to improve readability.
|
||||||
body = json.dumps(
|
body = json.dumps(
|
||||||
obj=obj,
|
obj=obj,
|
||||||
sort_keys=True,
|
sort_keys=self.format_options['json']['sort_keys'],
|
||||||
ensure_ascii=False,
|
ensure_ascii=False,
|
||||||
indent=DEFAULT_INDENT
|
indent=self.format_options['json']['indent']
|
||||||
)
|
)
|
||||||
return body
|
return body
|
||||||
|
@ -152,6 +152,7 @@ def get_stream_type_and_kwargs(
|
|||||||
groups=args.prettify,
|
groups=args.prettify,
|
||||||
color_scheme=args.style,
|
color_scheme=args.style,
|
||||||
explicit_json=args.json,
|
explicit_json=args.json,
|
||||||
|
format_options=args.format_options,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
@ -119,6 +119,7 @@ class FormatterPlugin(BasePlugin):
|
|||||||
"""
|
"""
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
self.format_options = kwargs['format_options']
|
||||||
|
|
||||||
def format_headers(self, headers: str) -> str:
|
def format_headers(self, headers: str) -> str:
|
||||||
"""Return processed `headers`
|
"""Return processed `headers`
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
# <https://docs.pytest.org/en/latest/customize.html>
|
# <https://docs.pytest.org/en/latest/customize.html>
|
||||||
norecursedirs = tests/fixtures
|
norecursedirs = tests/fixtures
|
||||||
|
addopts = --tb=native
|
||||||
|
|
||||||
|
|
||||||
[pycodestyle]
|
[pycodestyle]
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
import argparse
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from utils import MockEnvironment, http, HTTP_OK, COLOR, CRLF
|
from httpie.cli.argtypes import parse_format_options
|
||||||
from httpie.status import ExitStatus
|
|
||||||
from httpie.output.formatters.colors import get_lexer
|
from httpie.output.formatters.colors import get_lexer
|
||||||
|
from httpie.status import ExitStatus
|
||||||
|
from utils import COLOR, CRLF, HTTP_OK, MockEnvironment, http
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('stdout_isatty', [True, False])
|
@pytest.mark.parametrize('stdout_isatty', [True, False])
|
||||||
@ -58,19 +61,19 @@ class TestColors:
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
argnames=['mime', 'explicit_json', 'body', 'expected_lexer_name'],
|
argnames=['mime', 'explicit_json', 'body', 'expected_lexer_name'],
|
||||||
argvalues=[
|
argvalues=[
|
||||||
('application/json', False, None, 'JSON'),
|
('application/json', False, None, 'JSON'),
|
||||||
('application/json+foo', False, None, 'JSON'),
|
('application/json+foo', False, None, 'JSON'),
|
||||||
('application/foo+json', False, None, 'JSON'),
|
('application/foo+json', False, None, 'JSON'),
|
||||||
('application/json-foo', False, None, 'JSON'),
|
('application/json-foo', False, None, 'JSON'),
|
||||||
('application/x-json', False, None, 'JSON'),
|
('application/x-json', False, None, 'JSON'),
|
||||||
('foo/json', False, None, 'JSON'),
|
('foo/json', False, None, 'JSON'),
|
||||||
('foo/json+bar', False, None, 'JSON'),
|
('foo/json+bar', False, None, 'JSON'),
|
||||||
('foo/bar+json', False, None, 'JSON'),
|
('foo/bar+json', False, None, 'JSON'),
|
||||||
('foo/json-foo', False, None, 'JSON'),
|
('foo/json-foo', False, None, 'JSON'),
|
||||||
('foo/x-json', False, None, 'JSON'),
|
('foo/x-json', False, None, 'JSON'),
|
||||||
('application/vnd.comverge.grid+hal+json', False, None, 'JSON'),
|
('application/vnd.comverge.grid+hal+json', False, None, 'JSON'),
|
||||||
('text/plain', True, '{}', 'JSON'),
|
('text/plain', True, '{}', 'JSON'),
|
||||||
('text/plain', True, 'foo', 'Text only'),
|
('text/plain', True, 'foo', 'Text only'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_get_lexer(self, mime, explicit_json, body, expected_lexer_name):
|
def test_get_lexer(self, mime, explicit_json, body, expected_lexer_name):
|
||||||
@ -83,7 +86,7 @@ class TestColors:
|
|||||||
|
|
||||||
|
|
||||||
class TestPrettyOptions:
|
class TestPrettyOptions:
|
||||||
"""Test the --pretty flag handling."""
|
"""Test the --pretty handling."""
|
||||||
|
|
||||||
def test_pretty_enabled_by_default(self, httpbin):
|
def test_pretty_enabled_by_default(self, httpbin):
|
||||||
env = MockEnvironment(colors=256)
|
env = MockEnvironment(colors=256)
|
||||||
@ -138,6 +141,7 @@ class TestLineEndings:
|
|||||||
and as the headers/body separator.
|
and as the headers/body separator.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _validate_crlf(self, msg):
|
def _validate_crlf(self, msg):
|
||||||
lines = iter(msg.splitlines(True))
|
lines = iter(msg.splitlines(True))
|
||||||
for header in lines:
|
for header in lines:
|
||||||
@ -171,3 +175,77 @@ class TestLineEndings:
|
|||||||
def test_CRLF_formatted_request(self, httpbin):
|
def test_CRLF_formatted_request(self, httpbin):
|
||||||
r = http('--pretty=format', '--print=HB', 'GET', httpbin.url + '/get')
|
r = http('--pretty=format', '--print=HB', 'GET', httpbin.url + '/get')
|
||||||
self._validate_crlf(r)
|
self._validate_crlf(r)
|
||||||
|
|
||||||
|
|
||||||
|
class TestFormatOptions:
|
||||||
|
def test_header_formatting_options(self):
|
||||||
|
def get_headers(sort):
|
||||||
|
return http(
|
||||||
|
'--offline', '--print=H',
|
||||||
|
'--format-options', 'headers.sort=' + sort,
|
||||||
|
'example.org', 'ZZZ:foo', 'XXX:foo',
|
||||||
|
)
|
||||||
|
|
||||||
|
r_sorted = get_headers('true')
|
||||||
|
r_unsorted = get_headers('false')
|
||||||
|
assert r_sorted != r_unsorted
|
||||||
|
assert f'XXX: foo{CRLF}ZZZ: foo' in r_sorted
|
||||||
|
assert f'ZZZ: foo{CRLF}XXX: foo' in r_unsorted
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
argnames=['options', 'expected_json'],
|
||||||
|
argvalues=[
|
||||||
|
# @formatter:off
|
||||||
|
(
|
||||||
|
'json.sort_keys=true,json.indent=4',
|
||||||
|
json.dumps({'a': 0, 'b': 0}, indent=4),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'json.sort_keys=false,json.indent=2',
|
||||||
|
json.dumps({'b': 0, 'a': 0}, indent=2),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'json.format=false',
|
||||||
|
json.dumps({'b': 0, 'a': 0}),
|
||||||
|
),
|
||||||
|
# @formatter:on
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_json_formatting_options(self, options: str, expected_json: str):
|
||||||
|
r = http(
|
||||||
|
'--offline', '--print=B',
|
||||||
|
'--format-options', options,
|
||||||
|
'example.org', 'b:=0', 'a:=0',
|
||||||
|
)
|
||||||
|
assert expected_json in r
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
argnames=['defaults', 'options_string', 'expected'],
|
||||||
|
argvalues=[
|
||||||
|
# @formatter:off
|
||||||
|
({'foo': {'bar': 1}}, 'foo.bar=2', {'foo': {'bar': 2}}),
|
||||||
|
({'foo': {'bar': True}}, 'foo.bar=false', {'foo': {'bar': False}}),
|
||||||
|
({'foo': {'bar': 'a'}}, 'foo.bar=b', {'foo': {'bar': 'b'}}),
|
||||||
|
# @formatter:on
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_parse_format_options(self, defaults, options_string, expected):
|
||||||
|
actual = parse_format_options(s=options_string, defaults=defaults)
|
||||||
|
assert expected == actual
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
argnames=['options_string', 'expected_error'],
|
||||||
|
argvalues=[
|
||||||
|
('foo=2', 'invalid option'),
|
||||||
|
('foo.baz=2', 'invalid key'),
|
||||||
|
('foo.bar=false', 'expected int got bool'),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_parse_format_options_errors(self, options_string, expected_error):
|
||||||
|
defaults = {
|
||||||
|
'foo': {
|
||||||
|
'bar': 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
with pytest.raises(argparse.ArgumentTypeError, match=expected_error):
|
||||||
|
parse_format_options(s=options_string, defaults=defaults)
|
||||||
|
Loading…
Reference in New Issue
Block a user