Python bottle.request.path() Examples

The following are 30 code examples of bottle.request.path(). You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may also want to check out all available functions/classes of the module bottle.request , or try the search function .
Example #1
Source File: bottle.py    From annotated-py-bottle with MIT License 6 votes vote down vote up
def WSGIHandler(environ, start_response):
    """The bottle WSGI-handler."""
    global request     # 引用全局变量
    global response

    request.bind(environ)
    response.bind()
    ###############################################################################

    try:
        handler, args = match_url(request.path, request.method)  # 调用,下面定义.
        if not handler:
            raise HTTPError(404, "Not found")
        output = handler(**args)
    except BreakTheBottle, shard:
        output = shard.output 
Example #2
Source File: bottle2.py    From pyFileFixity with MIT License 6 votes vote down vote up
def mount(self, app, script_path):
        ''' Mount a Bottle application to a specific URL prefix '''
        if not isinstance(app, Bottle):
            raise TypeError('Only Bottle instances are supported for now.')
        script_path = '/'.join(filter(None, script_path.split('/')))
        path_depth = script_path.count('/') + 1
        if not script_path:
            raise TypeError('Empty script_path. Perhaps you want a merge()?')
        for other in self.mounts:
            if other.startswith(script_path):
                raise TypeError('Conflict with existing mount: %s' % other)
        @self.route('/%s/:#.*#' % script_path, method="ANY")
        def mountpoint():
            request.path_shift(path_depth)
            return app.handle(request.path, request.method)
        self.mounts[script_path] = app 
Example #3
Source File: bottle2.py    From pyFileFixity with MIT License 6 votes vote down vote up
def route(self, path=None, method='GET', **kargs):
        """ Decorator: Bind a function to a GET request path.

            If the path parameter is None, the signature of the decorated
            function is used to generate the path. See yieldroutes()
            for details.

            The method parameter (default: GET) specifies the HTTP request
            method to listen to. You can specify a list of methods.
        """
        if isinstance(method, str): #TODO: Test this
            method = method.split(';')
        def wrapper(callback):
            paths = [] if path is None else [path.strip().lstrip('/')]
            if not paths: # Lets generate the path automatically
                paths = yieldroutes(callback)
            for p in paths:
                for m in method:
                    route = m.upper() + ';' + p
                    self.routes.add(route, callback, **kargs)
            return callback
        return wrapper 
Example #4
Source File: bottle2.py    From pyFileFixity with MIT License 6 votes vote down vote up
def __call__(self, environ, start_response):
        """ The bottle WSGI-interface. """
        try:
            request.bind(environ, self)
            response.bind(self)
            out = self.handle(request.path, request.method)
            out = self._cast(out, request, response)
            if response.status in (100, 101, 204, 304) or request.method == 'HEAD':
                out = [] # rfc2616 section 4.3
            status = '%d %s' % (response.status, HTTP_CODES[response.status])
            start_response(status, response.headerlist)
            return out
        except (KeyboardInterrupt, SystemExit, MemoryError):
            raise
        except Exception, e:
            if not self.catchall:
                raise
            err = '<h1>Critical error while processing request: %s</h1>' \
                  % environ.get('PATH_INFO', '/')
            if DEBUG:
                err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e)
                err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10)
            environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html
            start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')])
            return [tob(err)] 
Example #5
Source File: bottle2.py    From pyFileFixity with MIT License 6 votes vote down vote up
def path_shift(self, count=1):
        ''' Shift some levels of PATH_INFO into SCRIPT_NAME and return the
            moved part. count defaults to 1'''
        #/a/b/  /c/d  --> 'a','b'  'c','d'
        if count == 0: return ''
        pathlist = self.path.strip('/').split('/')
        scriptlist = self.environ.get('SCRIPT_NAME','/').strip('/').split('/')
        if pathlist and pathlist[0] == '': pathlist = []
        if scriptlist and scriptlist[0] == '': scriptlist = []
        if count > 0 and count <= len(pathlist):
            moved = pathlist[:count]
            scriptlist = scriptlist + moved
            pathlist = pathlist[count:]
        elif count < 0 and count >= -len(scriptlist):
            moved = scriptlist[count:]
            pathlist = moved + pathlist
            scriptlist = scriptlist[:count]
        else:
            empty = 'SCRIPT_NAME' if count < 0 else 'PATH_INFO'
            raise AssertionError("Cannot shift. Nothing left from %s" % empty)
        self['PATH_INFO'] = self.path =  '/' + '/'.join(pathlist) \
                          + ('/' if self.path.endswith('/') and pathlist else '')
        self['SCRIPT_NAME'] = '/' + '/'.join(scriptlist)
        return '/'.join(moved) 
