<commit_before>from __future__ import print_function
import copy
import importlib
import json
import logging
import os
import six
import sys

from .serializing import SerializingObject

DATE_FORMAT = '%Y%m%dT%H%M%SZ'
REPR_OUTPUT_SIZE = 10
PENDING = 'pending'
COMPLETED = 'completed'

logger = logging.getLogger(__name__)


class ReadOnlyDictView(object):
    """
    Provides simplified read-only view upon dict object.
    """

    def __init__(self, viewed_dict):
        self.viewed_dict = viewed_dict

    def __getitem__(self, key):
        return copy.deepcopy(self.viewed_dict.__getitem__(key))

    def __contains__(self, k):
        return self.viewed_dict.__contains__(k)

    def __iter__(self):
        for value in self.viewed_dict:
            yield copy.deepcopy(value)

    def __len__(self):
        return len(self.viewed_dict)

    def __unicode__(self):
        return six.u('ReadOnlyDictView: {0}'.format(repr(self.viewed_dict)))

    __repr__ = __unicode__

    def get(self, key, default=None):
        return copy.deepcopy(self.viewed_dict.get(key, default))

    def items(self):
        return [copy.deepcopy(v) for v in self.viewed_dict.items()]

    def values(self):
        return [copy.deepcopy(v) for v in self.viewed_dict.values()]


class TaskResource(SerializingObject):
    read_only_fields = []

    def _load_data(self, data):
        self._data = dict((key, self._deserialize(key, value))
                          for key, value in data.items())
        # We need to use a copy for original data, so that changes
        # are not propagated.
        self._original_data = copy.deepcopy(self._data)

    def _update_data(self, data, update_original=False, remove_missing=False):
        """
        Low level update of the internal _data dict. Data which are coming as
        updates should already be serialized. If update_original is True, the
        original_data dict is updated as well.
        """
        self._data.update(dict((key, self._deserialize(key, value))
                               for key, value in data.items()))

        # In certain situations, we want to treat missing keys as removals
        if remove_missing:
            for key in set(self._data.keys()) - set(data.keys()):
                self._data[key] = None

        if update_original:
            self._original_data = copy.deepcopy(self._data)

    def __getitem__(self, key):
        # This is a workaround to make TaskResource non-iterable
        # over simple index-based iteration
        try:
            int(key)
            raise StopIteration
        except ValueError:
            pass

        if key not in self._data:
            self._data[key] = self._deserialize(key, None)

        return self._data.get(key)

    def __setitem__(self, key, value):
        if key in self.read_only_fields:
            raise RuntimeError('Field \'%s\' is read-only' % key)

        # Normalize the user input before saving it
        value = self._normalize(key, value)
        self._data[key] = value

    def __str__(self):
        s = six.text_type(self.__unicode__())
        if not six.PY3:
            s = s.encode('utf-8')
        return s

    def __repr__(self):
        return str(self)

    def export_data(self):
        """
        Exports current data contained in the Task as JSON
        """

        # We need to remove spaces for TW-1504, use custom separators
        data_tuples = ((key, self._serialize(key, value))
                       for key, value in six.iteritems(self._data))

        # Empty string denotes empty serialized value, we do not want
        # to pass that to TaskWarrior.
        data_tuples = filter(lambda t: t[1] is not '', data_tuples)
        data = dict(data_tuples)
        return json.dumps(data, separators=(',', ':'))

    @property
    def _modified_fields(self):
        writable_fields = set(self._data.keys()) - set(self.read_only_fields)
        for key in writable_fields:
            new_value = self._data.get(key)
            old_value = self._original_data.get(key)

            # Make sure not to mark data removal as modified field if the
            # field originally had some empty value
            if key in self._data and not new_value and not old_value:
                continue

            if new_value != old_value:
                yield key

    @property
    def modified(self):
        return bool(list(self._modified_fields))


class TaskAnnotation(TaskResource):
    read_only_fields = ['entry', 'description']

    def __init__(self, task, data=None):
        self.task = task
        self._load_data(data or dict())
        super(TaskAnnotation, self).__init__(task.backend)

    def remove(self):
        self.task.remove_annotation(self)

    def __unicode__(self):
        return self['description']

    def __eq__(self, other):
        # consider 2 annotations equal if they belong to the same task, and
        # their data dics are the same
        return self.task == other.task and self._data == other._data

    __repr__ = __unicode__


class Task(TaskResource):<commit_msg>task: Add LazyUUIDTask object<commit_after>from __future__ import print_function
import copy
import importlib
import json
import logging
import os
import six
import sys

from .serializing import SerializingObject

DATE_FORMAT = '%Y%m%dT%H%M%SZ'
REPR_OUTPUT_SIZE = 10
PENDING = 'pending'
COMPLETED = 'completed'

logger = logging.getLogger(__name__)


