diff --git a/reviewboard/webapi/base.py b/reviewboard/webapi/base.py
index 4fbbc9ecccfea131b96b2dbb1cc4ff5ad847948b..1b11e5055780057642ea35f4f5f35c26b95205f7 100644
--- a/reviewboard/webapi/base.py
+++ b/reviewboard/webapi/base.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.utils import six
 from django.utils.encoding import force_unicode
 from djblets.util.decorators import augment_method_from
diff --git a/reviewboard/webapi/decorators.py b/reviewboard/webapi/decorators.py
index 59d5e263b8daaf0b273aed41d2f6db4c05d3e9c0..6866693f295f312003654ce3a2aabf6fad9b2ded 100644
--- a/reviewboard/webapi/decorators.py
+++ b/reviewboard/webapi/decorators.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.http import HttpRequest
 from djblets.siteconfig.models import SiteConfiguration
 from djblets.webapi.core import WebAPIResponse, WebAPIResponseError
diff --git a/reviewboard/webapi/encoder.py b/reviewboard/webapi/encoder.py
index de584673aab911a9c88ac2d3ebd412a10e713042..0773a27d1db7a6bb1ff8f19226d695629d7bb960 100644
--- a/reviewboard/webapi/encoder.py
+++ b/reviewboard/webapi/encoder.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.template.defaultfilters import timesince
 from djblets.webapi.core import WebAPIEncoder
 
@@ -130,7 +132,7 @@ class DeprecatedReviewBoardAPIEncoder(WebAPIEncoder):
             return {
                 'id': o.id,
                 'caption': o.caption,
-                'title': u'Screenshot: %s' % (o.caption or o.image.name),
+                'title': 'Screenshot: %s' % (o.caption or o.image.name),
                 'image_url': o.get_absolute_url(),
                 'thumbnail_url': o.get_thumbnail_url(),
             }
diff --git a/reviewboard/webapi/errors.py b/reviewboard/webapi/errors.py
index 931d1a36b4e4223618846406466cd10afa6489cf..94360a7b99e995933a2a31dd12ed21e6b52ce45a 100644
--- a/reviewboard/webapi/errors.py
+++ b/reviewboard/webapi/errors.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.webapi.errors import WebAPIError
 
 
diff --git a/reviewboard/webapi/mixins.py b/reviewboard/webapi/mixins.py
index 35fc162b2566b5174b483e350c9d67b4af664b91..19a1e07ed750e9fc121b0870d29cee08fe7b56e9 100644
--- a/reviewboard/webapi/mixins.py
+++ b/reviewboard/webapi/mixins.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from reviewboard.reviews.markdown_utils import markdown_set_field_escaped
 
 
diff --git a/reviewboard/webapi/resources/__init__.py b/reviewboard/webapi/resources/__init__.py
index 47d3775810d048865141c9d10f6ef8e4ca8b29cc..32ea2743baf8f1cd4cc7ee394807d21cbe6882d7 100644
--- a/reviewboard/webapi/resources/__init__.py
+++ b/reviewboard/webapi/resources/__init__.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import logging
 
 from django.contrib.auth.models import User
diff --git a/reviewboard/webapi/resources/base_comment.py b/reviewboard/webapi/resources/base_comment.py
index 50160a1f3f08c2d6991423f9009a98263c7fc622..08341b4b57f8e089e3b5217da6d2e55073f95ba6 100644
--- a/reviewboard/webapi/resources/base_comment.py
+++ b/reviewboard/webapi/resources/base_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
 from django.utils import six
 from django.utils.formats import localize
@@ -45,11 +47,11 @@ class BaseCommentResource(MarkdownFieldsMixin, WebAPIResource):
                            '(Markdown) format.',
         },
         'text': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The comment text.',
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the comment was made '
                            '(in YYYY-MM-DD HH:MM:SS format).',
         },
diff --git a/reviewboard/webapi/resources/base_diff_comment.py b/reviewboard/webapi/resources/base_diff_comment.py
index 1b452cc9b5cc7cd641bf1433631efd2b4d760d68..6a803c2f99e2fefe80f73e1c993e4f3de527b3d5 100644
--- a/reviewboard/webapi/resources/base_diff_comment.py
+++ b/reviewboard/webapi/resources/base_diff_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.template.defaultfilters import timesince
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import webapi_request_fields
 
@@ -110,7 +113,7 @@ class BaseDiffCommentResource(BaseCommentResource):
                                'start on.',
             },
             'order-by': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'Comma-separated list of fields to order by',
             },
         },
diff --git a/reviewboard/webapi/resources/base_file_attachment.py b/reviewboard/webapi/resources/base_file_attachment.py
index f84b16a208e1e13e674119660771264764e80506..371fa40b535af24c2696b6aaeee30301ee7ccac0 100644
--- a/reviewboard/webapi/resources/base_file_attachment.py
+++ b/reviewboard/webapi/resources/base_file_attachment.py
@@ -1,7 +1,10 @@
+from __future__ import unicode_literals
+
 import logging
 
 from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
                                        webapi_request_fields)
@@ -26,33 +29,33 @@ class BaseFileAttachmentResource(WebAPIResource):
             'description': 'The numeric ID of the file.',
         },
         'caption': {
-            'type': str,
+            'type': six.text_type,
             'description': "The file's descriptive caption.",
         },
         'filename': {
-            'type': str,
+            'type': six.text_type,
             'description': "The name of the file.",
         },
         'url': {
-            'type': str,
+            'type': six.text_type,
             'description': "The URL of the file, for downloading purposes. "
                            "If this is not an absolute URL, then it's "
                            "relative to the Review Board server's URL.",
         },
         'icon_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL to a 24x24 icon representing this file.'
         },
         'mimetype': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The mimetype for the file.',
         },
         'thumbnail': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A thumbnail representing this file.',
         },
         'review_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL to a review UI for this file.',
         },
     }
@@ -136,7 +139,7 @@ class BaseFileAttachmentResource(WebAPIResource):
         },
         optional={
             'caption': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The optional caption describing the '
                                'file.',
             },
@@ -181,7 +184,7 @@ class BaseFileAttachmentResource(WebAPIResource):
         except ValueError as e:
             return INVALID_FORM_DATA, {
                 'fields': {
-                    'path': [str(e)],
+                    'path': [six.text_type(e)],
                 },
             }
 
@@ -195,11 +198,11 @@ class BaseFileAttachmentResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'caption': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new caption for the file.',
             },
             'thumbnail': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The thumbnail data for the file.',
             },
         }
@@ -244,7 +247,7 @@ class BaseFileAttachmentResource(WebAPIResource):
                     file.pk, e, request=request)
                 return INVALID_FORM_DATA, {
                     'fields': {
-                        'thumbnail': [str(e)],
+                        'thumbnail': [six.text_type(e)],
                     }
                 }
 
diff --git a/reviewboard/webapi/resources/base_file_attachment_comment.py b/reviewboard/webapi/resources/base_file_attachment_comment.py
index 19f6f059439d42840a41a5231f9ba65f7c10c06e..8a1b60d62a6953a511ef83e6c8c79dce7098b988 100644
--- a/reviewboard/webapi/resources/base_file_attachment_comment.py
+++ b/reviewboard/webapi/resources/base_file_attachment_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
 from django.template.defaultfilters import timesince
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.reviews.models import FileAttachmentComment
@@ -25,17 +28,17 @@ class BaseFileAttachmentCommentResource(BaseCommentResource):
             'description': 'The file the comment was made on.',
         },
         'link_text': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The text used to describe a link to the file. '
                            'This may differ depending on the comment.',
         },
         'review_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL to the review UI for the comment on this '
                            'file attachment.',
         },
         'thumbnail_html': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The HTML representing a thumbnail, if any, for '
                            'this comment.',
         },
diff --git a/reviewboard/webapi/resources/base_review.py b/reviewboard/webapi/resources/base_review.py
index 6d35ca106c4c657fdd5e9f68d5e12534976d88a3..9ba09fbfd8bb8c510b0ee8991bf01de21efe164d 100644
--- a/reviewboard/webapi/resources/base_review.py
+++ b/reviewboard/webapi/resources/base_review.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
 from django.utils import six
@@ -25,11 +27,11 @@ class BaseReviewResource(MarkdownFieldsMixin, WebAPIResource):
     model = Review
     fields = {
         'body_bottom': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The review content below the comments.',
         },
         'body_top': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The review content above the comments.',
         },
         'id': {
@@ -53,7 +55,7 @@ class BaseReviewResource(MarkdownFieldsMixin, WebAPIResource):
                            '"Ship It!"',
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the review was posted '
                            '(in YYYY-MM-DD HH:MM:SS format).',
         },
@@ -100,11 +102,11 @@ class BaseReviewResource(MarkdownFieldsMixin, WebAPIResource):
                 'description': 'Whether or not to mark the review "Ship It!"',
             },
             'body_top': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The review content above the comments.',
             },
             'body_bottom': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The review content below the comments.',
             },
             'public': {
@@ -180,11 +182,11 @@ class BaseReviewResource(MarkdownFieldsMixin, WebAPIResource):
                 'description': 'Whether or not to mark the review "Ship It!"',
             },
             'body_top': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The review content above the comments.',
             },
             'body_bottom': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The review content below the comments.',
             },
             'public': {
diff --git a/reviewboard/webapi/resources/base_screenshot.py b/reviewboard/webapi/resources/base_screenshot.py
index db32fa5db99f607d607bb40a5027339968e0b6a2..1f597abef23b188ab0ee35ac042f650cce3928cf 100644
--- a/reviewboard/webapi/resources/base_screenshot.py
+++ b/reviewboard/webapi/resources/base_screenshot.py
@@ -1,7 +1,10 @@
+from __future__ import unicode_literals
+
 import os
 
 from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
                                        webapi_request_fields)
@@ -25,32 +28,32 @@ class BaseScreenshotResource(WebAPIResource):
             'description': 'The numeric ID of the screenshot.',
         },
         'caption': {
-            'type': str,
+            'type': six.text_type,
             'description': "The screenshot's descriptive caption.",
         },
         'path': {
-            'type': str,
+            'type': six.text_type,
             'description': "The path of the screenshot's image file, "
                            "relative to the media directory configured "
                            "on the Review Board server.",
         },
         'filename': {
-            'type': str,
+            'type': six.text_type,
             'description': "The base file name of the screenshot's image.",
         },
         'review_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL to the review UI for this screenshot.',
         },
         'url': {
-            'type': str,
+            'type': six.text_type,
             'description': "The URL of the screenshot file. If this is not "
                            "an absolute URL (for example, if it is just a "
                            "path), then it's relative to the Review Board "
                            "server's URL.",
         },
         'thumbnail_url': {
-            'type': str,
+            'type': six.text_type,
             'description': "The URL of the screenshot's thumbnail file. "
                            "If this is not an absolute URL (for example, "
                            "if it is just a path), then it's relative to "
@@ -128,7 +131,7 @@ class BaseScreenshotResource(WebAPIResource):
         },
         optional={
             'caption': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The optional caption describing the '
                                'screenshot.',
             },
