mirror of
https://github.com/httpie/cli.git
synced 2024-11-25 09:13:25 +01:00
Improve support for 'type/subtype+suffix' mime types in the colors output formatter.
E.g.: * application/ld+json * application/hal+json Closes #189, #206
This commit is contained in:
parent
76ab8b84be
commit
faec00fd99
@ -1,11 +1,11 @@
|
|||||||
import pygments
|
import pygments.lexer
|
||||||
from pygments import token, lexer
|
import pygments.token
|
||||||
from pygments.styles import get_style_by_name, STYLE_MAP
|
import pygments.styles
|
||||||
from pygments.lexers import get_lexer_for_mimetype, get_lexer_by_name
|
import pygments.lexers
|
||||||
|
import pygments.style
|
||||||
from pygments.formatters.terminal import TerminalFormatter
|
from pygments.formatters.terminal import TerminalFormatter
|
||||||
from pygments.formatters.terminal256 import Terminal256Formatter
|
from pygments.formatters.terminal256 import Terminal256Formatter
|
||||||
from pygments.util import ClassNotFound
|
from pygments.util import ClassNotFound
|
||||||
from pygments.style import Style
|
|
||||||
|
|
||||||
from httpie.compat import is_windows
|
from httpie.compat import is_windows
|
||||||
from .base import BaseProcessor
|
from .base import BaseProcessor
|
||||||
@ -13,11 +13,39 @@ from .base import BaseProcessor
|
|||||||
|
|
||||||
# Colors on Windows via colorama don't look that
|
# Colors on Windows via colorama don't look that
|
||||||
# great and fruity seems to give the best result there.
|
# great and fruity seems to give the best result there.
|
||||||
AVAILABLE_STYLES = set(STYLE_MAP.keys())
|
AVAILABLE_STYLES = set(pygments.styles.STYLE_MAP.keys())
|
||||||
AVAILABLE_STYLES.add('solarized')
|
AVAILABLE_STYLES.add('solarized')
|
||||||
DEFAULT_STYLE = 'solarized' if not is_windows else 'fruity'
|
DEFAULT_STYLE = 'solarized' if not is_windows else 'fruity'
|
||||||
|
|
||||||
|
|
||||||
|
def get_lexer(mime):
|
||||||
|
mime_types, lexer_names = [mime], []
|
||||||
|
type_, subtype = mime.split('/')
|
||||||
|
if '+' not in subtype:
|
||||||
|
lexer_names.append(subtype)
|
||||||
|
else:
|
||||||
|
subtype_name, subtype_suffix = subtype.split('+')
|
||||||
|
lexer_names.extend([subtype_name, subtype_suffix])
|
||||||
|
mime_types.extend([
|
||||||
|
'%s/%s' % (type_, subtype_name),
|
||||||
|
'%s/%s' % (type_, subtype_suffix)
|
||||||
|
])
|
||||||
|
lexer = None
|
||||||
|
for mime_type in mime_types:
|
||||||
|
try:
|
||||||
|
lexer = pygments.lexers.get_lexer_for_mimetype(mime_type)
|
||||||
|
break
|
||||||
|
except ClassNotFound:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for name in lexer_names:
|
||||||
|
try:
|
||||||
|
lexer = pygments.lexers.get_lexer_by_name(name)
|
||||||
|
except ClassNotFound:
|
||||||
|
pass
|
||||||
|
return lexer
|
||||||
|
|
||||||
|
|
||||||
class PygmentsProcessor(BaseProcessor):
|
class PygmentsProcessor(BaseProcessor):
|
||||||
"""
|
"""
|
||||||
Colorize using Pygments
|
Colorize using Pygments
|
||||||
@ -37,7 +65,7 @@ class PygmentsProcessor(BaseProcessor):
|
|||||||
self.lexers_by_type = {}
|
self.lexers_by_type = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
style = get_style_by_name(
|
style = pygments.styles.get_style_by_name(
|
||||||
self.kwargs.get('pygments_style', DEFAULT_STYLE))
|
self.kwargs.get('pygments_style', DEFAULT_STYLE))
|
||||||
except ClassNotFound:
|
except ClassNotFound:
|
||||||
style = Solarized256Style
|
style = Solarized256Style
|
||||||
@ -58,23 +86,13 @@ class PygmentsProcessor(BaseProcessor):
|
|||||||
return body.strip()
|
return body.strip()
|
||||||
|
|
||||||
def get_lexer(self, mime):
|
def get_lexer(self, mime):
|
||||||
lexer = self.lexers_by_type.get(mime)
|
if mime in self.lexers_by_type:
|
||||||
if not lexer:
|
return self.lexers_by_type[mime]
|
||||||
try:
|
self.lexers_by_type[mime] = get_lexer(mime)
|
||||||
lexer = get_lexer_for_mimetype(mime)
|
return self.lexers_by_type[mime]
|
||||||
except ClassNotFound:
|
|
||||||
if '+' in mime:
|
|
||||||
# 'application/atom+xml' => 'xml'
|
|
||||||
subtype = mime.split('+')[-1]
|
|
||||||
try:
|
|
||||||
lexer = get_lexer_by_name(subtype)
|
|
||||||
except ClassNotFound:
|
|
||||||
pass
|
|
||||||
self.lexers_by_type[mime] = lexer
|
|
||||||
return lexer
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPLexer(lexer.RegexLexer):
|
class HTTPLexer(pygments.lexer.RegexLexer):
|
||||||
"""Simplified HTTP lexer for Pygments.
|
"""Simplified HTTP lexer for Pygments.
|
||||||
|
|
||||||
It only operates on headers and provides a stronger contrast between
|
It only operates on headers and provides a stronger contrast between
|
||||||
@ -90,39 +108,39 @@ class HTTPLexer(lexer.RegexLexer):
|
|||||||
'root': [
|
'root': [
|
||||||
# Request-Line
|
# Request-Line
|
||||||
(r'([A-Z]+)( +)([^ ]+)( +)(HTTP)(/)(\d+\.\d+)',
|
(r'([A-Z]+)( +)([^ ]+)( +)(HTTP)(/)(\d+\.\d+)',
|
||||||
lexer.bygroups(
|
pygments.lexer.bygroups(
|
||||||
token.Name.Function,
|
pygments.token.Name.Function,
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.Name.Namespace,
|
pygments.token.Name.Namespace,
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.Keyword.Reserved,
|
pygments.token.Keyword.Reserved,
|
||||||
token.Operator,
|
pygments.token.Operator,
|
||||||
token.Number
|
pygments.token.Number
|
||||||
)),
|
)),
|
||||||
# Response Status-Line
|
# Response Status-Line
|
||||||
(r'(HTTP)(/)(\d+\.\d+)( +)(\d{3})( +)(.+)',
|
(r'(HTTP)(/)(\d+\.\d+)( +)(\d{3})( +)(.+)',
|
||||||
lexer.bygroups(
|
pygments.lexer.bygroups(
|
||||||
token.Keyword.Reserved, # 'HTTP'
|
pygments.token.Keyword.Reserved, # 'HTTP'
|
||||||
token.Operator, # '/'
|
pygments.token.Operator, # '/'
|
||||||
token.Number, # Version
|
pygments.token.Number, # Version
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.Number, # Status code
|
pygments.token.Number, # Status code
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.Name.Exception, # Reason
|
pygments.token.Name.Exception, # Reason
|
||||||
)),
|
)),
|
||||||
# Header
|
# Header
|
||||||
(r'(.*?)( *)(:)( *)(.+)', lexer.bygroups(
|
(r'(.*?)( *)(:)( *)(.+)', pygments.lexer.bygroups(
|
||||||
token.Name.Attribute, # Name
|
pygments.token.Name.Attribute, # Name
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.Operator, # Colon
|
pygments.token.Operator, # Colon
|
||||||
token.Text,
|
pygments.token.Text,
|
||||||
token.String # Value
|
pygments.token.String # Value
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Solarized256Style(Style):
|
class Solarized256Style(pygments.style.Style):
|
||||||
"""
|
"""
|
||||||
solarized256
|
solarized256
|
||||||
------------
|
------------
|
||||||
@ -152,43 +170,43 @@ class Solarized256Style(Style):
|
|||||||
|
|
||||||
background_color = BASE03
|
background_color = BASE03
|
||||||
styles = {
|
styles = {
|
||||||
token.Keyword: GREEN,
|
pygments.token.Keyword: GREEN,
|
||||||
token.Keyword.Constant: ORANGE,
|
pygments.token.Keyword.Constant: ORANGE,
|
||||||
token.Keyword.Declaration: BLUE,
|
pygments.token.Keyword.Declaration: BLUE,
|
||||||
token.Keyword.Namespace: ORANGE,
|
pygments.token.Keyword.Namespace: ORANGE,
|
||||||
token.Keyword.Reserved: BLUE,
|
pygments.token.Keyword.Reserved: BLUE,
|
||||||
token.Keyword.Type: RED,
|
pygments.token.Keyword.Type: RED,
|
||||||
token.Name.Attribute: BASE1,
|
pygments.token.Name.Attribute: BASE1,
|
||||||
token.Name.Builtin: BLUE,
|
pygments.token.Name.Builtin: BLUE,
|
||||||
token.Name.Builtin.Pseudo: BLUE,
|
pygments.token.Name.Builtin.Pseudo: BLUE,
|
||||||
token.Name.Class: BLUE,
|
pygments.token.Name.Class: BLUE,
|
||||||
token.Name.Constant: ORANGE,
|
pygments.token.Name.Constant: ORANGE,
|
||||||
token.Name.Decorator: BLUE,
|
pygments.token.Name.Decorator: BLUE,
|
||||||
token.Name.Entity: ORANGE,
|
pygments.token.Name.Entity: ORANGE,
|
||||||
token.Name.Exception: YELLOW,
|
pygments.token.Name.Exception: YELLOW,
|
||||||
token.Name.Function: BLUE,
|
pygments.token.Name.Function: BLUE,
|
||||||
token.Name.Tag: BLUE,
|
pygments.token.Name.Tag: BLUE,
|
||||||
token.Name.Variable: BLUE,
|
pygments.token.Name.Variable: BLUE,
|
||||||
token.String: CYAN,
|
pygments.token.String: CYAN,
|
||||||
token.String.Backtick: BASE01,
|
pygments.token.String.Backtick: BASE01,
|
||||||
token.String.Char: CYAN,
|
pygments.token.String.Char: CYAN,
|
||||||
token.String.Doc: CYAN,
|
pygments.token.String.Doc: CYAN,
|
||||||
token.String.Escape: RED,
|
pygments.token.String.Escape: RED,
|
||||||
token.String.Heredoc: CYAN,
|
pygments.token.String.Heredoc: CYAN,
|
||||||
token.String.Regex: RED,
|
pygments.token.String.Regex: RED,
|
||||||
token.Number: CYAN,
|
pygments.token.Number: CYAN,
|
||||||
token.Operator: BASE1,
|
pygments.token.Operator: BASE1,
|
||||||
token.Operator.Word: GREEN,
|
pygments.token.Operator.Word: GREEN,
|
||||||
token.Comment: BASE01,
|
pygments.token.Comment: BASE01,
|
||||||
token.Comment.Preproc: GREEN,
|
pygments.token.Comment.Preproc: GREEN,
|
||||||
token.Comment.Special: GREEN,
|
pygments.token.Comment.Special: GREEN,
|
||||||
token.Generic.Deleted: CYAN,
|
pygments.token.Generic.Deleted: CYAN,
|
||||||
token.Generic.Emph: 'italic',
|
pygments.token.Generic.Emph: 'italic',
|
||||||
token.Generic.Error: RED,
|
pygments.token.Generic.Error: RED,
|
||||||
token.Generic.Heading: ORANGE,
|
pygments.token.Generic.Heading: ORANGE,
|
||||||
token.Generic.Inserted: GREEN,
|
pygments.token.Generic.Inserted: GREEN,
|
||||||
token.Generic.Strong: 'bold',
|
pygments.token.Generic.Strong: 'bold',
|
||||||
token.Generic.Subheading: ORANGE,
|
pygments.token.Generic.Subheading: ORANGE,
|
||||||
token.Token: BASE1,
|
pygments.token.Token: BASE1,
|
||||||
token.Token.Other: ORANGE,
|
pygments.token.Token.Other: ORANGE,
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ def has_docutils():
|
|||||||
try:
|
try:
|
||||||
#noinspection PyUnresolvedReferences
|
#noinspection PyUnresolvedReferences
|
||||||
import docutils
|
import docutils
|
||||||
|
|
||||||
return True
|
return True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return False
|
return False
|
||||||
|
@ -29,15 +29,17 @@ class TestVerboseFlag:
|
|||||||
class TestColors:
|
class TestColors:
|
||||||
|
|
||||||
@pytest.mark.parametrize('mime', [
|
@pytest.mark.parametrize('mime', [
|
||||||
'text/html',
|
'application/json',
|
||||||
'foo/html',
|
'application/json+foo',
|
||||||
'text/html+foo',
|
'application/foo+json',
|
||||||
'text/foo+html'
|
'foo/json',
|
||||||
|
'foo/json+bar',
|
||||||
|
'foo/bar+json',
|
||||||
])
|
])
|
||||||
def test_get_lexer(self, mime):
|
def test_get_lexer(self, mime):
|
||||||
lexer = get_lexer(mime)
|
lexer = get_lexer(mime)
|
||||||
assert lexer is not None
|
assert lexer is not None
|
||||||
assert lexer.name == 'HTML'
|
assert lexer.name == 'JSON'
|
||||||
|
|
||||||
def test_get_lexer_not_found(self):
|
def test_get_lexer_not_found(self):
|
||||||
assert get_lexer('xxx/yyy') is None
|
assert get_lexer('xxx/yyy') is None
|
||||||
|
Loading…
Reference in New Issue
Block a user