class ReadOnlyDictView(object):
    """
    Provides simplified read-only view upon dict object.
    """

    def __init__(self, viewed_dict):
        self.viewed_dict = viewed_dict

    def __getitem__(self, key):
        return copy.deepcopy(self.viewed_dict.__getitem__(key))

    def __contains__(self, k):
        return self.viewed_dict.__contains__(k)

    def __iter__(self):
        for value in self.viewed_dict:
            yield copy.deepcopy(value)

    def __len__(self):
        return len(self.viewed_dict)

    def __unicode__(self):
        return six.u('ReadOnlyDictView: {0}'.format(repr(self.viewed_dict)))

    __repr__ = __unicode__

    def get(self, key, default=None):
        return copy.deepcopy(self.viewed_dict.get(key, default))

    def items(self):
        return [copy.deepcopy(v) for v in self.viewed_dict.items()]

    def values(self):
        return [copy.deepcopy(v) for v in self.viewed_dict.values()]


class TaskResource(SerializingObject):
    read_only_fields = []

    def _load_data(self, data):
        self._data = dict((key, self._deserialize(key, value))
                          for key, value in data.items())
        # We need to use a copy for original data, so that changes
        # are not propagated.
        self._original_data = copy.deepcopy(self._data)

    def _update_data(self, data, update_original=False, remove_missing=False):
        """
        Low level update of the internal _data dict. Data which are coming as
        updates should already be serialized. If update_original is True, the
        original_data dict is updated as well.
        """
        self._data.update(dict((key, self._deserialize(key, value))
                               for key, value in data.items()))

        # In certain situations, we want to treat missing keys as removals
        if remove_missing:
            for key in set(self._data.keys()) - set(data.keys()):
                self._data[key] = None

        if update_original:
            self._original_data = copy.deepcopy(self._data)

    def __getitem__(self, key):
        # This is a workaround to make TaskResource non-iterable
        # over simple index-based iteration
        try:
            int(key)
            raise StopIteration
        except ValueError:
            pass

        if key not in self._data:
            self._data[key] = self._deserialize(key, None)

        return self._data.get(key)

    def __setitem__(self, key, value):
        if key in self.read_only_fields:
            raise RuntimeError('Field \'%s\' is read-only' % key)

        # Normalize the user input before saving it
        value = self._normalize(key, value)
        self._data[key] = value

    def __str__(self):
        s = six.text_type(self.__unicode__())
        if not six.PY3:
            s = s.encode('utf-8')
        return s

    def __repr__(self):
        return str(self)

    def export_data(self):
        """
        Exports current data contained in the Task as JSON
        """

        # We need to remove spaces for TW-1504, use custom separators
        data_tuples = ((key, self._serialize(key, value))
                       for key, value in six.iteritems(self._data))

        # Empty string denotes empty serialized value, we do not want
        # to pass that to TaskWarrior.
        data_tuples = filter(lambda t: t[1] is not '', data_tuples)
        data = dict(data_tuples)
        return json.dumps(data, separators=(',', ':'))

    @property
    def _modified_fields(self):
        writable_fields = set(self._data.keys()) - set(self.read_only_fields)
        for key in writable_fields:
            new_value = self._data.get(key)
            old_value = self._original_data.get(key)

            # Make sure not to mark data removal as modified field if the
            # field originally had some empty value
            if key in self._data and not new_value and not old_value:
                continue

            if new_value != old_value:
                yield key

    @property
    def modified(self):
        return bool(list(self._modified_fields))


class TaskAnnotation(TaskResource):
    read_only_fields = ['entry', 'description']

    def __init__(self, task, data=None):
        self.task = task
        self._load_data(data or dict())
        super(TaskAnnotation, self).__init__(task.backend)

    def remove(self):
        self.task.remove_annotation(self)

    def __unicode__(self):
        return self['description']

    def __eq__(self, other):
        # consider 2 annotations equal if they belong to the same task, and
        # their data dics are the same
        return self.task == other.task and self._data == other._data

    __repr__ = __unicode__


class LazyUUIDTask(object):
    """
    A lazy wrapper around Task object, referenced by UUID.

    - Supports comparison with LazyUUIDTask or Task objects (equality by UUIDs)
    - If any attribute other than 'uuid' requested, a lookup in the
      backend will be performed and this object will be replaced by a proper
      Task object.
    """

    def __init__(self, tw, uuid):
        self._tw = tw
        self._uuid = uuid

    def __getitem__(self, key):
        # LazyUUIDTask does not provide anything else other than 'uuid'
        if key is 'uuid':
            return self._uuid
        else:
            self.replace()
            return self[key]

    def __getattr__(self, name):
        # Getattr is called only if the attribute could not be found using
        # normal means
        self.replace()
        return self.name

    def __eq__(self, other):
        if other['uuid']:
            # For saved Tasks, just define equality by equality of uuids
            return self['uuid'] == other['uuid']

    def __hash__(self):
        return self['uuid'].__hash__()

    def replace(self):
        """
        Performs conversion to the regular Task object, referenced by the
        stored UUID.
        """

        replacement = self._tw.tasks.get(uuid=self._uuid)
        self.__class__ = replacement.__class__
        self.__dict__ = replacement.__dict__


class Task(TaskResource):
{'fraction_non_alphanumeric': 0.09042277825711821, 'fraction_numerical': 0.0018981880931837791, 'mean_word_length': 2.343236227285838, 'pattern_counts': {'":': 0, '<': 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': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3987342', 'n_tokens_mistral': 3348, 'n_tokens_neox': 3240, 'n_words': 1007}