Example #6
Source File: bottle2.py    From pyFileFixity with MIT License 6 votes vote down vote up
def __init__(self, source=None, name=None, lookup=[], encoding='utf8', settings={}):
        """ Create a new template.
        If the source parameter (str or buffer) is missing, the name argument
        is used to guess a template filename. Subclasses can assume that
        self.source and/or self.filename are set. Both are strings.
        The lookup, encoding and settings parameters are stored as instance
        variables.
        The lookup parameter stores a list containing directory paths.
        The encoding parameter should be used to decode byte strings or files.
        The settings parameter contains a dict for engine-specific settings.
        """
        self.name = name
        self.source = source.read() if hasattr(source, 'read') else source
        self.filename = source.filename if hasattr(source, 'filename') else None
        self.lookup = map(os.path.abspath, lookup)
        self.encoding = encoding
        self.settings = self.settings.copy() # Copy from class variable
        self.settings.update(settings) # Apply
        if not self.source and self.name:
            self.filename = self.search(self.name, self.lookup)
            if not self.filename:
                raise TemplateError('Template %s not found.' % repr(name))
        if not self.source and not self.filename:
            raise TemplateError('No template specified.')
        self.prepare(**self.settings) 
Example #7
Source File: bottle3.py    From pyFileFixity with MIT License 6 votes vote down vote up
def route(self, path=None, method='GET', **kargs):
        """ Decorator: Bind a function to a GET request path.

            If the path parameter is None, the signature of the decorated
            function is used to generate the path. See yieldroutes()
            for details.

            The method parameter (default: GET) specifies the HTTP request
            method to listen to. You can specify a list of methods. 
        """
        if isinstance(method, str): #TODO: Test this
            method = method.split(';')
        def wrapper(callback):
            paths = [] if path is None else [path.strip().lstrip('/')]
            if not paths: # Lets generate the path automatically 
                paths = yieldroutes(callback)
            for p in paths:
                for m in method:
                    route = m.upper() + ';' + p
                    self.routes.add(route, callback, **kargs)
            return callback
        return wrapper 
Example #8
Source File: bottle3.py    From pyFileFixity with MIT License 6 votes vote down vote up
def __call__(self, environ, start_response):
        """ The bottle WSGI-interface. """
        try:
            request.bind(environ, self)
            response.bind(self)
            out = self.handle(request.path, request.method)
            out = self._cast(out, request, response)
            if response.status in (100, 101, 204, 304) or request.method == 'HEAD':
                out = [] # rfc2616 section 4.3
            status = '%d %s' % (response.status, HTTP_CODES[response.status])
            start_response(status, response.headerlist)
            return out
        except (KeyboardInterrupt, SystemExit, MemoryError):
            raise
        except Exception as e:
            if not self.catchall:
                raise
            err = '<h1>Critical error while processing request: %s</h1>' \
                  % environ.get('PATH_INFO', '/')
            if DEBUG:
                err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e)
                err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10)
            environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html
            start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')])
            return [tob(err)] 
