You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
9.4 KiB
299 lines
9.4 KiB
5 years ago
|
# -*- coding: utf-8 -*-
|
||
|
# Copyright (c) 2017 Ian Stapleton Cordasco
|
||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
# you may not use this file except in compliance with the License.
|
||
|
# You may obtain a copy of the License at
|
||
|
#
|
||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||
|
#
|
||
|
# Unless required by applicable law or agreed to in writing, software
|
||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||
|
# implied.
|
||
|
# See the License for the specific language governing permissions and
|
||
|
# limitations under the License.
|
||
|
"""Module containing the logic for the URIBuilder object."""
|
||
|
from . import compat
|
||
|
from . import normalizers
|
||
|
from . import uri
|
||
|
|
||
|
|
||
|
class URIBuilder(object):
|
||
|
"""Object to aid in building up a URI Reference from parts.
|
||
|
|
||
|
.. note::
|
||
|
|
||
|
This object should be instantiated by the user, but it's recommended
|
||
|
that it is not provided with arguments. Instead, use the available
|
||
|
method to populate the fields.
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self, scheme=None, userinfo=None, host=None, port=None,
|
||
|
path=None, query=None, fragment=None):
|
||
|
"""Initialize our URI builder.
|
||
|
|
||
|
:param str scheme:
|
||
|
(optional)
|
||
|
:param str userinfo:
|
||
|
(optional)
|
||
|
:param str host:
|
||
|
(optional)
|
||
|
:param int port:
|
||
|
(optional)
|
||
|
:param str path:
|
||
|
(optional)
|
||
|
:param str query:
|
||
|
(optional)
|
||
|
:param str fragment:
|
||
|
(optional)
|
||
|
"""
|
||
|
self.scheme = scheme
|
||
|
self.userinfo = userinfo
|
||
|
self.host = host
|
||
|
self.port = port
|
||
|
self.path = path
|
||
|
self.query = query
|
||
|
self.fragment = fragment
|
||
|
|
||
|
def __repr__(self):
|
||
|
"""Provide a convenient view of our builder object."""
|
||
|
formatstr = ('URIBuilder(scheme={b.scheme}, userinfo={b.userinfo}, '
|
||
|
'host={b.host}, port={b.port}, path={b.path}, '
|
||
|
'query={b.query}, fragment={b.fragment})')
|
||
|
return formatstr.format(b=self)
|
||
|
|
||
|
def add_scheme(self, scheme):
|
||
|
"""Add a scheme to our builder object.
|
||
|
|
||
|
After normalizing, this will generate a new URIBuilder instance with
|
||
|
the specified scheme and all other attributes the same.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_scheme('HTTPS')
|
||
|
URIBuilder(scheme='https', userinfo=None, host=None, port=None,
|
||
|
path=None, query=None, fragment=None)
|
||
|
|
||
|
"""
|
||
|
scheme = normalizers.normalize_scheme(scheme)
|
||
|
return URIBuilder(
|
||
|
scheme=scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=self.query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_credentials(self, username, password):
|
||
|
"""Add credentials as the userinfo portion of the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_credentials('root', 's3crete')
|
||
|
URIBuilder(scheme=None, userinfo='root:s3crete', host=None,
|
||
|
port=None, path=None, query=None, fragment=None)
|
||
|
|
||
|
>>> URIBuilder().add_credentials('root', None)
|
||
|
URIBuilder(scheme=None, userinfo='root', host=None,
|
||
|
port=None, path=None, query=None, fragment=None)
|
||
|
"""
|
||
|
if username is None:
|
||
|
raise ValueError('Username cannot be None')
|
||
|
userinfo = normalizers.normalize_username(username)
|
||
|
|
||
|
if password is not None:
|
||
|
userinfo = '{}:{}'.format(
|
||
|
userinfo,
|
||
|
normalizers.normalize_password(password),
|
||
|
)
|
||
|
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=self.query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_host(self, host):
|
||
|
"""Add hostname to the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_host('google.com')
|
||
|
URIBuilder(scheme=None, userinfo=None, host='google.com',
|
||
|
port=None, path=None, query=None, fragment=None)
|
||
|
|
||
|
"""
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=normalizers.normalize_host(host),
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=self.query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_port(self, port):
|
||
|
"""Add port to the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_port(80)
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port='80',
|
||
|
path=None, query=None, fragment=None)
|
||
|
|
||
|
>>> URIBuilder().add_port(443)
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port='443',
|
||
|
path=None, query=None, fragment=None)
|
||
|
|
||
|
"""
|
||
|
port_int = int(port)
|
||
|
if port_int < 0:
|
||
|
raise ValueError(
|
||
|
'ports are not allowed to be negative. You provided {}'.format(
|
||
|
port_int,
|
||
|
)
|
||
|
)
|
||
|
if port_int > 65535:
|
||
|
raise ValueError(
|
||
|
'ports are not allowed to be larger than 65535. '
|
||
|
'You provided {}'.format(
|
||
|
port_int,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port='{}'.format(port_int),
|
||
|
path=self.path,
|
||
|
query=self.query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_path(self, path):
|
||
|
"""Add a path to the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_path('sigmavirus24/rfc3985')
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path='/sigmavirus24/rfc3986', query=None, fragment=None)
|
||
|
|
||
|
>>> URIBuilder().add_path('/checkout.php')
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path='/checkout.php', query=None, fragment=None)
|
||
|
|
||
|
"""
|
||
|
if not path.startswith('/'):
|
||
|
path = '/{}'.format(path)
|
||
|
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=normalizers.normalize_path(path),
|
||
|
query=self.query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_query_from(self, query_items):
|
||
|
"""Generate and add a query a dictionary or list of tuples.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_query_from({'a': 'b c'})
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path=None, query='a=b+c', fragment=None)
|
||
|
|
||
|
>>> URIBuilder().add_query_from([('a', 'b c')])
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path=None, query='a=b+c', fragment=None)
|
||
|
|
||
|
"""
|
||
|
query = normalizers.normalize_query(compat.urlencode(query_items))
|
||
|
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=query,
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_query(self, query):
|
||
|
"""Add a pre-formated query string to the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_query('a=b&c=d')
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path=None, query='a=b&c=d', fragment=None)
|
||
|
|
||
|
"""
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=normalizers.normalize_query(query),
|
||
|
fragment=self.fragment,
|
||
|
)
|
||
|
|
||
|
def add_fragment(self, fragment):
|
||
|
"""Add a fragment to the URI.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_fragment('section-2.6.1')
|
||
|
URIBuilder(scheme=None, userinfo=None, host=None, port=None,
|
||
|
path=None, query=None, fragment='section-2.6.1')
|
||
|
|
||
|
"""
|
||
|
return URIBuilder(
|
||
|
scheme=self.scheme,
|
||
|
userinfo=self.userinfo,
|
||
|
host=self.host,
|
||
|
port=self.port,
|
||
|
path=self.path,
|
||
|
query=self.query,
|
||
|
fragment=normalizers.normalize_fragment(fragment),
|
||
|
)
|
||
|
|
||
|
def finalize(self):
|
||
|
"""Create a URIReference from our builder.
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
>>> URIBuilder().add_scheme('https').add_host('github.com'
|
||
|
... ).add_path('sigmavirus24/rfc3986').finalize().unsplit()
|
||
|
'https://github.com/sigmavirus24/rfc3986'
|
||
|
|
||
|
>>> URIBuilder().add_scheme('https').add_host('github.com'
|
||
|
... ).add_path('sigmavirus24/rfc3986').add_credentials(
|
||
|
... 'sigmavirus24', 'not-re@l').finalize().unsplit()
|
||
|
'https://sigmavirus24:not-re%40l@github.com/sigmavirus24/rfc3986'
|
||
|
|
||
|
"""
|
||
|
return uri.URIReference(
|
||
|
self.scheme,
|
||
|
normalizers.normalize_authority(
|
||
|
(self.userinfo, self.host, self.port)
|
||
|
),
|
||
|
self.path,
|
||
|
self.query,
|
||
|
self.fragment,
|
||
|
)
|