<commit_before>from time import time
from threading import Lock as ThreadLock
from tori.db.common    import Serializer, PseudoObjectId, ProxyObject
from tori.db.exception import UOWRepeatedRegistrationError, UOWUpdateError, UOWUnknownRecordError, IntegrityConstraintError
from tori.db.mapper import CascadingType

class Record(object):
    serializer = Serializer(0)

    STATUS_CLEAN     = 1
    STATUS_DELETED   = 2
    STATUS_DIRTY     = 3
    STATUS_NEW       = 4

    def __init__(self, entity, status):
        self.entity = entity
        self.status = status

        self.original_data_set = Record.serializer.encode(entity)

    def update(self):
        self.original_data_set = Record.serializer.encode(self.entity)
        self.status = Record.STATUS_CLEAN

class DependencyNode(object):
    """ Dependency Node

    This is designed to be bi-directional to maximize flexibility on traversing the graph.
    """
    def __init__(self, record):
        self.created_at     = int(time() * 1000000)
        self.record         = record
        self.adjacent_nodes = set()
        self.reverse_edges  = set()

    def connect(self, other):
        self.adjacent_nodes.add(other)
        other.reverse_edges.add(self)

    @property
    def object_id(self):
        return self.record.entity.id

    @property
    def status(self):
        return self.record.status

    @property
    def score(self):
        score = 0

        for node in self.reverse_edges:
            if node.status == Record.STATUS_DELETED:
                continue

            score += 1

        return score

    def __eq__(self, other):
        return self.object_id == other.object_id

    def __ne__(self, other):
        return self.object_id != other.object_id

    def __lt__(self, other):
        return self.score < other.score

    def __le__(self, other):
        return self.score <= other.score

    def __gt__(self, other):
        return self.score > other.score

    def __ge__(self, other):
        return self.score >= other.score

    def __hash__(self):
        return self.created_at

    def __repr__(self):
        return '<DependencyNode for {}, {}>'.format(self.object_id, self.score)

class UnitOfWork(object):
    """ Unit of Work

    This Unit of Work (UOW) is designed specifically for non-relational databases.

    .. note::

        It is the design decision to make sub-commit methods available so that when it is used with Imagination
        Framework, the other Imagination entity may intercept before or after actually committing data. In the other
        word, Imagination Framework acts as an event controller for any actions (public methods) of this class.

    """
    serializer = Serializer(0)

    def __init__(self, entity_manager):
        # given property
        self._em = entity_manager

        # caching properties
        self._record_map    = {} # Object Hash => Record
        self._object_id_map = {} # str(ObjectID) => Object Hash
        self._dependency_map = None

        # Locks
        self._blocker_activated = False
        self._tlock = ThreadLock()

    def freeze(self):
        if not self._blocker_activated:
            return

        self._tlock.acquire()

    def unfreeze(self):
        if not self._blocker_activated:
            return

        self._tlock.release()

    def refresh(self, entity):
        collection = self._em.collection(entity.__class__)
        record     = self.retrieve_record(entity)

        updated_data_set = collection._api({'_id': entity.id})

        # Reset the attributes.
        for attribute_name in updated_data_set:
            entity.__setattr__(attribute_name, updated_data_set[attribute_name])

        # Remove the non-existed attributes.
        for attribute_name in record.original_data_set:
            if attribute_name in updated_data_set:
                continue

            entity.__delattr__(attribute_name)

        # Update the original data set and reset the status if necessary.
        record.original_data_set = Record.serializer.encode(entity)

        if record.status == Record.STATUS_DIRTY:
            record.status = Record.STATUS_CLEAN

        # Remap the object.
        self._em.apply_relational_map(entity)

    def register_new(self, entity):
        self.freeze()

        uid = self._retrieve_entity_guid(entity)

        if self.has_record(entity):
            raise UOWRepeatedRegistrationError('Could not mark the entity as new.')

        if not entity.id:
            entity.id = self._generate_pseudo_object_id()

        self._record_map[uid] = Record(entity, Record.STATUS_NEW)

        # Map the pseudo object ID to the entity.
        self._object_id_map[self._convert_object_id_to_str(entity.id)] = uid

        self.cascade_property_registration_of(entity, CascadingType.PERSIST)

        self.unfreeze()

    def register_dirty(self, entity):
        self.freeze()

        record = self.retrieve_record(entity)

        if record.status == Record.STATUS_DELETED:
            raise UOWUpdateError('Could not update the deleted entity.')

        if record.status != Record.STATUS_NEW and self.compute_change_set(record):
            record.status = Record.STATUS_DIRTY

        self.cascade_property_registration_of(entity, CascadingType.PERSIST)

        self.unfreeze()

    def register_clean(self, entity):
        uid = self._retrieve_entity_guid(entity)

        if uid in self._record_map:
            raise UOWRepeatedRegistrationError('Could not mark the entity as clean')

        self._record_map[uid] = Record(entity, Record.STATUS_CLEAN)

        # Map the real object ID to the entity
        self._object_id_map[self._convert_object_id_to_str(entity.id)] = uid

    def register_deleted(self, entity):
        self.freeze()

        record = self.retrieve_record(entity)

        if record.status == Record.STATUS_NEW:
            self.delete_record(entity)
        else:
            record.status = Record.STATUS_DELETED

        self.cascade_property_registration_of(entity, CascadingType.DELETE)

        self.unfreeze()

    def cascade_property_registration_of(self, reference, cascading_type):
        entity = reference

        if isinstance(reference, ProxyObject):
            entity = reference._actual

        if '__relational_map__' not in dir(entity):
            return

        for property_name in entity.__relational_map__:
            guide       = entity.__relational_map__[property_name]
            actual_data = entity.__getattribute__(property_name)
            reference   = self.hydrate_entity(actual_data)