Example #9
Source File: bottle3.py    From pyFileFixity with MIT License 6 votes vote down vote up
def bind(self, environ, app=None):
        """ Bind a new WSGI enviroment and clear out all previously computed
            attributes.
            
            This is done automatically for the global `bottle.request`
            instance on every request.
        """
        if isinstance(environ, Request): # Recycle already parsed content
            for key in self.__dict__: #TODO: Test this
                setattr(self, key, getattr(environ, key))
            self.app = app
            return
        self._GET = self._POST = self._GETPOST = self._COOKIES = None
        self._body = self._header = None
        self.environ = environ
        self.app = app
        # These attributes are used anyway, so it is ok to compute them here
        self.path = '/' + environ.get('PATH_INFO', '/').lstrip('/')
        self.method = environ.get('REQUEST_METHOD', 'GET').upper() 
Example #10
Source File: bottle3.py    From pyFileFixity with MIT License 6 votes vote down vote up
def path_shift(self, count=1):
        ''' Shift some levels of PATH_INFO into SCRIPT_NAME and return the
            moved part. count defaults to 1'''
        #/a/b/  /c/d  --> 'a','b'  'c','d'
        if count == 0: return ''
        pathlist = self.path.strip('/').split('/')
        scriptlist = self.environ.get('SCRIPT_NAME','/').strip('/').split('/')
        if pathlist and pathlist[0] == '': pathlist = []
        if scriptlist and scriptlist[0] == '': scriptlist = []
        if count > 0 and count <= len(pathlist):
            moved = pathlist[:count]
            scriptlist = scriptlist + moved
            pathlist = pathlist[count:]
        elif count < 0 and count >= -len(scriptlist):
            moved = scriptlist[count:]
            pathlist = moved + pathlist
            scriptlist = scriptlist[:count]
        else:
            empty = 'SCRIPT_NAME' if count < 0 else 'PATH_INFO'
            raise AssertionError("Cannot shift. Nothing left from %s" % empty)
        self['PATH_INFO'] = self.path =  '/' + '/'.join(pathlist) \
                          + ('/' if self.path.endswith('/') and pathlist else '')
        self['SCRIPT_NAME'] = '/' + '/'.join(scriptlist)
        return '/'.join(moved) 
Example #11
Source File: bottle3.py    From pyFileFixity with MIT License 6 votes vote down vote up
def yieldroutes(func):
    """ Return a generator for routes that match the signature (name, args) 
    of the func parameter. This may yield more than one route if the function
    takes optional keyword arguments. The output is best described by example:
      a()         -> '/a'
      b(x, y)     -> '/b/:x/:y'
      c(x, y=5)   -> '/c/:x' and '/c/:x/:y'
      d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y'
    """
    path = func.__name__.replace('__','/').lstrip('/')
    spec = inspect.getargspec(func)
    argc = len(spec[0]) - len(spec[3] or [])
    path += ('/:%s' * argc) % tuple(spec[0][:argc])
    yield path
    for arg in spec[0][argc:]:
        path += '/:%s' % arg
        yield path






# Decorators
#TODO: Replace default_app() with app() 
Example #12
Source File: bottle.py    From annotated-py-projects with MIT License 6 votes vote down vote up
def WSGIHandler(environ, start_response):
    """The bottle WSGI-handler."""
    global request     # 引用全局变量
    global response

    request.bind(environ)
    response.bind()
    ###############################################################################

    try:
        handler, args = match_url(request.path, request.method)  # 调用,下面定义.
        if not handler:
            raise HTTPError(404, "Not found")
        output = handler(**args)
    except BreakTheBottle, shard:
        output = shard.output 
Example #13
Source File: plugin.py    From buttervolume with Apache License 2.0 6 votes vote down vote up
def schedule(req):
    """Schedule or unschedule a job
    TODO add a lock
    """
    name = req['Name']
    timer = req['Timer']
    action = req['Action']
    schedule = []
    if timer:  # 0 means unschedule!
        schedule.append((name, action, timer))
    if os.path.exists(SCHEDULE):
        with open(SCHEDULE) as f:
            for n, a, t in csv.reader(f):
                # skip the line we want to write
                if n == name and a == action:
                    continue
                schedule.append((n, a, t))
    os.makedirs(dirname(SCHEDULE), exist_ok=True)
    with open(SCHEDULE, 'w') as f:
        for line in schedule:
            csv.writer(f).writerow(line)
    return {'Err': ''} 
