import sys import types from urllib.parse import urljoin as _urljoin import django.core.files.storage import django.utils.encoding import django.utils.translation from django.urls import ( NoReverseMatch, Resolver404, clear_url_caches, get_resolver, resolve, reverse, reverse_lazy, ) from django.utils.module_loading import import_string # --------------------------------------------------------------------------- # django.utils.translation # --------------------------------------------------------------------------- django.utils.translation.ugettext = ( django.utils.translation.gettext ) django.utils.translation.ugettext_lazy = ( django.utils.translation.gettext_lazy ) django.utils.translation.ugettext_noop = ( django.utils.translation.gettext_noop ) django.utils.translation.force_text = ( django.utils.encoding.force_str ) # --------------------------------------------------------------------------- # django.utils.encoding # --------------------------------------------------------------------------- # force_text() was renamed to force_str() django.utils.encoding.force_text = ( django.utils.encoding.force_str ) # Python 2 is no longer supported. def python_2_unicode_compatible(cls): return cls django.utils.encoding.python_2_unicode_compatible = ( python_2_unicode_compatible ) # --------------------------------------------------------------------------- # django.core.files.storage # --------------------------------------------------------------------------- def get_storage_class(import_path=None): if import_path is None: return django.core.files.storage.storages[ "default" ].__class__ return import_string(import_path) django.core.files.storage.get_storage_class = ( get_storage_class ) # --------------------------------------------------------------------------- # django.utils.six.moves.urllib.parse # --------------------------------------------------------------------------- from urllib.parse import ( urljoin as _urljoin, urlparse as _urlparse, ) def urljoin(base, url, allow_fragments=True): """ Compatibility implementation for legacy Django packages. """ return _urljoin( base, url, allow_fragments=allow_fragments, ) def urlparse(url, scheme='', allow_fragments=True): """ Compatibility implementation for legacy Django packages. """ return _urlparse( url, scheme=scheme, allow_fragments=allow_fragments, ) # --------------------------------------------------------------------------- # django.utils.six # --------------------------------------------------------------------------- # Django removed django.utils.six, but old django-cms/aldryn packages # still import it. Expose the real six package under the old namespace. import six sys.modules["django.utils.six"] = six sys.modules["django.utils.six.moves"] = six.moves sys.modules["django.utils.six.moves.urllib"] = six.moves.urllib sys.modules["django.utils.six.moves.urllib.parse"] = six.moves.urllib.parse # --------------------------------------------------------------------------- # django.core.urlresolvers # # Compatibility for legacy Django packages such as django-cms 3.2.5 # --------------------------------------------------------------------------- django_core_urlresolvers = types.ModuleType( "django.core.urlresolvers" ) # URL functions django_core_urlresolvers.reverse = reverse django_core_urlresolvers.resolve = resolve django_core_urlresolvers.get_resolver = get_resolver django_core_urlresolvers.clear_url_caches = ( clear_url_caches ) django_core_urlresolvers.reverse_lazy = reverse_lazy # URL exceptions django_core_urlresolvers.NoReverseMatch = ( NoReverseMatch ) django_core_urlresolvers.Resolver404 = ( Resolver404 ) # LocaleRegexURLResolver # # Removed from modern Django. try: from django.urls import LocaleRegexURLResolver django_core_urlresolvers.LocaleRegexURLResolver = ( LocaleRegexURLResolver ) except ImportError: class LocaleRegexURLResolver: pass django_core_urlresolvers.LocaleRegexURLResolver = ( LocaleRegexURLResolver ) # Register the legacy module exactly once. sys.modules["django.core.urlresolvers"] = ( django_core_urlresolvers ) # --------------------------------------------------------------------------- # distutils.version.LooseVersion # # distutils was removed from Python 3.12. # Legacy django-cms still imports LooseVersion. # --------------------------------------------------------------------------- from packaging.version import Version class LooseVersion: """ Compatibility replacement for distutils.version.LooseVersion sufficient for legacy django-cms version comparisons. """ def __init__(self, version): self.version = str(version) self._version = Version(self.version) def __str__(self): return self.version def __repr__(self): return f"LooseVersion('{self.version}')" def __eq__(self, other): return self._version == _to_version(other) def __lt__(self, other): return self._version < _to_version(other) def __le__(self, other): return self._version <= _to_version(other) def __gt__(self, other): return self._version > _to_version(other) def __ge__(self, other): return self._version >= _to_version(other) def _to_version(value): if isinstance(value, LooseVersion): return value._version return Version(str(value)) distutils_module = types.ModuleType("distutils") distutils_version_module = types.ModuleType( "distutils.version" ) distutils_version_module.LooseVersion = LooseVersion distutils_module.version = distutils_version_module sys.modules["distutils"] = distutils_module sys.modules["distutils.version"] = ( distutils_version_module ) # --------------------------------------------------------------------------- # django.utils.decorators.available_attrs # --------------------------------------------------------------------------- import functools import django.utils.decorators def available_attrs(fn): """ Compatibility implementation for the old Django django.utils.decorators.available_attrs API. Modern Python's functools.update_wrapper uses WRAPPER_ASSIGNMENTS for the same purpose. """ return functools.WRAPPER_ASSIGNMENTS django.utils.decorators.available_attrs = available_attrs # --------------------------------------------------------------------------- # django.utils.lru_cache # --------------------------------------------------------------------------- django_utils_lru_cache = types.ModuleType( "django.utils.lru_cache" ) django_utils_lru_cache.lru_cache = functools.lru_cache sys.modules["django.utils.lru_cache"] = ( django_utils_lru_cache ) # --------------------------------------------------------------------------- # django.forms.fields.EMPTY_VALUES # --------------------------------------------------------------------------- import django.forms.fields django.forms.fields.EMPTY_VALUES = (None, "", [], (), {}) # --------------------------------------------------------------------------- # django.forms.ChoiceField._get_choices # --------------------------------------------------------------------------- # Removed/changed in modern Django. Legacy django-cms accesses this private # property directly. from django import forms if not hasattr(forms.ChoiceField, "_get_choices"): forms.ChoiceField._get_choices = ( forms.ChoiceField.choices.fget ) # --------------------------------------------------------------------------- # django.template.base.Context # --------------------------------------------------------------------------- # Context was moved from django.template.base to django.template.context. from django.template import Context import django.template.base if not hasattr(django.template.base, "Context"): django.template.base.Context = Context # --------------------------------------------------------------------------- # imp.reload # --------------------------------------------------------------------------- # Python 3.12+ removed the imp module. # Legacy django-cms imports reload from imp. import importlib if "imp" not in sys.modules: imp_module = types.ModuleType("imp") imp_module.reload = importlib.reload sys.modules["imp"] = imp_module # --------------------------------------------------------------------------- # django.dispatch.Signal.providing_args # --------------------------------------------------------------------------- # Modern Django removed the providing_args argument from Signal.__init__. # Legacy django-cms still passes it. from django.dispatch import Signal as DjangoSignal import django.dispatch class LegacySignal(DjangoSignal): def __init__(self, providing_args=None, *args, **kwargs): super().__init__(*args, **kwargs) django.dispatch.Signal = LegacySignal # --------------------------------------------------------------------------- # django.dispatch.Signal.providing_args # --------------------------------------------------------------------------- # Django removed the providing_args argument from Signal.__init__. # Legacy django-cms still passes it. import inspect from django.dispatch import Signal _original_signal_init = Signal.__init__ if "providing_args" not in inspect.signature( _original_signal_init ).parameters: def legacy_signal_init(self, *args, providing_args=None, **kwargs): _original_signal_init(self, *args, **kwargs) Signal.__init__ = legacy_signal_init # --------------------------------------------------------------------------- # django.db.models.OneToOneField.on_delete # --------------------------------------------------------------------------- # Django 2.0+ requires on_delete. Old django-cms creates OneToOneField # instances without specifying it. from django.db import models _original_one_to_one_init = models.OneToOneField.__init__ def legacy_one_to_one_init( self, to, on_delete=None, *args, **kwargs, ): if on_delete is None: on_delete = models.CASCADE _original_one_to_one_init( self, to, on_delete, *args, **kwargs, ) _original_foreign_key_init = models.ForeignKey.__init__ def legacy_foreign_key_init( self, to, on_delete=None, *args, **kwargs, ): if on_delete is None: on_delete = models.CASCADE _original_foreign_key_init( self, to, on_delete, *args, **kwargs, ) models.ForeignKey.__init__ = legacy_foreign_key_init models.OneToOneField.__init__ = legacy_one_to_one_init # --------------------------------------------------------------------------- # django.shortcuts.render_to_response # --------------------------------------------------------------------------- # Removed from modern Django. Legacy django-cms still imports it. from django.shortcuts import render import django.shortcuts if not hasattr(django.shortcuts, "render_to_response"): def render_to_response( template_name, context=None, content_type=None, status=None, using=None, ): request = None return render( request, template_name, context=context, content_type=content_type, status=status, using=using, ) django.shortcuts.render_to_response = render_to_response # --------------------------------------------------------------------------- # django.conf.urls.url # --------------------------------------------------------------------------- # Removed from modern Django. Legacy django-cms uses url(). from django.conf import urls as django_conf_urls from django.urls import re_path if not hasattr(django_conf_urls, "url"): django_conf_urls.url = re_path # --------------------------------------------------------------------------- # django-cms 3.2.x compatibility: CharField positional arguments # --------------------------------------------------------------------------- from django import forms _original_charfield_init = forms.CharField.__init__ def legacy_charfield_init(self, *args, **kwargs): # Old django-cms passes: # CharField(max_length, min_length, ...) # # Modern Django expects: # CharField(max_length=..., min_length=...) if len(args) > 2: raise TypeError( "CharField received more than two positional arguments: " f"{args!r}" ) if len(args) >= 1 and "max_length" not in kwargs: kwargs["max_length"] = args[0] if len(args) >= 2 and "min_length" not in kwargs: kwargs["min_length"] = args[1] _original_charfield_init(self, **kwargs) forms.CharField.__init__ = legacy_charfield_init import sys import types from collections.abc import ( MutableSequence, MutableMapping, MutableSet, Sequence, Mapping, Set, ) def install_backport_collections_shim(): module = types.ModuleType("backport_collections") module.MutableSequence = MutableSequence module.MutableMapping = MutableMapping module.MutableSet = MutableSet module.Sequence = Sequence module.Mapping = Mapping module.Set = Set sys.modules["backport_collections"] = module install_backport_collections_shim() import django.template.library from django.template.library import Library if not hasattr(Library, "assignment_tag"): Library.assignment_tag = Library.simple_tag # --------------------------------------------------------------------------- # django-filer / old cmsplugin-filer-image compatibility # --------------------------------------------------------------------------- try: import filer.utils.compatibility as filer_compat if not hasattr(filer_compat, "python_2_unicode_compatible"): def python_2_unicode_compatible(cls): return cls filer_compat.python_2_unicode_compatible = python_2_unicode_compatible except ImportError: pass