Пример #1
0
 def set_context(self, context=None, **kwargs):
     if context is None:
         context = {}
     manager = _AttributeManager(context=self.context)
     ctx = self.context.copy()
     ctx.update(context)
     if kwargs:
         ctx.update(kwargs)
     self.context = ImmutableDict(ctx)
     return manager
Пример #2
0
 def set_user(self, user, set_context=False):
     if user != 0 and set_context:
         raise ValueError('set_context only allowed for root')
     manager = _AttributeManager(user=self.user, context=self.context)
     ctx = self.context.copy()
     if set_context:
         if user != self.user:
             ctx['user'] = self.user
     else:
         ctx.pop('user', None)
     self.context = ImmutableDict(ctx)
     self.user = user
     return manager
Пример #3
0
 def start(self,
           database_name,
           user,
           readonly=False,
           context=None,
           close=False,
           autocommit=False):
     '''
     Start transaction
     '''
     from trytond import backend
     assert self.user is None
     assert self.database is None
     assert self.close is None
     assert self.context is None
     # Compute started_at before connect to ensure
     # it is strictly before all transactions started after
     # but it may be also before transactions started before
     self.started_at = self.monotonic_time()
     if not database_name:
         database = backend.Database().connect()
     else:
         database = backend.Database(database_name).connect()
     Flavor.set(backend.Database.flavor)
     self.connection = database.get_connection(readonly=readonly,
                                               autocommit=autocommit)
     self.user = user
     self.database = database
     self.readonly = readonly
     self.close = close
     self.context = ImmutableDict(context or {})
     self.create_records = defaultdict(set)
     self.delete_records = defaultdict(set)
     self.trigger_records = defaultdict(set)
     self.timestamp = {}
     self.counter = 0
     self._datamanagers = []
     if database_name:
         from trytond.cache import Cache
         try:
             Cache.sync(self)
         except BaseException:
             self.stop(False)
             raise
     return self
Пример #4
0
 def get(self, ids, model, name, values=None):
     dicts = dict((id, None) for id in ids)
     for value in values or []:
         data = value[name]
         if data:
             # If stored as JSON conversion is done on backend
             if isinstance(data, str):
                 data = json.loads(data, object_hook=JSONDecoder())
             for key, val in data.items():
                 if isinstance(val, list):
                     data[key] = tuple(val)
             dicts[value['id']] = ImmutableDict(data)
     return dicts
Пример #5
0
 def __set__(self, inst, value):
     if value:
         value = ImmutableDict(value)
     super().__set__(inst, value)
