Support for Python3

Because of absolute imports, `uritemplates.py` has been merged with
`__init__.py`.
diff --git a/.travis.yml b/.travis.yml
index f55bb68..520ac1b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@
   - "2.5"
   - "2.6"
   - "2.7"
+  - "3.3"
   - "pypy"
 # dependencies
 install: pip install simplejson --use-mirrors
diff --git a/setup.py b/setup.py
index 469d006..9b71aae 100755
--- a/setup.py
+++ b/setup.py
@@ -27,6 +27,8 @@
     'Programming Language :: Python :: 2.5',
     'Programming Language :: Python :: 2.6',
     'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3',
+    'Programming Language :: Python :: 3.3',
     'Operating System :: POSIX',
     'Topic :: Internet :: WWW/HTTP',
     'Topic :: Software Development :: Libraries :: Python Modules',
diff --git a/test/uritemplate_test.py b/test/uritemplate_test.py
index f76ccb5..f691cd9 100644
--- a/test/uritemplate_test.py
+++ b/test/uritemplate_test.py
@@ -3,8 +3,8 @@
 import sys
 
 filename = sys.argv[1]
-print "Running", filename
-f = file(filename)
+print("Running", filename)
+f = open(filename)
 testdata = simplejson.load(f)
 
 try:
@@ -12,7 +12,7 @@
 except IndexError:
   desired_level = 4
 
-for name, testsuite in testdata.iteritems():
+for name, testsuite in testdata.items():
   vars = testsuite['variables']
   testcases = testsuite['testcases']
 
@@ -20,7 +20,7 @@
   if level > desired_level:
     continue
 
-  print name
+  print(name)
   for testcase in testcases:
     template = testcase[0]
     expected = testcase[1]
@@ -34,4 +34,4 @@
       if actual != expected:
         sys.stderr.write("%s expected to expand to %s, got %s instead\n" % (template, expected, actual))
         assert 0
-  print
+  print()
diff --git a/uritemplate/__init__.py b/uritemplate/__init__.py
index dc646f9..2894b94 100644
--- a/uritemplate/__init__.py
+++ b/uritemplate/__init__.py
@@ -1,4 +1,265 @@
+#!/usr/bin/env python
 
-from uritemplate import expand, variables
+"""
+URI Template (RFC6570) Processor
+"""
+
+__copyright__ = """\
+Copyright 2011-2012 Joe Gregorio
+
+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.
+"""
+
+import re
+try:
+   from urllib.parse import quote
+except ImportError:
+   from urllib import quote
+
+
 
 __version__ = "0.5.2"