@@ -173,7 +176,7 @@ class BaseScreenshotResource(WebAPIResource):
         except ValueError as e:
             return INVALID_FORM_DATA, {
                 'fields': {
-                    'path': [str(e)],
+                    'path': [six.text_type(e)],
                 },
             }
 
@@ -186,7 +189,7 @@ class BaseScreenshotResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'caption': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new caption for the screenshot.',
             },
         }
diff --git a/reviewboard/webapi/resources/base_screenshot_comment.py b/reviewboard/webapi/resources/base_screenshot_comment.py
index 582224017d2600d4b4c9bddaeeabe68f135ab3ad..c27587755d46ea4e4b57c3fbd15d48199c161b4b 100644
--- a/reviewboard/webapi/resources/base_screenshot_comment.py
+++ b/reviewboard/webapi/resources/base_screenshot_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
 from django.template.defaultfilters import timesince
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.reviews.models import ScreenshotComment
@@ -41,7 +44,7 @@ class BaseScreenshotCommentResource(BaseCommentResource):
                            'screenshot.',
         },
         'thumbnail_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL to an image showing what was commented '
                            'on.',
         },
diff --git a/reviewboard/webapi/resources/base_watched_object.py b/reviewboard/webapi/resources/base_watched_object.py
index 5234c6e3ea399f44f7720f53ff41036eb32da1f9..4db26a4f03a833322070fe1151f203a26fbe084f 100644
--- a/reviewboard/webapi/resources/base_watched_object.py
+++ b/reviewboard/webapi/resources/base_watched_object.py
@@ -1,6 +1,9 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
 from django.http import HttpResponseRedirect
+from djblets.util.compat import six
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
                                        webapi_request_fields)
@@ -78,7 +81,7 @@ class BaseWatchedObjectResource(WebAPIResource):
     @webapi_response_errors(DOES_NOT_EXIST, NOT_LOGGED_IN, PERMISSION_DENIED)
     @webapi_request_fields(required={
         'object_id': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The ID of the object to watch.',
         },
     })
diff --git a/reviewboard/webapi/resources/change.py b/reviewboard/webapi/resources/change.py
index 2f64b0fc47f3b4d5ca5cc8c62f2c263d99b44bf9..c6f9143a16fb8c6ab3af7b6f7d5cb47ccc1ed3a7 100644
--- a/reviewboard/webapi/resources/change.py
+++ b/reviewboard/webapi/resources/change.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from django.utils import six
 from djblets.util.decorators import augment_method_from
@@ -72,12 +74,12 @@ class ChangeResource(WebAPIResource):
                            '(Markdown) format.',
         },
         'text': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The description of the change written by the '
                            'submitter.'
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the change was made '
                            '(in YYYY-MM-DD HH:MM:SS format).',
         },
diff --git a/reviewboard/webapi/resources/default_reviewer.py b/reviewboard/webapi/resources/default_reviewer.py
index 3c38f69b26feb6cf7961b41e7b69a3ce38f13c22..e71445fbbfcbb072d3a675069358b7c00107ca7c 100644
--- a/reviewboard/webapi/resources/default_reviewer.py
+++ b/reviewboard/webapi/resources/default_reviewer.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -41,27 +44,27 @@ class DefaultReviewerResource(WebAPIResource):
             'description': 'The numeric ID of the default reviewer.',
         },
         'name': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The descriptive name of the entry.',
         },
         'file_regex': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The regular expression that is used to match '
                            'files uploaded in a diff.',
         },
         'repositories': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A comma-separated list of repository IDs that '
                            'this default reviewer will match against.',
         },
         'users': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A comma-separated list of usernames that '
                            'this default reviewer applies to matched review '
                            'requests.',
         },
         'groups': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A comma-separated list of group names that '
                            'this default reviewer applies to matched review '
                            'requests.',
@@ -161,26 +164,26 @@ class DefaultReviewerResource(WebAPIResource):
     @webapi_request_fields(
         required={
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The name of the default reviewer entry.',
             },
             'file_regex': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The regular expression used to match file '
                                'paths in newly uploaded diffs.',
             },
         },
         optional={
             'repositories': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of repository IDs.',
             },
             'groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of group names.',
             },
             'users': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of usernames.',
             }
         },
@@ -211,24 +214,24 @@ class DefaultReviewerResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The name of the default reviewer entry.',
             },
             'file_regex': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The regular expression used to match file '
                                'paths in newly uploaded diffs.',
             },
             'repositories': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of repository IDs.',
             },
             'groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of group names.',
             },
             'users': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of usernames.',
             }
         },
diff --git a/reviewboard/webapi/resources/diff.py b/reviewboard/webapi/resources/diff.py
index fdfc9080f05ca34be326fe01de535783c0d80a1e..80c758b1e51075447ecc666ff78b7967a531500f 100644
--- a/reviewboard/webapi/resources/diff.py
+++ b/reviewboard/webapi/resources/diff.py
@@ -1,7 +1,10 @@
+from __future__ import unicode_literals
+
 import logging
 
 from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
 from django.http import HttpResponse
+from djblets.util.compat import six
 from djblets.util.http import get_http_requested_mimetype, set_last_modified
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -38,7 +41,7 @@ class DiffResource(WebAPIResource):
             'description': 'The numeric ID of the diff.',
         },
         'name': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The name of the diff, usually the filename.',
         },
         'revision': {
@@ -47,7 +50,7 @@ class DiffResource(WebAPIResource):
                            'diffs. Draft diffs may be at 0.',
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the diff was uploaded '
                            '(in YYYY-MM-DD HH:MM:SS format).',
         },
@@ -57,7 +60,7 @@ class DiffResource(WebAPIResource):
             'description': 'The repository that the diff is applied against.',
         },
         'basedir': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The base directory that will prepended to all '
                            'paths in the diff. This is needed for some types '
                            'of repositories. The directory must be between '
@@ -65,7 +68,7 @@ class DiffResource(WebAPIResource):
                            'referenced in the diff paths.',
         },
         'base_commit_id': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The ID/revision this change is built upon. '
                            'If using a parent diff, then this is the base '
                            'for that diff. This may not be provided for all '
@@ -185,7 +188,7 @@ class DiffResource(WebAPIResource):
         },
         optional={
             'basedir': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The base directory that will prepended to '
                                'all paths in the diff. This is needed for '
                                'some types of repositories. The directory '
@@ -198,7 +201,7 @@ class DiffResource(WebAPIResource):
                 'description': 'The optional parent diff to upload.',
             },
             'base_commit_id': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The ID/revision this change is built upon. '
                                'If using a parent diff, then this is the base '
                                'for that diff. This may not be provided for '
@@ -268,13 +271,13 @@ class DiffResource(WebAPIResource):
         except FileNotFoundError as e:
             return REPO_FILE_NOT_FOUND, {
                 'file': e.path,
-                'revision': unicode(e.revision)
+                'revision': six.text_type(e.revision)
             }
         except EmptyDiffError as e:
             return DIFF_EMPTY
         except DiffTooBigError as e:
             return DIFF_TOO_BIG, {
-                'reason': str(e),
+                'reason': six.text_type(e),
                 'max_size': e.max_diff_size,
             }
         except Exception as e:
@@ -285,7 +288,7 @@ class DiffResource(WebAPIResource):
 
             return INVALID_FORM_DATA, {
                 'fields': {
-                    'path': [str(e)]
+                    'path': [six.text_type(e)]
                 }
             }
 
diff --git a/reviewboard/webapi/resources/diff_context.py b/reviewboard/webapi/resources/diff_context.py
index 233a92848ca34491dc705a329913f1af629cdf98..b39050a01471493b48b6bfa49f2fe557887349b6 100644
--- a/reviewboard/webapi/resources/diff_context.py
+++ b/reviewboard/webapi/resources/diff_context.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.http import Http404
 from djblets.webapi.decorators import (webapi_response_errors,
                                        webapi_request_fields)
diff --git a/reviewboard/webapi/resources/diff_file_attachment.py b/reviewboard/webapi/resources/diff_file_attachment.py
index 18b87a75077d41be36229fd4da51eec61140cce6..a64f7419a3c6d44f9ae4985ace7c5b985f88fc4a 100644
--- a/reviewboard/webapi/resources/diff_file_attachment.py
+++ b/reviewboard/webapi/resources/diff_file_attachment.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import webapi_request_fields
 
@@ -28,12 +31,12 @@ class DiffFileAttachmentResource(BaseFileAttachmentResource):
 
     fields = dict({
         'repository_file_path': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The file path inside the repository that this '
                            'file attachment represents.',
         },
         'repository_revision': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The revision that introduced this version of the '
                            'file, if committed in the repository.',
         },
