initial import from Twisted, with gitignore, license, code, tests, and setup.py

This commit is contained in:
Mahmoud Hashemi 2017-02-14 11:37:11 -08:00
parent dc187bd19e
commit f0176169ee
10 changed files with 166 additions and 65 deletions

50
.gitignore vendored Normal file
View File

@ -0,0 +1,50 @@
docs/_build
tmp.py
htmlcov/
*.py[cod]
# emacs
*~
._*
.\#*
\#*\#
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Vim
*.sw[op]
.cache/

6
hyperlink/__init__.py Normal file
View File

@ -0,0 +1,6 @@
from ._url import URL
__all__ = [
"URL",
]

View File

@ -528,10 +528,9 @@ class URL(object):
For example::
>>> u = URL.fromText(u"http://localhost/a/b?x=y")
>>> v = u.child(u"c", u"d")
>>> print(v.asText())
http://localhost/a/b/c/d?x=y
>>> (URL.fromText(u"http://localhost/a/b?x=y")
.child(u"c", u"d").asText())
u'http://localhost/a/b/c?x=y'
@param segments: A path segment.
@type segments: L{tuple} of L{unicode}

View File

@ -92,11 +92,11 @@ class TestURL(TestCase):
self.assertTrue(isinstance(u.host, unicode)
or u.host is None, repr(u))
for seg in u.path:
self.assertTrue(isinstance(seg, unicode), repr(u))
self.assertIsInstance(seg, unicode, repr(u))
for (k, v) in u.query:
self.assertTrue(isinstance(k, unicode), repr(u))
self.assertIsInstance(k, unicode, repr(u))
self.assertTrue(v is None or isinstance(v, unicode), repr(u))
self.assertTrue(isinstance(u.fragment, unicode), repr(u))
self.assertIsInstance(u.fragment, unicode, repr(u))
def assertURL(self, u, scheme, host, path, query,
@ -170,11 +170,11 @@ class TestURL(TestCase):
def test_repr(self):
"""
L{URL.__repr__} will display the canoncial form of the URL, wrapped in
L{URL.__repr__} will display the canonical form of the URL, wrapped in
a L{URL.fromText} invocation, so that it is C{eval}-able but still easy
to read.
"""
self.assertEquals(
self.assertEqual(
repr(URL(scheme=u'http', host=u'foo', path=[u'bar'],
query=[(u'baz', None), (u'k', u'v')],
fragment=u'frob')),
@ -188,7 +188,7 @@ class TestURL(TestCase):
URL.
"""
urlpath = URL.fromText(theurl)
self.assertEquals(theurl, urlpath.asText())
self.assertEqual(theurl, urlpath.asText())
def test_roundtrip(self):
@ -213,7 +213,7 @@ class TestURL(TestCase):
)
for test in tests:
result = URL.fromText(test).asText()
self.assertEquals(test, result)
self.assertEqual(test, result)
def test_equality(self):
@ -223,8 +223,8 @@ class TestURL(TestCase):
strings.
"""
urlpath = URL.fromText(theurl)
self.assertEquals(urlpath, URL.fromText(theurl))
self.assertNotEquals(
self.assertEqual(urlpath, URL.fromText(theurl))
self.assertNotEqual(
urlpath,
URL.fromText('ftp://www.anotherinvaliddomain.com/'
'foo/bar/baz/?zot=21&zut')
@ -247,15 +247,15 @@ class TestURL(TestCase):
or fragment.
"""
urlpath = URL.fromText(theurl)
self.assertEquals("http://www.foo.com/a/nice/path/gong?zot=23&zut",
self.assertEqual("http://www.foo.com/a/nice/path/gong?zot=23&zut",
urlpath.child(u'gong').asText())
self.assertEquals("http://www.foo.com/a/nice/path/gong%2F?zot=23&zut",
self.assertEqual("http://www.foo.com/a/nice/path/gong%2F?zot=23&zut",
urlpath.child(u'gong/').asText())
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/gong%2Fdouble?zot=23&zut",
urlpath.child(u'gong/double').asText()
)
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/gong%2Fdouble%2F?zot=23&zut",
urlpath.child(u'gong/double/').asText()
)
@ -277,8 +277,8 @@ class TestURL(TestCase):
path segment.
"""
childURL = URL(host=u"www.foo.com").child(u"c")
self.assertEquals(childURL.rooted, True)
self.assertEquals("http://www.foo.com/c", childURL.asText())
self.assertTrue(childURL.rooted)
self.assertEqual("http://www.foo.com/c", childURL.asText())
def test_sibling(self):
@ -287,14 +287,14 @@ class TestURL(TestCase):
affect the query or fragment.
"""
urlpath = URL.fromText(theurl)
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/sister?zot=23&zut",
urlpath.sibling(u'sister').asText()
)
# Use an url without trailing '/' to check child removal.
theurl2 = "http://www.foo.com/a/nice/path?zot=23&zut"
urlpath = URL.fromText(theurl2)
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/sister?zot=23&zut",
urlpath.sibling(u'sister').asText()
)
@ -307,16 +307,16 @@ class TestURL(TestCase):
"""
urlpath = URL.fromText(theurl)
# A null uri should be valid (return here).
self.assertEquals("http://www.foo.com/a/nice/path/?zot=23&zut",
self.assertEqual("http://www.foo.com/a/nice/path/?zot=23&zut",
urlpath.click("").asText())
# A simple relative path remove the query.
self.assertEquals("http://www.foo.com/a/nice/path/click",
self.assertEqual("http://www.foo.com/a/nice/path/click",
urlpath.click("click").asText())
# An absolute path replace path and query.
self.assertEquals("http://www.foo.com/click",
self.assertEqual("http://www.foo.com/click",
urlpath.click("/click").asText())
# Replace just the query.
self.assertEquals("http://www.foo.com/a/nice/path/?burp",
self.assertEqual("http://www.foo.com/a/nice/path/?burp",
urlpath.click("?burp").asText())
# One full url to another should not generate '//' between authority.
# and path
@ -326,14 +326,14 @@ class TestURL(TestCase):
# From a url with no query clicking a url with a query, the query
# should be handled properly.
u = URL.fromText('http://www.foo.com/me/noquery')
self.failUnlessEqual('http://www.foo.com/me/17?spam=158',
u.click('/me/17?spam=158').asText())
self.assertEqual('http://www.foo.com/me/17?spam=158',
u.click('/me/17?spam=158').asText())
# Check that everything from the path onward is removed when the click
# link has no path.
u = URL.fromText('http://localhost/foo?abc=def')
self.failUnlessEqual(u.click('http://www.python.org').asText(),
'http://www.python.org')
self.assertEqual(u.click('http://www.python.org').asText(),
'http://www.python.org')
def test_clickRFC3986(self):
@ -342,7 +342,7 @@ class TestURL(TestCase):
"""
base = URL.fromText(relativeLinkBaseForRFC3986)
for (ref, expected) in relativeLinkTestsForRFC3986:
self.failUnlessEqual(base.click(ref).asText(), expected)
self.assertEqual(base.click(ref).asText(), expected)
def test_clickSchemeRelPath(self):
@ -397,7 +397,7 @@ class TestURL(TestCase):
]
for start, click, expected in tests:
actual = URL.fromText(start).click(click).asText()
self.assertEquals(
self.assertEqual(
actual,
expected,
"{start}.click({click}) => {actual} not {expected}".format(
@ -413,30 +413,30 @@ class TestURL(TestCase):
"""
L{URL.add} adds query parameters.
"""
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?foo=bar",
URL.fromText("http://www.foo.com/a/nice/path/")
.add(u"foo", u"bar").asText())
self.assertEquals(
self.assertEqual(
"http://www.foo.com/?foo=bar",
URL(host=u"www.foo.com").add(u"foo", u"bar")
.asText())
urlpath = URL.fromText(theurl)
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=23&zut&burp",
urlpath.add(u"burp").asText())
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=23&zut&burp=xxx",
urlpath.add(u"burp", u"xxx").asText())
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=23&zut&burp=xxx&zing",
urlpath.add(u"burp", u"xxx").add(u"zing").asText())
# Note the inversion!
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=23&zut&zing&burp=xxx",
urlpath.add(u"zing").add(u"burp", u"xxx").asText())
# Note the two values for the same name.
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=23&zut&burp=xxx&zot=32",
urlpath.add(u"burp", u"xxx").add(u"zot", u'32')
.asText())
@ -447,17 +447,17 @@ class TestURL(TestCase):
L{URL.set} replaces query parameters by name.
"""
urlpath = URL.fromText(theurl)
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=32&zut",
urlpath.set(u"zot", u'32').asText())
# Replace name without value with name/value and vice-versa.
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot&zut=itworked",
urlpath.set(u"zot").set(u"zut", u"itworked").asText()
)
# Q: what happens when the query has two values and we replace?
# A: we replace both values with a single one
self.assertEquals(
self.assertEqual(
"http://www.foo.com/a/nice/path/?zot=32&zut",
urlpath.add(u"zot", u"xxx").set(u"zot", u'32').asText()
)
@ -527,7 +527,7 @@ class TestURL(TestCase):
"""
u1 = URL.fromText('http://localhost/a')
u2 = URL.fromText('http://localhost/b')
self.failIf(u1 == u2, "%r != %r" % (u1, u2))
self.assertFalse(u1 == u2, "%r != %r" % (u1, u2))
self.assertNotEqual(u1, u2)
@ -536,8 +536,8 @@ class TestURL(TestCase):
L{URL} is not equal (C{==}) to other types.
"""
u = URL.fromText('http://localhost/')
self.failIf(u == 42, "URL must not equal a number.")
self.failIf(u == object(), "URL must not equal an object.")
self.assertFalse(u == 42, "URL must not equal a number.")
self.assertFalse(u == object(), "URL must not equal an object.")
self.assertNotEqual(u, 42)
self.assertNotEqual(u, object())
@ -547,7 +547,7 @@ class TestURL(TestCase):
Identical L{URL}s are not unequal (C{!=}) to each other.
"""
u = URL.fromText('http://localhost/')
self.failIf(u != u, "%r == itself" % u)
self.assertFalse(u != u, "%r == itself" % u)
def test_similarNotUnequal(self):
@ -556,7 +556,7 @@ class TestURL(TestCase):
"""
u1 = URL.fromText('http://localhost/')
u2 = URL.fromText('http://localhost/')
self.failIf(u1 != u2, "%r == %r" % (u1, u2))
self.assertFalse(u1 != u2, "%r == %r" % (u1, u2))
def test_differentUnequal(self):
@ -565,7 +565,7 @@ class TestURL(TestCase):
"""
u1 = URL.fromText('http://localhost/a')
u2 = URL.fromText('http://localhost/b')
self.failUnless(u1 != u2, "%r == %r" % (u1, u2))
self.assertTrue(u1 != u2, "%r == %r" % (u1, u2))
def test_otherTypesUnequal(self):
@ -573,8 +573,8 @@ class TestURL(TestCase):
L{URL} is unequal (C{!=}) to other types.
"""
u = URL.fromText('http://localhost/')
self.failUnless(u != 42, "URL must differ from a number.")
self.failUnless(u != object(), "URL must be differ from an object.")
self.assertTrue(u != 42, "URL must differ from a number.")
self.assertTrue(u != object(), "URL must be differ from an object.")
def test_asURI(self):

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
pytest==2.9.2
pytest-cov==2.3.0

60
setup.py Normal file
View File

@ -0,0 +1,60 @@
"""The humble, but powerful, URL runs everything around us. Chances
are you've used several just to read this text.
Hyperlink is a featureful, pure-Python implementation of the URL, with
an emphasis on correctness. BSD licensed.
"""
from setuptools import setup
__author__ = 'Mahmoud Hashemi and Glyph Lefkowitz'
__version__ = '17.0.0'
__contact__ = 'mahmoud@hatnote.com'
__url__ = 'https://github.com/mahmoud/hyperlink'
__license__ = 'BSD'
setup(name='hyperlink',
version=__version__,
description="A featureful, correct URL for Python.",
long_description=__doc__,
author=__author__,
author_email=__contact__,
url=__url__,
packages=['hyperlink'],
include_package_data=True,
zip_safe=False,
license=__license__,
platforms='any',
classifiers=[
'Topic :: Utilities',
'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries',
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: PyPy', ]
)
"""
A brief checklist for release:
* tox
* git commit (if applicable)
* Bump setup.py version off of -dev
* git commit -a -m "bump version for x.y.z release"
* python setup.py sdist bdist_wheel upload
* bump docs/conf.py version
* git commit
* git tag -a x.y.z -m "brief summary"
* write CHANGELOG
* git commit
* bump setup.py version onto n+1 dev
* git commit
* git push
"""

View File

@ -1,15 +0,0 @@
# -*- test-case-name: twisted.python.test.test_url -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
URL parsing, construction and rendering.
@see: L{URL}
"""
from ._url import URL
__all__ = [
"URL",
]

View File

@ -1 +0,0 @@
twisted.python.url is a new abstraction for URLs, supporting RFC 3987 IRIs.