Skip to content

Commit

Permalink
Initial Commit - Submitted 8:06pm
Browse files Browse the repository at this point in the history
  • Loading branch information
madeofstars0 committed Nov 13, 2013
0 parents commit 0af9c89
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
*.pyo
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Nuri

A happy little uri parsing and handy dandy little helper.

**Need a little help with query params?**

myuri = Uri("http://example.com/path/page")
myuri.query_params["id"] = 1239
str(myuri) == "http://example.com/path/page?id=1239"
Empty file added nuri/__init__.py
Empty file.
107 changes: 107 additions & 0 deletions nuri/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import unittest
from uri import Uri

class GeneralTest(unittest.TestCase):
def testClassInstatiates(self):
myuri = Uri()
self.assertIsNotNone(myuri)

class EasyHttpsUriParseTest(unittest.TestCase):
def setUp(self):
self.myuri = Uri("https://www.example.com/subdir/file.html")

def testUriScheme(self):
self.assertEqual("https", self.myuri.scheme)

def testUriHost(self):
self.assertEqual("www.example.com", self.myuri.host)

def testUriPath(self):
self.assertEqual("/subdir/file.html", self.myuri.path)

def testUriQueryParams(self):
self.assertEqual({}, self.myuri.query_params)


class EasyHttpUriParseTest(unittest.TestCase):
def setUp(self):
self.myuri = Uri("http://sub.example.com/directory/path")

def testUriScheme(self):
self.assertEqual("http", self.myuri.scheme)

def testUriHost(self):
self.assertEqual("sub.example.com", self.myuri.host)

def testUriPath(self):
self.assertEqual("/directory/path", self.myuri.path)

def testUriQueryParams(self):
self.assertEqual({}, self.myuri.query_params)

class EasyFileUriParseTest(unittest.TestCase):
def setUp(self):
self.myuri = Uri("file:///etc/nginx/sites-available/default")

def testUriScheme(self):
self.assertEqual("file", self.myuri.scheme)

def testUriHost(self):
self.assertEqual("", self.myuri.host)

def testUriPath(self):
self.assertEqual("/etc/nginx/sites-available/default", self.myuri.path)

def testUriQueryParams(self):
self.assertEqual({}, self.myuri.query_params)

class TrixyUriParseTest(unittest.TestCase):
def testEmptyUri(self):
myuri = Uri("")
self.assertIsNotNone(myuri)
self.assertIsNone(myuri.scheme)
self.assertIsNone(myuri.authority)
self.assertIsNone(myuri.host)
self.assertIsNone(myuri.path)
self.assertEqual({}, myuri.query_params)

def testJustPathUri(self):
myuri = Uri("path.html")
self.assertIsNone(myuri.scheme)
self.assertIsNone(myuri.authority)
self.assertIsNone(myuri.host)
self.assertEqual("path.html", myuri.path)
self.assertEqual({}, myuri.query_params)

def testUrnUri(self):
myuri = Uri("urn:path:to:nowhere")
self.assertEqual(myuri.scheme, "urn")
self.assertIsNone(myuri.authority)
self.assertIsNone(myuri.host)
self.assertEqual(myuri.path, "path:to:nowhere")
self.assertEqual(myuri.query_params, {})

def testPathParsing(self):
self.assertEqual(Uri("http://bob/abc?q=1").path, "/abc")
self.assertEqual(Uri("http://bob/zyx#thing").path, "/zyx")
self.assertEqual(Uri("urn:/etc/path?abc#thing").path, "/etc/path")

def testIpHostParsing(self):
self.assertEqual(Uri("http://10.0.0.1/bob.html").host, "10.0.0.1")
self.assertEqual(Uri("http://[::1]/bob.html").host, "[::1]")

class UriStringTest(unittest.TestCase):
def testHttpToString(self):
myuri = Uri("http://example.com/path/to/file.html")
self.assertEqual(str(myuri), "http://example.com/path/to/file.html")

def testHttpWithParamsToString(self):
myuri = Uri("http://example.com/path/to/file.html")
myuri.query_params["test"] = "bob"
self.assertEqual(str(myuri), "http://example.com/path/to/file.html?test=bob")
myuri.query_params["id"] = 1234
self.assertEqual(str(myuri), "http://example.com/path/to/file.html?test=bob&id=1234")

# Run the unit tests if called directly
if __name__ == '__main__':
unittest.main()
104 changes: 104 additions & 0 deletions nuri/uri.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

class Uri:
"""
A class for dealing with Uniform Resource Indicators defined by rfc3986
"""
scheme = None;
authority = None;
host = None;
path = None;
query_params = {};

def __init__(self, uri=None):
if uri != None:
self.parse(uri)

def parse(self, uri):
"""Parse the provided uri into the various components of a uri"""

scheme_range = None
# Grab the scheme (the part before the colon)
if uri.find(":") >= 0:
scheme_idx = uri.find(':')
scheme_range = (0, scheme_idx,)
self.scheme = uri[scheme_range[0]:scheme_range[1]]

# Grab the authority, if we have one
authority_range = None
if uri.find("//") >= 0:
authority_start_idx = uri.find("//") + 2 # account for the // characters
authority_end_idx = -1
# we have an authority, look for and ending (/, ?, #)
if uri.find('/', authority_start_idx) >= 0:
authority_end_idx = uri.find('/', authority_start_idx)
elif uri.find('?', authority_start_idx) >= 0:
authority_end_idx = uri.find('?', authority_start_idx)
elif uri.find('#', authority_start_idx) >= 0:
authority_end_idx = uri.find('#', authority_start_idx)
else:
authority_end_idx = len(uri)

authority_range = (authority_start_idx, authority_end_idx,)
self.authority = uri[authority_start_idx:authority_end_idx]

# TODO: parse authority into user-spec, host, port - converting this properly
# requires being able to parse domain, IPv4, IPv6, users, ports
# authority = [ userinfo "@" ] host [ ":" port ]
# userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
# host = IP-literal / IPv4address / reg-name
# port = *DIGIT
self.host = self.authority

# Parse out the path
path_range = None

# Check for authority, then scheme (authority always comes after scheme)
path_start_idx = -1
if authority_range != None:
path_start_idx = authority_range[1]
elif authority_range == None and scheme_range != None:
path_start_idx = scheme_range[1] + 1 # account for the :
# Check for naked path
elif scheme_range == None and authority_range == None:
path_start_idx = 0

# Determine the path range if we have a place to start
if path_start_idx >= 0:
# Calculate range
if uri.find('?', path_start_idx) >= 0:
path_range = (path_start_idx, uri.find('?', path_start_idx),)
elif uri.find('#', path_start_idx) >= 0:
path_range = (path_start_idx, uri.find('#', path_start_idx),)
else:
path_range = (path_start_idx, len(uri),)

# Grab the path if we have a range to work with
if path_range != None and path_range[0] != path_range[1]:
self.path = uri[path_range[0]:path_range[1]]



def __str__(self):
uri_components = []
if self.scheme != None:
uri_components.append(self.scheme)
uri_components.append(':')
if self.authority != None:
uri_components.append('//')
uri_components.append(self.authority)
if self.path != None:
uri_components.append(self.path)

query_indicated = False
for k,v in self.query_params.iteritems():
if not query_indicated:
uri_components.append('?')
query_indicated = True
else:
uri_components.append('&')
uri_components.append(str(k))
uri_components.append('=')
uri_components.append(str(v)) # TODO: URL Encode
return ''.join(uri_components)


0 comments on commit 0af9c89

Please sign in to comment.