@@ -93,21 +96,21 @@ class DiffFileAttachmentResource(BaseFileAttachmentResource):
     @webapi_request_fields(
         optional=dict({
             'repository-file-path': {
-                'type': str,
+                'type': six.text_type,
                 'description': (
                     'Filter file attachments with the given path in the '
                     'repository.'
                 ),
             },
             'repository-revision': {
-                'type': str,
+                'type': six.text_type,
                 'description': (
                     'Filter file attachments for files with the given '
                     'revision.'
                 ),
             },
             'mimetype': {
-                'type': str,
+                'type': six.text_type,
                 'description': (
                     'Filter file attachments with the given mimetype.'
                 ),
diff --git a/reviewboard/webapi/resources/draft_diff.py b/reviewboard/webapi/resources/draft_diff.py
index 9f404645241eed96dc579a60648d8894fffddab0..556c77a2fd55779135301014662b4071e979cfed 100644
--- a/reviewboard/webapi/resources/draft_diff.py
+++ b/reviewboard/webapi/resources/draft_diff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import webapi_login_required
diff --git a/reviewboard/webapi/resources/draft_file_attachment.py b/reviewboard/webapi/resources/draft_file_attachment.py
index 7c2688e0090b303888f4bafd66b91a1d77c52e80..b01e777d3bd711384a0c12ca04a431d3088bfd3a 100644
--- a/reviewboard/webapi/resources/draft_file_attachment.py
+++ b/reviewboard/webapi/resources/draft_file_attachment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
 from djblets.util.decorators import augment_method_from
diff --git a/reviewboard/webapi/resources/draft_filediff.py b/reviewboard/webapi/resources/draft_filediff.py
index 58f627a04709538fa1f32a780a272f6fd0aadace..0f14bf9dd46431db291a61c4729558271016bd93 100644
--- a/reviewboard/webapi/resources/draft_filediff.py
+++ b/reviewboard/webapi/resources/draft_filediff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
diff --git a/reviewboard/webapi/resources/draft_screenshot.py b/reviewboard/webapi/resources/draft_screenshot.py
index 07493d2ef0ed57b07fca4369fd1673318e8fd6d2..cc11d9328057b8688300962da55f3e3e3cd82fa5 100644
--- a/reviewboard/webapi/resources/draft_screenshot.py
+++ b/reviewboard/webapi/resources/draft_screenshot.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
 from djblets.util.decorators import augment_method_from
diff --git a/reviewboard/webapi/resources/file_attachment.py b/reviewboard/webapi/resources/file_attachment.py
index a5c9f3a428deebcdfac953b6266b2baa2054b497..996fffe1b0ed77559f45e51ec4c7c049a7debc76 100644
--- a/reviewboard/webapi/resources/file_attachment.py
+++ b/reviewboard/webapi/resources/file_attachment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import webapi_login_required
 
diff --git a/reviewboard/webapi/resources/file_attachment_comment.py b/reviewboard/webapi/resources/file_attachment_comment.py
index b31946be21f46c1479e57dc2c5cb8014adb9ba38..947871ab25b9774774c534be4b8b208d12f4ae50 100644
--- a/reviewboard/webapi/resources/file_attachment_comment.py
+++ b/reviewboard/webapi/resources/file_attachment_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.decorators import webapi_check_local_site
diff --git a/reviewboard/webapi/resources/filediff.py b/reviewboard/webapi/resources/filediff.py
index 2517c8c63a18e5a429982a067a3cab481e19d84a..36624601ad95b543418f0a0369ac5ecf0c81e1e0 100644
--- a/reviewboard/webapi/resources/filediff.py
+++ b/reviewboard/webapi/resources/filediff.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.http import HttpResponse
+from djblets.util.compat import six
 from djblets.util.compat.six.moves.urllib.parse import quote as urllib_quote
 from djblets.util.decorators import augment_method_from
 from djblets.util.http import get_http_requested_mimetype, set_last_modified
@@ -32,22 +35,22 @@ class FileDiffResource(WebAPIResource):
             'description': 'The numeric ID of the file diff.',
         },
         'source_file': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The original name of the modified file in the '
                            'diff.',
         },
         'dest_file': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The new name of the patched file. This may be '
                            'the same as the existing file.',
         },
         'source_revision': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The revision of the file being modified. This '
                            'is a valid revision in the repository.',
         },
         'dest_detail': {
-            'type': str,
+            'type': six.text_type,
             'description': 'Additional information of the destination file. '
                            'This is parsed from the diff, but is usually '
                            'not used for anything.',
diff --git a/reviewboard/webapi/resources/filediff_comment.py b/reviewboard/webapi/resources/filediff_comment.py
index c3766f68e57359bf3bf82365edce56a165167b71..3a4b29dcedc48e906c6c116eceba61138bbe2611 100644
--- a/reviewboard/webapi/resources/filediff_comment.py
+++ b/reviewboard/webapi/resources/filediff_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.resources.base_diff_comment import \
diff --git a/reviewboard/webapi/resources/hosting_service_account.py b/reviewboard/webapi/resources/hosting_service_account.py
index 2147c43bf1a35e797139bc4135338cf9ce39a2e2..a54e44e5abe8c17ab1833968659f4d3ca0b58784 100644
--- a/reviewboard/webapi/resources/hosting_service_account.py
+++ b/reviewboard/webapi/resources/hosting_service_account.py
@@ -1,3 +1,6 @@
+from __future__ import unicode_literals
+
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -33,11 +36,11 @@ class HostingServiceAccountResource(WebAPIResource):
             'description': 'The numeric ID of the hosting service account.',
         },
         'username': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The username of the account.',
         },
         'service': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The ID of the service this account is on.',
         },
     }
@@ -93,23 +96,23 @@ class HostingServiceAccountResource(WebAPIResource):
     @webapi_request_fields(
         required={
             'username': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The username on the account.',
             },
             'service_id': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The registered ID of the service for the '
                                'account.',
             },
         },
         optional={
             'hosting_url': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The hosting URL on the account, if the '
                                'hosting service is self-hosted.',
             },
             'password': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The password on the account, if the hosting '
                                'service needs it.',
             },
@@ -152,7 +155,7 @@ class HostingServiceAccountResource(WebAPIResource):
                                   local_site_name)
             except AuthorizationError as e:
                 return HOSTINGSVC_AUTH_ERROR, {
-                    'reason': str(e),
+                    'reason': six.text_type(e),
                 }
 
         service.save()
diff --git a/reviewboard/webapi/resources/original_file.py b/reviewboard/webapi/resources/original_file.py
index 6f47ba228a38d50624ebfffa58e0697da31e9784..288228b23637ec55c2a80b697e8f8f1b806f6577 100644
--- a/reviewboard/webapi/resources/original_file.py
+++ b/reviewboard/webapi/resources/original_file.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import logging
 
 from django.core.exceptions import ObjectDoesNotExist
diff --git a/reviewboard/webapi/resources/patched_file.py b/reviewboard/webapi/resources/patched_file.py
index e1581338f3ccf270a06189af9efdea5475fa263b..decff216f8899b1985a9c060df37ce2c3fa063e7 100644
--- a/reviewboard/webapi/resources/patched_file.py
+++ b/reviewboard/webapi/resources/patched_file.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import logging
 
 from django.core.exceptions import ObjectDoesNotExist
diff --git a/reviewboard/webapi/resources/repository.py b/reviewboard/webapi/resources/repository.py
index 437a4701454d8be81349dd9af600f9fa56a5806b..34f5e7604d93ccbee70bb5e2b2e3f62eb645a084 100644
--- a/reviewboard/webapi/resources/repository.py
+++ b/reviewboard/webapi/resources/repository.py
@@ -1,7 +1,10 @@
+from __future__ import unicode_literals
+
 import logging
 from time import time
 
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -48,17 +51,17 @@ class RepositoryResource(WebAPIResource):
             'description': 'The numeric ID of the repository.',
         },
         'name': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The name of the repository.',
         },
         'path': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The main path to the repository, which is used '
                            'for communicating with the repository and '
                            'accessing files.',
         },
         'mirror_path': {
-            'type': str,
+            'type': six.text_type,
             'description': 'An alternate path to the repository, for '
                            'lookup purposes.',
         },
@@ -68,7 +71,7 @@ class RepositoryResource(WebAPIResource):
                            'only).',
         },
         'tool': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The name of the internal repository '
                            'communication class used to talk to the '
                            'repository. This is generally the type of the '
@@ -154,38 +157,38 @@ class RepositoryResource(WebAPIResource):
     @webapi_request_fields(
         required={
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The human-readable name of the repository.',
             },
             'path': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The path to the repository.',
             },
             'tool': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The ID of the SCMTool to use.',
             },
         },
         optional={
             'bug_tracker': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The URL to a bug in the bug tracker for '
                                'this repository, with ``%s`` in place of the '
                                'bug ID.',
             },
             'encoding': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The encoding used for files in the '
                                'repository. This is an advanced setting '
                                'and should only be used if you absolutely '
                                'need it.',
             },
             'mirror_path': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'An alternate path to the repository.',
             },
             'password': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The password used to access the repository.',
             },
             'public': {
@@ -195,7 +198,7 @@ class RepositoryResource(WebAPIResource):
                                'by users on the site. The default is true.',
             },
             'raw_file_url': {
-                'type': str,
+                'type': six.text_type,
                 'description': "A URL mask used to check out a particular "
                                "file using HTTP. This is needed for "
                                "repository types that can't access files "
@@ -212,7 +215,7 @@ class RepositoryResource(WebAPIResource):
                                'certificate.',
             },
             'username': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The username used to access the repository.',
             },
             'visible': {
@@ -301,32 +304,32 @@ class RepositoryResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'bug_tracker': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The URL to a bug in the bug tracker for '
                                'this repository, with ``%s`` in place of the '
                                'bug ID.',
             },
             'encoding': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The encoding used for files in the '
                                'repository. This is an advanced setting '
                                'and should only be used if you absolutely '
                                'need it.',
             },
             'mirror_path': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'An alternate path to the repository.',
             },
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The human-readable name of the repository.',
             },
             'password': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The password used to access the repository.',
             },
             'path': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The path to the repository.',
             },
             'public': {
@@ -336,7 +339,7 @@ class RepositoryResource(WebAPIResource):
                                'by users on the site. The default is true.',
             },
             'raw_file_url': {
-                'type': str,
+                'type': six.text_type,
                 'description': "A URL mask used to check out a particular "
                                "file using HTTP. This is needed for "
                                "repository types that can't access files "
@@ -353,7 +356,7 @@ class RepositoryResource(WebAPIResource):
                                'certificate.',
             },
             'username': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The username used to access the repository.',
             },
             'archive_name': {
@@ -489,7 +492,7 @@ class RepositoryResource(WebAPIResource):
                                                 e.raw_key)
                     except IOError as e:
                         return SERVER_CONFIG_ERROR, {
-                            'reason': str(e),
+                            'reason': six.text_type(e),
                         }
                 else:
                     return BAD_HOST_KEY, {
@@ -504,7 +507,7 @@ class RepositoryResource(WebAPIResource):
                         client.add_host_key(e.hostname, e.raw_key)
                     except IOError as e:
                         return SERVER_CONFIG_ERROR, {
-                            'reason': str(e),
+                            'reason': six.text_type(e),
                         }
                 else:
                     return UNVERIFIED_HOST_KEY, {
@@ -521,7 +524,7 @@ class RepositoryResource(WebAPIResource):
                             ret_cert.update(cert)
                     except IOError as e:
                         return SERVER_CONFIG_ERROR, {
-                            'reason': str(e),
+                            'reason': six.text_type(e),
                         }
                 else:
                     return UNVERIFIED_HOST_CERT, {
@@ -541,21 +544,21 @@ class RepositoryResource(WebAPIResource):
                     return MISSING_USER_KEY
                 else:
                     return REPO_AUTHENTICATION_ERROR, {
-                        'reason': str(e),
+                        'reason': six.text_type(e),
                     }
             except SSHError as e:
                 logging.error('Got unexpected SSHError when checking '
                               'repository: %s'
                               % e, exc_info=1, request=request)
                 return REPO_INFO_ERROR, {
-                    'error': str(e),
+                    'error': six.text_type(e),
                 }
             except SCMError as e:
                 logging.error('Got unexpected SCMError when checking '
                               'repository: %s'
                               % e, exc_info=1, request=request)
                 return REPO_INFO_ERROR, {
-                    'error': str(e),
+                    'error': six.text_type(e),
                 }
             except Exception as e:
                 logging.error('Unknown error in checking repository %s: %s',
diff --git a/reviewboard/webapi/resources/repository_branches.py b/reviewboard/webapi/resources/repository_branches.py
index cb4b7605bf3324383aac59c05a2371acb4b48bf1..5a8f97329f5c9c6d7b2153091f8ec8ac091951f8 100644
--- a/reviewboard/webapi/resources/repository_branches.py
+++ b/reviewboard/webapi/resources/repository_branches.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.webapi.decorators import webapi_response_errors
 from djblets.webapi.errors import DOES_NOT_EXIST
diff --git a/reviewboard/webapi/resources/repository_commits.py b/reviewboard/webapi/resources/repository_commits.py
index 4e64143ff091dcd5fe1b0fb0ff2032c5a80d854c..0943f8b254263add63bdad882aec2aa9793c3a8f 100644
--- a/reviewboard/webapi/resources/repository_commits.py
+++ b/reviewboard/webapi/resources/repository_commits.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.webapi.decorators import (webapi_response_errors,
                                        webapi_request_fields)
 from djblets.webapi.errors import DOES_NOT_EXIST
@@ -52,7 +55,7 @@ class RepositoryCommitsResource(WebAPIResource):
     @webapi_request_fields(
         required={
             'start': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A commit ID to start listing from.',
             },
         })
diff --git a/reviewboard/webapi/resources/repository_info.py b/reviewboard/webapi/resources/repository_info.py
index a4b796ba1b754f294119806242a1e1de492df915..4fd771c11f38afe7be914fb6a976a081c2a0013c 100644
--- a/reviewboard/webapi/resources/repository_info.py
+++ b/reviewboard/webapi/resources/repository_info.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.webapi.decorators import webapi_response_errors
 from djblets.webapi.errors import DOES_NOT_EXIST
diff --git a/reviewboard/webapi/resources/review.py b/reviewboard/webapi/resources/review.py
index 1137f938574853b367a722c6d330ead26715c037..7a5fbb988de863d1034908cf6e7ebcb209565f50 100644
--- a/reviewboard/webapi/resources/review.py
+++ b/reviewboard/webapi/resources/review.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.decorators import webapi_check_local_site
diff --git a/reviewboard/webapi/resources/review_diff_comment.py b/reviewboard/webapi/resources/review_diff_comment.py
index b1dd7bdae53bfa5a868930265c1564e5b77b4225..79905a2c677b8bda761de2f858af814a58f71cc7 100644
--- a/reviewboard/webapi/resources/review_diff_comment.py
+++ b/reviewboard/webapi/resources/review_diff_comment.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -54,7 +57,7 @@ class ReviewDiffCommentResource(BaseDiffCommentResource):
                 'description': 'The number of lines the comment spans.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -152,7 +155,7 @@ class ReviewDiffCommentResource(BaseDiffCommentResource):
                 'description': 'The number of lines the comment spans.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
             'issue_opened': {
diff --git a/reviewboard/webapi/resources/review_draft.py b/reviewboard/webapi/resources/review_draft.py
index 9f79ce49df3600e6165711efa17868d7f9185257..193291aba110416fbd44e4a2031048c5090b6f75 100644
--- a/reviewboard/webapi/resources/review_draft.py
+++ b/reviewboard/webapi/resources/review_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.webapi.decorators import webapi_login_required
 from djblets.webapi.errors import DOES_NOT_EXIST
diff --git a/reviewboard/webapi/resources/review_file_attachment_comment.py b/reviewboard/webapi/resources/review_file_attachment_comment.py
index f2bdd70ed79b6558fcf13c8448996b9094ab08d3..f246f4755d4a8841299eb9194a58fe44ec4a65ee 100644
--- a/reviewboard/webapi/resources/review_file_attachment_comment.py
+++ b/reviewboard/webapi/resources/review_file_attachment_comment.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -44,7 +47,7 @@ class ReviewFileAttachmentCommentResource(BaseFileAttachmentCommentResource):
                                'commented on.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -135,7 +138,7 @@ class ReviewFileAttachmentCommentResource(BaseFileAttachmentCommentResource):
     @webapi_request_fields(
         optional={
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
             'issue_opened': {
diff --git a/reviewboard/webapi/resources/review_group.py b/reviewboard/webapi/resources/review_group.py
index e5b67a0178f7108f465604acced650f7113e5500..254031fa9164ba7cdffde98fc4054c73ae0cfbb7 100644
--- a/reviewboard/webapi/resources/review_group.py
+++ b/reviewboard/webapi/resources/review_group.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -30,12 +33,12 @@ class ReviewGroupResource(WebAPIResource):
             'description': 'The numeric ID of the review group.',
         },
         'name': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The short name of the group, used in the '
                            'reviewer list and the Dashboard.',
         },
         'display_name': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The human-readable name of the group, sometimes '
                            'used as a short description.',
         },
@@ -46,12 +49,12 @@ class ReviewGroupResource(WebAPIResource):
                            'of the group.',
         },
         'mailing_list': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The e-mail address that all posts on a review '
                            'group are sent to.',
         },
         'url': {
-            'type': str,
+            'type': six.text_type,
             'description': "The URL to the user's page on the site. "
                            "This is deprecated and will be removed in a "
                            "future version.",
@@ -127,7 +130,7 @@ class ReviewGroupResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'q': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The string that the group name (or the  '
                                'display name when using ``displayname``) '
                                'must start with in order to be included in '
@@ -168,17 +171,17 @@ class ReviewGroupResource(WebAPIResource):
     @webapi_request_fields(
         required={
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The name of the group.',
             },
             'display_name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The human-readable name of the group.',
             },
         },
         optional={
             'mailing_list': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The e-mail address that all posts on a review '
                                'group are sent to.',
             },
@@ -233,15 +236,15 @@ class ReviewGroupResource(WebAPIResource):
     @webapi_request_fields(
         optional={
             'name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new name for the group.',
             },
             'display_name': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The human-readable name of the group.',
             },
             'mailing_list': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The e-mail address that all posts on a review '
                                'group are sent to.',
             },
diff --git a/reviewboard/webapi/resources/review_group_user.py b/reviewboard/webapi/resources/review_group_user.py
index 29b5252a9693f0307ef6fc2a86d379f668fac97c..2e3c42d0adf2612dd625c8bbf3a31778285d0c12 100644
--- a/reviewboard/webapi/resources/review_group_user.py
+++ b/reviewboard/webapi/resources/review_group_user.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -43,7 +46,7 @@ class ReviewGroupUserResource(UserResource):
                             NOT_LOGGED_IN, PERMISSION_DENIED)
     @webapi_request_fields(required={
         'username': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The user to add to the group.',
         },
     })
