diff --git a/tests/__init__.py b/tests/__init__.py index 8c9e503d..8e916266 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,3 +1,6 @@ +"""Utilities used by HTTPie tests. + +""" import os import sys import time @@ -5,7 +8,7 @@ import json import shutil import tempfile -from httpie import ExitStatus +import httpie from httpie.models import Environment from httpie.core import main from httpie.compat import bytes, str @@ -74,49 +77,14 @@ class TestEnvironment(Environment): self._shutil.rmtree(self.config_dir) -class Response(object): - stderr = None - json = None - exit_status = None - - -class BytesResponse(bytes, Response): - pass - - -class StrResponse(str, Response): - - @property - def json(self): - if not hasattr(self, '_json'): - self._json = None - # De-serialize JSON body if possible. - if COLOR in self: - # Colorized output cannot be parsed. - pass - elif self.strip().startswith('{'): - # Looks like JSON body. - self._json = json.loads(self) - elif self.count('Content-Type:') == 1 and 'application/json' in self: - # Looks like a JSON HTTP message, try to extract its body. - try: - j = self.strip()[self.strip().rindex('\r\n\r\n'):] - except ValueError: - pass - else: - try: - self._json = json.loads(j) - except ValueError: - pass - return self._json - - def http(*args, **kwargs): """ Invoke `httpie.core.main()` with `args` and `kwargs`, - and return a unicode response. + and return a `CLIResponse` subclass. + + It is either a `StrResponse`, or `BytesResponse` + if unable to decode the output. - Return a `StrResponse`, or `BytesResponse` if unable to decode the output. The response has the following attributes: `stderr`: text written to stderr @@ -125,6 +93,30 @@ def http(*args, **kwargs): Exceptions are propagated except for SystemExit. + $ http GET example.org: + + >>> r = http('GET', 'example.org') + >>> type(r) == StrCLIResponse + True + >>> r.exit_status + 0 + >>> r.stderr + '' + >>> 'HTTP/1.1 200 OK' in r + True + >>> r.json is None + True + + $ http --version: + + >>> r = http('--version') + >>> r.exit_status + 1 + >>> r.stderr.strip() == httpie.__version__ + True + >>> r == '' + True + """ env = kwargs.get('env') if not env: @@ -136,7 +128,6 @@ def http(*args, **kwargs): if '--debug' not in args and '--traceback' not in args: args = ['--traceback'] + args - print(args) try: try: exit_status = main(args=args, **kwargs) @@ -147,15 +138,17 @@ def http(*args, **kwargs): sys.stderr.write(stderr.read()) raise except SystemExit: - exit_status = ExitStatus.ERROR + exit_status = httpie.ExitStatus.ERROR stdout.seek(0) stderr.seek(0) output = stdout.read() try: - r = StrResponse(output.decode('utf8')) + # noinspection PyArgumentList + r = StrCLIResponse(output.decode('utf8')) except UnicodeDecodeError: - r = BytesResponse(output) + # noinspection PyArgumentList + r = BytesCLIResponse(output) r.stderr = stderr.read() r.exit_status = exit_status @@ -165,3 +158,55 @@ def http(*args, **kwargs): stdout.close() stderr.close() + +class BaseCLIResponse(object): + """ + Represents the result of simulated `$ http' invocation via `http()`. + + Holds and provides access to: + + - stdout output: print(self) + - stderr output: print(self.stderr) + - exit_status output: print(self.exit_status) + + """ + stderr = None + json = None + exit_status = None + + +class BytesCLIResponse(bytes, BaseCLIResponse): + """ + Used as a fallback when a StrCLIResponse cannot be used. + + E.g. the output contains binary data. `.json` will always be None. + + """ + + +class StrCLIResponse(str, BaseCLIResponse): + + @property + def json(self): + if not hasattr(self, '_json'): + self._json = None + # De-serialize JSON body if possible. + if COLOR in self: + # Colorized output cannot be parsed. + pass + elif self.strip().startswith('{'): + # Looks like JSON body. + self._json = json.loads(self) + elif (self.count('Content-Type:') == 1 + and 'application/json' in self): + # Looks like a JSON HTTP message, try to extract its body. + try: + j = self.strip()[self.strip().rindex('\r\n\r\n'):] + except ValueError: + pass + else: + try: + self._json = json.loads(j) + except ValueError: + pass + return self._json