<commit_before>"""Revision management for django-reversion."""

from __future__ import unicode_literals

import operator, sys
from functools import wraps, reduce, partial
from threading import local
from weakref import WeakValueDictionary
import copy

from django.contrib.contenttypes.models import ContentType
from django.core import serializers
from django.core.exceptions import ObjectDoesNotExist
from django.core.signals import request_finished
from django.db import models, connection, transaction
from django.db.models import Q, Max, get_model
from django.db.models.query import QuerySet
from django.db.models.signals import post_save
from django.utils.encoding import force_text

from reversion.models import Revision, Version, has_int_pk, pre_revision_commit, post_revision_commit


class VersionAdapter(object):

    """Adapter class for serializing a registered model."""

    # Fields to include in the serialized data.
    fields = ()

    # Fields to exclude from the serialized data.
    exclude = ()

    # Foreign key relationships to follow when saving a version of this model.
    follow = ()

    # The serialization format to use.
    format = "json"

    def __init__(self, model):
        """Initializes the version adapter."""
        self.model = model

    def get_fields_to_serialize(self):
        """Returns an iterable of field names to serialize in the version data."""
        opts = self.model._meta.concrete_model._meta
        fields = self.fields or (field.name for field in opts.local_fields + opts.local_many_to_many)
        fields = (opts.get_field(field) for field in fields if not field in self.exclude)
        for field in fields:
            if field.rel:
                yield field.name
            else:
                yield field.attname

    def get_followed_relations(self, obj):
        """Returns an iterable of related models that should be included in the revision data."""
        for relationship in self.follow:
            # Clear foreign key cache.
            try:
                related_field = obj._meta.get_field(relationship)
            except models.FieldDoesNotExist:
                pass
            else:
                if isinstance(related_field, models.ForeignKey):
                    if hasattr(obj, related_field.get_cache_name()):
                        delattr(obj, related_field.get_cache_name())
            # Get the referenced obj(s).
            try:
                related = getattr(obj, relationship)
            except ObjectDoesNotExist:
                continue
            if isinstance(related, models.Model):
                yield related
            elif isinstance(related, (models.Manager, QuerySet)):
                for related_obj in related.all():
                    yield related_obj
            elif related is not None:
                raise TypeError("Cannot follow the relationship {relationship}. Expected a model or QuerySet, found {related}".format(
                    relationship = relationship,
                    related = related,
                ))

    def get_serialization_format(self):
        """Returns the serialization format to use."""
        return self.format

    def get_serialized_data(self, obj):
        """Returns a string of serialized data for the given obj."""
        return serializers.serialize(
            self.get_serialization_format(),
            (obj,),
            fields = list(self.get_fields_to_serialize()),
        )

    def get_version_data(self, obj, db=None):
        """Creates the version data to be saved to the version model."""
        object_id = force_text(obj.pk)
        content_type = ContentType.objects.db_manager(db).get_for_model(obj)
        if has_int_pk(obj.__class__):
            object_id_int = int(obj.pk)
        else:
            object_id_int = None
        return {
            "object_id": object_id,
            "object_id_int": object_id_int,
            "content_type": content_type,
            "format": self.get_serialization_format(),
            "serialized_data": self.get_serialized_data(obj),
            "object_repr": force_text(obj),
        }


class RevisionManagementError(Exception):

    """Exception that is thrown when something goes wrong with revision managment."""


class RevisionContextManager(local):

    """Manages the state of the current revision."""

    def __init__(self):
        """Initializes the revision state."""
        self.clear()
        # Connect to the request finished signal.
        request_finished.connect(self._request_finished_receiver)

    def clear(self):
        """Puts the revision manager back into its default state."""
        self._objects = {}
        self._user = None
        self._comment = ""
        self._stack = []
        self._is_invalid = False
        self._meta = []
        self._ignore_duplicates = False
        self._db = None

    def is_active(self):
        """Returns whether there is an active revision for this thread."""
        return bool(self._stack)

    def is_managing_manually(self):
        """Returns whether this revision context has manual management enabled."""
        self._assert_active()
        return self._stack[-1]

    def _assert_active(self):
        """Checks for an active revision, throwning an exception if none."""
        if not self.is_active():
            raise RevisionManagementError("There is no active revision for this thread")

    def start(self, manage_manually=False):
        """
        Begins a revision for this thread.

        This MUST be balanced by a call to `end`.  It is recommended that you
        leave these methods alone and instead use the revision context manager
        or the `create_revision` decorator.
        """
        self._stack.append(manage_manually)

    def end(self):
        """Ends a revision for this thread."""
        self._assert_active()
        self._stack.pop()
        if not self._stack:
            try:
                if not self.is_invalid():
                    # Save the revision data.
                    for manager, manager_context in self._objects.items():
                        manager.save_revision(
                            dict(
                                (obj, callable(data) and data() or data)
                                for obj, data
                                in manager_context.items()
                                if obj.pk
                            ),
                            user = self._user,
                            comment = self._comment,
                            meta = self._meta,
                            ignore_duplicates = self._ignore_duplicates,
                            db = self._db,
                        )
            finally:
                self.clear()

    def invalidate(self):
        """Marks this revision as broken, so should not be commited."""
        self._assert_active()
        self._is_invalid = True

    def is_invalid(self):
        """Checks whether this revision is invalid."""
        return self._is_invalid

    def add_to_context(self, manager, obj, version_data):
        """Adds an object to the current revision."""
        self._assert_active()<commit_msg>Improves null PK check when loading context data<commit_after>"""Revision management for django-reversion."""

from __future__ import unicode_literals