+
+RESERVED = ":/?#[]@!$&'()*+,;="
+OPERATOR = "+#./;?&|!@"
+MODIFIER = ":^"
+TEMPLATE = re.compile("{([^\}]+)}")
+
+
+def variables(template):
+    '''Returns the set of keywords in a uri template'''
+    vars = set()
+    for varlist in TEMPLATE.findall(template):
+        if varlist[0] in OPERATOR:
+            varlist = varlist[1:]
+        varspecs = varlist.split(',')
+        for var in varspecs:
+            # handle prefix values
+            var = var.split(':')[0]
+            # handle composite values
+            if var.endswith('*'):
+                var = var[:-1]
+            vars.add(var)
+    return vars
+
+
+def _quote(value, safe, prefix=None):
+    if prefix is not None:
+        return quote(str(value)[:prefix], safe)
+    return quote(str(value), safe)
+
+
+def _tostring(varname, value, explode, prefix, operator, safe=""):
+    if isinstance(value, list):
+        return ",".join([_quote(x, safe) for x in value])
+    if isinstance(value, dict):
+        keys = sorted(value.keys())
+        if explode:
+            return ",".join([_quote(key, safe) + "=" + \
+                             _quote(value[key], safe) for key in keys])
+        else:
+            return ",".join([_quote(key, safe) + "," + \
+                             _quote(value[key], safe) for key in keys])
+    elif value is None:
+        return
+    else:
+        return _quote(value, safe, prefix)
+
+
+def _tostring_path(varname, value, explode, prefix, operator, safe=""):
+    joiner = operator
+    if isinstance(value, list):
+        if explode:
+            out = [_quote(x, safe) for x in value if value is not None]
+        else:
+            joiner = ","
+            out = [_quote(x, safe) for x in value if value is not None]
+        if out:
+            return joiner.join(out)
+        else:
+            return
+    elif isinstance(value, dict):
+        keys = sorted(value.keys())
+        if explode:
+            out = [_quote(key, safe) + "=" + \
+                   _quote(value[key], safe) for key in keys \
+                   if value[key] is not None]
+        else:
+            joiner = ","
+            out = [_quote(key, safe) + "," + \
+                   _quote(value[key], safe) \
+                   for key in keys if value[key] is not None]
+        if out:
+            return joiner.join(out)
+        else:
+            return
+    elif value is None:
+        return
+    else:
+        return _quote(value, safe, prefix)
+
+
+def _tostring_semi(varname, value, explode, prefix, operator, safe=""):
+    joiner = operator
+    if operator == "?":
+        joiner = "&"
+    if isinstance(value, list):
+        if explode:
+            out = [varname + "=" + _quote(x, safe) \
+                   for x in value if x is not None]
+            if out:
+                return joiner.join(out)
+            else:
+                return
+        else:
+            return varname + "=" + ",".join([_quote(x, safe) \
+                                             for x in value])
+    elif isinstance(value, dict):
+        keys = sorted(value.keys())
+        if explode:
+            return joiner.join([_quote(key, safe) + "=" + \
+                                _quote(value[key], safe) \
+                                for key in keys if key is not None])
+        else:
+            return varname + "=" + ",".join([_quote(key, safe) + "," + \
+                             _quote(value[key], safe) for key in keys \
+                             if key is not None])
+    else:
+        if value is None:
+            return
+        elif value:
+            return (varname + "=" + _quote(value, safe, prefix))
+        else:
+            return varname
+
+
+def _tostring_query(varname, value, explode, prefix, operator, safe=""):
+    joiner = operator
+    if operator in ["?", "&"]:
+        joiner = "&"
+    if isinstance(value, list):
+        if 0 == len(value):
+            return None
+        if explode:
+            return joiner.join([varname + "=" + _quote(x, safe) \
+                                for x in value])
+        else:
+            return (varname + "=" + ",".join([_quote(x, safe) \
+                                             for x in value]))
+    elif isinstance(value, dict):
+        if 0 == len(value):
+            return None
+        keys = sorted(value.keys())
+        if explode:
+            return joiner.join([_quote(key, safe) + "=" + \
+                                _quote(value[key], safe) \
+                                for key in keys])
+        else:
+            return varname + "=" + \
+                   ",".join([_quote(key, safe) + "," + \
+                             _quote(value[key], safe) for key in keys])
+    else:
+        if value is None:
+            return
+        elif value:
+            return (varname + "=" + _quote(value, safe, prefix))
+        else:
+            return (varname  + "=")
+
+
+TOSTRING = {
+    "" : _tostring,
+    "+": _tostring,
+    "#": _tostring,
+    ";": _tostring_semi,
+    "?": _tostring_query,
+    "&": _tostring_query,
+    "/": _tostring_path,
+    ".": _tostring_path,
+    }
+
+
+def expand(template, variables):
+    """
+    Expand template as a URI Template using variables.
+    """
+    def _sub(match):
+        expression = match.group(1)
+        operator = ""
+        if expression[0] in OPERATOR:
+            operator = expression[0]
+            varlist = expression[1:]
+        else:
+            varlist = expression
+
+        safe = ""
+        if operator in ["+", "#"]:
+            safe = RESERVED
+        varspecs = varlist.split(",")
+        varnames = []
+        defaults = {}
+        for varspec in varspecs:
+            default = None
+            explode = False
+            prefix = None
+            if "=" in varspec:
+                varname, default = tuple(varspec.split("=", 1))
+            else:
+                varname = varspec
+            if varname[-1] == "*":
+                explode = True
+                varname = varname[:-1]
+            elif ":" in varname:
+                try:
+                    prefix = int(varname[varname.index(":")+1:])
+                except ValueError:
+                    raise ValueError("non-integer prefix '{0}'".format(
+                       varname[varname.index(":")+1:]))
+                varname = varname[:varname.index(":")]
+            if default:
+                defaults[varname] = default
+            varnames.append((varname, explode, prefix))
+
+        retval = []
+        joiner = operator
+        start = operator
+        if operator == "+":
+            start = ""
+            joiner = ","
+        if operator == "#":
+            joiner = ","
+        if operator == "?":
+            joiner = "&"
+        if operator == "&":
+            start = "&"
+        if operator == "":
+            joiner = ","
+        for varname, explode, prefix in varnames:
+            if varname in variables:
+                value = variables[varname]
+                if not value and value != "" and varname in defaults:
+                    value = defaults[varname]
+            elif varname in defaults:
+                value = defaults[varname]
+            else:
+                continue
+            expanded = TOSTRING[operator](
+              varname, value, explode, prefix, operator, safe=safe)
+            if expanded is not None:
+                retval.append(expanded)
+        if len(retval) > 0:
+            return start + joiner.join(retval)
+        else:
+            return ""
+
+    return TEMPLATE.sub(_sub, template)
diff --git a/uritemplate/uritemplate.py b/uritemplate/uritemplate.py
deleted file mode 100644
index d8c7f1a..0000000
--- a/uritemplate/uritemplate.py
+++ /dev/null
@@ -1,262 +0,0 @@
-#!/usr/bin/env python
-
-"""
-URI Template (RFC6570) Processor
-"""
-
-__copyright__ = """\
-Copyright 2011-2012 Joe Gregorio
-
-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.
-"""
-
-import re
-from urllib import quote
-
-RESERVED = ":/?#[]@!$&'()*+,;="
-OPERATOR = "+#./;?&|!@"
-MODIFIER = ":^"
-TEMPLATE = re.compile("{([^\}]+)}")
-
-
-def variables(template):
-    '''Returns the set of keywords in a uri template'''
-    vars = set()
-    for varlist in TEMPLATE.findall(template):
-        if varlist[0] in OPERATOR:
-            varlist = varlist[1:]
-        varspecs = varlist.split(',')
-        for var in varspecs:
-            # handle prefix values
-            var = var.split(':')[0]
-            # handle composite values
-            if var.endswith('*'):
-                var = var[:-1]
-            vars.add(var)
-    return vars
-
-
-def _quote(value, safe, prefix=None):
-    if prefix is not None:
-        return quote(str(value)[:prefix], safe)
-    return quote(str(value), safe)
-
-
-def _tostring(varname, value, explode, prefix, operator, safe=""):
-    if isinstance(value, list):
-        return ",".join([_quote(x, safe) for x in value])
-    if isinstance(value, dict):
-        keys = value.keys()
-        keys.sort()
-        if explode:
-            return ",".join([_quote(key, safe) + "=" + \
-                             _quote(value[key], safe) for key in keys])
-        else:
-            return ",".join([_quote(key, safe) + "," + \
-                             _quote(value[key], safe) for key in keys])
-    elif value is None:
-        return
-    else:
-        return _quote(value, safe, prefix)
-
-
-def _tostring_path(varname, value, explode, prefix, operator, safe=""):
-    joiner = operator
-    if isinstance(value, list):
-        if explode:
-            out = [_quote(x, safe) for x in value if value is not None]
-        else:
-            joiner = ","
-            out = [_quote(x, safe) for x in value if value is not None]
-        if out:
-            return joiner.join(out)
-        else:
-            return
-    elif isinstance(value, dict):
-        keys = value.keys()
-        keys.sort()
-        if explode:
-            out = [_quote(key, safe) + "=" + \
-                   _quote(value[key], safe) for key in keys \
-                   if value[key] is not None]
-        else:
-            joiner = ","
-            out = [_quote(key, safe) + "," + \
-                   _quote(value[key], safe) \
-                   for key in keys if value[key] is not None]
-        if out:
-            return joiner.join(out)
-        else:
-            return
-    elif value is None:
-        return
-    else:
-        return _quote(value, safe, prefix)
-
-
-def _tostring_semi(varname, value, explode, prefix, operator, safe=""):
-    joiner = operator
-    if operator == "?":
-        joiner = "&"
-    if isinstance(value, list):
-        if explode:
-            out = [varname + "=" + _quote(x, safe) \
-                   for x in value if x is not None]
-            if out:
-                return joiner.join(out)
-            else:
-                return
-        else:
-            return varname + "=" + ",".join([_quote(x, safe) \
-                                             for x in value])
-    elif isinstance(value, dict):
-        keys = value.keys()
-        keys.sort()
-        if explode:
-            return joiner.join([_quote(key, safe) + "=" + \
-                                _quote(value[key], safe) \
-                                for key in keys if key is not None])
-        else:
-            return varname + "=" + ",".join([_quote(key, safe) + "," + \
-                             _quote(value[key], safe) for key in keys \
-                             if key is not None])
-    else:
-        if value is None:
-            return
-        elif value:
-            return (varname + "=" + _quote(value, safe, prefix))
-        else:
-            return varname
-
-
-def _tostring_query(varname, value, explode, prefix, operator, safe=""):
-    joiner = operator
-    if operator in ["?", "&"]:
-        joiner = "&"
-    if isinstance(value, list):
-        if 0 == len(value):
-            return None
-        if explode:
-            return joiner.join([varname + "=" + _quote(x, safe) \
-                                for x in value])
-        else:
-            return (varname + "=" + ",".join([_quote(x, safe) \
-                                             for x in value]))
-    elif isinstance(value, dict):
-        if 0 == len(value):
-            return None
-        keys = value.keys()
-        keys.sort()
-        if explode:
-            return joiner.join([_quote(key, safe) + "=" + \
-                                _quote(value[key], safe) \
-                                for key in keys])
-        else:
-            return varname + "=" + \
-                   ",".join([_quote(key, safe) + "," + \
-                             _quote(value[key], safe) for key in keys])
-    else:
-        if value is None:
-            return
-        elif value:
-            return (varname + "=" + _quote(value, safe, prefix))
-        else:
-            return (varname  + "=")
-
-
-TOSTRING = {
-    "" : _tostring,
-    "+": _tostring,
-    "#": _tostring,
-    ";": _tostring_semi,
-    "?": _tostring_query,
-    "&": _tostring_query,
-    "/": _tostring_path,
-    ".": _tostring_path,
-    }
-
-
-def expand(template, variables):
-    """
-    Expand template as a URI Template using variables.
-    """
-    def _sub(match):
-        expression = match.group(1)
-        operator = ""
-        if expression[0] in OPERATOR:
-            operator = expression[0]
-            varlist = expression[1:]
-        else:
-            varlist = expression
-
-        safe = ""
-        if operator in ["+", "#"]:
-            safe = RESERVED
-        varspecs = varlist.split(",")
-        varnames = []
-        defaults = {}
-        for varspec in varspecs:
-            default = None
-            explode = False
-            prefix = None
-            if "=" in varspec:
-                varname, default = tuple(varspec.split("=", 1))
-            else:
-                varname = varspec
-            if varname[-1] == "*":
-                explode = True
-                varname = varname[:-1]
-            elif ":" in varname:
-                try:
-                    prefix = int(varname[varname.index(":")+1:])
-                except ValueError:
-                    raise ValueError, "non-integer prefix '%s'" \
-                                      % varname[varname.index(":")+1:]
-                varname = varname[:varname.index(":")]
-            if default:
-                defaults[varname] = default
-            varnames.append((varname, explode, prefix))
-
-        retval = []
-        joiner = operator
-        start = operator
-        if operator == "+":
-            start = ""
-            joiner = ","
-        if operator == "#":
-            joiner = ","
-        if operator == "?":
-            joiner = "&"
-        if operator == "&":
-            start = "&"
-        if operator == "":
-            joiner = ","
-        for varname, explode, prefix in varnames:
-            if varname in variables:
-                value = variables[varname]
-                if not value and value != "" and varname in defaults:
-                    value = defaults[varname]
-            elif varname in defaults:
-                value = defaults[varname]
-            else:
-                continue
-            expanded = TOSTRING[operator](
-              varname, value, explode, prefix, operator, safe=safe)
-            if expanded is not None:
-                retval.append(expanded)
-        if len(retval) > 0:
-            return start + joiner.join(retval)
-        else:
-            return ""
-
-    return TEMPLATE.sub(_sub, template)