From 52138b1fd08f80fe98def7e22a9693415b4f7744 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 26 Jan 2017 09:37:07 +0100 Subject: [PATCH] Refs #23919 -- Removed usage of obsolete SafeBytes class The class will be removed as part of #27753. Thanks Tim Graham for the review. --- django/db/backends/postgresql/base.py | 3 +-- django/utils/encoding.py | 13 +++------- django/utils/safestring.py | 37 ++++----------------------- docs/howto/custom-template-tags.txt | 10 +++++--- docs/ref/utils.txt | 10 +------- docs/releases/2.0.txt | 10 ++++++++ tests/i18n/tests.py | 4 +-- tests/utils_tests/test_encoding.py | 4 --- tests/utils_tests/test_safestring.py | 7 +---- 9 files changed, 29 insertions(+), 69 deletions(-) diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 2459748f1b..7dab1cb364 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -13,7 +13,7 @@ from django.db import DEFAULT_DB_ALIAS from django.db.backends.base.base import BaseDatabaseWrapper from django.db.utils import DatabaseError as WrappedDatabaseError from django.utils.functional import cached_property -from django.utils.safestring import SafeBytes, SafeText +from django.utils.safestring import SafeText try: import psycopg2 as Database @@ -44,7 +44,6 @@ from .schema import DatabaseSchemaEditor # NOQA isort:skip from .utils import utc_tzinfo_factory # NOQA isort:skip from .version import get_version # NOQA isort:skip -psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString) psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString) psycopg2.extras.register_uuid() diff --git a/django/utils/encoding.py b/django/utils/encoding.py index e889d5f3a9..ed0a97e5eb 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -68,10 +68,7 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): else: s = str(s) else: - # Note: We use .decode() here, instead of str(s, encoding, - # errors), so that if s is a SafeBytes, it ends up being a - # SafeText at the end. - s = s.decode(encoding, errors) + str(s, encoding, errors) except UnicodeDecodeError as e: if not isinstance(s, Exception): raise DjangoUnicodeDecodeError(s, *e.args) @@ -209,7 +206,7 @@ def escape_uri_path(path): # and "?" according to section 3.3 of RFC 2396. # The reason for not subtracting and escaping "/" is that we are escaping # the entire path, not a path segment. - return quote(force_bytes(path), safe=b"/:@&+$,-_.!~*'()") + return quote(path, safe="/:@&+$,-_.!~*'()") def repercent_broken_unicode(path): @@ -231,20 +228,18 @@ def filepath_to_uri(path): """Convert a file system path to a URI portion that is suitable for inclusion in a URL. - Assume the input is either UTF-8 bytes or a string. - This method will encode certain chars that would normally be recognized as special chars for URIs. Note that this method does not encode the ' character, as it is a valid character within URIs. See encodeURIComponent() JavaScript function for more details. - Returns an ASCII string containing the encoded result. + Return a string containing the result. """ if path is None: return path # I know about `os.sep` and `os.altsep` but I want to leave # some flexibility for hardcoding separators. - return quote(force_bytes(path).replace(b"\\", b"/"), safe=b"/~!*()'") + return quote(path.replace("\\", "/"), safe="/~!*()'") def get_system_encoding(): diff --git a/django/utils/safestring.py b/django/utils/safestring.py index 2aed4a3cb9..5f5e7098f2 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -5,7 +5,7 @@ that the producer of the string has already turned characters that should not be interpreted by the HTML engine (e.g. '<') into the appropriate entities. """ -from django.utils.functional import Promise, curry, wraps +from django.utils.functional import Promise, wraps class SafeData: @@ -22,6 +22,9 @@ class SafeBytes(bytes, SafeData): """ A bytes subclass that has been specifically marked as "safe" (requires no further escaping) for HTML output purposes. + + Kept in Django 2.0 for usage by apps supporting Python 2. Shouldn't be used + in Django anymore. """ def __add__(self, rhs): """ @@ -35,20 +38,6 @@ class SafeBytes(bytes, SafeData): return SafeBytes(t) return t - def _proxy_method(self, *args, **kwargs): - """ - Wrap a call to a normal bytes method up so that the result is safe. - The method that is being wrapped is passed in the 'method' argument. - """ - method = kwargs.pop('method') - data = method(self, *args, **kwargs) - if isinstance(data, bytes): - return SafeBytes(data) - else: - return SafeText(data) - - decode = curry(_proxy_method, method=bytes.decode) - class SafeText(str, SafeData): """ @@ -65,20 +54,6 @@ class SafeText(str, SafeData): return SafeText(t) return t - def _proxy_method(self, *args, **kwargs): - """ - Wrap a call to a normal str method up so that the result is safe. - The method that is being wrapped is passed in the 'method' argument. - """ - method = kwargs.pop('method') - data = method(self, *args, **kwargs) - if isinstance(data, bytes): - return SafeBytes(data) - else: - return SafeText(data) - - encode = curry(_proxy_method, method=str.encode) - SafeString = SafeText @@ -101,10 +76,8 @@ def mark_safe(s): """ if hasattr(s, '__html__'): return s - if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes): - return SafeBytes(s) if isinstance(s, (str, Promise)): return SafeText(s) if callable(s): return _safety_decorator(mark_safe, s) - return SafeString(str(s)) + return SafeText(str(s)) diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt index 9ce743549d..d568292229 100644 --- a/docs/howto/custom-template-tags.txt +++ b/docs/howto/custom-template-tags.txt @@ -199,11 +199,13 @@ passed around inside the template code: They're commonly used for output that contains raw HTML that is intended to be interpreted as-is on the client side. - Internally, these strings are of type ``SafeBytes`` or ``SafeText``. - They share a common base class of ``SafeData``, so you can test - for them using code like:: + Internally, these strings are of type + :class:`~django.utils.safestring.SafeText`. You can test for them + using code like:: - if isinstance(value, SafeData): + from django.utils.safestring import SafeText + + if isinstance(value, SafeText): # Do something with the "safe" string. ... diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index cd5bbee707..1e37d28ca8 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -763,20 +763,12 @@ string" means that the producer of the string has already turned characters that should not be interpreted by the HTML engine (e.g. '<') into the appropriate entities. -.. class:: SafeBytes - - A ``bytes`` subclass that has been specifically marked as "safe" - (requires no further escaping) for HTML output purposes. - .. class:: SafeString A ``str`` subclass that has been specifically marked as "safe" (requires no further escaping) for HTML output purposes. Alias of :class:`SafeText`. - Alias of :class:`SafeBytes` on Python 2 (in older versions of Django that - support it). - .. class:: SafeText A ``str`` subclass that has been specifically marked as "safe" for HTML @@ -799,7 +791,7 @@ appropriate entities. >>> mystr = 'Hello World ' >>> mystr = mark_safe(mystr) >>> type(mystr) - + >>> mystr = mystr.strip() # removing whitespace >>> type(mystr) diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 7cb9b5da73..dec2b85228 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -205,6 +205,16 @@ Validators Backwards incompatible changes in 2.0 ===================================== +Removed support for bytestrings in some places +---------------------------------------------- + +To support native Python 2 strings, older Django versions had to accept both +bytestrings and unicode strings. Now that Python 2 support is dropped, +bytestrings should only be encountered around input/output boundaries (handling +of binary fields or HTTP streams, for example). You might have to update your +code to limit bytestring usage to a minimum, as Django no longer accepts +bytestrings in certain code paths. + Database backend API -------------------- diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index acff0dfc28..d2b878b435 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -20,7 +20,7 @@ from django.utils.formats import ( localize_input, reset_format_cache, sanitize_separators, time_format, ) from django.utils.numberformat import format as nformat -from django.utils.safestring import SafeBytes, SafeText +from django.utils.safestring import SafeText from django.utils.translation import ( LANGUAGE_SESSION_KEY, activate, check_for_language, deactivate, get_language, get_language_from_request, get_language_info, gettext_lazy, @@ -1280,8 +1280,6 @@ class TestModels(TestCase): c = Company(cents_paid=12, products_delivered=1) c.name = SafeText('Iñtërnâtiônàlizætiøn1') c.save() - c.name = SafeBytes('Iñtërnâtiônàlizætiøn1'.encode('utf-8')) - c.save() class TestLanguageInfo(SimpleTestCase): diff --git a/tests/utils_tests/test_encoding.py b/tests/utils_tests/test_encoding.py index ea3b97ddf5..bd937bf960 100644 --- a/tests/utils_tests/test_encoding.py +++ b/tests/utils_tests/test_encoding.py @@ -62,10 +62,6 @@ class TestRFC3987IEncodingUtils(unittest.TestCase): def test_filepath_to_uri(self): self.assertEqual(filepath_to_uri('upload\\чубака.mp4'), 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4') - self.assertEqual( - filepath_to_uri('upload\\чубака.mp4'.encode('utf-8')), - 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4' - ) def test_iri_to_uri(self): cases = [ diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py index d1ef28944e..d7afb38950 100644 --- a/tests/utils_tests/test_safestring.py +++ b/tests/utils_tests/test_safestring.py @@ -1,12 +1,9 @@ from django.template import Context, Template from django.test import SimpleTestCase from django.utils import html, text -from django.utils.encoding import force_bytes -from django.utils.functional import lazy, lazystr +from django.utils.functional import lazystr from django.utils.safestring import SafeData, mark_safe -lazybytes = lazy(force_bytes, bytes) - class customescape(str): def __html__(self): @@ -37,10 +34,8 @@ class SafeStringTest(SimpleTestCase): def test_mark_safe_lazy(self): s = lazystr('a&b') - b = lazybytes(b'a&b') self.assertIsInstance(mark_safe(s), SafeData) - self.assertIsInstance(mark_safe(b), SafeData) self.assertRenderEqual('{{ s }}', 'a&b', s=mark_safe(s)) def test_mark_safe_object_implementing_dunder_str(self):