Example #14
Source File: api.py    From ray with MIT License 5 votes vote down vote up
def dispatch(url):
    """
        This class is the beginning of all entrypoints in the Ray API. Here, each url
        will be redirect to the right handler
    """

    url = bottle_req.path
    log.info('request: %s', bottle_req.url)

    if url[-1] == '/':
        url = url[:-1]

    response_code = 200

    try:
        processed = process(url, bottle_req, bottle_resp)

        try:
            from_func, http_status = processed[0], processed[1]
            bottle_resp.status = http_status
            return from_func
        except:
            return processed

    except exceptions.RayException as e:
        log.exception('ray exception: ')
        response_code = e.http_code

    except:
        log.exception('exception:')
        raise

    bottle_resp.status = response_code 
Example #15
Source File: bottle.py    From annotated-py-bottle with MIT License 5 votes vote down vote up
def bind(self, environ):  # 请求,绑定
        """ Binds the enviroment of the current request to this request handler """
        self._environ = environ
        self._GET = None
        self._POST = None
        self._GETPOST = None
        self._COOKIES = None
        self.path = self._environ.get('PATH_INFO', '/').strip()
        if not self.path.startswith('/'):
            self.path = '/' + self.path 
Example #16
Source File: bottle.py    From annotated-py-bottle with MIT License 5 votes vote down vote up
def set_cookie(self, key, value, **kargs):
        """ Sets a Cookie. Optional settings: expires, path, comment, domain, max-age, secure, version, httponly """
        self.COOKIES[key] = value
        for k in kargs:
            self.COOKIES[key][k] = kargs[k] 
Example #17
Source File: bottle.py    From annotated-py-bottle with MIT License 5 votes vote down vote up
def send_file(filename, root, guessmime=True, mimetype='text/plain'):
    """ Aborts execution and sends a static files as response. """
    root = os.path.abspath(root) + '/'
    filename = os.path.normpath(filename).strip('/')
    filename = os.path.join(root, filename)

    if not filename.startswith(root):    # HTTP状态码: 401
        abort(401, "Access denied.")
    if not os.path.exists(filename) or not os.path.isfile(filename):
        abort(404, "File does not exist.")     # 文件不存在
    if not os.access(filename, os.R_OK):
        abort(401, "You do not have permission to access this file.")

    if guessmime:
        guess = mimetypes.guess_type(filename)[0]
        if guess:
            response.content_type = guess
        elif mimetype:
            response.content_type = mimetype
    elif mimetype:
        response.content_type = mimetype

    stats = os.stat(filename)
    # TODO: HTTP_IF_MODIFIED_SINCE -> 304 (Thu, 02 Jul 2009 23:16:31 CEST)
    if 'Content-Length' not in response.header:
        response.header['Content-Length'] = stats.st_size
    if 'Last-Modified' not in response.header:
        ts = time.gmtime(stats.st_mtime)
        ts = time.strftime("%a, %d %b %Y %H:%M:%S +0000", ts)
        response.header['Last-Modified'] = ts

    raise BreakTheBottle(open(filename, 'r'))    # 抛出异常


###############################################################################
###############################################################################
###############################################################################


# Routing   路由处理部分-定义 
Example #18
Source File: bottle.py    From annotated-py-bottle with MIT License 5 votes vote down vote up
def find(cls, name):
        files = [path % name for path in TEMPLATE_PATH if os.path.isfile(path % name)]
        if files:
            return cls(filename=files[0])
        else:
            raise TemplateError('Template not found: %s' % repr(name)) 
Example #19
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def __init__(self, catchall=True, autojson=True, path = ''):
        """ Create a new bottle instance.
            You usually don't do that. Use `bottle.app.push()` instead.
        """
        self.routes = Router()
        self.mounts = {}
        self.error_handler = {}
        self.catchall = catchall
        self.config = dict()
        self.serve = True
        self.castfilter = []
        if autojson and json_dumps:
            self.add_filter(dict, dict2json) 
