Browse Source

Make sure __repr__ and __str__ returns bytes on Python 2

Ask Solem 9 years ago
parent
commit
9b68e8f9df

+ 5 - 2
celery/app/base.py

@@ -32,7 +32,9 @@ from celery._state import (
 )
 from celery.datastructures import AttributeDictMixin
 from celery.exceptions import AlwaysEagerIgnored, ImproperlyConfigured
-from celery.five import UserDict, module_name_t, values
+from celery.five import (
+    UserDict, bytes_if_py2, python_2_unicode_compatible, values,
+)
 from celery.loaders import get_loader_cls
 from celery.local import PromiseProxy, maybe_evaluate
 from celery.utils import abstract
@@ -108,6 +110,7 @@ class PendingConfiguration(UserDict, AttributeDictMixin):
         return self.callback(key)
 
 
+@python_2_unicode_compatible
 class Celery(object):
     """Celery application.
 
@@ -950,7 +953,7 @@ class Celery(object):
         if not keep_reduce:
             attrs['__reduce__'] = __reduce__
 
-        return type(module_name_t(name or Class.__name__), (Class,), attrs)
+        return type(bytes_if_py2(name or Class.__name__), (Class,), attrs)
 
     def _rgetattr(self, path):
         return attrgetter(path)(self)

+ 2 - 1
celery/app/defaults.py

@@ -13,7 +13,7 @@ import sys
 from collections import deque, namedtuple
 from datetime import timedelta
 
-from celery.five import items, keys, values
+from celery.five import items, keys, python_2_unicode_compatible, values
 from celery.utils import strtobool
 from celery.utils.functional import memoize
 
@@ -58,6 +58,7 @@ def old_ns(ns):
     return {'{0}_{{0}}'.format(ns)}
 
 
+@python_2_unicode_compatible
 class Option(object):
     alt = None
     deprecate_by = None

+ 3 - 1
celery/app/task.py

@@ -17,7 +17,7 @@ from celery import states
 from celery._state import _task_stack
 from celery.canvas import signature
 from celery.exceptions import Ignore, MaxRetriesExceededError, Reject, Retry
-from celery.five import class_property, items
+from celery.five import class_property, items, python_2_unicode_compatible
 from celery.result import EagerResult
 from celery.utils import abstract
 from celery.utils import uuid, maybe_reraise
@@ -65,6 +65,7 @@ def _reprtask(task, fmt=None, flags=None):
     )
 
 
+@python_2_unicode_compatible
 class Context(object):
     # Default context
     logfile = None
@@ -119,6 +120,7 @@ class Context(object):
         return self._children
 
 
+@python_2_unicode_compatible
 class Task(object):
     """Task base class.
 

+ 3 - 0
celery/backends/database/models.py

@@ -14,12 +14,14 @@ import sqlalchemy as sa
 from sqlalchemy.types import PickleType
 
 from celery import states
+from celery.five import python_2_unicode_compatible
 
 from .session import ResultModelBase
 
 __all__ = ['Task', 'TaskSet']
 
 
+@python_2_unicode_compatible
 class Task(ResultModelBase):
     """Task result/status."""
     __tablename__ = 'celery_taskmeta'
@@ -49,6 +51,7 @@ class Task(ResultModelBase):
         return '<Task {0.task_id} state: {0.status}>'.format(self)
 
 
+@python_2_unicode_compatible
 class TaskSet(ResultModelBase):
     """TaskSet result"""
     __tablename__ = 'celery_tasksetmeta'

+ 8 - 3
celery/beat.py

@@ -29,14 +29,18 @@ from kombu.utils.functional import maybe_evaluate
 from . import __version__
 from . import platforms
 from . import signals
-from .five import items, reraise, values, monotonic
+from .five import (
+    items, monotonic, python_2_unicode_compatible, reraise, values,
+)
 from .schedules import maybe_schedule, crontab
 from .utils.imports import instantiate
 from .utils.timeutils import humanize_seconds
 from .utils.log import get_logger, iter_open_logger_fds
 
-__all__ = ['SchedulingError', 'ScheduleEntry', 'Scheduler',
-           'PersistentScheduler', 'Service', 'EmbeddedService']
+__all__ = [
+    'SchedulingError', 'ScheduleEntry', 'Scheduler',
+    'PersistentScheduler', 'Service', 'EmbeddedService',
+]
 
 event_t = namedtuple('event_t', ('time', 'priority', 'entry'))
 
@@ -52,6 +56,7 @@ class SchedulingError(Exception):
 
 
 @total_ordering