import operator, sys
from functools import wraps, reduce, partial
from threading import local
from weakref import WeakValueDictionary
import copy

from django.contrib.contenttypes.models import ContentType
from django.core import serializers
from django.core.exceptions import ObjectDoesNotExist
from django.core.signals import request_finished
from django.db import models, connection, transaction
from django.db.models import Q, Max, get_model
from django.db.models.query import QuerySet
from django.db.models.signals import post_save
from django.utils.encoding import force_text

from reversion.models import Revision, Version, has_int_pk, pre_revision_commit, post_revision_commit


class VersionAdapter(object):

    """Adapter class for serializing a registered model."""

    # Fields to include in the serialized data.
    fields = ()

    # Fields to exclude from the serialized data.
    exclude = ()

    # Foreign key relationships to follow when saving a version of this model.
    follow = ()

    # The serialization format to use.
    format = "json"

    def __init__(self, model):
        """Initializes the version adapter."""
        self.model = model

    def get_fields_to_serialize(self):
        """Returns an iterable of field names to serialize in the version data."""
        opts = self.model._meta.concrete_model._meta
        fields = self.fields or (field.name for field in opts.local_fields + opts.local_many_to_many)
        fields = (opts.get_field(field) for field in fields if not field in self.exclude)
        for field in fields:
            if field.rel:
                yield field.name
            else:
                yield field.attname

    def get_followed_relations(self, obj):
        """Returns an iterable of related models that should be included in the revision data."""
        for relationship in self.follow:
            # Clear foreign key cache.
            try:
                related_field = obj._meta.get_field(relationship)
            except models.FieldDoesNotExist:
                pass
            else:
                if isinstance(related_field, models.ForeignKey):
                    if hasattr(obj, related_field.get_cache_name()):
                        delattr(obj, related_field.get_cache_name())
            # Get the referenced obj(s).
            try:
                related = getattr(obj, relationship)
            except ObjectDoesNotExist:
                continue
            if isinstance(related, models.Model):
                yield related
            elif isinstance(related, (models.Manager, QuerySet)):
                for related_obj in related.all():
                    yield related_obj
            elif related is not None:
                raise TypeError("Cannot follow the relationship {relationship}. Expected a model or QuerySet, found {related}".format(
                    relationship = relationship,
                    related = related,
                ))

    def get_serialization_format(self):
        """Returns the serialization format to use."""
        return self.format

    def get_serialized_data(self, obj):
        """Returns a string of serialized data for the given obj."""
        return serializers.serialize(
            self.get_serialization_format(),
            (obj,),
            fields = list(self.get_fields_to_serialize()),
        )

    def get_version_data(self, obj, db=None):
        """Creates the version data to be saved to the version model."""
        object_id = force_text(obj.pk)
        content_type = ContentType.objects.db_manager(db).get_for_model(obj)
        if has_int_pk(obj.__class__):
            object_id_int = int(obj.pk)
        else:
            object_id_int = None
        return {
            "object_id": object_id,
            "object_id_int": object_id_int,
            "content_type": content_type,
            "format": self.get_serialization_format(),
            "serialized_data": self.get_serialized_data(obj),
            "object_repr": force_text(obj),
        }


class RevisionManagementError(Exception):

    """Exception that is thrown when something goes wrong with revision managment."""


class RevisionContextManager(local):

    """Manages the state of the current revision."""

    def __init__(self):
        """Initializes the revision state."""
        self.clear()
        # Connect to the request finished signal.
        request_finished.connect(self._request_finished_receiver)

    def clear(self):
        """Puts the revision manager back into its default state."""
        self._objects = {}
        self._user = None
        self._comment = ""
        self._stack = []
        self._is_invalid = False
        self._meta = []
        self._ignore_duplicates = False
        self._db = None

    def is_active(self):
        """Returns whether there is an active revision for this thread."""
        return bool(self._stack)

    def is_managing_manually(self):
        """Returns whether this revision context has manual management enabled."""
        self._assert_active()
        return self._stack[-1]

    def _assert_active(self):
        """Checks for an active revision, throwning an exception if none."""
        if not self.is_active():
            raise RevisionManagementError("There is no active revision for this thread")

    def start(self, manage_manually=False):
        """
        Begins a revision for this thread.

        This MUST be balanced by a call to `end`.  It is recommended that you
        leave these methods alone and instead use the revision context manager
        or the `create_revision` decorator.
        """
        self._stack.append(manage_manually)

    def end(self):
        """Ends a revision for this thread."""
        self._assert_active()
        self._stack.pop()
        if not self._stack:
            try:
                if not self.is_invalid():
                    # Save the revision data.
                    for manager, manager_context in self._objects.items():
                        manager.save_revision(
                            dict(
                                (obj, callable(data) and data() or data)
                                for obj, data
                                in manager_context.items()
                                if obj.pk is not None
                            ),
                            user = self._user,
                            comment = self._comment,
                            meta = self._meta,
                            ignore_duplicates = self._ignore_duplicates,
                            db = self._db,
                        )
            finally:
                self.clear()

    def invalidate(self):
        """Marks this revision as broken, so should not be commited."""
        self._assert_active()
        self._is_invalid = True

    def is_invalid(self):
        """Checks whether this revision is invalid."""
        return self._is_invalid

    def add_to_context(self, manager, obj, version_data):
        """Adds an object to the current revision."""
        self._assert_active()
{'fraction_non_alphanumeric': 0.07901813417913535, 'fraction_numerical': 0.00013790250293042817, 'mean_word_length': 2.008504459655673, 'pattern_counts': {'":': 12, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '329125', 'n_tokens_mistral': 3633, 'n_tokens_neox': 3504, 'n_words': 1155}