Example #20
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def match_url(self, path, method='GET'):
        """ Find a callback bound to a path and a specific HTTP method.
            Return (callback, param) tuple or (None, {}).
            method: HEAD falls back to GET. HEAD and GET fall back to ALL.
        """
        path = path.strip().lstrip('/')
        handler, param = self.routes.match(method + ';' + path)
        if handler: return handler, param
        if method == 'HEAD':
            handler, param = self.routes.match('GET;' + path)
            if handler: return handler, param
        handler, param = self.routes.match('ANY;' + path)
        if handler: return handler, param
        return None, {} 
Example #21
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def fullpath(self):
        """ Request path including SCRIPT_NAME (if present) """
        return self.environ.get('SCRIPT_NAME', '').rstrip('/') + self.path 
Example #22
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def url(self):
        """ Full URL as requested by the client (computed).

            This value is constructed out of different environment variables
            and includes scheme, host, port, scriptname, path and query string.
        """
        scheme = self.environ.get('wsgi.url_scheme', 'http')
        host   = self.environ.get('HTTP_X_FORWARDED_HOST', self.environ.get('HTTP_HOST', None))
        if not host:
            host = self.environ.get('SERVER_NAME')
            port = self.environ.get('SERVER_PORT', '80')
            if scheme + port not in ('https443', 'http80'):
                host += ':' + port
        parts = (scheme, host, urlquote(self.fullpath), self.query_string, '')
        return urlunsplit(parts) 
Example #23
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def set_cookie(self, key, value, **kargs):
        """ Add a new cookie with various options.

        If the cookie value is not a string, a secure cookie is created.

        Possible options are:
            expires, path, comment, domain, max_age, secure, version, httponly
            See http://de.wikipedia.org/wiki/HTTP-Cookie#Aufbau for details
        """
        if not isinstance(value, basestring):
            sec = self.app.config['securecookie.key']
            value = cookie_encode(value, sec).decode('ascii') #2to3 hack
        self.COOKIES[key] = value
        for k, v in kargs.iteritems():
            self.COOKIES[key][k.replace('_', '-')] = v 
Example #24
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def static_file(filename, root, guessmime=True, mimetype=None, download=False):
    """ Opens a file in a save way and returns a HTTPError object with status
        code 200, 305, 401 or 404. Sets Content-Type, Content-Length and
        Last-Modified header. Obeys If-Modified-Since header and HEAD requests.
    """
    root = os.path.abspath(root) + os.sep
    filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
    header = dict()

    if not filename.startswith(root):
        return HTTPError(401, "Access denied.")
    if not os.path.exists(filename) or not os.path.isfile(filename):
        return HTTPError(404, "File does not exist.")
    if not os.access(filename, os.R_OK):
        return HTTPError(401, "You do not have permission to access this file.")

    if not mimetype and guessmime:
        header['Content-Type'] = mimetypes.guess_type(filename)[0]
    else:
        header['Content-Type'] = mimetype if mimetype else 'text/plain'

    if download == True:
        download = os.path.basename(filename)
    if download:
        header['Content-Disposition'] = 'attachment; filename="%s"' % download

    stats = os.stat(filename)
    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
    header['Last-Modified'] = lm
    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = ims.split(";")[0].strip() # IE sends "<date>; length=146"
        ims = parse_date(ims)
        if ims is not None and ims >= int(stats.st_mtime):
           return HTTPResponse(status=304, header=header)
    header['Content-Length'] = stats.st_size
    if request.method == 'HEAD':
        return HTTPResponse('', header=header)
    else:
        return HTTPResponse(open(filename, 'rb'), header=header) 