Пример #6
0
class Transaction(object):
    '''
    Control the transaction
    '''

    _local = _Local()

    cache_keys = {'language', 'fuzzy_translation', '_datetime'}

    database = None
    readonly = False
    connection = None
    close = None
    user = None
    context = None
    create_records = None
    delete_records = None
    trigger_records = None
    timestamp = None
    started_at = None

    def __new__(cls, new=False):
        from trytond.pool import Pool
        from trytond.cache import LRUDict
        transactions = cls._local.transactions
        if new or not transactions:
            instance = super(Transaction, cls).__new__(cls)
            instance.cache = LRUDict(
                _cache_transaction,
                lambda: LRUDict(_cache_model,
                                lambda name: LRUDict(
                                    record_cache_size(instance),
                                    Pool().get(name)._record),
                                default_factory_with_key=True))
            instance._atexit = []
            transactions.append(instance)
        else:
            instance = transactions[-1]
        return instance

    @staticmethod
    def monotonic_time():
        try:
            return time.monotonic_ns()
        except AttributeError:
            return time.monotonic()

    @property
    def tasks(self):
        return self._local.tasks

    def get_cache(self):
        keys = tuple(((key, self.context[key])
                      for key in sorted(self.cache_keys)
                      if key in self.context))
        return self.cache[(self.user, keys)]

    def start(self,
              database_name,
              user,
              readonly=False,
              context=None,
              close=False,
              autocommit=False):
        '''
        Start transaction
        '''
        from trytond import backend
        assert self.user is None
        assert self.database is None
        assert self.close is None
        assert self.context is None
        # Compute started_at before connect to ensure
        # it is strictly before all transactions started after
        # but it may be also before transactions started before
        self.started_at = self.monotonic_time()
        if not database_name:
            database = backend.Database().connect()
        else:
            database = backend.Database(database_name).connect()
        Flavor.set(backend.Database.flavor)
        self.connection = database.get_connection(readonly=readonly,
                                                  autocommit=autocommit)
        self.user = user
        self.database = database
        self.readonly = readonly
        self.close = close
        self.context = ImmutableDict(context or {})
        self.create_records = defaultdict(set)
        self.delete_records = defaultdict(set)
        self.trigger_records = defaultdict(set)
        self.timestamp = {}
        self.counter = 0
        self._datamanagers = []
        if database_name:
            from trytond.cache import Cache
            try:
                Cache.sync(self)
            except BaseException:
                self.stop(False)
                raise
        return self

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.stop(type is None)

    def stop(self, commit=False):
        transactions = self._local.transactions
        try:
            if transactions.count(self) == 1:
                try:
                    try:
                        # Transaction must be commited to send notifications
                        if commit and (not self.readonly
                                       or self.database.has_channel()):
                            self.commit()
                        else:
                            self.rollback()
                    finally:
                        self.database.put_connection(self.connection,
                                                     self.close)
                finally:
                    self.database = None
                    self.readonly = False
                    self.connection = None
                    self.close = None
                    self.user = None
                    self.context = None
                    self.create_records = None
                    self.delete_records = None
                    self.trigger_records = None
                    self.timestamp = None
                    self._datamanagers = []

                for func, args, kwargs in self._atexit:
                    func(*args, **kwargs)
        finally:
            transactions.reverse()
            try:
                transactions.remove(self)
            finally:
                transactions.reverse()

    def set_context(self, context=None, **kwargs):
        if context is None:
            context = {}
        manager = _AttributeManager(context=self.context)
        ctx = self.context.copy()
        ctx.update(context)
        if kwargs:
            ctx.update(kwargs)
        self.context = ImmutableDict(ctx)
        return manager

    def reset_context(self):
        manager = _AttributeManager(context=self.context)
        self.context = ImmutableDict()
        return manager

    def set_user(self, user, set_context=False):
        if user != 0 and set_context:
            raise ValueError('set_context only allowed for root')
        manager = _AttributeManager(user=self.user, context=self.context)
        ctx = self.context.copy()
        if set_context:
            if user != self.user:
                ctx['user'] = self.user
        else:
            ctx.pop('user', None)
        self.context = ImmutableDict(ctx)
        self.user = user
        return manager

    def set_current_transaction(self, transaction):
        self._local.transactions.append(transaction)
        return transaction

    def new_transaction(self, autocommit=False, readonly=False):
        transaction = Transaction(new=True)
        return transaction.start(self.database.name,
                                 self.user,
                                 context=self.context,
                                 close=self.close,
                                 readonly=readonly,
                                 autocommit=autocommit)

    def commit(self):
        from trytond.cache import Cache
        try:
            if self._datamanagers:
                for datamanager in self._datamanagers:
                    datamanager.tpc_begin(self)
                for datamanager in self._datamanagers:
                    datamanager.commit(self)
                for datamanager in self._datamanagers:
                    datamanager.tpc_vote(self)
            self.started_at = self.monotonic_time()
            Cache.commit(self)
            self.connection.commit()
        except Exception:
            self.rollback()
            raise
        else:
            try:
                for datamanager in self._datamanagers:
                    datamanager.tpc_finish(self)
            except Exception:
                logger.critical(
                    'A datamanager raised an exception in'
                    ' tpc_finish, the data might be inconsistant',
                    exc_info=True)

    def rollback(self):
        from trytond.cache import Cache
        for cache in self.cache.values():
            cache.clear()
        for datamanager in self._datamanagers:
            datamanager.tpc_abort(self)
        Cache.rollback(self)
        self.connection.rollback()

    def join(self, datamanager):
        try:
            idx = self._datamanagers.index(datamanager)
            return self._datamanagers[idx]
        except ValueError:
            self._datamanagers.append(datamanager)
            return datamanager

    def atexit(self, func, *args, **kwargs):
        self._atexit.append((func, args, kwargs))

    @property
    def language(self):
        def get_language():
            from trytond.pool import Pool
            Config = Pool().get('ir.configuration')
            return Config.get_language()

        if self.context:
            return self.context.get('language') or get_language()
        return get_language()
Пример #7
0
 def reset_context(self):
     manager = _AttributeManager(context=self.context)
     self.context = ImmutableDict()
     return manager
Пример #8
0
 def test_immutable_dict(self):
     "Test ImmutableDict"
     self.dumps_loads(ImmutableDict(foo='bar'))
Пример #9
0
    def test_setdefault(self):
        "setdefault not allowed"
        d = ImmutableDict()

        with self.assertRaises(TypeError):
            d.setdefault('foo', 'bar')
Пример #10
0
    def test_update(self):
        "update not allowed"
        d = ImmutableDict()

        with self.assertRaises(TypeError):
            d.update({'foo': 'bar'})
Пример #11
0
    def test_popitem(self):
        "popitem not allowed"
        d = ImmutableDict(foo='bar')

        with self.assertRaises(TypeError):
            d.popitem('foo')
Пример #12
0
    def test_clear(self):
        "clear not allowed"
        d = ImmutableDict(foo='bar')

        with self.assertRaises(TypeError):
            d.clear()
Пример #13
0
    def test_ior(self):
        "__ior__ not allowed"
        d = ImmutableDict()

        with self.assertRaises(TypeError):
            d |= {'foo': 'bar'}
Пример #14
0
    def test_delitem(self):
        "__delitem__ not allowed"
        d = ImmutableDict(foo='bar')

        with self.assertRaises(TypeError):
            del d['foo']
Пример #15
0
    def test_setitem(self):
        "__setitem__ not allowed"
        d = ImmutableDict()

        with self.assertRaises(TypeError):
            d['foo'] = 'bar'