diff --git a/reviewboard/webapi/resources/review_reply.py b/reviewboard/webapi/resources/review_reply.py
index 012a16449d2212b6d82bd42ea479eaf08ec5e5e7..118f42cc5136426a170c069dc723d0f688290785 100644
--- a/reviewboard/webapi/resources/review_reply.py
+++ b/reviewboard/webapi/resources/review_reply.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -28,12 +31,12 @@ class ReviewReplyResource(BaseReviewResource):
     name_plural = 'replies'
     fields = {
         'body_bottom': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The response to the review content below '
                            'the comments.',
         },
         'body_top': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The response to the review content above '
                            'the comments.',
         },
@@ -53,7 +56,7 @@ class ReviewReplyResource(BaseReviewResource):
                            'format.',
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the reply was posted '
                            '(in YYYY-MM-DD HH:MM:SS format).',
         },
@@ -90,12 +93,12 @@ class ReviewReplyResource(BaseReviewResource):
     @webapi_request_fields(
         optional={
             'body_top': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The response to the review content above '
                                'the comments.',
             },
             'body_bottom': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The response to the review content below '
                                'the comments.',
             },
@@ -169,12 +172,12 @@ class ReviewReplyResource(BaseReviewResource):
     @webapi_request_fields(
         optional={
             'body_top': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The response to the review content above '
                                'the comments.',
             },
             'body_bottom': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The response to the review content below '
                                'the comments.',
             },
diff --git a/reviewboard/webapi/resources/review_reply_diff_comment.py b/reviewboard/webapi/resources/review_reply_diff_comment.py
index bfb0577dff9b4764c41f04ff8edba48ae26bca7d..79025cea93d3735ededb959ed33b464365654699 100644
--- a/reviewboard/webapi/resources/review_reply_diff_comment.py
+++ b/reviewboard/webapi/resources/review_reply_diff_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -54,7 +57,7 @@ class ReviewReplyDiffCommentResource(BaseDiffCommentResource):
                 'description': 'The ID of the comment being replied to.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -139,7 +142,7 @@ class ReviewReplyDiffCommentResource(BaseDiffCommentResource):
                                '(Markdown) format. The default is false.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new comment text.',
             },
         },
diff --git a/reviewboard/webapi/resources/review_reply_draft.py b/reviewboard/webapi/resources/review_reply_draft.py
index 51a9f517710212436a37f7719d326ef3a92a1da2..74107558b3b8892dcaf9f6ae250c345b89425652 100644
--- a/reviewboard/webapi/resources/review_reply_draft.py
+++ b/reviewboard/webapi/resources/review_reply_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from djblets.webapi.decorators import webapi_login_required
 from djblets.webapi.errors import DOES_NOT_EXIST
diff --git a/reviewboard/webapi/resources/review_reply_file_attachment_comment.py b/reviewboard/webapi/resources/review_reply_file_attachment_comment.py
index 9eafc8f5b5b33949cdb196818d3f69a67f8997de..f7a2fee1751c48c253bd61c284b4661be88f6c71 100644
--- a/reviewboard/webapi/resources/review_reply_file_attachment_comment.py
+++ b/reviewboard/webapi/resources/review_reply_file_attachment_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -57,7 +60,7 @@ class ReviewReplyFileAttachmentCommentResource(
                 'description': 'The ID of the comment being replied to.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -142,7 +145,7 @@ class ReviewReplyFileAttachmentCommentResource(
                                '(Markdown) format. The default is false.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new comment text.',
             },
         },
diff --git a/reviewboard/webapi/resources/review_reply_screenshot_comment.py b/reviewboard/webapi/resources/review_reply_screenshot_comment.py
index b6999c1d01e5dde8c07b14fc32edc5d32b18a982..2182c84c839c75a78607bf4fd0a5916d5a904593 100644
--- a/reviewboard/webapi/resources/review_reply_screenshot_comment.py
+++ b/reviewboard/webapi/resources/review_reply_screenshot_comment.py
@@ -1,5 +1,8 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -55,7 +58,7 @@ class ReviewReplyScreenshotCommentResource(BaseScreenshotCommentResource):
                 'description': 'The ID of the comment being replied to.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -145,7 +148,7 @@ class ReviewReplyScreenshotCommentResource(BaseScreenshotCommentResource):
                                '(Markdown) format. The default is false.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new comment text.',
             },
         },
diff --git a/reviewboard/webapi/resources/review_request.py b/reviewboard/webapi/resources/review_request.py
index 75d60e45bbf72e885fb980d6aa5e0efb11009052..ad5605e5133afc7e161ebe073b67576197b2a29e 100644
--- a/reviewboard/webapi/resources/review_request.py
+++ b/reviewboard/webapi/resources/review_request.py
@@ -1,9 +1,12 @@
+from __future__ import unicode_literals
+
 import logging
 
 import dateutil.parser
 from django.contrib.auth.models import User
 from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -90,12 +93,12 @@ class ReviewRequestResource(WebAPIResource):
             'description': 'The user who submitted the review request.',
         },
         'time_added': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the review request was '
                            'added (in YYYY-MM-DD HH:MM:SS format).',
         },
         'last_updated': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the review request was '
                            'last updated (in YYYY-MM-DD HH:MM:SS format).',
         },
@@ -124,7 +127,7 @@ class ReviewRequestResource(WebAPIResource):
                            '``commit_id`` field.',
         },
         'commit_id': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The commit that the review request represents. '
                            'This obsoletes the ``changenum`` field.',
         },
@@ -134,25 +137,25 @@ class ReviewRequestResource(WebAPIResource):
                            "is stored on.",
         },
         'summary': {
-            'type': str,
+            'type': six.text_type,
             'description': "The review request's brief summary.",
         },
         'description': {
-            'type': str,
+            'type': six.text_type,
             'description': "The review request's description.",
         },
         'testing_done': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The information on the testing that was done '
                            'for the change.',
         },
         'bugs_closed': {
-            'type': [str],
+            'type': [six.text_type],
             'description': 'The list of bugs closed or referenced by this '
                            'change.',
         },
         'branch': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The branch that the code was changed on or that '
                            'the code will be committed to. This is a '
                            'free-form field that can store any text.',