Example #25
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def reloader_run(server, app, interval):
    if os.environ.get('BOTTLE_CHILD') == 'true':
        # We are a child process
        files = dict()
        for module in sys.modules.values():
            file_path = getattr(module, '__file__', None)
            if file_path and os.path.isfile(file_path):
                file_split = os.path.splitext(file_path)
                if file_split[1] in ('.py', '.pyc', '.pyo'):
                    file_path = file_split[0] + '.py'
                    files[file_path] = os.stat(file_path).st_mtime
        thread.start_new_thread(server.run, (app,))
        while True:
            time.sleep(interval)
            for file_path, file_mtime in files.iteritems():
                if not os.path.exists(file_path):
                    print "File changed: %s (deleted)" % file_path
                elif os.stat(file_path).st_mtime > file_mtime:
                    print "File changed: %s (modified)" % file_path
                else: continue
                print "Restarting..."
                app.serve = False
                time.sleep(interval) # be nice and wait for running requests
                sys.exit(3)
    while True:
        args = [sys.executable] + sys.argv
        environ = os.environ.copy()
        environ['BOTTLE_CHILD'] = 'true'
        exit_status = subprocess.call(args, env=environ)
        if exit_status != 3:
            sys.exit(exit_status)






# Templates 
Example #26
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def search(cls, name, lookup=[]):
        """ Search name in all directiries specified in lookup.
        First without, then with common extentions. Return first hit. """
        if os.path.isfile(name): return name
        for spath in lookup:
            fname = os.path.join(spath, name)
            if os.path.isfile(fname):
                return fname
            for ext in cls.extentions:
                if os.path.isfile('%s.%s' % (fname, ext)):
                    return '%s.%s' % (fname, ext) 
Example #27
Source File: bottle2.py    From pyFileFixity with MIT License 5 votes vote down vote up
def prepare(self, **options):
        from mako.template import Template
        from mako.lookup import TemplateLookup
        options.update({'input_encoding':self.encoding})
        #TODO: This is a hack... http://github.com/defnull/bottle/issues#issue/8
        mylookup = TemplateLookup(directories=['.']+self.lookup, **options)
        if self.source:
            self.tpl = Template(self.source, lookup=mylookup)
        else: #mako cannot guess extentions. We can, but only at top level...
            name = self.name
            if not os.path.splitext(name)[1]:
                name += os.path.splitext(self.filename)[1]
            self.tpl = mylookup.get_template(name) 
Example #28
Source File: bottle3.py    From pyFileFixity with MIT License 5 votes vote down vote up
def __init__(self, catchall=True, autojson=True, path = ''):
        """ Create a new bottle instance.
            You usually don't do that. Use `bottle.app.push()` instead.
        """
        self.routes = Router()
        self.mounts = {}
        self.error_handler = {}
        self.catchall = catchall
        self.config = dict()
        self.serve = True
        self.castfilter = []
        if autojson and json_dumps:
            self.add_filter(dict, dict2json) 
Example #29
Source File: bottle3.py    From pyFileFixity with MIT License 5 votes vote down vote up
def match_url(self, path, method='GET'):
        """ Find a callback bound to a path and a specific HTTP method.
            Return (callback, param) tuple or (None, {}).
            method: HEAD falls back to GET. HEAD and GET fall back to ALL.
        """
        path = path.strip().lstrip('/')
        handler, param = self.routes.match(method + ';' + path)
        if handler: return handler, param
        if method == 'HEAD':
            handler, param = self.routes.match('GET;' + path)
            if handler: return handler, param
        handler, param = self.routes.match('ANY;' + path)
        if handler: return handler, param
        return None, {} 
Example #30
Source File: bottle3.py    From pyFileFixity with MIT License 5 votes vote down vote up
def url(self):
        """ Full URL as requested by the client (computed).

            This value is constructed out of different environment variables
            and includes scheme, host, port, scriptname, path and query string. 
        """
        scheme = self.environ.get('wsgi.url_scheme', 'http')
        host   = self.environ.get('HTTP_X_FORWARDED_HOST', self.environ.get('HTTP_HOST', None))
        if not host:
            host = self.environ.get('SERVER_NAME')
            port = self.environ.get('SERVER_PORT', '80')
            if scheme + port not in ('https443', 'http80'):
                host += ':' + port
        parts = (scheme, host, urlquote(self.fullpath), self.query_string, '')
        return urlunsplit(parts)