From 16f23d81474584e64c9752988f3df994eedfa154 Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Fri, 20 Jul 2012 21:54:41 +0200 Subject: [PATCH] Improved highlighting of HTTP headers. Closes #60. --- README.rst | 3 +- httpie/pretty.py | 97 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index c2189c08..c277d729 100644 --- a/README.rst +++ b/README.rst @@ -184,7 +184,7 @@ See ``http -h`` for more details:: ITEM A key-value pair whose type is defined by the separator used. It can be an HTTP header (header:value), a query parameter (name=:value), - a data field to be used in the request body + a data field to be used in the request body (field_name=value), a raw JSON data field (field_name:=value), or a file field (field_name@/path/to/file). You can use a backslash to @@ -280,6 +280,7 @@ Changelog --------- * `0.2.6dev `_ + * Improved highlighing of HTTP headers. * Added query string parameters (param=:value). * Added support for terminal colors under Windows. * `0.2.5 `_ (2012-07-17) diff --git a/httpie/pretty.py b/httpie/pretty.py index b380892e..fa5a7921 100644 --- a/httpie/pretty.py +++ b/httpie/pretty.py @@ -1,52 +1,110 @@ +""" +Colorizing of HTTP messages and content processing. + +""" import os import re import json - import pygments - -from pygments.util import ClassNotFound +from pygments import token, lexer from pygments.styles import get_style_by_name, STYLE_MAP -from pygments.lexers import get_lexer_for_mimetype, HttpLexer -from pygments.formatters.terminal256 import Terminal256Formatter +from pygments.lexers import get_lexer_for_mimetype from pygments.formatters.terminal import TerminalFormatter +from pygments.formatters.terminal256 import Terminal256Formatter +from pygments.util import ClassNotFound from requests.compat import is_windows from . import solarized + +DEFAULT_STYLE = 'solarized' +AVAILABLE_STYLES = [DEFAULT_STYLE] + list(STYLE_MAP.keys()) if is_windows: import colorama colorama.init() # 256 looks better on Windows - FORMATTER = Terminal256Formatter + formatter_class = Terminal256Formatter else: - FORMATTER = ( + formatter_class = ( Terminal256Formatter if '256color' in os.environ.get('TERM', '') else TerminalFormatter ) -DEFAULT_STYLE = 'solarized' -AVAILABLE_STYLES = [DEFAULT_STYLE] + list(STYLE_MAP.keys()) +class HTTPLexer(lexer.RegexLexer): + """ + Simplified HTTP lexer for Pygments. -application_content_type_re = re.compile(r'application/(.+\+)(json|xml)$') + It only operates on headers and provides a stronger contrast between + their names and values than the original one bundled with Pygments + (`pygments.lexers.text import HttpLexer`), especially when + Solarized color scheme is used. + + """ + name = 'HTTP' + aliases = ['http'] + filenames = ['*.http'] + tokens = { + 'root': [ + + # Request-Line + (r'([A-Z]+)( +)([^ ]+)( +)(HTTP)(/)(\d+\.\d+)', + lexer.bygroups( + token.Name.Function, + token.Text, + token.Name.Namespace, + token.Text, + token.Keyword.Reserved, + token.Operator, + token.Number + )), + + # Response Status-Line + (r'(HTTP)(/)(\d+\.\d+)( +)(\d{3})( +)(.+)', + lexer.bygroups( + token.Keyword.Reserved, # 'HTTP' + token.Operator, # '/' + token.Number, # Version + token.Text, + token.Number, # Status code + token.Text, + token.Name.Exception, # Reason + )), + + # Header + (r'(.*?)( *)(:)( *)(.+)', lexer.bygroups( + token.Name.Attribute, # Name + token.Text, + token.Operator, # Colon + token.Text, + token.String # Value + )) + ]} class PrettyHttp(object): + """HTTP headers & body prettyfier.""" def __init__(self, style_name): - if style_name == 'solarized': - style = solarized.SolarizedStyle - else: + try: style = get_style_by_name(style_name) - self.formatter = FORMATTER(style=style) + except ClassNotFound: + style = solarized.SolarizedStyle + self.formatter = formatter_class(style=style) def headers(self, content): - return pygments.highlight(content, HttpLexer(), self.formatter) + """Pygmentize HTTP headers.""" + return pygments.highlight(content, HTTPLexer(), self.formatter) def body(self, content, content_type): + """Pygmentize `content` based on `content_type`.""" + content_type = content_type.split(';')[0] - application_match = re.match(application_content_type_re, - content_type) + + application_match = re.match( + r'application/(.+\+)(json|xml)$', + content_type + ) if application_match: # Strip vendor and extensions from Content-Type vendor, extension = application_match.groups() @@ -57,13 +115,14 @@ class PrettyHttp(object): except ClassNotFound: return content - if content_type == "application/json": + if content_type == 'application/json': try: # Indent and sort the JSON data. content = json.dumps(json.loads(content), sort_keys=True, indent=4, ensure_ascii=False) - except: + except ValueError: + # Invalid JSON - we don't care. pass return pygments.highlight(content, lexer, self.formatter)