@@ -168,7 +171,7 @@ class ReviewRequestResource(WebAPIResource):
                            'this change.',
         },
         'url': {
-            'type': str,
+            'type': six.text_type,
             'description': "The URL to the review request's page on the site.",
         },
     }
@@ -416,18 +419,18 @@ class ReviewRequestResource(WebAPIResource):
                                'the ``commit_id`` field.',
             },
             'commit_id': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The optional commit to create the review '
                                'request for. This can be used in place of '
                                'the ``changenum`` field.',
             },
             'repository': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The path or ID of the repository that the '
                                'review request is for.',
             },
             'submit_as': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The optional user to submit the review '
                                'request as. This requires that the actual '
                                'logged in user is either a superuser or has '
@@ -471,7 +474,7 @@ class ReviewRequestResource(WebAPIResource):
         local_site = self._get_local_site(local_site_name)
 
         if changenum is not None and commit_id is None:
-            commit_id = str(changenum)
+            commit_id = six.text_type(changenum)
 
         if submit_as and user.username != submit_as:
             if not user.has_perm('reviews.can_submit_as_another_user',
@@ -529,7 +532,7 @@ class ReviewRequestResource(WebAPIResource):
         except DiffParserError as e:
             return DIFF_PARSE_ERROR, {
                 'linenum': e.linenum,
-                'message': str(e),
+                'message': six.text_type(e),
             }
         except SSHError as e:
             logging.error("Got unexpected SSHError when creating "
@@ -565,7 +568,7 @@ class ReviewRequestResource(WebAPIResource):
                                '``commit_id`` field.',
             },
             'commit_id': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The commit to set or update. This can be used '
                                'to re-associate with a new commit ID, or to '
                                'create/update a draft with new information '
@@ -575,7 +578,7 @@ class ReviewRequestResource(WebAPIResource):
                                '``changenum`` field.',
             },
             'description': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The description of the update. Should only be '
                                'used if the review request have been '
                                'submitted or discarded.',
@@ -631,7 +634,7 @@ class ReviewRequestResource(WebAPIResource):
                 return self._no_access_error(request.user)
 
         if changenum is not None and commit_id is None:
-            commit_id = str(changenum)
+            commit_id = six.text_type(changenum)
 
         if commit_id is not None:
             if commit_id != review_request.commit:
@@ -685,28 +688,28 @@ class ReviewRequestResource(WebAPIResource):
                                'the ``commit_id`` field.',
             },
             'commit_id': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The commit that review requests must have '
                                'set. This will only return one review request '
                                'per repository. This obsoletes the '
                                '``changenum`` field.',
             },
             'time-added-to': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The date/time that all review requests must '
                                'be added before. This is compared against the '
                                'review request\'s ``time_added`` field. This '
                                'must be a valid :term:`date/time format`.',
             },
             'time-added-from': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The earliest date/time the review request '
                                'could be added. This is compared against the '
                                'review request\'s ``time_added`` field. This '
                                'must be a valid :term:`date/time format`.',
             },
             'last-updated-to': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The date/time that all review requests must '
                                'be last updated before. This is compared '
                                'against the review request\'s '
@@ -714,7 +717,7 @@ class ReviewRequestResource(WebAPIResource):
                                ':term:`date/time format`.',
             },
             'last-updated-from': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The earliest date/time the review request '
                                'could be last updated. This is compared '
                                'against the review request\'s '
@@ -722,7 +725,7 @@ class ReviewRequestResource(WebAPIResource):
                                ':term:`date/time format`.',
             },
             'from-user': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The username that the review requests must '
                                'be owned by.',
             },
@@ -743,26 +746,26 @@ class ReviewRequestResource(WebAPIResource):
                 'description': 'The status of the review requests.'
             },
             'to-groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of review group names '
                                'that the review requests must have in the '
                                'reviewer list.',
             },
             'to-user-groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of usernames who are '
                                'in groups that the review requests must have '
                                'in the reviewer list.',
             },
             'to-users': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of usernames that the '
                                'review requests must either have in the '
                                'reviewer list specifically or by way of '
                                'a group.',
             },
             'to-users-directly': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of usernames that the '
                                'review requests must have in the reviewer '
                                'list specifically.',
diff --git a/reviewboard/webapi/resources/review_request_draft.py b/reviewboard/webapi/resources/review_request_draft.py
index 4bdf98b9349c832ab9dc2a64ec12eba0da5ae0d4..9f777a99172325156ce7a8b4479b298467657caf 100644
--- a/reviewboard/webapi/resources/review_request_draft.py
+++ b/reviewboard/webapi/resources/review_request_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import re
 
 from django.contrib import auth
@@ -60,17 +62,17 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
             'mutable': False,
         },
         'last_updated': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The date and time that the draft was last updated '
                            '(in YYYY-MM-DD HH:MM:SS format).',
             'mutable': False,
         },
         'branch': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The branch name.',
         },
         'bugs_closed': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The new list of bugs closed or referenced by this '
                            'change.',
         },
@@ -81,13 +83,13 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
                            'review request depends on.',
         },
         'changedescription': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A custom description of what changes are being '
                            'made in this update. It often will be used to '
                            'describe the changes in the diff.',
         },
         'description': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The new review request description.',
         },
         'public': {
@@ -104,21 +106,21 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
                            'in rich-text (Markdown) format.',
         },
         'summary': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The new review request summary.',
         },
         'target_groups': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A comma-separated list of review groups '
                            'that will be on the reviewer list.',
         },
         'target_people': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A comma-separated list of users that will '
                            'be on a reviewer list.',
         },
         'testing_done': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The new testing done text.',
         },
     }
@@ -173,24 +175,24 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
     @webapi_request_fields(
         optional={
             'branch': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new branch name.',
             },
             'bugs_closed': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of bug IDs.',
             },
             'depends_on': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new list of dependencies of this review '
                                'request.',
             },
             'changedescription': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The change description for this update.',
             },
             'description': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new review request description.',
             },
             'public': {
@@ -207,21 +209,21 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
                                '(Markdown) format.',
             },
             'summary': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new review request summary.',
             },
             'target_groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of review groups '
                                'that will be on the reviewer list.',
             },
             'target_people': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of users that will '
                                'be on a reviewer list.',
             },
             'testing_done': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new testing done text.',
             },
         },
@@ -259,24 +261,24 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
     @webapi_request_fields(
         optional={
             'branch': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new branch name.',
             },
             'bugs_closed': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of bug IDs.',
             },
             'depends_on': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new list of dependencies of this review '
                                'request.',
             },
             'changedescription': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The change description for this update.',
             },
             'description': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new review request description.',
             },
             'public': {
@@ -294,21 +296,21 @@ class ReviewRequestDraftResource(MarkdownFieldsMixin, WebAPIResource):
                                '(Markdown) format.',
             },
             'summary': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new review request summary.',
             },
             'target_groups': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of review groups '
                                'that will be on the reviewer list.',
             },
             'target_people': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'A comma-separated list of users that will '
                                'be on a reviewer list.',
             },
             'testing_done': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The new testing done text.',
             },
         },
diff --git a/reviewboard/webapi/resources/review_request_last_update.py b/reviewboard/webapi/resources/review_request_last_update.py
index fbeae527b444b778fa58e6d3890b1e2994f65488..1af134bcc98ac10acbf7a6cb31b38753df6bb73d 100644
--- a/reviewboard/webapi/resources/review_request_last_update.py
+++ b/reviewboard/webapi/resources/review_request_last_update.py
@@ -1,6 +1,9 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
 from django.http import HttpResponseNotModified
 from django.utils.translation import ugettext as _
+from djblets.util.compat import six
 from djblets.util.http import get_modified_since, http_date
 from djblets.webapi.errors import DOES_NOT_EXIST
 from reviewboard.diffviewer.models import DiffSet
@@ -23,13 +26,13 @@ class ReviewRequestLastUpdateResource(WebAPIResource):
 
     fields = {
         'summary': {
-            'type': str,
+            'type': six.text_type,
             'description': 'A short summary of the update. This should be one '
                            'of "Review request updated", "Diff updated", '
                            '"New reply" or "New review".',
         },
         'timestamp': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The timestamp of this most recent update '
                            '(YYYY-MM-DD HH:MM:SS format).',
         },
@@ -43,7 +46,7 @@ class ReviewRequestLastUpdateResource(WebAPIResource):
                            "a new review was posted.",
         },
         'user': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The user who made the last update.',
         },
     }
diff --git a/reviewboard/webapi/resources/review_screenshot_comment.py b/reviewboard/webapi/resources/review_screenshot_comment.py
index dfb58f506881451df3e865df991eadca1f84c0e4..7508eeb2623d8f10d65fe88b371b6cbbb0dc8c5e 100644
--- a/reviewboard/webapi/resources/review_screenshot_comment.py
+++ b/reviewboard/webapi/resources/review_screenshot_comment.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.core.exceptions import ObjectDoesNotExist
+from djblets.util.compat import six
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
@@ -56,7 +59,7 @@ class ReviewScreenshotCommentResource(BaseScreenshotCommentResource):
                 'description': 'The height of the comment region.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
         },
