Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(229)

Unified Diff: commit-queue/model.py

Issue 135363007: Delete public commit queue to avoid confusion after move to internal repo (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « commit-queue/loop.sh ('k') | commit-queue/natsort.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: commit-queue/model.py
===================================================================
--- commit-queue/model.py (revision 249146)
+++ commit-queue/model.py (working copy)
@@ -1,406 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Defines the PersistentMixIn utility class to easily convert classes to and
-from dict for serialization.
-
-This class is aimed at json-compatible serialization, so it supports the limited
-set of structures supported by json; strings, numbers as int or float, list and
-dictionaries.
-
-PersistentMixIn._persistent_members() returns a dict of each member with the
-tuple of expected types. Each member can be decoded in multiple types, for
-example, a subversion revision number could have (None, int, str), meaning that
-the revision could be None, when not known, an int or the int as a string
-representation. The tuple is listed in the prefered order of conversions.
-
-Composites types that cannot be represented exactly in json like tuple, set and
-frozenset are converted from and back to list automatically. Any class instance
-that has been serialized can be unserialized in the same class instance or into
-a bare dict.
-
-See tests/model_tests.py for examples.
-"""
-
-import json
-import logging
-import os
-
-# Set in the output dict to be able to know which class was serialized to help
-# deserialization.
-TYPE_FLAG = '__persistent_type__'
-
-# Marker to tell the deserializer that we don't know the expected type, used in
-# composite types.
-_UNKNOWN = object()
-
-
-def as_dict(value):
- """Recursively converts an object into a dictionary.
-
- Converts tuple,set,frozenset into list and recursively process each items.
- """
- if hasattr(value, 'as_dict') and callable(value.as_dict):
- return value.as_dict()
- elif isinstance(value, (list, tuple, set, frozenset)):
- return [as_dict(v) for v in value]
- elif isinstance(value, dict):
- return dict((as_dict(k), as_dict(v))
- for k, v in value.iteritems())
- elif isinstance(value, (bool, float, int, basestring)) or value is None:
- return value
- else:
- raise AttributeError('Can\'t type %s into a dictionary' % type(value))
-
-
-def _inner_from_dict(name, value, member_types):
- """Recursively regenerates an object.
-
- For each of the allowable types, try to convert it. If None is an allowable
- type, any data that can't be parsed will be parsed as None and will be
- silently discarded. Otherwise, an exception will be raise.
- """
- logging.debug('_inner_from_dict(%s, %r, %s)', name, value, member_types)
- result = None
- if member_types is _UNKNOWN:
- # Use guesswork a bit more and accept anything.
- if isinstance(value, dict):
- if TYPE_FLAG in value:
- result = PersistentMixIn.from_dict(value, _UNKNOWN)
- else:
- # Unserialize it as a raw dict.
- result = dict(
- (_inner_from_dict(None, k, _UNKNOWN),
- _inner_from_dict(None, v, _UNKNOWN))
- for k, v in value.iteritems())
- elif isinstance(value, list):
- # All of these are serialized to list.
- result = [_inner_from_dict(None, v, _UNKNOWN) for v in value]
- elif isinstance(value, (bool, float, int, unicode)):
- result = value
- else:
- raise TypeError('No idea how to convert %r' % value)
- else:
- for member_type in member_types:
- # Explicitly leave None out of this loop.
- if issubclass(member_type, PersistentMixIn):
- if isinstance(value, dict) and TYPE_FLAG in value:
- result = PersistentMixIn.from_dict(value, member_type)
- break
- elif member_type is dict:
- if isinstance(value, dict):
- result = dict(
- (_inner_from_dict(None, k, _UNKNOWN),
- _inner_from_dict(None, v, _UNKNOWN))
- for k, v in value.iteritems())
- break
- elif member_type in (list, tuple, set, frozenset):
- # All of these are serialized to list.
- if isinstance(value, list):
- result = member_type(
- _inner_from_dict(None, v, _UNKNOWN) for v in value)
- break
- elif member_type in (bool, float, int, str, unicode):
- if isinstance(value, member_type):
- result = member_type(value)
- break
- elif member_type is None.__class__ and value is None:
- result = None
- break
- else:
- logging.info(
- 'Ignored %s: didn\'t fit types %s; %s',
- name,
- ', '.join(i.__name__ for i in member_types),
- repr(value)[:200])
- _check_type_value(name, result, member_types)
- return result
-
-
-def to_yaml(obj):
- """Converts a PersistentMixIn into a yaml-inspired format.
-
- Warning: Not unit tested, use at your own risk!
- """
- def align(x):
- y = x.splitlines(True)
- if len(y) > 1:
- return ''.join(y[0:1] + [' ' + z for z in y[1:]])
- return x
- def align_value(x):
- if '\n' in x:
- return '\n ' + align(x)
- return x
-
- if hasattr(obj, 'as_dict') and callable(obj.as_dict):
- out = (to_yaml(obj.as_dict()),)
- elif isinstance(obj, (bool, float, int, unicode)) or obj is None:
- out = (align(str(obj)),)
- elif isinstance(obj, dict):
- if TYPE_FLAG in obj:
- out = ['%s:' % obj[TYPE_FLAG]]
- else:
- out = []
- for k, v in obj.iteritems():
- # Skips many members resolving to bool() == False
- if k.startswith('__') or v in (None, '', False, 0):
- continue
- r = align_value(to_yaml(v))
- if not r:
- continue
- out.append('- %s: %s' % (k, r))
- elif hasattr(obj, '__iter__') and callable(obj.__iter__):
- out = ['- %s' % align(to_yaml(x)) for x in obj]
- else:
- out = ('%s' % obj.__class__.__name__,)
- return '\n'.join(out)
-
-
-def _default_value(member_types):
- """Returns an instance of the first allowed type. Special case None."""
- if member_types[0] is None.__class__:
- return None
- else:
- return member_types[0]()
-
-
-def _check_type_value(name, value, member_types):
- """Raises a TypeError exception if value is not one of the allowed types in
- member_types.
- """
- if not isinstance(value, member_types):
- prefix = '%s e' % name if name else 'E'
- raise TypeError(
- '%sxpected type(s) %s; got %r' %
- (prefix, ', '.join(i.__name__ for i in member_types), value))
-
-
-
-class PersistentMixIn(object):
- """Class to be used as a base class to persistent data in a simplistic way.
-
- Persistent class member needs to be set to a tuple containing the instance
- member variable that needs to be saved or loaded. The first item will be
- default value, e.g.:
- foo = (None, str, dict)
- Will default initialize self.foo to None.
- """
- # Cache of all the subclasses of PersistentMixIn.
- __persistent_classes_cache = None
-
- _read_only = False
-
- def __init__(self, **kwargs):
- """Initializes with the default members."""
- super(PersistentMixIn, self).__init__()
- persistent_members = self._persistent_members()
- for member, member_types in persistent_members.iteritems():
- if member in kwargs:
- value = kwargs.pop(member)
- if isinstance(value, str):
- # Assume UTF-8 all the time. Note: This is explicitly when the object
- # is constructed in the code. This code path is never used when
- # deserializing the object.
- value = value.decode('utf-8')
- else:
- value = _default_value(member_types)
- _check_type_value(member, value, member_types)
- setattr(self, member, value)
- if kwargs:
- raise AttributeError('Received unexpected initializers: %s' % kwargs)
-
- def __setattr__(self, name, value):
- """Enforces immutability if cls._read_only is True."""
- if self._read_only:
- raise TypeError()
- return super(PersistentMixIn, self).__setattr__(name, value)
-
- @classmethod
- def _persistent_members(cls):
- """Returns the persistent items as a dict.
-
- Each entry value can be a tuple when the member can be assigned different
- types.
- """
- # Note that here, cls is the subclass, not PersistentMixIn.
- # TODO(maruel): Cache the results. It's tricky because setting
- # cls.__persistent_members_cache on a class will implicitly set it on its
- # subclass. So in a class hierarchy with A -> B -> PersistentMixIn, calling
- # B()._persistent_members() will incorrectly set the cache for A.
- persistent_members_cache = {}
- # Enumerate on the subclass, not on an instance.
- for item in dir(cls):
- if item.startswith('_'):
- continue
- item_value = getattr(cls, item)
- if isinstance(item_value, type):
- item_value = (item_value,)
- if not isinstance(item_value, tuple):
- continue
- if not all(i is None or i.__class__ == type for i in item_value):
- continue
- if any(i is str for i in item_value):
- raise TypeError(
- '%s is type \'str\' which is currently not supported' % item)
- item_value = tuple(
- f if f is not None else None.__class__ for f in item_value)
- persistent_members_cache[item] = item_value
- return persistent_members_cache
-
- @staticmethod
- def _get_subclass(typename):
- """Returns the PersistentMixIn subclass with the name |typename|."""
- subclass = None
- if PersistentMixIn.__persistent_classes_cache is not None:
- subclass = PersistentMixIn.__persistent_classes_cache.get(typename)
- if not subclass:
- # Get the subclasses recursively.
- PersistentMixIn.__persistent_classes_cache = {}
- def recurse(c):
- for s in c.__subclasses__():
- assert s.__name__ not in PersistentMixIn.__persistent_classes_cache
- PersistentMixIn.__persistent_classes_cache[s.__name__] = s
- recurse(s)
- recurse(PersistentMixIn)
-
- subclass = PersistentMixIn.__persistent_classes_cache.get(typename)
- if not subclass:
- raise KeyError('Couldn\'t find type %s' % typename)
- return subclass
-
- def as_dict(self):
- """Create a dictionary out of this object, i.e. Serialize the object."""
- out = {}
- for member, member_types in self._persistent_members().iteritems():
- value = getattr(self, member)
- _check_type_value(member, value, member_types)
- out[member] = as_dict(value)
- out[TYPE_FLAG] = self.__class__.__name__
- return out
-
- @staticmethod
- def from_dict(data, subclass=_UNKNOWN):
- """Returns an instance of a class inheriting from PersistentMixIn,
- initialized with 'data' dict, i.e. Deserialize the object.
- """
- logging.debug('from_dict(%r, %s)', data, subclass)
- if subclass is _UNKNOWN:
- subclass = PersistentMixIn._get_subclass(data[TYPE_FLAG])
- # This initializes the instance with the default values.
-
- # pylint: disable=W0212
- kwargs = {}
- for member, member_types in subclass._persistent_members().iteritems():
- if member in data:
- try:
- value = _inner_from_dict(member, data[member], member_types)
- except TypeError:
- # pylint: disable=E1103
- logging.error(
- 'Failed to instantiate %s because of member %s',
- subclass.__name__, member)
- raise
- else:
- value = _default_value(member_types)
- _check_type_value(member, value, member_types)
- kwargs[member] = value
- try:
- obj = subclass(**kwargs)
- except TypeError:
- # pylint: disable=E1103
- logging.error('Failed to instantiate %s: %r', subclass.__name__, kwargs)
- raise
- assert isinstance(obj, PersistentMixIn) and obj.__class__ != PersistentMixIn
- return obj
-
- def __str__(self):
- return to_yaml(self)
-
- def __eq__(self, _):
- raise TypeError()
-
- # pylint: disable=R0201
- def __ne__(self, _):
- raise TypeError()
-
-
-def is_equivalent(lhs, rhs):
- """Implements the equivalent of __eq__.
-
- The reason for not implementing __eq__ is to not encourage bad behavior by
- implicitly and recursively using __eq__() in a list().remove() call.
- """
- # pylint: disable=W0212
- if lhs._persistent_members() != rhs._persistent_members():
- return False
- for i in lhs._persistent_members():
- if getattr(lhs, i) != getattr(rhs, i):
- return False
- return True
-
-
-def immutable(func):
- """Member function decorators that convert 'self' to an immutable object.
-
- Member functions of the object can't be called unless they are immutable too.
- Properties can be looked up, this function assumes properties do not mutate
- the object.
-
- Note: a user can still call classmethod and do mutation on the class, or they
- can lookup a member object and mutate this one. Don't be silly.
- """
- class Immutable(object):
- def __init__(self, obj):
- object.__setattr__(self, '__ref', obj)
-
- def __getattribute__(self, name):
- ref = object.__getattribute__(self, '__ref')
- value = getattr(ref, name)
- if not callable(value):
- return value
- if getattr(value, 'is_immutable', None):
- # It is immutable too.
- return value
- if getattr(value, 'im_self', None) == None:
- # It is static.
- return value
- raise TypeError(
- 'Can\'t call mutable member function \'%s\' on an immutable '
- 'instance of %s' % (name, ref.__class__.__name__))
-
- def __setattr__(self, name, _value):
- ref = object.__getattribute__(self, '__ref')
- raise TypeError(
- 'Can\'t change attribute \'%s\' on an immutable instance of \'%s\'' %
- (name, ref.__class__.__name__))
-
- def __delattr__(self, name):
- ref = object.__getattribute__(self, '__ref')
- raise TypeError(
- 'Can\'t delete attribute \'%s\' on an immutable instance of \'%s\'' %
- (name, ref.__class__.__name__))
-
- def hook(self, *args, **kwargs):
- return func(Immutable(self), *args, **kwargs)
-
- hook.is_immutable = True
- return hook
-
-
-def load_from_json_file(filename):
- """Loads one object from a JSON file."""
- with open(filename, 'r') as f:
- return PersistentMixIn.from_dict(json.load(f))
-
-
-def save_to_json_file(filename, obj):
- """Save one object in a JSON file."""
- try:
- old = filename + '.old'
- if os.path.exists(filename):
- os.rename(filename, old)
- finally:
- with open(filename, 'wb') as f:
- json.dump(obj.as_dict(), f, sort_keys=True, indent=2)
- f.write('\n')
« no previous file with comments | « commit-queue/loop.sh ('k') | commit-queue/natsort.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698