<commit_msg>Prevented cascading from reverse mapping.<commit_after>from time import time
from threading import Lock as ThreadLock
from tori.db.common    import Serializer, PseudoObjectId, ProxyObject
from tori.db.exception import UOWRepeatedRegistrationError, UOWUpdateError, UOWUnknownRecordError, IntegrityConstraintError
from tori.db.mapper import CascadingType

class Record(object):
    serializer = Serializer(0)

    STATUS_CLEAN     = 1
    STATUS_DELETED   = 2
    STATUS_DIRTY     = 3
    STATUS_NEW       = 4

    def __init__(self, entity, status):
        self.entity = entity
        self.status = status

        self.original_data_set = Record.serializer.encode(entity)

    def update(self):
        self.original_data_set = Record.serializer.encode(self.entity)
        self.status = Record.STATUS_CLEAN

class DependencyNode(object):
    """ Dependency Node

    This is designed to be bi-directional to maximize flexibility on traversing the graph.
    """
    def __init__(self, record):
        self.created_at     = int(time() * 1000000)
        self.record         = record
        self.adjacent_nodes = set()
        self.reverse_edges  = set()

    def connect(self, other):
        self.adjacent_nodes.add(other)
        other.reverse_edges.add(self)

    @property
    def object_id(self):
        return self.record.entity.id

    @property
    def status(self):
        return self.record.status

    @property
    def score(self):
        score = 0

        for node in self.reverse_edges:
            if node.status == Record.STATUS_DELETED:
                continue

            score += 1

        return score

    def __eq__(self, other):
        return self.object_id == other.object_id

    def __ne__(self, other):
        return self.object_id != other.object_id

    def __lt__(self, other):
        return self.score < other.score

    def __le__(self, other):
        return self.score <= other.score

    def __gt__(self, other):
        return self.score > other.score

    def __ge__(self, other):
        return self.score >= other.score

    def __hash__(self):
        return self.created_at

    def __repr__(self):
        return '<DependencyNode for {}, {}>'.format(self.object_id, self.score)

