<commit_before># -*- coding: utf-8 -*-

import contextlib
import functools

from .. import exceptions, sync
from ..utils import uniq
from ..utils.compat import with_metaclass
from ..utils.vobject import Item  # noqa


def mutating_storage_method(f):
    @functools.wraps(f)
    def inner(self, *args, **kwargs):
        if self.read_only:
            raise exceptions.ReadOnlyError('This storage is read-only.')
        return f(self, *args, **kwargs)
    return inner


class StorageMeta(type):
    def __init__(cls, name, bases, d):
        for method in ('update', 'upload', 'delete'):
            setattr(cls, method, mutating_storage_method(getattr(cls, method)))
        return super(StorageMeta, cls).__init__(name, bases, d)


class Storage(with_metaclass(StorageMeta)):

    '''Superclass of all storages, mainly useful to summarize the interface to
    implement.

    Terminology:
      - ITEM: Instance of the Item class, represents a calendar event, task or
          contact.
      - HREF: String; Per-storage identifier of item, might be UID. The reason
          items aren't just referenced by their UID is because the CalDAV and
          CardDAV specifications make this imperformant to implement.
      - ETAG: String; Checksum of item, or something similar that changes when
          the item does.

    Strings can be either unicode strings or bytestrings. If bytestrings, an
    ASCII encoding is assumed.

    :param read_only: Whether the synchronization algorithm should avoid writes
        to this storage. Some storages accept no value other than ``True``.
    '''

    fileext = '.txt'

    syncer_class = sync.StorageSyncer

    # The string used in the config to denote the type of storage. Should be
    # overridden by subclasses.
    storage_name = None

    # The string used in the config to denote a particular instance. Will be
    # overridden during instantiation.
    instance_name = None

    # The machine-readable name of this collection.
    collection = None

    # A value of True means the storage does not support write-methods such as
    # upload, update and delete.  A value of False means the storage does
    # support those methods.
    read_only = False

    # The attribute values to show in the representation of the storage.
    _repr_attributes = ()

    def __init__(self, instance_name=None, read_only=None, collection=None):
        if read_only is None:
            read_only = self.read_only
        if self.read_only and not read_only:
            raise exceptions.UserError('This storage can only be read-only.')
        self.read_only = bool(read_only)

        if collection and instance_name:
            assert isinstance(collection, str)
            instance_name = '{}/{}'.format(instance_name, collection)
        self.instance_name = instance_name
        self.collection = collection

    @classmethod
    def discover(cls, **kwargs):
        '''Discover collections given a basepath or -URL to many collections.

        :param **kwargs: Keyword arguments to additionally pass to the storage
            instances returned. You shouldn't pass `collection` here, otherwise
            TypeError will be raised.
        :returns: iterable of ``storage_args``.
            ``storage_args`` is a dictionary of ``**kwargs`` to pass to this
            class to obtain a storage instance pointing to this collection. It
            also must contain a ``"collection"`` key.  That key's value is used
            to match two collections together for synchronization. IOW it is a
            machine-readable identifier for the collection, usually obtained
            from the last segment of a URL or filesystem path.

        '''
        raise NotImplementedError()

    @classmethod
    def create_collection(cls, collection, **kwargs):
        '''<commit_msg>Work around massive string type inconsistency<commit_after># -*- coding: utf-8 -*-

import contextlib
import functools

from .. import exceptions, sync
from ..utils import uniq
from ..utils.compat import to_native, with_metaclass
from ..utils.vobject import Item  # noqa


def mutating_storage_method(f):
    @functools.wraps(f)
    def inner(self, *args, **kwargs):
        if self.read_only:
            raise exceptions.ReadOnlyError('This storage is read-only.')
        return f(self, *args, **kwargs)
    return inner


class StorageMeta(type):
    def __init__(cls, name, bases, d):
        for method in ('update', 'upload', 'delete'):
            setattr(cls, method, mutating_storage_method(getattr(cls, method)))
        return super(StorageMeta, cls).__init__(name, bases, d)


class Storage(with_metaclass(StorageMeta)):

    '''Superclass of all storages, mainly useful to summarize the interface to
    implement.

    Terminology:
      - ITEM: Instance of the Item class, represents a calendar event, task or
          contact.
      - HREF: String; Per-storage identifier of item, might be UID. The reason
          items aren't just referenced by their UID is because the CalDAV and
          CardDAV specifications make this imperformant to implement.
      - ETAG: String; Checksum of item, or something similar that changes when
          the item does.

    Strings can be either unicode strings or bytestrings. If bytestrings, an
    ASCII encoding is assumed.

    :param read_only: Whether the synchronization algorithm should avoid writes
        to this storage. Some storages accept no value other than ``True``.
    '''

    fileext = '.txt'

    syncer_class = sync.StorageSyncer

    # The string used in the config to denote the type of storage. Should be
    # overridden by subclasses.
    storage_name = None

    # The string used in the config to denote a particular instance. Will be
    # overridden during instantiation.
    instance_name = None

    # The machine-readable name of this collection.
    collection = None

    # A value of True means the storage does not support write-methods such as
    # upload, update and delete.  A value of False means the storage does
    # support those methods.
    read_only = False

    # The attribute values to show in the representation of the storage.
    _repr_attributes = ()

    def __init__(self, instance_name=None, read_only=None, collection=None):
        if read_only is None:
            read_only = self.read_only
        if self.read_only and not read_only:
            raise exceptions.UserError('This storage can only be read-only.')
        self.read_only = bool(read_only)

        if collection and instance_name:
            # XXX: PY2 hack
            instance_name = '{}/{}'.format(instance_name,
                                           to_native(collection, 'utf-8'))
        self.instance_name = instance_name
        self.collection = collection

    @classmethod
    def discover(cls, **kwargs):
        '''Discover collections given a basepath or -URL to many collections.

        :param **kwargs: Keyword arguments to additionally pass to the storage
            instances returned. You shouldn't pass `collection` here, otherwise
            TypeError will be raised.
        :returns: iterable of ``storage_args``.
            ``storage_args`` is a dictionary of ``**kwargs`` to pass to this
            class to obtain a storage instance pointing to this collection. It
            also must contain a ``"collection"`` key.  That key's value is used
            to match two collections together for synchronization. IOW it is a
            machine-readable identifier for the collection, usually obtained
            from the last segment of a URL or filesystem path.

        '''
        raise NotImplementedError()

    @classmethod
    def create_collection(cls, collection, **kwargs):
        '''
{'fraction_non_alphanumeric': 0.07320530996262405, 'fraction_numerical': 0.0005155303518494652, 'mean_word_length': 2.689966714217784, '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': '4410372', 'n_tokens_mistral': 2020, 'n_tokens_neox': 1955, 'n_words': 827}