+@python_2_unicode_compatible
 class ScheduleEntry(object):
     """An entry in the scheduler.
 

+ 9 - 5
celery/bin/base.py

@@ -18,7 +18,9 @@ from pprint import pformat
 from celery import VERSION_BANNER, Celery, maybe_patch_concurrency
 from celery import signals
 from celery.exceptions import CDeprecationWarning, CPendingDeprecationWarning
-from celery.five import getfullargspec, items, string, string_t
+from celery.five import (
+    getfullargspec, items, python_2_unicode_compatible, string, string_t,
+)
 from celery.platforms import EX_FAILURE, EX_OK, EX_USAGE
 from celery.utils import term
 from celery.utils import text
@@ -30,6 +32,11 @@ try:
 except NameError:  # pragma: no cover
     pass
 
+__all__ = [
+    'Error', 'UsageError', 'Extensions',
+    'HelpFormatter', 'Command', 'Option', 'daemon_options',
+]
+
 # always enable DeprecationWarnings, so our users can see them.
 for warning in (CDeprecationWarning, CPendingDeprecationWarning):
     warnings.simplefilter('once', warning, 0)
@@ -43,10 +50,8 @@ Try --help?
 find_long_opt = re.compile(r'.+?(--.+?)(?:\s|,|$)')
 find_rst_ref = re.compile(r':\w+:`(.+?)`')
 
-__all__ = ['Error', 'UsageError', 'Extensions', 'HelpFormatter',
-           'Command', 'Option', 'daemon_options']
-
 
+@python_2_unicode_compatible
 class Error(Exception):
     status = EX_FAILURE
 
@@ -57,7 +62,6 @@ class Error(Exception):
 
     def __str__(self):
         return self.reason
-    __unicode__ = __str__
 
 
 class UsageError(Error):

+ 2 - 1
celery/bin/graph.py

@@ -11,7 +11,7 @@ from __future__ import absolute_import, unicode_literals
 from operator import itemgetter
 
 from celery.datastructures import DependencyGraph, GraphFormatter
-from celery.five import items
+from celery.five import items, python_2_unicode_compatible
 
 from .base import Command
 
@@ -58,6 +58,7 @@ class graph(Command):
             return '{0} ({1}://)'.format(type(node).__name__,
                                          node._label.split('://')[0])
 
+        @python_2_unicode_compatible
         class Node(object):
             force_label = None
             scheme = {}

+ 4 - 4
celery/bootsteps.py

@@ -16,7 +16,7 @@ from kombu.utils import symbol_by_name
 from kombu.utils.encoding import bytes_to_str
 
 from .datastructures import DependencyGraph, GraphFormatter
-from .five import values, with_metaclass
+from .five import bytes_if_py2, values, with_metaclass
 from .utils.imports import instantiate, qualname
 from .utils.log import get_logger
 
@@ -291,10 +291,10 @@ class StepType(type):
         return super(StepType, cls).__new__(cls, name, bases, attrs)
 
     def __str__(self):
-        return self.name
+        return bytes_if_py2(self.name)
 
     def __repr__(self):
-        return 'step:{0.name}{{{0.requires!r}}}'.format(self)
+        return bytes_if_py2('step:{0.name}{{{0.requires!r}}}'.format(self))
 
 
 @with_metaclass(StepType)
@@ -354,7 +354,7 @@ class Step(object):
         pass
 
     def __repr__(self):
-        return '<step: {0.alias}>'.format(self)
+        return bytes_if_py2('<step: {0.alias}>'.format(self))
 
     @property
     def alias(self):

+ 7 - 0
celery/canvas.py

@@ -24,6 +24,7 @@ from kombu.utils import cached_property, fxrange, reprcall, uuid
 from vine import barrier
 
 from celery._state import current_app
+from celery.five import python_2_unicode_compatible
 from celery.local import try_import
 from celery.result import GroupResult
 from celery.utils import abstract
@@ -119,6 +120,7 @@ def _upgrade(fields, sig):
     return sig
 
 
+@python_2_unicode_compatible
 class Signature(dict):
     """Class that wraps the arguments and execution options
     for a single task invocation.
@@ -397,6 +399,7 @@ abstract.CallableSignature.register(Signature)
 
 
 @Signature.register_type
+@python_2_unicode_compatible
 class chain(Signature):
     tasks = _getitem_property('kwargs.tasks')
 
@@ -617,6 +620,7 @@ class _basemap(Signature):
 
 
 @Signature.register_type
+@python_2_unicode_compatible
 class xmap(_basemap):
     _task_name = 'celery.map'
 
@@ -627,6 +631,7 @@ class xmap(_basemap):
 
 
 @Signature.register_type
+@python_2_unicode_compatible
 class xstarmap(_basemap):
     _task_name = 'celery.starmap'
 
@@ -689,6 +694,7 @@ def _maybe_group(tasks, app):
 
 
 @Signature.register_type
+@python_2_unicode_compatible
 class group(Signature):
     tasks = _getitem_property('kwargs.tasks')
 
@@ -879,6 +885,7 @@ class group(Signature):
 
 
 @Signature.register_type
