# Path: openid/urinorm.py
# def urinorm(uri):
#     """Return normalized URI.
# 
#     Normalization if performed according to RFC 3986, section 6 https://tools.ietf.org/html/rfc3986#section-6.
#     Supported URIs are URLs and OpenID realm URIs.
# 
#     @type uri: six.text_type, six.binary_type deprecated
#     @rtype: six.text_type
#     @raise ValueError: If URI is invalid.
#     """
#     uri = string_to_text(uri, "Binary input for urinorm is deprecated. Use text input instead.")
# 
#     split_uri = urlsplit(uri)
# 
#     # Normalize scheme
#     scheme = split_uri.scheme.lower()
#     if scheme not in ('http', 'https'):
#         raise ValueError('Not an absolute HTTP or HTTPS URI: {!r}'.format(uri))
# 
#     # Normalize netloc
#     if not split_uri.netloc:
#         raise ValueError('Not an absolute URI: {!r}'.format(uri))
# 
#     hostname = split_uri.hostname
#     if hostname is None:
#         hostname = ''
#     else:
#         hostname = hostname.lower()
#     # Unquote percent encoded characters
#     hostname = unquote(hostname)
#     # Quote IDN domain names
#     try:
#         # hostname: str --[idna]--> bytes --[utf-8]--> str
#         hostname = hostname.encode('idna').decode('utf-8')
#     except ValueError as error:
#         raise ValueError('Invalid hostname {!r}: {}'.format(hostname, error))
#     _check_disallowed_characters(hostname, 'hostname')
# 
#     try:
#         port = split_uri.port
#     except ValueError as error:
#         raise ValueError('Invalid port in {!r}: {}'.format(split_uri.netloc, error))
#     if port is None:
#         port = ''
#     elif (scheme == 'http' and port == 80) or (scheme == 'https' and port == 443):
#         port = ''
# 
#     netloc = hostname
#     if port:
#         netloc = netloc + ':' + six.text_type(port)
#     userinfo_chunks = [i for i in (split_uri.username, split_uri.password) if i is not None]
#     if userinfo_chunks:
#         userinfo = ':'.join(userinfo_chunks)
#         _check_disallowed_characters(userinfo, 'userinfo')
#         netloc = userinfo + '@' + netloc
# 
#     # Normalize path
#     path = split_uri.path
#     # Unquote and quote - this normalizes the percent encoding
# 
#     # This is hackish. `unquote` and `quote` requires `str` in both py27 and py3+.
#     if isinstance(path, str):
#         # Python 3 branch
#         path = quote(unquote(path), safe='/' + SUB_DELIMS)
#     else:
#         # Python 2 branch
#         path = quote(unquote(path.encode('utf-8')), safe=('/' + SUB_DELIMS).encode('utf-8')).decode('utf-8')
# 
#     path = remove_dot_segments(path)
#     if not path:
#         path = '/'
#     _check_disallowed_characters(path, 'path')
# 
#     # Normalize query.  On Python 2, `urlencode` without `doseq=True`
#     # requires values to be convertible to native strings using `str()`.
#     if isinstance(split_uri.query, str):
#         # Python 3 branch
#         data = parse_qsl(split_uri.query)
#     else:
#         # Python 2 branch
#         data = parse_qsl(split_uri.query.encode('utf-8'))
#     query = urlencode(data)
#     _check_disallowed_characters(query, 'query')
# 
#     # Normalize fragment
#     fragment = unquote(split_uri.fragment)
#     _check_disallowed_characters(fragment, 'fragment')
# 
#     return urlunsplit((scheme, netloc, path, query, fragment))


import unittest
import warnings
import six
from testfixtures import ShouldWarn
from openid.urinorm import urinorm



class UrinormTest(unittest.TestCase):
    """Test `urinorm` function."""

    def test_normalized(self):
        self.assertEqual(urinorm('http://example.com/'), 'http://example.com/')
        warning_msg = "Binary input for urinorm is deprecated. Use text input instead."
        with ShouldWarn(DeprecationWarning(warning_msg)):
            warnings.simplefilter('always')
            self.assertEqual(urinorm(b'http://example.com/'), 'http://example.com/')

    def test_lowercase_scheme(self):
        self.assertEqual(urinorm('htTP://example.com/'), 'http://example.com/')

    def test_unsupported_scheme(self):
        six.assertRaisesRegex(self, ValueError, 'Not an absolute HTTP or HTTPS URI', urinorm, 'ftp://example.com/')

    def test_lowercase_hostname(self):
        self.assertEqual(urinorm('http://exaMPLE.COm/'), 'http://example.com/')

    def test_idn_hostname(self):
        self.assertEqual(urinorm('http://π.example.com/'), 'http://xn--1xa.example.com/')

    def test_empty_hostname(self):
        self.assertEqual(urinorm('http://username@/'), 'http://username@/')

    def test_invalid_hostname(self):
        six.assertRaisesRegex(self, ValueError, 'Invalid hostname', urinorm, 'http://.it/')