class UnitOfWork(object):
    """ Unit of Work

    This Unit of Work (UOW) is designed specifically for non-relational databases.

    .. note::

        It is the design decision to make sub-commit methods available so that when it is used with Imagination
        Framework, the other Imagination entity may intercept before or after actually committing data. In the other
        word, Imagination Framework acts as an event controller for any actions (public methods) of this class.

    """
    serializer = Serializer(0)

    def __init__(self, entity_manager):
        # given property
        self._em = entity_manager

        # caching properties
        self._record_map    = {} # Object Hash => Record
        self._object_id_map = {} # str(ObjectID) => Object Hash
        self._dependency_map = None

        # Locks
        self._blocker_activated = False
        self._tlock = ThreadLock()

    def freeze(self):
        if not self._blocker_activated:
            return

        self._tlock.acquire()

    def unfreeze(self):
        if not self._blocker_activated:
            return

        self._tlock.release()

    def refresh(self, entity):
        collection = self._em.collection(entity.__class__)
        record     = self.retrieve_record(entity)

        updated_data_set = collection._api({'_id': entity.id})

        # Reset the attributes.
        for attribute_name in updated_data_set:
            entity.__setattr__(attribute_name, updated_data_set[attribute_name])

        # Remove the non-existed attributes.
        for attribute_name in record.original_data_set:
            if attribute_name in updated_data_set:
                continue

            entity.__delattr__(attribute_name)

        # Update the original data set and reset the status if necessary.
        record.original_data_set = Record.serializer.encode(entity)

        if record.status == Record.STATUS_DIRTY:
            record.status = Record.STATUS_CLEAN

        # Remap the object.
        self._em.apply_relational_map(entity)

    def register_new(self, entity):
        self.freeze()

        uid = self._retrieve_entity_guid(entity)

        if self.has_record(entity):
            raise UOWRepeatedRegistrationError('Could not mark the entity as new.')

        if not entity.id:
            entity.id = self._generate_pseudo_object_id()

        self._record_map[uid] = Record(entity, Record.STATUS_NEW)

        # Map the pseudo object ID to the entity.
        self._object_id_map[self._convert_object_id_to_str(entity.id)] = uid

        self.cascade_property_registration_of(entity, CascadingType.PERSIST)

        self.unfreeze()

    def register_dirty(self, entity):
        self.freeze()

        record = self.retrieve_record(entity)

        if record.status == Record.STATUS_DELETED:
            raise UOWUpdateError('Could not update the deleted entity.')

        if record.status != Record.STATUS_NEW and self.compute_change_set(record):
            record.status = Record.STATUS_DIRTY

        self.cascade_property_registration_of(entity, CascadingType.PERSIST)

        self.unfreeze()

    def register_clean(self, entity):
        uid = self._retrieve_entity_guid(entity)

        if uid in self._record_map:
            raise UOWRepeatedRegistrationError('Could not mark the entity as clean')

        self._record_map[uid] = Record(entity, Record.STATUS_CLEAN)

        # Map the real object ID to the entity
        self._object_id_map[self._convert_object_id_to_str(entity.id)] = uid

    def register_deleted(self, entity):
        self.freeze()

        record = self.retrieve_record(entity)

        if record.status == Record.STATUS_NEW:
            self.delete_record(entity)
        else:
            record.status = Record.STATUS_DELETED

        self.cascade_property_registration_of(entity, CascadingType.DELETE)

        self.unfreeze()

    def cascade_property_registration_of(self, reference, cascading_type):
        entity = reference

        if isinstance(reference, ProxyObject):
            entity = reference._actual

        if '__relational_map__' not in dir(entity):
            return

        for property_name in entity.__relational_map__:
            guide = entity.__relational_map__[property_name]

            if guide.is_reverse_mapping:
                continue

            actual_data = entity.__getattribute__(property_name)
            reference   = self.hydrate_entity(actual_data)

{'fraction_non_alphanumeric': 0.07822362488728585, 'fraction_numerical': 0.002254283137962128, 'mean_word_length': 2.496847083552286, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5236886', 'n_tokens_mistral': 3849, 'n_tokens_neox': 3618, 'n_words': 929}