+@python_2_unicode_compatible
 class chord(Signature):
 
     def __init__(self, header, body=None, task='celery.chord',

+ 2 - 1
celery/contrib/migrate.py

@@ -18,7 +18,7 @@ from kombu.common import maybe_declare
 from kombu.utils.encoding import ensure_bytes
 
 from celery.app import app_or_default
-from celery.five import string, string_t
+from celery.five import python_2_unicode_compatible, string, string_t
 from celery.utils import worker_direct
 
 __all__ = [
@@ -38,6 +38,7 @@ class StopFiltering(Exception):
     pass
 
 
+@python_2_unicode_compatible
 class State(object):
     count = 0
     filtered = 0

+ 4 - 1
celery/datastructures.py

@@ -21,7 +21,7 @@ from billiard.einfo import ExceptionInfo  # noqa
 from kombu.utils.encoding import safe_str, bytes_to_str
 from kombu.utils.limits import TokenBucket  # noqa
 
-from celery.five import items, values
+from celery.five import items, python_2_unicode_compatible, values
 from celery.utils.functional import LRUCache, first, uniq  # noqa
 from celery.utils.text import match_case
 
@@ -148,6 +148,7 @@ class CycleError(Exception):
     """A cycle was detected in an acyclic graph."""
 
 
+@python_2_unicode_compatible
 class DependencyGraph(object):
     """A directed acyclic graph of objects and their dependencies.
 
@@ -444,6 +445,7 @@ class DictAttribute(object):
 MutableMapping.register(DictAttribute)
 
 
+@python_2_unicode_compatible
 class ConfigurationView(AttributeDictMixin):
     """A view over an applications configuration dicts.
 
@@ -587,6 +589,7 @@ class ConfigurationView(AttributeDictMixin):
 MutableMapping.register(ConfigurationView)
 
 
+@python_2_unicode_compatible
 class LimitedSet(object):
     """Kind-of Set (or priority queue) with limitations.
 

+ 3 - 1
celery/events/state.py

@@ -33,7 +33,7 @@ from kombu.clocks import timetuple
 from kombu.utils import cached_property
 
 from celery import states
-from celery.five import items, values
+from celery.five import items, python_2_unicode_compatible, values
 from celery.utils.functional import LRUCache, memoize
 from celery.utils.log import get_logger
 
@@ -110,6 +110,7 @@ def with_unique_field(attr):
 
 
 @with_unique_field('hostname')
+@python_2_unicode_compatible
 class Worker(object):
     """Worker State."""
     heartbeat_max = 4
@@ -202,6 +203,7 @@ class Worker(object):
 
 
 @with_unique_field('uuid')
+@python_2_unicode_compatible
 class Task(object):
     """Task State."""
     name = received = sent = started = succeeded = failed = retried = \

+ 4 - 1
celery/exceptions.py

@@ -10,7 +10,7 @@ from __future__ import absolute_import, unicode_literals
 
 import numbers
 
-from .five import string_t
+from .five import python_2_unicode_compatible, string_t
 
 from billiard.exceptions import (  # noqa
     SoftTimeLimitExceeded, TimeLimitExceeded, WorkerLostError, Terminated,
@@ -54,6 +54,7 @@ class TaskPredicate(CeleryError):
     pass
 
 
+@python_2_unicode_compatible
 class Retry(TaskPredicate):
     """The task is to be retried later."""
 
@@ -98,6 +99,7 @@ class Ignore(TaskPredicate):
     """A task can raise this to ignore doing state updates."""
 
 
+@python_2_unicode_compatible
 class Reject(TaskPredicate):
     """A task can raise this if it wants to reject/requeue the message."""
 
@@ -127,6 +129,7 @@ class ImproperlyConfigured(ImportError):
     """Celery is somehow improperly configured."""
 
 
+@python_2_unicode_compatible
 class NotRegistered(KeyError, CeleryError):
     """The task is not registered."""
 

+ 1 - 1
celery/five.py

@@ -191,7 +191,7 @@ def create_module(name, attrs, cls_attrs=None, pkg=None,
         for attr_name, attr in items(attrs)
     }
     module = sys.modules[fqdn] = type(
-        module_name_t(modname), (base,), cls_attrs)(module_name_t(name))
+        bytes_if_py2(modname), (base,), cls_attrs)(bytes_if_py2(name))
     module.__dict__.update(attrs)
     return module
 

+ 8 - 8
celery/local.py

@@ -15,7 +15,7 @@ from __future__ import absolute_import, unicode_literals
 import importlib
 import sys
 
-from .five import module_name_t, string
+from .five import bytes_if_py2, string
 
 __all__ = ['Proxy', 'PromiseProxy', 'try_import', 'maybe_evaluate']
 
@@ -39,7 +39,7 @@ def _default_cls_attr(name, type_, cls_value):
     def __get__(self, obj, cls=None):
         return self.__getter(obj) if obj is not None else self
 
-    return type(module_name_t(name), (type_,), {
+    return type(bytes_if_py2(name), (type_,), {
         '__new__': __new__, '__get__': __get__,
     })
 
@@ -126,12 +126,6 @@ class Proxy(object):
             return False
     __nonzero__ = __bool__  # Py2
 
-    def __unicode__(self):
-        try:
-            return string(self._get_current_object())
-        except RuntimeError:  # pragma: no cover
-            return repr(self)
-
     def __dir__(self):
         try:
             return dir(self._get_current_object())
@@ -294,6 +288,12 @@ class Proxy(object):
         def __long__(self):
             return long(self._get_current_object())  # noqa
 
+        def __unicode__(self):
+            try:
+                return string(self._get_current_object())
+            except RuntimeError:  # pragma: no cover
+                return repr(self)
+
 
 class PromiseProxy(Proxy):
     """This is a proxy to an object that has not yet been evaulated.

+ 7 - 1
celery/result.py

@@ -23,7 +23,9 @@ from ._state import _set_task_join_will_block, task_join_will_block
 from .app import app_or_default
 from .datastructures import DependencyGraph, GraphFormatter
 from .exceptions import ImproperlyConfigured, IncompleteStream, TimeoutError
-from .five import items, range, string_t, monotonic
+from .five import (
+    items, python_2_unicode_compatible, range, string_t, monotonic,
+)
 from .utils import deprecated
 
 __all__ = ['ResultBase', 'AsyncResult', 'ResultSet', 'GroupResult',
@@ -58,6 +60,7 @@ class ResultBase(object):
     parent = None
 
 
+@python_2_unicode_compatible
 class AsyncResult(ResultBase):
     """Query task state.
 
@@ -425,6 +428,7 @@ class AsyncResult(ResultBase):
 Thenable.register(AsyncResult)
 
 
+@python_2_unicode_compatible
 class ResultSet(ResultBase):
     """Working with more than one result.
 
@@ -790,6 +794,7 @@ class ResultSet(ResultBase):
 Thenable.register(ResultSet)
 
 
+@python_2_unicode_compatible
 class GroupResult(ResultSet):
     """Like :class:`ResultSet`, but with an associated id.
 
@@ -864,6 +869,7 @@ class GroupResult(ResultSet):
 Thenable.register(ResultSet)
 
 
+@python_2_unicode_compatible
 class EagerResult(AsyncResult):
     """Result that we know has already been executed."""
 

+ 4 - 1
celery/schedules.py

@@ -19,7 +19,7 @@ from datetime import datetime, timedelta
 from kombu.utils import cached_property
 
 from . import current_app
-from .five import range, string_t
+from .five import python_2_unicode_compatible, range, string_t
 from .utils import is_iterable
 from .utils.timeutils import (
     weekday, maybe_timedelta, remaining, humanize_seconds,
@@ -69,6 +69,7 @@ class ParseException(Exception):
     """Raised by crontab_parser when the input can't be parsed."""
 
 
+@python_2_unicode_compatible
 class schedule(object):
     """Schedule for periodic task.
 
@@ -303,6 +304,7 @@ class crontab_parser(object):
         return i
 
 
+@python_2_unicode_compatible
 class crontab(schedule):
     """A crontab can be used as the `run_every` value of a
     :class:`PeriodicTask` to add cron-like scheduling.
@@ -622,6 +624,7 @@ def maybe_schedule(s, relative=False, app=None):
     return s
 
 
+@python_2_unicode_compatible
 class solar(schedule):
     """A solar event can be used as the `run_every` value of a
     :class:`PeriodicTask` to schedule based on certain solar events.

+ 6 - 1
celery/task/base.py

@@ -15,7 +15,10 @@ from kombu import Exchange
 
 from celery import current_app
 from celery.app.task import Context, Task as BaseTask, _reprtask
-from celery.five import class_property, reclassmethod, with_metaclass
+from celery.five import (
+    class_property, reclassmethod,
+    python_2_unicode_compatible, with_metaclass,
+)
 from celery.local import Proxy
 from celery.schedules import maybe_schedule
 from celery.utils.log import get_task_logger
@@ -30,6 +33,7 @@ _COMPAT_CLASSMETHODS = (
 )
 
 
+@python_2_unicode_compatible
 class _CompatShared(object):
 
     def __init__(self, name, cons):
@@ -124,6 +128,7 @@ class TaskType(type):
 
 
 @with_metaclass(TaskType)
+@python_2_unicode_compatible
 class Task(BaseTask):
     """Deprecated Task base class.
 

+ 2 - 1
celery/task/http.py

@@ -20,7 +20,7 @@ from kombu.utils import json
 from kombu.utils.encoding import bytes_to_str, str_to_bytes
 
 from celery import shared_task, __version__ as celery_version
-from celery.five import items, reraise
+from celery.five import items, python_2_unicode_compatible, reraise
 from celery.utils.log import get_task_logger
 
 __all__ = ['InvalidResponseError', 'RemoteExecuteError', 'UnknownStatusError',
@@ -83,6 +83,7 @@ def extract_response(raw_response, loads=json.loads):
         raise UnknownStatusError(str(status))
 
 
+@python_2_unicode_compatible
 class MutableURL(object):
     """Object wrapping a Uniform Resource Locator.
 

+ 2 - 2
celery/tests/app/test_loaders.py

@@ -6,7 +6,7 @@ import warnings
 
 from celery import loaders
 from celery.exceptions import NotConfigured
-from celery.five import module_name_t
+from celery.five import bytes_if_py2
 from celery.loaders import base
 from celery.loaders import default
 from celery.loaders.app import AppLoader
@@ -164,7 +164,7 @@ class test_DefaultLoader(AppCase):
             pass
 
         configname = os.environ.get('CELERY_CONFIG_MODULE') or 'celeryconfig'
-        celeryconfig = ConfigModule(module_name_t(configname))
+        celeryconfig = ConfigModule(bytes_if_py2(configname))
         celeryconfig.imports = ('os', 'sys')
 
         prevconfig = sys.modules.get(configname)

+ 2 - 0
celery/tests/app/test_log.py

@@ -9,6 +9,7 @@ from tempfile import mktemp
 
 from celery import signals
 from celery.app.log import TaskFormatter
+from celery.five import python_2_unicode_compatible
 from celery.utils.log import LoggingProxy
 from celery.utils import uuid
 from celery.utils.log import (
@@ -134,6 +135,7 @@ class test_ColorFormatter(AppCase):
                 safe_str.side_effect = None
         safe_str.side_effect = on_safe_str
 
+        @python_2_unicode_compatible
         class Record(object):
             levelname = 'ERROR'
             msg = 'HELLO'

+ 5 - 5
celery/tests/backends/test_base.py

@@ -6,7 +6,7 @@ import types
 from contextlib import contextmanager
 
 from celery.exceptions import ChordError, TimeoutError
-from celery.five import items, module_name_t, range
+from celery.five import items, bytes_if_py2, range
 from celery.utils import serialization
 from celery.utils.serialization import subclass_exception
 from celery.utils.serialization import find_pickleable_exception as fnpe
@@ -36,15 +36,15 @@ class wrapobject(object):
 if sys.version_info[0] == 3 or getattr(sys, 'pypy_version_info', None):
     Oldstyle = None
 else:
-    Oldstyle = types.ClassType(module_name_t('Oldstyle'), (), {})
+    Oldstyle = types.ClassType(bytes_if_py2('Oldstyle'), (), {})
 Unpickleable = subclass_exception(
-    module_name_t('Unpickleable'), KeyError, 'foo.module',
+    bytes_if_py2('Unpickleable'), KeyError, 'foo.module',
 )
 Impossible = subclass_exception(
-    module_name_t('Impossible'), object, 'foo.module',
+    bytes_if_py2('Impossible'), object, 'foo.module',
 )
 Lookalike = subclass_exception(
-    module_name_t('Lookalike'), wrapobject, 'foo.module',
+    bytes_if_py2('Lookalike'), wrapobject, 'foo.module',
 )
 
 

+ 3 - 3
celery/tests/backends/test_cache.py

@@ -12,7 +12,7 @@ from celery import states
 from celery import group
 from celery.backends.cache import CacheBackend, DummyClient, backends
 from celery.exceptions import ImproperlyConfigured
-from celery.five import items, module_name_t, string, text_t
+from celery.five import items, bytes_if_py2, string, text_t
 from celery.utils import uuid
 
 from celery.tests.case import AppCase, Mock, mock, patch, skip
@@ -168,7 +168,7 @@ class MockCacheMixin(object):
 
     @contextmanager
     def mock_memcache(self):
-        memcache = types.ModuleType(module_name_t('memcache'))
+        memcache = types.ModuleType(bytes_if_py2('memcache'))
         memcache.Client = MemcachedClient
         memcache.Client.__module__ = memcache.__name__
         prev, sys.modules['memcache'] = sys.modules.get('memcache'), memcache
@@ -180,7 +180,7 @@ class MockCacheMixin(object):
 
     @contextmanager
     def mock_pylibmc(self):
-        pylibmc = types.ModuleType(module_name_t('pylibmc'))
+        pylibmc = types.ModuleType(bytes_if_py2('pylibmc'))
         pylibmc.Client = MemcachedClient
         pylibmc.Client.__module__ = pylibmc.__name__
         prev = sys.modules.get('pylibmc')

+ 2 - 2
celery/tests/bin/test_base.py

@@ -8,7 +8,7 @@ from celery.bin.base import (
     Extensions,
     HelpFormatter,
 )
-from celery.five import module_name_t
+from celery.five import bytes_if_py2
 from celery.utils.objects import Bunch
 
 from celery.tests.case import (
@@ -353,7 +353,7 @@ class test_Command(AppCase):
         cmd = MockCommand(app=self.app)
         with patch('celery.bin.base.symbol_by_name') as sbn:
             from types import ModuleType
-            x = ModuleType(module_name_t('proj'))
+            x = ModuleType(bytes_if_py2('proj'))
 
             def on_sbn(*args, **kwargs):
 

+ 2 - 2
celery/tests/utils/test_imports.py

@@ -1,6 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 
-from celery.five import module_name_t
+from celery.five import bytes_if_py2
 
 from celery.utils.imports import (
     qualname,
@@ -24,7 +24,7 @@ class test_import_utils(Case):
         self.assertTrue(find_module('celery.worker.request'))
 
     def test_qualname(self):
-        Class = type(module_name_t('Fox'), (object,), {
+        Class = type(bytes_if_py2('Fox'), (object,), {
             '__module__': 'quick.brown',
         })
         self.assertEqual(qualname(Class), 'quick.brown.Fox')

+ 4 - 2
celery/tests/utils/test_local.py

@@ -2,14 +2,14 @@ from __future__ import absolute_import, unicode_literals
 
 import sys
 
-from celery.five import string, long_t
+from celery.five import python_2_unicode_compatible, string, long_t
 from celery.local import (
     Proxy,
     PromiseProxy,
     maybe_evaluate,
     try_import,
 )
-from celery.tests.case import Case, Mock
+from celery.tests.case import Case, Mock, skip
 
 PY3 = sys.version_info[0] == 3
 
@@ -81,8 +81,10 @@ class test_Proxy(Case):
         with self.assertRaises(AttributeError):
             x.__dict__
 
+    @skip.if_python3()
     def test_unicode(self):
 
+        @python_2_unicode_compatible
         class X(object):
 
             def __unicode__(self):

+ 9 - 1
celery/tests/utils/test_saferepr.py

@@ -5,7 +5,9 @@ import re
 from decimal import Decimal
 from pprint import pprint
 
-from celery.five import items, long_t, text_t, values
+from celery.five import (
+    items, long_t, python_2_unicode_compatible, text_t, values,
+)
 
 from celery.utils.saferepr import saferepr
 
@@ -73,6 +75,7 @@ class list2(list):
     pass
 
 
+@python_2_unicode_compatible
 class list3(list):
 
     def __repr__(self):
@@ -83,6 +86,7 @@ class tuple2(tuple):
     pass
 
 
+@python_2_unicode_compatible
 class tuple3(tuple):
 
     def __repr__(self):
@@ -93,6 +97,7 @@ class set2(set):
     pass
 
 
+@python_2_unicode_compatible
 class set3(set):
 
     def __repr__(self):
@@ -103,6 +108,7 @@ class frozenset2(frozenset):
     pass
 
 
+@python_2_unicode_compatible
 class frozenset3(frozenset):
 
     def __repr__(self):
@@ -113,12 +119,14 @@ class dict2(dict):
     pass
 
 
+@python_2_unicode_compatible
 class dict3(dict):
 
     def __repr__(self):
         return dict.__repr__(self)
 
 
+@python_2_unicode_compatible
 class Unorderable:
 
     def __repr__(self):

+ 2 - 1
celery/tests/worker/test_loops.py

@@ -9,7 +9,7 @@ from celery.bootsteps import CLOSE, RUN
 from celery.exceptions import (
     InvalidTaskError, WorkerLostError, WorkerShutdown, WorkerTerminate,
 )
-from celery.five import Empty
+from celery.five import Empty, python_2_unicode_compatible
 from celery.platforms import EX_FAILURE
 from celery.worker import state
 from celery.worker.consumer import Consumer
@@ -18,6 +18,7 @@ from celery.worker.loops import _quick_drain, asynloop, synloop
 from celery.tests.case import AppCase, Mock, task_message_from_sig
 
 
+@python_2_unicode_compatible
 class PromiseEqual(object):
 
     def __init__(self, fun, *args, **kwargs):

+ 5 - 2
celery/utils/dispatch/saferef.py

@@ -11,6 +11,8 @@ import sys
 import traceback
 import weakref
 
+from celery.five import python_2_unicode_compatible
+
 __all__ = ['safe_ref']
 
 PY3 = sys.version_info[0] == 3
@@ -42,6 +44,7 @@ def safe_ref(target, on_delete=None):  # pragma: no cover
         return weakref.ref(target)
 
 
+@python_2_unicode_compatible
 class BoundMethodWeakref(object):  # pragma: no cover
     """'Safe' and reusable weak references to instance methods.
 
@@ -167,14 +170,14 @@ class BoundMethodWeakref(object):  # pragma: no cover
     calculate_key = classmethod(calculate_key)
 
     def __str__(self):
-        """Give a friendly representation of the object"""
         return '{0}( {1}.{2} )'.format(
             type(self).__name__,
             self.self_name,
             self.fun_name,
         )
 
-    __repr__ = __str__
+    def __repr__(self):
+        return str(self)
 
     def __bool__(self):
         """Whether we are still a valid reference"""

+ 13 - 14
celery/utils/dispatch/signal.py

@@ -3,12 +3,13 @@
 from __future__ import absolute_import, unicode_literals
 
 import weakref
-from . import saferef
 
-from celery.five import range, text_t
+from celery.five import python_2_unicode_compatible, range, text_t
 from celery.local import PromiseProxy, Proxy
 from celery.utils.log import get_logger
 
+from . import saferef
+
 __all__ = ['Signal']
 
 logger = get_logger(__name__)
@@ -27,23 +28,20 @@ def _make_id(target):  # pragma: no cover
     return id(target)
 
 
+@python_2_unicode_compatible
 class Signal(object):  # pragma: no cover
-    """Base class for all signals
-
+    """Observer pattern implementation.
 
-    .. attribute:: receivers
-        Internal attribute, holds a dictionary of
-        `{receiverkey (id): weakref(receiver)}` mappings.
+    :param providing_args: A list of the arguments this signal can pass
+        along in a :meth:`send` call.
 
     """
 
-    def __init__(self, providing_args=None):
-        """Create a new signal.
-
-        :param providing_args: A list of the arguments this signal can pass
-            along in a :meth:`send` call.
+    #: Holds a dictionary of
+    #: ``{receiverkey (id): weakref(receiver)}`` mappings.
+    receivers = None
 
-        """
+    def __init__(self, providing_args=None):
         self.receivers = []
         if providing_args is None:
             providing_args = []
@@ -214,4 +212,5 @@ class Signal(object):  # pragma: no cover
     def __repr__(self):
         return '<Signal: {0}>'.format(type(self).__name__)
 
-    __str__ = __repr__
+    def __str__(self):
+        return repr(self)

+ 3 - 0
celery/utils/mail.py

@@ -15,6 +15,8 @@ import warnings
 
 from email.mime.text import MIMEText
 
+from celery.five import python_2_unicode_compatible
+
 from .functional import maybe_list
 
 try:
@@ -39,6 +41,7 @@ class SendmailWarning(UserWarning):
     """Problem happened while sending the email message."""
 
 
+@python_2_unicode_compatible
 class Message(object):
 
     def __init__(self, to=None, sender=None, subject=None,

+ 3 - 2
celery/utils/serialization.py

@@ -8,7 +8,7 @@
 """
 from __future__ import absolute_import, unicode_literals
 
-from celery.five import module_name_t
+from celery.five import bytes_if_py2, python_2_unicode_compatible
 
 from base64 import b64encode as base64encode, b64decode as base64decode
 from inspect import getmro
@@ -36,7 +36,7 @@ except NameError:  # pragma: no cover
 
 
 def subclass_exception(name, parent, module):  # noqa
-    return type(module_name_t(name), (parent,), {'__module__': module})
+    return type(bytes_if_py2(name), (parent,), {'__module__': module})
 
 
 def find_pickleable_exception(exc, loads=pickle.loads,
@@ -79,6 +79,7 @@ def create_exception_cls(name, module, parent=None):
     return subclass_exception(name, parent, module)
 
 
+@python_2_unicode_compatible
 class UnpickleableExceptionWrapper(Exception):
     """Wraps unpickleable exceptions.
 

+ 4 - 5
celery/utils/term.py

@@ -13,7 +13,8 @@ import platform
 from functools import reduce
 
 from kombu.utils.encoding import safe_str
-from celery.five import string
+
+from celery.five import python_2_unicode_compatible, string
 
 __all__ = ['colored']
 
@@ -29,6 +30,7 @@ def fg(s):
     return COLOR_SEQ % s
 
 
+@python_2_unicode_compatible
 class colored(object):
     """Terminal colored text.
 
@@ -80,15 +82,12 @@ class colored(object):
             prefix = self.op
         return ''.join((string(prefix), string(reduce(self._add, self.s))))
 
-    def __unicode__(self):
+    def __str__(self):
         suffix = ''
         if self.enabled:
             suffix = RESET_SEQ
         return string(''.join((self.embed(), string(suffix))))
 
-    def __str__(self):
-        return safe_str(self.__unicode__())
-
     def node(self, s, op):
         return self.__class__(enabled=self.enabled, op=op, *s)
 

+ 6 - 3
celery/utils/threads.py

@@ -17,10 +17,12 @@ import traceback
 from contextlib import contextmanager
 
 from celery.local import Proxy
-from celery.five import THREAD_TIMEOUT_MAX, items
+from celery.five import THREAD_TIMEOUT_MAX, items, python_2_unicode_compatible
 
-__all__ = ['bgThread', 'Local', 'LocalStack', 'LocalManager',
-           'get_ident', 'default_socket_timeout']
+__all__ = [
+    'bgThread', 'Local', 'LocalStack', 'LocalManager',
+    'get_ident', 'default_socket_timeout',
+]
 
 USE_FAST_LOCALS = os.environ.get('USE_FAST_LOCALS')
 PY3 = sys.version_info[0] == 3
@@ -255,6 +257,7 @@ class _LocalStack(object):
             return None
 
 
+@python_2_unicode_compatible
 class LocalManager(object):
     """Local objects cannot manage themselves. For that you need a local
     manager.  You can pass a local manager multiple locals or add them

+ 10 - 6
celery/utils/timeutils.py

@@ -20,17 +20,19 @@ from kombu.utils import cached_property, reprcall
 
 from pytz import timezone as _timezone, AmbiguousTimeError, FixedOffset
 
-from celery.five import string_t
+from celery.five import python_2_unicode_compatible, string_t
 
 from .functional import dictfilter
 from .iso8601 import parse_iso8601
 from .text import pluralize
 
-__all__ = ['LocalTimezone', 'timezone', 'maybe_timedelta',
-           'delta_resolution', 'remaining', 'rate', 'weekday',
-           'humanize_seconds', 'maybe_iso8601', 'is_naive', 'make_aware',
-           'localize', 'to_utc', 'maybe_make_aware', 'ffwd', 'utcoffset',
-           'adjust_timestamp', 'maybe_s_to_ms']
+__all__ = [
+    'LocalTimezone', 'timezone', 'maybe_timedelta',
+    'delta_resolution', 'remaining', 'rate', 'weekday',
+    'humanize_seconds', 'maybe_iso8601', 'is_naive', 'make_aware',
+    'localize', 'to_utc', 'maybe_make_aware', 'ffwd', 'utcoffset',
+    'adjust_timestamp', 'maybe_s_to_ms',
+]
 
 PY3 = sys.version_info[0] == 3
 PY33 = sys.version_info >= (3, 3)
@@ -54,6 +56,7 @@ ZERO = timedelta(0)
 _local_timezone = None
 
 
+@python_2_unicode_compatible
 class LocalTimezone(tzinfo):
     """Local time implementation taken from Python's docs.
 
@@ -313,6 +316,7 @@ def maybe_make_aware(dt, tz=None):
     )
 
 
+@python_2_unicode_compatible
 class ffwd(object):
     """Version of relativedelta that only supports addition."""
 

+ 2 - 1
celery/worker/__init__.py

@@ -30,7 +30,7 @@ from celery import signals
 from celery.exceptions import (
     ImproperlyConfigured, WorkerTerminate, TaskRevokedError,
 )
-from celery.five import string_t, values
+from celery.five import python_2_unicode_compatible, string_t, values
 from celery.platforms import EX_FAILURE, create_pidlock
 from celery.utils import default_nodename, worker_direct
 from celery.utils.imports import reload_from_cwd
@@ -64,6 +64,7 @@ def str_to_list(s):
     return s
 
 
+@python_2_unicode_compatible
 class WorkController(object):
     """Unmanaged worker instance."""
     app = None

+ 2 - 1
celery/worker/consumer/consumer.py

@@ -20,7 +20,6 @@ from time import sleep
 from billiard.common import restart_state
 from billiard.exceptions import RestartFreqExceeded
 from kombu.async.semaphore import DummyLock
-from kombu.five import buffer_t, items
 from kombu.syn import _detect_environment
 from kombu.utils.encoding import safe_repr, bytes_t
 from kombu.utils.limits import TokenBucket
@@ -30,6 +29,7 @@ from celery import bootsteps
 from celery import signals
 from celery.app.trace import build_tracer
 from celery.exceptions import InvalidTaskError, NotRegistered
+from celery.five import buffer_t, items, python_2_unicode_compatible
 from celery.utils import gethostname
 from celery.utils.functional import noop
 from celery.utils.log import get_logger
@@ -120,6 +120,7 @@ def dump_body(m, body):
                                len(m.body))
 
 
+@python_2_unicode_compatible
 class Consumer(object):
 
     Strategies = dict

+ 2 - 1
celery/worker/request.py

@@ -25,7 +25,7 @@ from celery.exceptions import (
     SoftTimeLimitExceeded, TimeLimitExceeded,
     WorkerLostError, Terminated, Retry, Reject,
 )
-from celery.five import string
+from celery.five import python_2_unicode_compatible, string
 from celery.platforms import signals as _signals
 from celery.utils import cached_property, gethostname
 from celery.utils.functional import noop
@@ -64,6 +64,7 @@ task_ready = state.task_ready
 revoked_tasks = state.revoked
 
 
+@python_2_unicode_compatible
 class Request(object):
     """A request for task execution."""
     acknowledged = False

+ 6 - 1
funtests/stress/stress/data.py

@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import, unicode_literals
 
+from celery.five import python_2_unicode_compatible
+
 try:
     import simplejson as json
 except ImportError:
@@ -50,6 +52,7 @@ def jsonable(cls):
 
 
 @jsonable
+@python_2_unicode_compatible
 class Data(object):
 
     def __init__(self, label, data):
@@ -61,6 +64,9 @@ class Data(object):
             self.label, humanbytes(len(self.data)),
         )
 
+    def __repr__(self):
+        return str(self)
+
     def __to_json__(self):
         return json_reduce(self, {'label': self.label, 'data': self.data})
 
@@ -70,7 +76,6 @@ class Data(object):
 
     def __reduce__(self):
         return Data, (self.label, self.data)
-    __unicode__ = __repr__ = __str__
 
 BIG = Data('BIG', 'x' * 2 ** 20 * 8)
 SMALL = Data('SMALL', 'e' * 1024)