# Path: openid/urinorm.py
# def urinorm(uri):
#     """Return normalized URI.
# 
#     Normalization if performed according to RFC 3986, section 6 https://tools.ietf.org/html/rfc3986#section-6.
#     Supported URIs are URLs and OpenID realm URIs.
# 
#     @type uri: six.text_type, six.binary_type deprecated
#     @rtype: six.text_type
#     @raise ValueError: If URI is invalid.
#     """
#     uri = string_to_text(uri, "Binary input for urinorm is deprecated. Use text input instead.")
# 
#     split_uri = urlsplit(uri)
# 
#     # Normalize scheme
#     scheme = split_uri.scheme.lower()
#     if scheme not in ('http', 'https'):
#         raise ValueError('Not an absolute HTTP or HTTPS URI: {!r}'.format(uri))
# 
#     # Normalize netloc
#     if not split_uri.netloc:
#         raise ValueError('Not an absolute URI: {!r}'.format(uri))
# 
#     hostname = split_uri.hostname
#     if hostname is None:
#         hostname = ''
#     else:
#         hostname = hostname.lower()
#     # Unquote percent encoded characters
#     hostname = unquote(hostname)
#     # Quote IDN domain names
#     try:
#         # hostname: str --[idna]--> bytes --[utf-8]--> str
#         hostname = hostname.encode('idna').decode('utf-8')
#     except ValueError as error:
#         raise ValueError('Invalid hostname {!r}: {}'.format(hostname, error))
#     _check_disallowed_characters(hostname, 'hostname')
# 
#     try:
#         port = split_uri.port
#     except ValueError as error:
#         raise ValueError('Invalid port in {!r}: {}'.format(split_uri.netloc, error))
#     if port is None:
#         port = ''
#     elif (scheme == 'http' and port == 80) or (scheme == 'https' and port == 443):
#         port = ''
# 
#     netloc = hostname
#     if port:
#         netloc = netloc + ':' + six.text_type(port)
#     userinfo_chunks = [i for i in (split_uri.username, split_uri.password) if i is not None]
#     if userinfo_chunks:
#         userinfo = ':'.join(userinfo_chunks)
#         _check_disallowed_characters(userinfo, 'userinfo')
#         netloc = userinfo + '@' + netloc
# 
#     # Normalize path
#     path = split_uri.path
#     # Unquote and quote - this normalizes the percent encoding
# 
#     # This is hackish. `unquote` and `quote` requires `str` in both py27 and py3+.
#     if isinstance(path, str):
#         # Python 3 branch
#         path = quote(unquote(path), safe='/' + SUB_DELIMS)
#     else:
#         # Python 2 branch
#         path = quote(unquote(path.encode('utf-8')), safe=('/' + SUB_DELIMS).encode('utf-8')).decode('utf-8')
# 
#     path = remove_dot_segments(path)
#     if not path:
#         path = '/'
#     _check_disallowed_characters(path, 'path')
# 
#     # Normalize query.  On Python 2, `urlencode` without `doseq=True`
#     # requires values to be convertible to native strings using `str()`.
#     if isinstance(split_uri.query, str):
#         # Python 3 branch
#         data = parse_qsl(split_uri.query)
#     else:
#         # Python 2 branch
#         data = parse_qsl(split_uri.query.encode('utf-8'))
#     query = urlencode(data)
#     _check_disallowed_characters(query, 'query')
# 
#     # Normalize fragment
#     fragment = unquote(split_uri.fragment)
#     _check_disallowed_characters(fragment, 'fragment')
# 
#     return urlunsplit((scheme, netloc, path, query, fragment))


# Path: openid/test/test_urinorm.py
import unittest
import warnings
import six
from testfixtures import ShouldWarn
from openid.urinorm import urinorm




class UrinormTest(unittest.TestCase):
    """Test `urinorm` function."""

    def test_normalized(self):
        self.assertEqual(urinorm('http://example.com/'), 'http://example.com/')
        warning_msg = "Binary input for urinorm is deprecated. Use text input instead."
        with ShouldWarn(DeprecationWarning(warning_msg)):
            warnings.simplefilter('always')
            self.assertEqual(urinorm(b'http://example.com/'), 'http://example.com/')

    def test_lowercase_scheme(self):
        self.assertEqual(urinorm('htTP://example.com/'), 'http://example.com/')

    def test_unsupported_scheme(self):
        six.assertRaisesRegex(self, ValueError, 'Not an absolute HTTP or HTTPS URI', urinorm, 'ftp://example.com/')

    def test_lowercase_hostname(self):
        self.assertEqual(urinorm('http://exaMPLE.COm/'), 'http://example.com/')

    def test_idn_hostname(self):
        self.assertEqual(urinorm('http://π.example.com/'), 'http://xn--1xa.example.com/')

    def test_empty_hostname(self):
        self.assertEqual(urinorm('http://username@/'), 'http://username@/')

    def test_invalid_hostname(self):
        six.assertRaisesRegex(self, ValueError, 'Invalid hostname', urinorm, 'http://.it/')

        six.assertRaisesRegex(self, ValueError, 'Invalid hostname', urinorm, 'http://..it/')