diff options
Diffstat (limited to 'lib/python2.7/site-packages/south/modelsinspector.py')
-rw-r--r-- | lib/python2.7/site-packages/south/modelsinspector.py | 464 |
1 files changed, 0 insertions, 464 deletions
diff --git a/lib/python2.7/site-packages/south/modelsinspector.py b/lib/python2.7/site-packages/south/modelsinspector.py deleted file mode 100644 index 13e493d..0000000 --- a/lib/python2.7/site-packages/south/modelsinspector.py +++ /dev/null @@ -1,464 +0,0 @@ -""" -Like the old south.modelsparser, but using introspection where possible -rather than direct inspection of models.py. -""" - -from __future__ import print_function - -import datetime -import re -import decimal - -from south.utils import get_attribute, auto_through -from south.utils.py3 import text_type - -from django.db import models -from django.db.models.base import ModelBase, Model -from django.db.models.fields import NOT_PROVIDED -from django.conf import settings -from django.utils.functional import Promise -from django.contrib.contenttypes import generic -from django.utils.datastructures import SortedDict -from django.utils import datetime_safe - -NOISY = False - -try: - from django.utils import timezone -except ImportError: - timezone = False - - -# Define any converter functions first to prevent NameErrors - -def convert_on_delete_handler(value): - django_db_models_module = 'models' # relative to standard import 'django.db' - if hasattr(models, "PROTECT"): - if value in (models.CASCADE, models.PROTECT, models.DO_NOTHING, models.SET_DEFAULT): - # straightforward functions - return '%s.%s' % (django_db_models_module, value.__name__) - else: - # This is totally dependent on the implementation of django.db.models.deletion.SET - func_name = getattr(value, '__name__', None) - if func_name == 'set_on_delete': - # we must inspect the function closure to see what parameters were passed in - closure_contents = value.__closure__[0].cell_contents - if closure_contents is None: - return "%s.SET_NULL" % (django_db_models_module) - # simple function we can perhaps cope with: - elif hasattr(closure_contents, '__call__'): - raise ValueError("South does not support on_delete with SET(function) as values.") - else: - # Attempt to serialise the value - return "%s.SET(%s)" % (django_db_models_module, value_clean(closure_contents)) - raise ValueError("%s was not recognized as a valid model deletion handler. Possible values: %s." % (value, ', '.join(f.__name__ for f in (models.CASCADE, models.PROTECT, models.SET, models.SET_NULL, models.SET_DEFAULT, models.DO_NOTHING)))) - else: - raise ValueError("on_delete argument encountered in Django version that does not support it") - -# Gives information about how to introspect certain fields. -# This is a list of triples; the first item is a list of fields it applies to, -# (note that isinstance is used, so superclasses are perfectly valid here) -# the second is a list of positional argument descriptors, and the third -# is a list of keyword argument descriptors. -# Descriptors are of the form: -# [attrname, options] -# Where attrname is the attribute on the field to get the value from, and options -# is an optional dict. -# -# The introspector uses the combination of all matching entries, in order. - -introspection_details = [ - ( - (models.Field, ), - [], - { - "null": ["null", {"default": False}], - "blank": ["blank", {"default": False, "ignore_if":"primary_key"}], - "primary_key": ["primary_key", {"default": False}], - "max_length": ["max_length", {"default": None}], - "unique": ["_unique", {"default": False}], - "db_index": ["db_index", {"default": False}], - "default": ["default", {"default": NOT_PROVIDED, "ignore_dynamics": True}], - "db_column": ["db_column", {"default": None}], - "db_tablespace": ["db_tablespace", {"default": settings.DEFAULT_INDEX_TABLESPACE}], - }, - ), - ( - (models.ForeignKey, models.OneToOneField), - [], - dict([ - ("to", ["rel.to", {}]), - ("to_field", ["rel.field_name", {"default_attr": "rel.to._meta.pk.name"}]), - ("related_name", ["rel.related_name", {"default": None}]), - ("db_index", ["db_index", {"default": True}]), - ("on_delete", ["rel.on_delete", {"default": getattr(models, "CASCADE", None), "is_django_function": True, "converter": convert_on_delete_handler, "ignore_missing": True}]) - ]) - ), - ( - (models.ManyToManyField,), - [], - { - "to": ["rel.to", {}], - "symmetrical": ["rel.symmetrical", {"default": True}], - "related_name": ["rel.related_name", {"default": None}], - "db_table": ["db_table", {"default": None}], - # TODO: Kind of ugly to add this one-time-only option - "through": ["rel.through", {"ignore_if_auto_through": True}], - }, - ), - ( - (models.DateField, models.TimeField), - [], - { - "auto_now": ["auto_now", {"default": False}], - "auto_now_add": ["auto_now_add", {"default": False}], - }, - ), - ( - (models.DecimalField, ), - [], - { - "max_digits": ["max_digits", {"default": None}], - "decimal_places": ["decimal_places", {"default": None}], - }, - ), - ( - (models.SlugField, ), - [], - { - "db_index": ["db_index", {"default": True}], - }, - ), - ( - (models.BooleanField, ), - [], - { - "default": ["default", {"default": NOT_PROVIDED, "converter": bool}], - "blank": ["blank", {"default": True, "ignore_if":"primary_key"}], - }, - ), - ( - (models.FilePathField, ), - [], - { - "path": ["path", {"default": ''}], - "match": ["match", {"default": None}], - "recursive": ["recursive", {"default": False}], - }, - ), - ( - (generic.GenericRelation, ), - [], - { - "to": ["rel.to", {}], - "symmetrical": ["rel.symmetrical", {"default": True}], - "object_id_field": ["object_id_field_name", {"default": "object_id"}], - "content_type_field": ["content_type_field_name", {"default": "content_type"}], - "blank": ["blank", {"default": True}], - }, - ), -] - -# Regexes of allowed field full paths -allowed_fields = [ - "^django\.db", - "^django\.contrib\.contenttypes\.generic", - "^django\.contrib\.localflavor", - "^django_localflavor_\w\w", -] - -# Regexes of ignored fields (custom fields which look like fields, but have no column behind them) -ignored_fields = [ - "^django\.contrib\.contenttypes\.generic\.GenericRelation", - "^django\.contrib\.contenttypes\.generic\.GenericForeignKey", -] - -# Similar, but for Meta, so just the inner level (kwds). -meta_details = { - "db_table": ["db_table", {"default_attr_concat": ["%s_%s", "app_label", "module_name"]}], - "db_tablespace": ["db_tablespace", {"default": settings.DEFAULT_TABLESPACE}], - "unique_together": ["unique_together", {"default": []}], - "index_together": ["index_together", {"default": [], "ignore_missing": True}], - "ordering": ["ordering", {"default": []}], - "proxy": ["proxy", {"default": False, "ignore_missing": True}], -} - - -def add_introspection_rules(rules=[], patterns=[]): - "Allows you to add some introspection rules at runtime, e.g. for 3rd party apps." - assert isinstance(rules, (list, tuple)) - assert isinstance(patterns, (list, tuple)) - allowed_fields.extend(patterns) - introspection_details.extend(rules) - - -def add_ignored_fields(patterns): - "Allows you to add some ignore field patterns." - assert isinstance(patterns, (list, tuple)) - ignored_fields.extend(patterns) - - -def can_ignore(field): - """ - Returns True if we know for certain that we can ignore this field, False - otherwise. - """ - full_name = "%s.%s" % (field.__class__.__module__, field.__class__.__name__) - for regex in ignored_fields: - if re.match(regex, full_name): - return True - return False - - -def can_introspect(field): - """ - Returns True if we are allowed to introspect this field, False otherwise. - ('allowed' means 'in core'. Custom fields can declare they are introspectable - by the default South rules by adding the attribute _south_introspects = True.) - """ - # Check for special attribute - if hasattr(field, "_south_introspects") and field._south_introspects: - return True - # Check it's an introspectable field - full_name = "%s.%s" % (field.__class__.__module__, field.__class__.__name__) - for regex in allowed_fields: - if re.match(regex, full_name): - return True - return False - - -def matching_details(field): - """ - Returns the union of all matching entries in introspection_details for the field. - """ - our_args = [] - our_kwargs = {} - for classes, args, kwargs in introspection_details: - if any([isinstance(field, x) for x in classes]): - our_args.extend(args) - our_kwargs.update(kwargs) - return our_args, our_kwargs - - -class IsDefault(Exception): - """ - Exception for when a field contains its default value. - """ - - -def get_value(field, descriptor): - """ - Gets an attribute value from a Field instance and formats it. - """ - attrname, options = descriptor - # If the options say it's not a attribute name but a real value, use that. - if options.get('is_value', False): - value = attrname - else: - try: - value = get_attribute(field, attrname) - except AttributeError: - if options.get("ignore_missing", False): - raise IsDefault - else: - raise - - # Lazy-eval functions get eval'd. - if isinstance(value, Promise): - value = text_type(value) - # If the value is the same as the default, omit it for clarity - if "default" in options and value == options['default']: - raise IsDefault - # If there's an ignore_if, use it - if "ignore_if" in options: - if get_attribute(field, options['ignore_if']): - raise IsDefault - # If there's an ignore_if_auto_through which is True, use it - if options.get("ignore_if_auto_through", False): - if auto_through(field): - raise IsDefault - # Some default values need to be gotten from an attribute too. - if "default_attr" in options: - default_value = get_attribute(field, options['default_attr']) - if value == default_value: - raise IsDefault - # Some are made from a formatting string and several attrs (e.g. db_table) - if "default_attr_concat" in options: - format, attrs = options['default_attr_concat'][0], options['default_attr_concat'][1:] - default_value = format % tuple(map(lambda x: get_attribute(field, x), attrs)) - if value == default_value: - raise IsDefault - # Clean and return the value - return value_clean(value, options) - - -def value_clean(value, options={}): - "Takes a value and cleans it up (so e.g. it has timezone working right)" - # Lazy-eval functions get eval'd. - if isinstance(value, Promise): - value = text_type(value) - # Callables get called. - if not options.get('is_django_function', False) and callable(value) and not isinstance(value, ModelBase): - # Datetime.datetime.now is special, as we can access it from the eval - # context (and because it changes all the time; people will file bugs otherwise). - if value == datetime.datetime.now: - return "datetime.datetime.now" - elif value == datetime.datetime.utcnow: - return "datetime.datetime.utcnow" - elif value == datetime.date.today: - return "datetime.date.today" - # In case we use Django's own now function, revert to datetime's - # original one since we'll deal with timezones on our own. - elif timezone and value == timezone.now: - return "datetime.datetime.now" - # All other callables get called. - value = value() - # Models get their own special repr() - if isinstance(value, ModelBase): - # If it's a proxy model, follow it back to its non-proxy parent - if getattr(value._meta, "proxy", False): - value = value._meta.proxy_for_model - return "orm['%s.%s']" % (value._meta.app_label, value._meta.object_name) - # As do model instances - if isinstance(value, Model): - if options.get("ignore_dynamics", False): - raise IsDefault - return "orm['%s.%s'].objects.get(pk=%r)" % (value.__class__._meta.app_label, value.__class__._meta.object_name, value.pk) - # Make sure Decimal is converted down into a string - if isinstance(value, decimal.Decimal): - value = str(value) - # in case the value is timezone aware - datetime_types = ( - datetime.datetime, - datetime.time, - datetime_safe.datetime, - ) - if (timezone and isinstance(value, datetime_types) and - getattr(settings, 'USE_TZ', False) and - value is not None and timezone.is_aware(value)): - default_timezone = timezone.get_default_timezone() - value = timezone.make_naive(value, default_timezone) - # datetime_safe has an improper repr value - if isinstance(value, datetime_safe.datetime): - value = datetime.datetime(*value.utctimetuple()[:7]) - # converting a date value to a datetime to be able to handle - # timezones later gracefully - elif isinstance(value, (datetime.date, datetime_safe.date)): - value = datetime.datetime(*value.timetuple()[:3]) - # Now, apply the converter func if there is one - if "converter" in options: - value = options['converter'](value) - # Return the final value - if options.get('is_django_function', False): - return value - else: - return repr(value) - - -def introspector(field): - """ - Given a field, introspects its definition triple. - """ - arg_defs, kwarg_defs = matching_details(field) - args = [] - kwargs = {} - # For each argument, use the descriptor to get the real value. - for defn in arg_defs: - try: - args.append(get_value(field, defn)) - except IsDefault: - pass - for kwd, defn in kwarg_defs.items(): - try: - kwargs[kwd] = get_value(field, defn) - except IsDefault: - pass - return args, kwargs - - -def get_model_fields(model, m2m=False): - """ - Given a model class, returns a dict of {field_name: field_triple} defs. - """ - - field_defs = SortedDict() - inherited_fields = {} - - # Go through all bases (that are themselves models, but not Model) - for base in model.__bases__: - if hasattr(base, '_meta') and issubclass(base, models.Model): - if not base._meta.abstract: - # Looks like we need their fields, Ma. - inherited_fields.update(get_model_fields(base)) - - # Now, go through all the fields and try to get their definition - source = model._meta.local_fields[:] - if m2m: - source += model._meta.local_many_to_many - - for field in source: - # Can we ignore it completely? - if can_ignore(field): - continue - # Does it define a south_field_triple method? - if hasattr(field, "south_field_triple"): - if NOISY: - print(" ( Nativing field: %s" % field.name) - field_defs[field.name] = field.south_field_triple() - # Can we introspect it? - elif can_introspect(field): - # Get the full field class path. - field_class = field.__class__.__module__ + "." + field.__class__.__name__ - # Run this field through the introspector - args, kwargs = introspector(field) - # Workaround for Django bug #13987 - if model._meta.pk.column == field.column and 'primary_key' not in kwargs: - kwargs['primary_key'] = True - # That's our definition! - field_defs[field.name] = (field_class, args, kwargs) - # Shucks, no definition! - else: - if NOISY: - print(" ( Nodefing field: %s" % field.name) - field_defs[field.name] = None - - # If they've used the horrific hack that is order_with_respect_to, deal with - # it. - if model._meta.order_with_respect_to: - field_defs['_order'] = ("django.db.models.fields.IntegerField", [], {"default": "0"}) - - return field_defs - - -def get_model_meta(model): - """ - Given a model class, will return the dict representing the Meta class. - """ - - # Get the introspected attributes - meta_def = {} - for kwd, defn in meta_details.items(): - try: - meta_def[kwd] = get_value(model._meta, defn) - except IsDefault: - pass - - # Also, add on any non-abstract model base classes. - # This is called _ormbases as the _bases variable was previously used - # for a list of full class paths to bases, so we can't conflict. - for base in model.__bases__: - if hasattr(base, '_meta') and issubclass(base, models.Model): - if not base._meta.abstract: - # OK, that matches our terms. - if "_ormbases" not in meta_def: - meta_def['_ormbases'] = [] - meta_def['_ormbases'].append("%s.%s" % ( - base._meta.app_label, - base._meta.object_name, - )) - - return meta_def - - -# Now, load the built-in South introspection plugins -import south.introspection_plugins |