Repository URL to install this package:
|
Version:
2.0.2 ▾
|
import argparse
import base64
import sys
import textwrap
from urllib.parse import unquote
from pyramid.request import Request
from pyramid.scripts.common import get_config_loader, parse_vars
def main(argv=sys.argv, quiet=False):
command = PRequestCommand(argv, quiet)
return command.run()
class PRequestCommand:
description = """\
Submit a HTTP request to a web application.
This command makes an artificial request to a web application that uses a
PasteDeploy (.ini) configuration file for the server and application.
Use "prequest config.ini /path" to request "/path".
Use "prequest --method=POST config.ini /path < data" to do a POST with
the given request body.
Use "prequest --method=PUT config.ini /path < data" to do a
PUT with the given request body.
Use "prequest --method=PATCH config.ini /path < data" to do a
PATCH with the given request body.
Use "prequest --method=OPTIONS config.ini /path" to do an
OPTIONS request.
Use "prequest --method=PROPFIND config.ini /path" to do a
PROPFIND request.
If the path is relative (doesn't begin with "/") it is interpreted as
relative to "/". The path passed to this script should be URL-quoted.
The path can be succeeded with a query string (e.g. '/path?a=1&=b2').
The variable "environ['paste.command_request']" will be set to "True" in
the request's WSGI environment, so your application can distinguish these
calls from normal requests.
"""
parser = argparse.ArgumentParser(
description=textwrap.dedent(description),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
'-n',
'--app-name',
dest='app_name',
metavar='NAME',
help=(
"Load the named application from the config file (default 'main')"
),
)
parser.add_argument(
'--header',
dest='headers',
metavar='NAME:VALUE',
action='append',
help=(
"Header to add to request (you can use this option multiple times)"
),
)
parser.add_argument(
'-d',
'--display-headers',
dest='display_headers',
action='store_true',
help='Display status and headers before the response body',
)
parser.add_argument(
'-m',
'--method',
dest='method',
choices=[
'GET',
'HEAD',
'POST',
'PUT',
'PATCH',
'DELETE',
'PROPFIND',
'OPTIONS',
],
help='Request method type (GET, POST, PUT, PATCH, DELETE, '
'PROPFIND, OPTIONS)',
)
parser.add_argument(
'-l',
'--login',
dest='login',
help='HTTP basic auth username:password pair',
)
parser.add_argument(
'config_uri',
nargs='?',
default=None,
help='The URI to the configuration file.',
)
parser.add_argument(
'path_info', nargs='?', default=None, help='The path of the request.'
)
parser.add_argument(
'config_vars',
nargs='*',
default=(),
help="Variables required by the config file. For example, "
"`http_port=%%(http_port)s` would expect `http_port=8080` to be "
"passed here.",
)
_get_config_loader = staticmethod(get_config_loader)
stdin = sys.stdin
def __init__(self, argv, quiet=False):
self.quiet = quiet
self.args = self.parser.parse_args(argv[1:])
def out(self, msg): # pragma: no cover
if not self.quiet:
print(msg)
def run(self):
if not self.args.config_uri or not self.args.path_info:
self.out('You must provide at least two arguments')
return 2
config_uri = self.args.config_uri
config_vars = parse_vars(self.args.config_vars)
path = self.args.path_info
loader = self._get_config_loader(config_uri)
loader.setup_logging(config_vars)
app = loader.get_wsgi_app(self.args.app_name, config_vars)
if not path.startswith('/'):
path = '/' + path
try:
path, qs = path.split('?', 1)
except ValueError:
qs = ''
path = unquote(path)
headers = {}
if self.args.login:
enc = base64.b64encode(self.args.login.encode('ascii'))
headers['Authorization'] = 'Basic ' + enc.decode('ascii')
if self.args.headers:
for item in self.args.headers:
if ':' not in item:
self.out(
"Bad --header=%s option, value must be in the form "
"'name:value'" % item
)
return 2
name, value = item.split(':', 1)
headers[name] = value.strip()
request_method = (self.args.method or 'GET').upper()
environ = {
'REQUEST_METHOD': request_method,
'SCRIPT_NAME': '', # may be empty if app is at the root
'PATH_INFO': path,
'SERVER_NAME': 'localhost', # always mandatory
'SERVER_PORT': '80', # always mandatory
'SERVER_PROTOCOL': 'HTTP/1.0',
'CONTENT_TYPE': 'text/plain',
'REMOTE_ADDR': '127.0.0.1',
'wsgi.run_once': True,
'wsgi.multithread': False,
'wsgi.multiprocess': False,
'wsgi.errors': sys.stderr,
'wsgi.url_scheme': 'http',
'wsgi.version': (1, 0),
'QUERY_STRING': qs,
'HTTP_ACCEPT': 'text/plain;q=1.0, */*;q=0.1',
'paste.command_request': True,
}
if request_method in ('POST', 'PUT', 'PATCH'):
environ['wsgi.input'] = self.stdin
environ['CONTENT_LENGTH'] = '-1'
for name, value in headers.items():
if name.lower() == 'content-type':
name = 'CONTENT_TYPE'
else:
name = 'HTTP_' + name.upper().replace('-', '_')
environ[name] = value
request = Request.blank(path, environ=environ)
response = request.get_response(app)
if self.args.display_headers:
self.out(response.status)
for name, value in response.headerlist:
self.out('%s: %s' % (name, value))
if response.charset:
self.out(response.ubody)
else:
self.out(response.body)
return 0
if __name__ == '__main__': # pragma: no cover
sys.exit(main() or 0)