@@ -136,7 +139,7 @@ class ReviewScreenshotCommentResource(BaseScreenshotCommentResource):
                 'description': 'The height of the comment region.',
             },
             'text': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The comment text.',
             },
             'issue_opened': {
diff --git a/reviewboard/webapi/resources/root.py b/reviewboard/webapi/resources/root.py
index 092598bc38dd0bd5150131221e970dd2e54c1d73..9931de510b0776ec54b53a87d33a42e510e7f1ee 100644
--- a/reviewboard/webapi/resources/root.py
+++ b/reviewboard/webapi/resources/root.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.resources import RootResource as DjbletsRootResource
 
diff --git a/reviewboard/webapi/resources/screenshot.py b/reviewboard/webapi/resources/screenshot.py
index 3fe7221cd95bfd13b8b3f80f18db68c5678d4148..2c5d6c76d57385d22b6ce6e7869bf320a02b7141 100644
--- a/reviewboard/webapi/resources/screenshot.py
+++ b/reviewboard/webapi/resources/screenshot.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/resources/screenshot_comment.py b/reviewboard/webapi/resources/screenshot_comment.py
index 134957f7f866042fad9a5331db4b4d93f20be968..a95698ec99ca4e8af2b86b9e48d21710d8e646f8 100644
--- a/reviewboard/webapi/resources/screenshot_comment.py
+++ b/reviewboard/webapi/resources/screenshot_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.decorators import webapi_check_local_site
diff --git a/reviewboard/webapi/resources/search.py b/reviewboard/webapi/resources/search.py
index d1f028c6edccdcd9b7c5adee6ec525f4f4dda5ec..ce8b2dd3ca1b06f8176cb85202f97aa21d691c76 100644
--- a/reviewboard/webapi/resources/search.py
+++ b/reviewboard/webapi/resources/search.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
 from djblets.webapi.resources import UserResource as DjbletsUserResource
 
diff --git a/reviewboard/webapi/resources/server_info.py b/reviewboard/webapi/resources/server_info.py
index a506978edbd8fa2273c70179f7e77b024cb2bc72..95f061bd04e93b257e4c2a4b12d9a5f1bb4fb825 100644
--- a/reviewboard/webapi/resources/server_info.py
+++ b/reviewboard/webapi/resources/server_info.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.conf import settings
 from django.contrib.sites.models import Site
 from djblets.siteconfig.models import SiteConfiguration
diff --git a/reviewboard/webapi/resources/session.py b/reviewboard/webapi/resources/session.py
index 0fc810d276facf8b0aa8b3f85d1ad5f44ad1abd4..305b302e179f24e112322a38c1d1b49bab0416e4 100644
--- a/reviewboard/webapi/resources/session.py
+++ b/reviewboard/webapi/resources/session.py
@@ -1,3 +1,6 @@
+from __future__ import unicode_literals
+
+from djblets.util.compat import six
 from djblets.webapi.resources import get_resource_for_object
 
 from reviewboard.webapi.base import WebAPIResource
@@ -52,7 +55,7 @@ class SessionResource(WebAPIResource):
             links['user'] = {
                 'method': 'GET',
                 'href': href,
-                'title': unicode(request.user),
+                'title': six.text_type(request.user),
                 'resource': user_resource,
                 'list-resource': False,
             }
diff --git a/reviewboard/webapi/resources/user.py b/reviewboard/webapi/resources/user.py
index 275d9afbbf1d724340d7e135ab1a0022c3a69c33..fd7992b626acc6cd67fcf80f8105152d249b8f72 100644
--- a/reviewboard/webapi/resources/user.py
+++ b/reviewboard/webapi/resources/user.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
 from django.utils import six
 from djblets.gravatars import get_gravatar_url
@@ -24,7 +26,7 @@ class UserResource(WebAPIResource, DjbletsUserResource):
 
     fields = dict({
         'avatar_url': {
-            'type': str,
+            'type': six.text_type,
             'description': 'The URL for an avatar representing the user.',
         },
     }, **DjbletsUserResource.fields)
@@ -91,7 +93,7 @@ class UserResource(WebAPIResource, DjbletsUserResource):
     @webapi_request_fields(
         optional={
             'q': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The string that the username (or the first '
                                'name or last name when using ``fullname``) '
                                'must start with in order to be included in '
diff --git a/reviewboard/webapi/resources/validate_diff.py b/reviewboard/webapi/resources/validate_diff.py
index af224881d26a3d8777043d380d20e78f007762b0..6c867e980282783ce2f57436dc31837424bd9680 100644
--- a/reviewboard/webapi/resources/validate_diff.py
+++ b/reviewboard/webapi/resources/validate_diff.py
@@ -1,4 +1,7 @@
+from __future__ import unicode_literals
+
 from django.db.models import Q
+from djblets.util.compat import six
 from djblets.webapi.decorators import (webapi_login_required,
                                        webapi_response_errors,
                                        webapi_request_fields)
@@ -57,7 +60,7 @@ class ValidateDiffResource(DiffResource):
     @webapi_request_fields(
         required={
             'repository': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The path or ID of the repository.',
             },
             'path': {
@@ -67,7 +70,7 @@ class ValidateDiffResource(DiffResource):
         },
         optional={
             'basedir': {
-                'type': str,
+                'type': six.text_type,
                 'description': 'The base directory that will prepended to '
                                'all paths in the diff. This is needed for '
                                'some types of repositories. The directory '
@@ -126,29 +129,29 @@ class ValidateDiffResource(DiffResource):
         except FileNotFoundError as e:
             return REPO_FILE_NOT_FOUND, {
                 'file': e.path,
-                'revision': unicode(e.revision),
+                'revision': six.text_type(e.revision),
             }
         except EmptyDiffError:
             return DIFF_EMPTY
         except DiffTooBigError as e:
             return DIFF_TOO_BIG, {
-                'reason': str(e),
+                'reason': six.text_type(e),
                 'max_size': e.max_diff_size,
             }
         except DiffParserError as e:
             return DIFF_PARSE_ERROR, {
-                'reason': str(e),
+                'reason': six.text_type(e),
                 'linenum': e.linenum,
             }
         except ShortSHA1Error as e:
             return REPO_FILE_NOT_FOUND, {
-                'reason': str(e),
+                'reason': six.text_type(e),
                 'file': e.path,
-                'revision': unicode(e.revision),
+                'revision': six.text_type(e.revision),
             }
         except SCMError as e:
             return DIFF_PARSE_ERROR, {
-                'reason': str(e),
+                'reason': six.text_type(e),
             }
 
         return 200, {}
diff --git a/reviewboard/webapi/resources/validation.py b/reviewboard/webapi/resources/validation.py
index a55e347c158823c3a1b923634811835daab8b303..e5fae2ed4cffec29cf120d3d357a0165ea582bf3 100644
--- a/reviewboard/webapi/resources/validation.py
+++ b/reviewboard/webapi/resources/validation.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 from djblets.webapi.resources import RootResource as DjbletsRootResource
 
diff --git a/reviewboard/webapi/resources/watched.py b/reviewboard/webapi/resources/watched.py
index d33886bff123fd5f6b0f8b2ade8f39cac89a7df9..7e8a2ac682c861fcf1aa43d2324dab570a8267fe 100644
--- a/reviewboard/webapi/resources/watched.py
+++ b/reviewboard/webapi/resources/watched.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from reviewboard.webapi.base import WebAPIResource
 from reviewboard.webapi.decorators import webapi_check_login_required
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/resources/watched_review_group.py b/reviewboard/webapi/resources/watched_review_group.py
index 6521235dd97da0c43d218caa1dbd06fbaefbeaab..f4f4d12e9a435d95a837eebb63fd3e08fc707813 100644
--- a/reviewboard/webapi/resources/watched_review_group.py
+++ b/reviewboard/webapi/resources/watched_review_group.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.decorators import webapi_check_local_site
diff --git a/reviewboard/webapi/resources/watched_review_request.py b/reviewboard/webapi/resources/watched_review_request.py
index 6f80a4cce6911b5f612c36b2eb279ff26e4bc473..881e90644f7cc6571836529ea5a35b8f37c8e22b 100644
--- a/reviewboard/webapi/resources/watched_review_request.py
+++ b/reviewboard/webapi/resources/watched_review_request.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.decorators import augment_method_from
 
 from reviewboard.webapi.decorators import webapi_check_local_site
diff --git a/reviewboard/webapi/tests/base.py b/reviewboard/webapi/tests/base.py
index 204d0f02081b25b3096b10772c0800fb36ed96f6..d77f656af2bd12104dd4690aca8a29dfaa49cf8b 100644
--- a/reviewboard/webapi/tests/base.py
+++ b/reviewboard/webapi/tests/base.py
@@ -1,4 +1,4 @@
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 import json
 import os
@@ -343,7 +343,7 @@ class BaseWebAPITestCase(TestCase, EmailTestHelper):
 
         self.apiDelete(
             get_screenshot_list_url(review_request, local_site_name) +
-            str(screenshot.id) + '/')
+            six.text_type(screenshot.id) + '/')
 
     def _postNewFileAttachmentComment(self, review_request, review_id,
                                       file_attachment, comment_text,
diff --git a/reviewboard/webapi/tests/mimetypes.py b/reviewboard/webapi/tests/mimetypes.py
index 0050932f4266a870628ba17bfcd1d6990dcdc8b9..869e669c80b5610abcc2e825c094a8d1e53682fc 100644
--- a/reviewboard/webapi/tests/mimetypes.py
+++ b/reviewboard/webapi/tests/mimetypes.py
@@ -1,3 +1,6 @@
+from __future__ import unicode_literals
+
+
 def _build_mimetype(resource_name, fmt='json'):
     return 'application/vnd.reviewboard.org.%s+%s' % (resource_name, fmt)
 
diff --git a/reviewboard/webapi/tests/mixins.py b/reviewboard/webapi/tests/mixins.py
index 69103eaa2cae8a2169e6a72cc819a0f7fb99b66c..23fd20f460a7d972c616065305e5bce7a4261358 100644
--- a/reviewboard/webapi/tests/mixins.py
+++ b/reviewboard/webapi/tests/mixins.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
diff --git a/reviewboard/webapi/tests/mixins_comment.py b/reviewboard/webapi/tests/mixins_comment.py
index fda63ad7a6bd7edaf837a51dd9cbdf7260a79c97..5283959178b86e711a679801575b53e60026f8ef 100644
--- a/reviewboard/webapi/tests/mixins_comment.py
+++ b/reviewboard/webapi/tests/mixins_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from reviewboard.webapi.tests.mixins import test_template
 
 
@@ -57,7 +59,7 @@ class BaseCommentItemMixin(object):
         self._test_put_with_rich_text_and_not_text(
             True,
             '`Test` **diff** comment',
-            '\\`Test\\` \\*\\*diff\\*\\* comment')
+            r'\`Test\` \*\*diff\*\* comment')
 
     @test_template
     def test_put_with_rich_text_false_and_not_text(self):
@@ -66,7 +68,7 @@ class BaseCommentItemMixin(object):
         """
         self._test_put_with_rich_text_and_not_text(
             False,
-            '\\`Test\\` \\*\\*diff\\*\\* comment',
+            r'\`Test\` \*\*diff\*\* comment',
             '`Test` **diff** comment')
 
     @test_template
diff --git a/reviewboard/webapi/tests/mixins_review.py b/reviewboard/webapi/tests/mixins_review.py
index 2f23bdf6e366b99f673f39737c5dbca6710e0f51..e2ff3ea6806f7e48923fad2ad37494e2ff08ed06 100644
--- a/reviewboard/webapi/tests/mixins_review.py
+++ b/reviewboard/webapi/tests/mixins_review.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from reviewboard.webapi.tests.mixins import test_template
 
 
@@ -58,8 +60,8 @@ class ReviewItemMixin(object):
             True,
             '`This` is **body_top**',
             '`This` is **body_bottom**',
-            '\\`This\\` is \\*\\*body\\_top\\*\\*',
-            '\\`This\\` is \\*\\*body\\_bottom\\*\\*')
+            r'\`This\` is \*\*body\_top\*\*',
+            r'\`This\` is \*\*body\_bottom\*\*')
 
     @test_template
     def test_put_with_rich_text_false_escaping_all_fields(self):
@@ -68,8 +70,8 @@ class ReviewItemMixin(object):
         """
         self._test_put_with_rich_text_escaping_all_fields(
             False,
-            '\\`This\\` is \\*\\*body\\_top\\*\\*',
-            '\\`This\\` is \\*\\*body\\_bottom\\*\\*',
+            r'\`This\` is \*\*body\_top\*\*',
+            r'\`This\` is \*\*body\_bottom\*\*',
             '`This` is **body_top**',
             '`This` is **body_bottom**')
 
@@ -81,7 +83,7 @@ class ReviewItemMixin(object):
         self._test_put_with_rich_text_escaping_unspecified_fields(
             True,
             '`This` is **body_top**',
-            '\\`This\\` is \\*\\*body\\_top\\*\\*')
+            r'\`This\` is \*\*body\_top\*\*')
 
     @test_template
     def test_put_with_rich_text_false_escaping_unspecified_fields(self):
@@ -90,7 +92,7 @@ class ReviewItemMixin(object):
         """
         self._test_put_with_rich_text_escaping_unspecified_fields(
             False,
-            '\\`This\\` is \\*\\*body\\_top\\*\\*',
+            r'\`This\` is \*\*body\_top\*\*',
             '`This` is **body_top**')
 
     @test_template
@@ -117,9 +119,9 @@ class ReviewItemMixin(object):
         review_rsp = rsp[self.resource.item_result_key]
         self.assertTrue(review_rsp['rich_text'])
         self.assertEqual(review_rsp['body_top'],
-                         '\\`This\\` is \\*\\*body\\_top\\*\\*')
+                         r'\`This\` is \*\*body\_top\*\*')
         self.assertEqual(review_rsp['body_bottom'],
-                         '\\`This\\` is \\*\\*body\\_bottom\\*\\*')
+                         r'\`This\` is \*\*body\_bottom\*\*')
         self.compare_item(review_rsp,
                           self.resource.model.objects.get(pk=review_rsp['id']))
 
diff --git a/reviewboard/webapi/tests/test_change.py b/reviewboard/webapi/tests/test_change.py
index 32cc184306c734a0ba2416e0fd64e593fa6afbd1..d7e270d5bb36c8b36ee5d4ad3b4d29abaebdf145 100644
--- a/reviewboard/webapi/tests/test_change.py
+++ b/reviewboard/webapi/tests/test_change.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from datetime import timedelta
 
 from django.contrib.auth.models import User
@@ -173,9 +175,8 @@ class ResourceItemTests(ReviewRequestChildItemMixin, BaseWebAPITestCase):
         screenshot3 = Screenshot.objects.create(caption=old_screenshot_caption)
 
         for screenshot in [screenshot1, screenshot2, screenshot3]:
-            f = open(self._getTrophyFilename(), 'r')
-            screenshot.image.save('foo.png', File(f), save=True)
-            f.close()
+            with open(self._getTrophyFilename(), 'r') as f:
+                screenshot.image.save('foo.png', File(f), save=True)
 
         test_data = {
             'summary': ('old summary', 'new summary', None, None),
@@ -252,7 +253,7 @@ class ResourceItemTests(ReviewRequestChildItemMixin, BaseWebAPITestCase):
 
         self.assertTrue('screenshot_captions' in change.fields_changed)
         field_data = change.fields_changed['screenshot_captions']
-        screenshot_id = str(screenshot3.pk)
+        screenshot_id = six.text_type(screenshot3.pk)
         self.assertTrue(screenshot_id in field_data)
         self.assertTrue('old' in field_data[screenshot_id])
         self.assertTrue('new' in field_data[screenshot_id])
diff --git a/reviewboard/webapi/tests/test_default_reviewer.py b/reviewboard/webapi/tests/test_default_reviewer.py
index 6d4f68d56dffd4baa019acb0d7b768badbc06895..1a82ec4faaf40b63f1bc923ca3af16c61c27b883 100644
--- a/reviewboard/webapi/tests/test_default_reviewer.py
+++ b/reviewboard/webapi/tests/test_default_reviewer.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
@@ -232,7 +234,8 @@ class ResourceListTests(BaseWebAPITestCase):
                 'file_regex': '.*',
                 'users': 'doc,dopey',
                 'groups': 'group1,group2',
-                'repositories': ','.join([str(repo1.pk), str(repo2.pk)]),
+                'repositories': ','.join([six.text_type(repo1.pk),
+                                          six.text_type(repo2.pk)]),
             }
         else:
             post_data = {}
@@ -435,7 +438,7 @@ class ResourceListTests(BaseWebAPITestCase):
             {
                 'name': 'default1',
                 'file_regex': '.*',
-                'repositories': str(repository.pk),
+                'repositories': six.text_type(repository.pk),
             },
             expected_status=400)
 
@@ -580,7 +583,8 @@ class ResourceItemTests(BaseWebAPITestCase):
                 'file_regex': '/foo/',
                 'users': 'doc,dopey',
                 'groups': 'group1,group2',
-                'repositories': ','.join([str(repo1.pk), str(repo2.pk)]),
+                'repositories': ','.join([six.text_type(repo1.pk),
+                                          six.text_type(repo2.pk)]),
             }
         else:
             put_data = {}
@@ -719,7 +723,7 @@ class ResourceItemTests(BaseWebAPITestCase):
 
         rsp = self.apiPut(
             get_default_reviewer_item_url(default_reviewer.pk),
-            {'repositories': str(repository.pk)},
+            {'repositories': six.text_type(repository.pk)},
             expected_status=400)
 
         self.assertTrue('fields' in rsp)
diff --git a/reviewboard/webapi/tests/test_diff.py b/reviewboard/webapi/tests/test_diff.py
index 7a8b85c819587ea0249954db9607220a037a75d6..5dbc19fc6388b2e17f794c38cd0c4243b866ca0b 100644
--- a/reviewboard/webapi/tests/test_diff.py
+++ b/reviewboard/webapi/tests/test_diff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 from djblets.util.compat import six
@@ -119,12 +121,11 @@ class ResourceListTests(ReviewRequestChildListMixin, BaseWebAPITestCase):
 
         diff_filename = os.path.join(os.path.dirname(scmtools.__file__),
                                      'testdata', 'git_readme.diff')
-        f = open(diff_filename, "r")
-        rsp = self.apiPost(
-            get_diff_list_url(review_request),
-            {'path': f},
-            expected_status=400)
-        f.close()
+        with open(diff_filename, "r") as f:
+            rsp = self.apiPost(
+                get_diff_list_url(review_request),
+                {'path': f},
+                expected_status=400)
 
         self.assertEqual(rsp['stat'], 'fail')
         self.assertEqual(rsp['err']['code'], INVALID_FORM_DATA.code)
@@ -145,15 +146,14 @@ class ResourceListTests(ReviewRequestChildListMixin, BaseWebAPITestCase):
 
         diff_filename = os.path.join(os.path.dirname(scmtools.__file__),
                                      'testdata', 'git_readme.diff')
-        f = open(diff_filename, "r")
-
-        rsp = self.apiPost(
-            get_diff_list_url(review_request),
-            {
-                'path': f,
-                'basedir': "/trunk",
-            },
-            expected_status=400)
+        with open(diff_filename, "r") as f:
+            rsp = self.apiPost(
+                get_diff_list_url(review_request),
+                {
+                    'path': f,
+                    'basedir': "/trunk",
+                },
+                expected_status=400)
 
         self.assertEqual(rsp['stat'], 'fail')
         self.assertEqual(rsp['err']['code'], DIFF_TOO_BIG.code)
diff --git a/reviewboard/webapi/tests/test_diff_file_attachment.py b/reviewboard/webapi/tests/test_diff_file_attachment.py
index 8d9486222f1920ba6c069aa5e9b9d2c16a7ee065..3bc2edbe865d221f2a19395df2597eae6819a953 100644
--- a/reviewboard/webapi/tests/test_diff_file_attachment.py
+++ b/reviewboard/webapi/tests/test_diff_file_attachment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 from djblets.webapi.errors import INVALID_FORM_DATA, PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_draft_diff.py b/reviewboard/webapi/tests/test_draft_diff.py
index 4ac87a108522e6dfa5c67629d0536b593f9a80b2..a0e6086b5557e3f015282a935fd7cf8473de0c93 100644
--- a/reviewboard/webapi/tests/test_draft_diff.py
+++ b/reviewboard/webapi/tests/test_draft_diff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 from djblets.util.compat import six
diff --git a/reviewboard/webapi/tests/test_draft_filediff.py b/reviewboard/webapi/tests/test_draft_filediff.py
index 8fa1ba53459ff754dc742392f19a3bff2095da76..ecdcb9da6f7b1eb45c4d0520d34faceb622c5352 100644
--- a/reviewboard/webapi/tests/test_draft_filediff.py
+++ b/reviewboard/webapi/tests/test_draft_filediff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 from djblets.util.compat import six
diff --git a/reviewboard/webapi/tests/test_file_attachment.py b/reviewboard/webapi/tests/test_file_attachment.py
index 8cad5189d1dd236141736a7551f51c91b969a246..458d2b9ec06bc5f1a8d92941bc2f7c946785c156 100644
--- a/reviewboard/webapi/tests/test_file_attachment.py
+++ b/reviewboard/webapi/tests/test_file_attachment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
 
@@ -110,16 +112,15 @@ class ResourceListTests(ReviewRequestChildListMixin, BaseWebAPITestCase):
         review_request = self.create_review_request()
         self.assertNotEqual(review_request.submitter, self.user)
 
-        f = open(self._getTrophyFilename(), "r")
-        self.assertTrue(f)
-        rsp = self.apiPost(
-            get_file_attachment_list_url(review_request),
-            {
-                'caption': 'Trophy',
-                'path': f,
-            },
-            expected_status=403)
-        f.close()
+        with open(self._getTrophyFilename(), "r") as f:
+            self.assertTrue(f)
+            rsp = self.apiPost(
+                get_file_attachment_list_url(review_request),
+                {
+                    'caption': 'Trophy',
+                    'path': f,
+                },
+                expected_status=403)
 
         self.assertEqual(rsp['stat'], 'fail')
         self.assertEqual(rsp['err']['code'], PERMISSION_DENIED.code)
diff --git a/reviewboard/webapi/tests/test_file_attachment_comment.py b/reviewboard/webapi/tests/test_file_attachment_comment.py
index f4bb59bca4f90e03377828bbd209bc6351a7ebba..50a51edaee96f8c2a6754eafe855f44fa8c79cb9 100644
--- a/reviewboard/webapi/tests/test_file_attachment_comment.py
+++ b/reviewboard/webapi/tests/test_file_attachment_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_file_attachment_draft.py b/reviewboard/webapi/tests/test_file_attachment_draft.py
index aa3454ab28c0b3d492876eea9ae35b45c626c052..a194ec3bb7ebf497ff595ec8f26287d0cc9b8569 100644
--- a/reviewboard/webapi/tests/test_file_attachment_draft.py
+++ b/reviewboard/webapi/tests/test_file_attachment_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
 
diff --git a/reviewboard/webapi/tests/test_file_diff_comment.py b/reviewboard/webapi/tests/test_file_diff_comment.py
index 7a5edabda6c8dc19f7a1f63a58e27ec7099acca3..4b04f09421ac00c77f2b983b4b2e92206564c25d 100644
--- a/reviewboard/webapi/tests/test_file_diff_comment.py
+++ b/reviewboard/webapi/tests/test_file_diff_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_repository.py b/reviewboard/webapi/tests/test_repository.py
index c92a9cf1513bf8c79df803309eef285878bb127f..fe1321a008112cef3ab2cbf4ce9bc4149d31c015 100644
--- a/reviewboard/webapi/tests/test_repository.py
+++ b/reviewboard/webapi/tests/test_repository.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 import paramiko
diff --git a/reviewboard/webapi/tests/test_repository_branches.py b/reviewboard/webapi/tests/test_repository_branches.py
index 28dabf6a4908e629e5eca6b0e19142fba761be98..6b3cb792f58329704dc075ddc8f4079bc14d3230 100644
--- a/reviewboard/webapi/tests/test_repository_branches.py
+++ b/reviewboard/webapi/tests/test_repository_branches.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.errors import REPO_NOT_IMPLEMENTED
diff --git a/reviewboard/webapi/tests/test_repository_commits.py b/reviewboard/webapi/tests/test_repository_commits.py
index af463da39b93692421aaef59bef8a02cc4fe0609..f7e940e52e31eb41c2f32d4be8f16c1d9446ab3d 100644
--- a/reviewboard/webapi/tests/test_repository_commits.py
+++ b/reviewboard/webapi/tests/test_repository_commits.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 from djblets.testing.decorators import add_fixtures
diff --git a/reviewboard/webapi/tests/test_repository_info.py b/reviewboard/webapi/tests/test_repository_info.py
index fc45ed7f247d4b309eb045d1172b636fcc0a6f2c..f2ad3c0ee62c799250b3e39690c94bf2c805371c 100644
--- a/reviewboard/webapi/tests/test_repository_info.py
+++ b/reviewboard/webapi/tests/test_repository_info.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_review.py b/reviewboard/webapi/tests/test_review.py
index c61fdd28a8f8aec3cca47978b2605d55d4c66e5e..0d4349807bb4117ac66a53ace08fb8dbe9984766 100644
--- a/reviewboard/webapi/tests/test_review.py
+++ b/reviewboard/webapi/tests/test_review.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core import mail
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
diff --git a/reviewboard/webapi/tests/test_review_comment.py b/reviewboard/webapi/tests/test_review_comment.py
index f20bf700bce94d7843e1b02055b132fb0443524d..0479ec37c6297f7741e4e8744b08bd196669aeb7 100644
--- a/reviewboard/webapi/tests/test_review_comment.py
+++ b/reviewboard/webapi/tests/test_review_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_review_group.py b/reviewboard/webapi/tests/test_review_group.py
index 960a53a600e90e42f591eff46974eb927ef2e372..d9e1035177f9bf74c7ff3869b62eca57c9990f0f 100644
--- a/reviewboard/webapi/tests/test_review_group.py
+++ b/reviewboard/webapi/tests/test_review_group.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 from djblets.util.misc import get_object_or_none
diff --git a/reviewboard/webapi/tests/test_review_group_user.py b/reviewboard/webapi/tests/test_review_group_user.py
index f1df3b12eb332e36a944b250b81f84120dd34be2..84e2f2953ae491168573eedf14ed77559d4838d6 100644
--- a/reviewboard/webapi/tests/test_review_group_user.py
+++ b/reviewboard/webapi/tests/test_review_group_user.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_review_reply.py b/reviewboard/webapi/tests/test_review_reply.py
index 4f9545a03977231de6645af2c212538314c5a74f..318e6c52800388d25e6e40449f5501155472aac1 100644
--- a/reviewboard/webapi/tests/test_review_reply.py
+++ b/reviewboard/webapi/tests/test_review_reply.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.core import mail
 from djblets.util.compat import six
 
diff --git a/reviewboard/webapi/tests/test_review_reply_diff_comment.py b/reviewboard/webapi/tests/test_review_reply_diff_comment.py
index 75520bdb37b6e8cc12d884596efa598209a61b16..15cb512c28ed4e88794fe4ffd2c57b7b75419598 100644
--- a/reviewboard/webapi/tests/test_review_reply_diff_comment.py
+++ b/reviewboard/webapi/tests/test_review_reply_diff_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.reviews.models import Comment
diff --git a/reviewboard/webapi/tests/test_review_reply_file_attachment_comment.py b/reviewboard/webapi/tests/test_review_reply_file_attachment_comment.py
index 3a3fee79ac2d8a110bfe3c0561d3a8adca4b4b6b..414cd10a989a1d38c2adc88a4a437dcff81820ac 100644
--- a/reviewboard/webapi/tests/test_review_reply_file_attachment_comment.py
+++ b/reviewboard/webapi/tests/test_review_reply_file_attachment_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.reviews.models import FileAttachmentComment
diff --git a/reviewboard/webapi/tests/test_review_reply_screenshot_comment.py b/reviewboard/webapi/tests/test_review_reply_screenshot_comment.py
index fab09c56290882bb6c50219793b910978522284e..6cc07a647e8039345e45d347cd7b33c16fc26a77 100644
--- a/reviewboard/webapi/tests/test_review_reply_screenshot_comment.py
+++ b/reviewboard/webapi/tests/test_review_reply_screenshot_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.reviews.models import ScreenshotComment
diff --git a/reviewboard/webapi/tests/test_review_request.py b/reviewboard/webapi/tests/test_review_request.py
index 93c79c1e7c6ac1ae2d8e8db727b717666fb2a7ef..69b6614932a0fe722ad7f4e31b2562fa846539e8 100644
--- a/reviewboard/webapi/tests/test_review_request.py
+++ b/reviewboard/webapi/tests/test_review_request.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User, Permission
 from django.db.models import Q
 from djblets.testing.decorators import add_fixtures
@@ -495,7 +497,7 @@ class ResourceListTests(BaseWebAPITestCase):
 
         self.assertEqual(review_request.commit_id, None)
 
-        commit_id = str(review_request.changenum)
+        commit_id = six.text_type(review_request.changenum)
 
         rsp = self.apiGet(get_review_request_list_url(), {
             'repository': review_request.repository.id,
diff --git a/reviewboard/webapi/tests/test_review_request_draft.py b/reviewboard/webapi/tests/test_review_request_draft.py
index 85da86e0c104bc2b15ad6870e3526604453ad0aa..5f4c791853930989f16518be3fec38c4516215b4 100644
--- a/reviewboard/webapi/tests/test_review_request_draft.py
+++ b/reviewboard/webapi/tests/test_review_request_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import Permission, User
 from django.core import mail
 from djblets.testing.decorators import add_fixtures
diff --git a/reviewboard/webapi/tests/test_review_screenshot_comment.py b/reviewboard/webapi/tests/test_review_screenshot_comment.py
index 35e20dfe9b3b9d579ca4b2abd829e15c80a06b82..eac5b8f91ced87e0f9396f4c7010f2c5eaf5f177 100644
--- a/reviewboard/webapi/tests/test_review_screenshot_comment.py
+++ b/reviewboard/webapi/tests/test_review_screenshot_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_root.py b/reviewboard/webapi/tests/test_root.py
index 006922dbace75200215678660ce26d75cef70742..ddd42f7ccd859b0e22e3e907961e42122c5b2f6e 100644
--- a/reviewboard/webapi/tests/test_root.py
+++ b/reviewboard/webapi/tests/test_root.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 
diff --git a/reviewboard/webapi/tests/test_screenshot.py b/reviewboard/webapi/tests/test_screenshot.py
index 0eb3ce437cf22fe2ebfc1a16e3d5cbac771fac02..255eb83c3205afd26760a9d107a02942801f3aa0 100644
--- a/reviewboard/webapi/tests/test_screenshot.py
+++ b/reviewboard/webapi/tests/test_screenshot.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
 
diff --git a/reviewboard/webapi/tests/test_screenshot_comment.py b/reviewboard/webapi/tests/test_screenshot_comment.py
index 062b1405419d71fe710971862999c79f39bdf7e8..5f1c225c109e483fd125c103adc5c317868c8179 100644
--- a/reviewboard/webapi/tests/test_screenshot_comment.py
+++ b/reviewboard/webapi/tests/test_screenshot_comment.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_screenshot_draft.py b/reviewboard/webapi/tests/test_screenshot_draft.py
index 3f6d7cfee5692cd40ab52f61f29c23c4f1217642..b0ed3a48d1dc319883086f3e92416f5282df67a0 100644
--- a/reviewboard/webapi/tests/test_screenshot_draft.py
+++ b/reviewboard/webapi/tests/test_screenshot_draft.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 from djblets.webapi.errors import PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_server_info.py b/reviewboard/webapi/tests/test_server_info.py
index 03f4aedd3a2b6568a3cc0af3373b024770b01f7a..09e861ced130aeb5eb7e8c9c5259be300ab397cf 100644
--- a/reviewboard/webapi/tests/test_server_info.py
+++ b/reviewboard/webapi/tests/test_server_info.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_session.py b/reviewboard/webapi/tests/test_session.py
index 253e589dbdab792a61082fde5cdc59194b648045..21b5c4f307e192562b9979bdf2adb154e6089dbb 100644
--- a/reviewboard/webapi/tests/test_session.py
+++ b/reviewboard/webapi/tests/test_session.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.util.compat import six
 
 from reviewboard.webapi.resources import resources
diff --git a/reviewboard/webapi/tests/test_user.py b/reviewboard/webapi/tests/test_user.py
index 74347a1a319e648719ac0cd72a7fa10bf02f6b53..aeb27b296c1c34fa337c29a8337db49c83702e71 100644
--- a/reviewboard/webapi/tests/test_user.py
+++ b/reviewboard/webapi/tests/test_user.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from django.contrib.auth.models import User
 from djblets.util.compat import six
 from djblets.testing.decorators import add_fixtures
diff --git a/reviewboard/webapi/tests/test_validate_diff.py b/reviewboard/webapi/tests/test_validate_diff.py
index 52a7b53e45a4fb43ade4d8f5d23eff336df0154e..8c19fef379a4ea8cdc5b9c00b43031fa6204d8b3 100644
--- a/reviewboard/webapi/tests/test_validate_diff.py
+++ b/reviewboard/webapi/tests/test_validate_diff.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 import os
 
 from djblets.testing.decorators import add_fixtures
diff --git a/reviewboard/webapi/tests/test_watched_review_group.py b/reviewboard/webapi/tests/test_watched_review_group.py
index 8de7c147c881ab66a9ddd404ebb33386e6537187..2bcea2490213073c03832a507d11a98005712470 100644
--- a/reviewboard/webapi/tests/test_watched_review_group.py
+++ b/reviewboard/webapi/tests/test_watched_review_group.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 from djblets.webapi.errors import DOES_NOT_EXIST, PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/test_watched_review_request.py b/reviewboard/webapi/tests/test_watched_review_request.py
index 2719bbc3b57f576411b739b5a2a9e559a6571c6f..371fdf3a20560ea150bcc16b9205c6139a997313 100644
--- a/reviewboard/webapi/tests/test_watched_review_request.py
+++ b/reviewboard/webapi/tests/test_watched_review_request.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from djblets.testing.decorators import add_fixtures
 from djblets.util.compat import six
 from djblets.webapi.errors import DOES_NOT_EXIST, PERMISSION_DENIED
diff --git a/reviewboard/webapi/tests/urls.py b/reviewboard/webapi/tests/urls.py
index 59400dd0a7c047bc1f61f30ae04d4e0fa9f0d23f..c54f6213227365ae595345c395b2fc42601af14c 100644
--- a/reviewboard/webapi/tests/urls.py
+++ b/reviewboard/webapi/tests/urls.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
 from reviewboard.site.urlresolvers import local_site_reverse
 from reviewboard.webapi.resources import resources
 
