diff --git a/rbcommenttype/rbcommenttype/__init__.py b/rbcommenttype/rbcommenttype/__init__.py
index 1ba34ba920d12902962b6890961daccaebd279db..ad4c49b56fd034bf6a6adf32ca791c3a5eb81a00 100644
--- a/rbcommenttype/rbcommenttype/__init__.py
+++ b/rbcommenttype/rbcommenttype/__init__.py
@@ -1,4 +1,4 @@
-from __future__ import unicode_literals
+"""The comment type extension."""
 
 
 # The version of rbcommenttype
@@ -7,47 +7,77 @@ from __future__ import unicode_literals
 #
 #   (Major, Minor, Micro, Patch, alpha/beta/rc/final, Release Number, Released)
 #
-VERSION = (2, 0, 0, 0, 'final', 0, True)
+VERSION = (3, 0, 0, 0, 'alpha', 0, False)
 
 
 def get_version_string():
-    version = '%s.%s' % (VERSION[0], VERSION[1])
+    """Return the version as a string.
 
-    if VERSION[2] or VERSION[3]:
-        version += ".%s" % VERSION[2]
+    Returns:
+        str:
+        The version number, formatted as a string.
+    """
+    major, minor, micro, patch, tag, relnum, is_release = VERSION
 
-    if VERSION[3]:
-        version += ".%s" % VERSION[3]
+    version = '%s.%s' % (major, minor)
 
-    if VERSION[4] != 'final':
-        if VERSION[4] == 'rc':
-            version += ' RC%s' % VERSION[5]
-        else:
-            version += ' %s %s' % (VERSION[4], VERSION[5])
+    if micro or patch:
+        version += '.%s' % micro
 
-    if not is_release():
-        version += " (dev)"
+        if patch:
+            version += '.%s' % patch
+
+        if tag != 'final':
+            if tag == 'rc':
+                version += ' RC'
+            else:
+                version += ' %s ' % tag
+
+            version += '%s' % relnum
+
+    if not is_release:
+        version += ' (dev)'
 
     return version
 
 
 def get_package_version():
-    version = '%s.%s' % (VERSION[0], VERSION[1])
+    """Return the package version as a string.
+
+    Returns:
+        str:
+        The package version number, formatted as a string.
+    """
+    major, minor, micro, patch, tag, relnum = VERSION[:-1]
+
+    version = '%s.%s' % (major, minor)
 
-    if VERSION[2] or VERSION[3]:
-        version += ".%s" % VERSION[2]
+    if micro or patch:
+        version += '.%s' % micro
 
-    if VERSION[3]:
-        version += ".%s" % VERSION[3]
+        if patch:
+            version += '.%s' % patch
 
-    if VERSION[4] != 'final':
-        version += '%s%s' % (VERSION[4], VERSION[5])
+    if tag != 'final':
+        version += '%s%s' % (
+            {
+                'alpha': 'a',
+                'beta': 'b',
+            }.get(tag, tag),
+            relnum)
 
     return version
 
 
 def is_release():
-    return VERSION[6]
+    """Return whether this is a final release.
+
+    Returns:
+        bool:
+        ``True`` if this version is a final release. ``False`` if built from
+        git or a prerelease.
+    """
+    return VERSION[-1]
 
 
 __version_info__ = VERSION[:-1]
diff --git a/rbcommenttype/rbcommenttype/admin_urls.py b/rbcommenttype/rbcommenttype/admin_urls.py
index 9a0328d8b84f894063cc04f7e08bf32fc9b70623..2e0bfd03d1ae67c9509f4421d01dd3770a6c6869 100644
--- a/rbcommenttype/rbcommenttype/admin_urls.py
+++ b/rbcommenttype/rbcommenttype/admin_urls.py
@@ -1,15 +1,15 @@
-from __future__ import unicode_literals
+"""URLs for the comment type extension."""
 
-from django.conf.urls import url
+from django.urls import path
+from reviewboard.extensions.views import configure_extension
 
 from rbcommenttype.extension import CommentTypeExtension
 from rbcommenttype.forms import CommentTypeSettingsForm
 
-from reviewboard.extensions.views import configure_extension
-
 
 urlpatterns = [
-    url('^$',
+    path(
+        '',
         configure_extension,
         {
             'ext_class': CommentTypeExtension,
diff --git a/rbcommenttype/rbcommenttype/extension.py b/rbcommenttype/rbcommenttype/extension.py
index 073df1aa3b43166c6a1c6664ec2e14749f41985c..dc36dd49ea034fcdb7851ffdd7887d8b82ba831c 100644
--- a/rbcommenttype/rbcommenttype/extension.py
+++ b/rbcommenttype/rbcommenttype/extension.py
@@ -1,4 +1,4 @@
-from __future__ import unicode_literals
+"""The comment type extension."""
 
 import json
 
diff --git a/rbcommenttype/rbcommenttype/forms.py b/rbcommenttype/rbcommenttype/forms.py
index 0e078471b4464ef3c7920011b8602e0c41f30b2e..e66627ac382b7549c74ec893ab2ab7b33a3b3b19 100644
--- a/rbcommenttype/rbcommenttype/forms.py
+++ b/rbcommenttype/rbcommenttype/forms.py
@@ -1,4 +1,4 @@
-from __future__ import unicode_literals
+"""Forms for the comment type extension."""
 
 import json
 
@@ -13,7 +13,7 @@ from djblets.extensions.forms import SettingsForm
 class CommentTypesWidget(Widget):
     """A form widget for configuring comment types."""
 
-    def render(self, name, value, attrs=None):
+    def render(self, name, value, attrs=None, renderer=None):
         """Render the widget."""
         attrs = self.build_attrs(attrs, {
             'type': 'hidden',
diff --git a/rbcommenttype/setup.py b/rbcommenttype/setup.py
index 205d3df5b4d011e043e3496f64cde747cc4d78ea..c96c8b70262af20ad72ec4c2cda03546f543cae9 100755
--- a/rbcommenttype/setup.py
+++ b/rbcommenttype/setup.py
@@ -1,7 +1,5 @@
 #!/usr/bin/env python
 
-from __future__ import unicode_literals
-
 from reviewboard.extensions.packaging import setup
 
 from rbcommenttype import get_package_version
@@ -33,10 +31,7 @@ setup(
             'templates/*.txt',
         ],
     },
-    python_requires=(
-        '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*'
-        '!=3.5.*'
-    ),
+    python_requires='>=3.7',
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
