From cbf52a61af4d7ffdb29736a00ac4bd2013d2b5da Mon Sep 17 00:00:00 2001 From: Tomas Peterka Date: Sun, 21 Oct 2012 15:22:55 +0200 Subject: [PATCH] Porting to Python 3.2 Removed compatibility_tools (because we aim to Python 2.7 and Python 3.2) Performed 2to3 and modified Usage of django.utils.six which adds dependency on Django >= 1.4.2 --- polymorphic/__init__.py | 21 ++--- polymorphic/admin.py | 7 +- polymorphic/base.py | 5 +- polymorphic/compatibility_tools.py | 91 ------------------- polymorphic/manager.py | 3 +- polymorphic/polymorphic_model.py | 13 +-- polymorphic/query.py | 20 ++-- polymorphic/query_translate.py | 10 +- polymorphic/showfields.py | 16 ++-- .../templatetags/polymorphic_admin_tags.py | 3 +- polymorphic/tools_for_tests.py | 5 +- setup.py | 1 + 12 files changed, 56 insertions(+), 139 deletions(-) delete mode 100644 polymorphic/compatibility_tools.py diff --git a/polymorphic/__init__.py b/polymorphic/__init__.py index d71f106..c598b84 100644 --- a/polymorphic/__init__.py +++ b/polymorphic/__init__.py @@ -6,23 +6,22 @@ Copyright: This code and affiliated files are (C) by Bert Constantin and individual contributors. Please see LICENSE and AUTHORS for more information. """ +from __future__ import absolute_import import django -from polymorphic_model import PolymorphicModel -from manager import PolymorphicManager -from query import PolymorphicQuerySet -from query_translate import translate_polymorphic_Q_object -from showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent -from showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility +from .polymorphic_model import PolymorphicModel +from .manager import PolymorphicManager +from .query import PolymorphicQuerySet +from .query_translate import translate_polymorphic_Q_object +from .showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent +from .showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility # Monkey-patch Django < 1.5 to allow ContentTypes for proxy models. -if django.VERSION[:2] < (1, 5): +if django.VERSION[:2] < (1, 5): from django.contrib.contenttypes.models import ContentTypeManager - from django.utils.encoding import smart_unicode + from django.utils.encoding import smart_text def get_for_model(self, model, for_concrete_model=True): - from django.utils.encoding import smart_unicode - if for_concrete_model: model = model._meta.concrete_model elif model._deferred: @@ -36,7 +35,7 @@ if django.VERSION[:2] < (1, 5): ct, created = self.get_or_create( app_label = opts.app_label, model = opts.object_name.lower(), - defaults = {'name': smart_unicode(opts.verbose_name_raw)}, + defaults = {'name': smart_text(opts.verbose_name_raw)}, ) self._add_to_cache(self.db, ct) diff --git a/polymorphic/admin.py b/polymorphic/admin.py index 442bf70..7da7fec 100644 --- a/polymorphic/admin.py +++ b/polymorphic/admin.py @@ -13,7 +13,8 @@ from django.core.urlresolvers import RegexURLResolver from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response from django.template.context import RequestContext -from django.utils.encoding import force_unicode +from django.utils import six +from django.utils.encoding import force_text from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ @@ -315,7 +316,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin): opts = self.model._meta context = { - 'title': _('Add %s') % force_unicode(opts.verbose_name), + 'title': _('Add %s') % force_text(opts.verbose_name), 'adminform': adminForm, 'is_popup': "_popup" in request.REQUEST, 'media': mark_safe(media), @@ -482,7 +483,7 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin): # By not declaring the fields/form in the base class, # get_form() will populate the form with all available fields. form = self.get_form(request, obj, exclude=exclude) - subclass_fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) + subclass_fields = list(six.iterkeys(form.base_fields)) + list(self.get_readonly_fields(request, obj)) # Find which fields are not part of the common fields. for fieldset in self.base_fieldsets: diff --git a/polymorphic/base.py b/polymorphic/base.py index 4004407..9d959e6 100644 --- a/polymorphic/base.py +++ b/polymorphic/base.py @@ -2,6 +2,7 @@ """ PolymorphicModel Meta Class Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ """ +from __future__ import absolute_import import sys import inspect @@ -10,8 +11,8 @@ from django.db import models from django.db.models.base import ModelBase from django.db.models.manager import ManagerDescriptor -from manager import PolymorphicManager -from query import PolymorphicQuerySet +from .manager import PolymorphicManager +from .query import PolymorphicQuerySet # PolymorphicQuerySet Q objects (and filter()) support these additional key words. # These are forbidden as field names (a descriptive exception is raised) diff --git a/polymorphic/compatibility_tools.py b/polymorphic/compatibility_tools.py deleted file mode 100644 index efa23ee..0000000 --- a/polymorphic/compatibility_tools.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Compatibility layer for Python 2.4 -================================== - -Currently implements: - -+ collections.defaultdict -+ compat_partition (compatibility replacement for str.partition) - -""" -try: - assert False - from collections import defaultdict -except: - class defaultdict(dict): - def __init__(self, default_factory=None, *a, **kw): - if (default_factory is not None and - not hasattr(default_factory, '__call__')): - raise TypeError('first argument must be callable') - dict.__init__(self, *a, **kw) - self.default_factory = default_factory - - def __getitem__(self, key): - try: - return dict.__getitem__(self, key) - except KeyError: - return self.__missing__(key) - - def __missing__(self, key): - if self.default_factory is None: - raise KeyError(key) - self[key] = value = self.default_factory() - return value - - def __reduce__(self): - if self.default_factory is None: - args = tuple() - else: - args = self.default_factory, - return type(self), args, None, None, self.items() - - def copy(self): - return self.__copy__() - - def __copy__(self): - return type(self)(self.default_factory, self) - - def __deepcopy__(self, memo): - import copy - return type(self)(self.default_factory, copy.deepcopy(self.items())) - - def __repr__(self): - return 'defaultdict(%s, %s)' % (self.default_factory, dict.__repr__(self)) - - -if getattr(str, 'partition', None): - def compat_partition(s, sep): - return s.partition(sep) -else: - """ from: - http://mail.python.org/pipermail/python-dev/2005-September/055962.html - """ - def compat_partition(s, sep, at_sep=1): - """ Returns a three element tuple, (head, sep, tail) where: - - head + sep + tail == s - sep == '' or sep is t - bool(sep) == (t in s) # sep indicates if the string was found - """ - if not isinstance(sep, basestring) or not sep: - raise ValueError('partititon argument must be a non-empty string') - if at_sep == 0: - result = ('', '', s) - else: - if at_sep > 0: - parts = s.split(sep, at_sep) - if len(parts) <= at_sep: - result = (s, '', '') - else: - result = (sep.join(parts[:at_sep]), sep, parts[at_sep]) - else: - parts = s.rsplit(sep, at_sep) - if len(parts) <= at_sep: - result = ('', '', s) - else: - result = (parts[0], sep, sep.join(parts[1:])) - assert len(result) == 3 - assert ''.join(result) == s - assert result[1] == '' or result[1] is sep - return result diff --git a/polymorphic/manager.py b/polymorphic/manager.py index 83db77b..3e0e6d8 100644 --- a/polymorphic/manager.py +++ b/polymorphic/manager.py @@ -2,6 +2,7 @@ """ PolymorphicManager Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ """ +from __future__ import unicode_literals import warnings from django.db import models from polymorphic.query import PolymorphicQuerySet @@ -42,4 +43,4 @@ class PolymorphicManager(models.Manager): return getattr(self.get_query_set(), name) def __unicode__(self): - return u'%s (PolymorphicManager) using %s' % (self.__class__.__name__, self.queryset_class.__name__) + return '%s (PolymorphicManager) using %s' % (self.__class__.__name__, self.queryset_class.__name__) diff --git a/polymorphic/polymorphic_model.py b/polymorphic/polymorphic_model.py index d7e84c5..1a819d2 100644 --- a/polymorphic/polymorphic_model.py +++ b/polymorphic/polymorphic_model.py @@ -13,20 +13,22 @@ Copyright: This code and affiliated files are (C) by Bert Constantin and individual contributors. Please see LICENSE and AUTHORS for more information. """ +from __future__ import absolute_import from django.db import models from django.contrib.contenttypes.models import ContentType from django import VERSION as django_VERSION +from django.utils import six -from base import PolymorphicModelBase -from manager import PolymorphicManager -from query_translate import translate_polymorphic_Q_object +from .base import PolymorphicModelBase +from .manager import PolymorphicManager +from .query_translate import translate_polymorphic_Q_object ################################################################################### ### PolymorphicModel -class PolymorphicModel(models.Model): +class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)): """ Abstract base class that provides polymorphic behaviour for any model directly or indirectly derived from it. @@ -44,7 +46,6 @@ class PolymorphicModel(models.Model): super(YourClass,self).save(*args,**kwargs) """ - __metaclass__ = PolymorphicModelBase # for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing) polymorphic_model_marker = True @@ -168,7 +169,7 @@ class PolymorphicModel(models.Model): subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models() from django.db.models.fields.related import SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor - for name, model in subclasses_and_superclasses_accessors.iteritems(): + for name, model in subclasses_and_superclasses_accessors.items(): orig_accessor = getattr(self.__class__, name, None) if type(orig_accessor) in [SingleRelatedObjectDescriptor, ReverseSingleRelatedObjectDescriptor]: #print >>sys.stderr, '---------- replacing', name, orig_accessor, '->', model diff --git a/polymorphic/query.py b/polymorphic/query.py index bd8aa55..22cadf0 100644 --- a/polymorphic/query.py +++ b/polymorphic/query.py @@ -2,14 +2,16 @@ """ QuerySet for PolymorphicModel Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ """ +from __future__ import absolute_import -from compatibility_tools import defaultdict +from collections import defaultdict from django.db.models.query import QuerySet from django.contrib.contenttypes.models import ContentType +from django.utils import six -from query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args -from query_translate import translate_polymorphic_field_path +from .query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args +from .query_translate import translate_polymorphic_field_path # chunk-size: maximum number of objects requested per db-request # by the polymorphic queryset.iterator() implementation; we use the same chunk size as Django @@ -90,7 +92,7 @@ class PolymorphicQuerySet(QuerySet): Modifies kwargs if needed (these are Aggregate objects, we translate the lookup member variable)""" for a in args: assert not '___' in a.lookup, 'PolymorphicModel: annotate()/aggregate(): ___ model lookup supported for keyword arguments only' - for a in kwargs.values(): + for a in six.itervalues(kwargs): a.lookup = translate_polymorphic_field_path(self.model, a.lookup) def annotate(self, *args, **kwargs): @@ -207,12 +209,12 @@ class PolymorphicQuerySet(QuerySet): real_object = transmogrify(real_class, real_object) if self.query.aggregates: - for anno_field_name in self.query.aggregates.keys(): + for anno_field_name in six.iterkeys(self.query.aggregates): attr = getattr(base_result_objects_by_id[o_pk], anno_field_name) setattr(real_object, anno_field_name, attr) if self.query.extra_select: - for select_field_name in self.query.extra_select.keys(): + for select_field_name in six.iterkeys(self.query.extra_select): attr = getattr(base_result_objects_by_id[o_pk], select_field_name) setattr(real_object, select_field_name, attr) @@ -223,13 +225,13 @@ class PolymorphicQuerySet(QuerySet): # set polymorphic_annotate_names in all objects (currently just used for debugging/printing) if self.query.aggregates: - annotate_names = self.query.aggregates.keys() # get annotate field list + annotate_names = six.iterkeys(self.query.aggregates) # get annotate field list for real_object in resultlist: real_object.polymorphic_annotate_names = annotate_names # set polymorphic_extra_select_names in all objects (currently just used for debugging/printing) if self.query.extra_select: - extra_select_names = self.query.extra_select.keys() # get extra select field list + extra_select_names = six.iterkeys(self.query.extra_select) # get extra select field list for real_object in resultlist: real_object.polymorphic_extra_select_names = extra_select_names @@ -264,7 +266,7 @@ class PolymorphicQuerySet(QuerySet): for i in range(Polymorphic_QuerySet_objects_per_request): try: - o = base_iter.next() + o = next(base_iter) base_result_objects.append(o) except StopIteration: reached_end = True diff --git a/polymorphic/query_translate.py b/polymorphic/query_translate.py index e42e349..fe300b8 100644 --- a/polymorphic/query_translate.py +++ b/polymorphic/query_translate.py @@ -2,11 +2,13 @@ """ PolymorphicQuerySet support functions Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ """ +from __future__ import absolute_import from django.db import models from django.contrib.contenttypes.models import ContentType from django.db.models import Q -from compatibility_tools import compat_partition + +from functools import reduce ################################################################################### @@ -124,7 +126,7 @@ def translate_polymorphic_field_path(queryset_model, field_path): into modela__modelb__modelc__field3. Returns: translated path (unchanged, if no translation needed) """ - classname, sep, pure_field_path = compat_partition(field_path, '___') + classname, sep, pure_field_path = field_path.partition('___') if not sep: return field_path assert classname, 'PolymorphicModel: %s: bad field specification' % field_path @@ -136,7 +138,7 @@ def translate_polymorphic_field_path(queryset_model, field_path): if '__' in classname: # the user has app label prepended to class name via __ => use Django's get_model function - appname, sep, classname = compat_partition(classname, '__') + appname, sep, classname = classname.partition('__') model = models.get_model(appname, classname) assert model, 'PolymorphicModel: model %s (in app %s) not found!' % (model.__name__, appname) if not issubclass(model, queryset_model): @@ -211,7 +213,7 @@ def _create_model_filter_Q(modellist, not_instance_of=False): if not modellist: return None - from polymorphic_model import PolymorphicModel + from .polymorphic_model import PolymorphicModel if type(modellist) != list and type(modellist) != tuple: if issubclass(modellist, PolymorphicModel): diff --git a/polymorphic/showfields.py b/polymorphic/showfields.py index 67b84a9..1a279d6 100644 --- a/polymorphic/showfields.py +++ b/polymorphic/showfields.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from django.db import models - +from django.utils import six class ShowFieldBase(object): """ base class for the ShowField... model mixins, does the work """ @@ -33,12 +33,12 @@ class ShowFieldBase(object): out += content.__class__.__name__ elif issubclass(field_type, models.ManyToManyField): out += '%d' % content.count() - elif type(content) in (int, long): - out += unicode(content) + elif isinstance(content, six.integer_types): + out += str(content) elif content is None: out += 'None' else: - txt = unicode(content) + txt = str(content) if len(txt) > self.polymorphic_showfield_max_field_width: txt = txt[:self.polymorphic_showfield_max_field_width - 2] + '..' out += '"' + txt + '"' @@ -58,7 +58,7 @@ class ShowFieldBase(object): # if this is the standard primary key named "id", print it as we did with older versions of django_polymorphic if field.primary_key and field.name == 'id' and type(field) == models.AutoField: - out += ' ' + unicode(getattr(self, field.name)) + out += ' ' + str(getattr(self, field.name)) # otherwise, display it just like all other fields (with correct type, shortened content etc.) else: @@ -108,11 +108,11 @@ class ShowFieldBase(object): indent = len(self.__class__.__name__) + 5 indentstr = ''.rjust(indent) - out = u'' + out = '' xpos = 0 possible_line_break_pos = None - for i in xrange(len(parts)): + for i in range(len(parts)): new_section, p, separator = parts[i] final = (i == len(parts) - 1) if not final: @@ -139,7 +139,7 @@ class ShowFieldBase(object): if not new_section: possible_line_break_pos = len(out) - return u'<' + out + '>' + return '<' + out + '>' class ShowFieldType(ShowFieldBase): diff --git a/polymorphic/templatetags/polymorphic_admin_tags.py b/polymorphic/templatetags/polymorphic_admin_tags.py index bf645bf..8fc5dca 100644 --- a/polymorphic/templatetags/polymorphic_admin_tags.py +++ b/polymorphic/templatetags/polymorphic_admin_tags.py @@ -1,4 +1,5 @@ from django.template import Library, Node, TemplateSyntaxError +from django.utils import six register = Library() @@ -31,7 +32,7 @@ class BreadcrumbScope(Node): # Instead, have an assignment tag that inserts that in the template. base_opts = self.base_opts.resolve(context) new_vars = {} - if base_opts and not isinstance(base_opts, basestring): + if base_opts and not isinstance(base_opts, six.string_types): new_vars = { 'app_label': base_opts.app_label, # What this is all about 'opts': base_opts, diff --git a/polymorphic/tools_for_tests.py b/polymorphic/tools_for_tests.py index 9c75bb8..fd18230 100644 --- a/polymorphic/tools_for_tests.py +++ b/polymorphic/tools_for_tests.py @@ -7,13 +7,13 @@ import uuid from django import forms from django.db import models from django.utils.encoding import smart_unicode - +from django.utils import six class UUIDVersionError(Exception): pass -class UUIDField(models.CharField): +class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)): """Encode and stores a Python uuid.UUID in a manner that is appropriate for the given datatabase that we are using. @@ -22,7 +22,6 @@ class UUIDField(models.CharField): This class supports type 1, 2, 4, and 5 UUID's. """ - __metaclass__ = models.SubfieldBase _CREATE_COLUMN_TYPES = { 'postgresql_psycopg2': 'uuid', diff --git a/setup.py b/setup.py index 887ce33..dda5080 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Libraries :: Python Modules', ] )