Move Cache in trytond.cache and convert outside tools for issue1653
This commit is contained in:
1 parent
3a9ae031b8
commit
aa5816be0f
15 files changed
+205
-194
No files matched your search
@@ -0,0 +1,168 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import inspect
|
||||
import time
|
||||
from threading import Lock
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.config import CONFIG
|
||||
from trytond.backend import Database
|
||||
|
||||
|
||||
class Cache(object):
|
||||
"""
|
||||
Use it as a decorator of the function you plan to cache
|
||||
Timeout: 0 = no timeout, otherwise in seconds
|
||||
"""
|
||||
_cache_instance = []
|
||||
_resets = {}
|
||||
_resets_lock = Lock()
|
||||
|
||||
def __init__(self, name, timeout=3600, max_len=1024):
|
||||
self.timeout = timeout
|
||||
self.max_len = max_len
|
||||
self._cache = {}
|
||||
self._cache_instance.append(self)
|
||||
self._name = name
|
||||
self._timestamp = None
|
||||
self._lock = Lock()
|
||||
|
||||
def __call__(self, function):
|
||||
arg_names = inspect.getargspec(function)[0]
|
||||
|
||||
def reset():
|
||||
dbname = Transaction().cursor.dbname
|
||||
Cache.reset(dbname, self._name)
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache[dbname] = {}
|
||||
finally:
|
||||
self._lock.release()
|
||||
return True
|
||||
|
||||
def call(model, *args, **kwargs):
|
||||
result = None
|
||||
find = False
|
||||
cursor = Transaction().cursor
|
||||
# Update named arguments with positional argument values
|
||||
kwargs_origin = kwargs.copy()
|
||||
kwargs.update(dict(zip(arg_names, args)))
|
||||
kwargs = kwargs.items()
|
||||
kwargs.sort()
|
||||
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache.setdefault(cursor.dbname, {})
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
lower = None
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if len(self._cache[cursor.dbname]) > self.max_len:
|
||||
mintime = time.time() - self.timeout
|
||||
for key in self._cache[cursor.dbname].keys():
|
||||
last_time = self._cache[cursor.dbname][key][1]
|
||||
if mintime > last_time:
|
||||
del self._cache[cursor.dbname][key]
|
||||
else:
|
||||
if not lower or lower[1] > last_time:
|
||||
lower = (key, last_time)
|
||||
if (len(self._cache[cursor.dbname]) > self.max_len
|
||||
and lower):
|
||||
del self._cache[cursor.dbname][lower[0]]
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
# Work out key as a tuple
|
||||
key = (id(model), Transaction().user, repr(Transaction().context),
|
||||
repr(kwargs))
|
||||
|
||||
# Check cache and return cached value if possible
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if key in self._cache[cursor.dbname]:
|
||||
(value, last_time) = self._cache[cursor.dbname][key]
|
||||
mintime = time.time() - self.timeout
|
||||
if self.timeout <= 0 or mintime <= last_time:
|
||||
self._cache[cursor.dbname][key] = (value,
|
||||
time.time())
|
||||
result = value
|
||||
find = True
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
if not find:
|
||||
# Work out new value, cache it and return it
|
||||
result = function(model, *args, **kwargs_origin)
|
||||
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache[cursor.dbname][key] = (result, time.time())
|
||||
finally:
|
||||
self._lock.release()
|
||||
return result
|
||||
|
||||
call.__doc__ = function.__doc__
|
||||
call.reset = reset
|
||||
return call
|
||||
|
||||
@staticmethod
|
||||
def clean(dbname):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
database = Database(dbname).connect()
|
||||
cursor = database.cursor()
|
||||
try:
|
||||
cursor.execute('SELECT "timestamp", "name" FROM ir_cache')
|
||||
timestamps = {}
|
||||
for timestamp, name in cursor.fetchall():
|
||||
timestamps[name] = timestamp
|
||||
finally:
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
for obj in Cache._cache_instance:
|
||||
if obj._name in timestamps:
|
||||
if not obj._timestamp or timestamps[obj._name] > obj._timestamp:
|
||||
obj._timestamp = timestamps[obj._name]
|
||||
obj._lock.acquire()
|
||||
try:
|
||||
obj._cache[dbname] = {}
|
||||
finally:
|
||||
obj._lock.release()
|
||||
|
||||
@staticmethod
|
||||
def reset(dbname, name):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
Cache._resets_lock.acquire()
|
||||
try:
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
Cache._resets[dbname].add(name)
|
||||
finally:
|
||||
Cache._resets_lock.release()
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def resets(dbname):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
database = Database(dbname).connect()
|
||||
cursor = database.cursor()
|
||||
Cache._resets_lock.acquire()
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
try:
|
||||
for name in Cache._resets[dbname]:
|
||||
cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
|
||||
(name,))
|
||||
if cursor.fetchone():
|
||||
cursor.execute('UPDATE ir_cache SET "timestamp" = %s '\
|
||||
'WHERE name = %s', (datetime.datetime.now(), name))
|
||||
else:
|
||||
cursor.execute('INSERT INTO ir_cache ' \
|
||||
'("timestamp", "name") ' \
|
||||
'VALUES (%s, %s)', (datetime.datetime.now(), name))
|
||||
Cache._resets[dbname].clear()
|
||||
finally:
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
Cache._resets_lock.release()
|
||||
File renamed without changes.
@@ -4,10 +4,11 @@ from __future__ import with_statement
|
||||
import base64
|
||||
import os
|
||||
from trytond.model import ModelView, ModelSQL, ModelStorage, fields
|
||||
from trytond.tools import file_open, Cache, safe_eval
|
||||
from trytond.tools import file_open, safe_eval
|
||||
from trytond.backend import TableHandler
|
||||
from trytond.pyson import PYSONEncoder, CONTEXT, PYSON
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
|
||||
class Action(ModelSQL, ModelView):
|
||||
|
||||
@@ -4,9 +4,10 @@ from decimal import Decimal
|
||||
import datetime
|
||||
import time
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.tools import Cache, safe_eval
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.pyson import Eval
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
|
||||
class Default(ModelSQL, ModelView):
|
||||
|
||||
+1
-1
@@ -4,10 +4,10 @@ import datetime
|
||||
import base64
|
||||
import re
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.tools import Cache
|
||||
from trytond.report import Report
|
||||
from trytond.wizard import Wizard
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
IDENTIFIER = re.compile(r'^[a-zA-z_][a-zA-Z0-9_]*$')
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -4,9 +4,10 @@ from __future__ import with_statement
|
||||
import contextlib
|
||||
import time
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.tools import Cache, safe_eval
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.pyson import Eval, Get
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
|
||||
class RuleGroup(ModelSQL, ModelView):
|
||||
|
||||
@@ -4,10 +4,11 @@ import datetime
|
||||
import time
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.pyson import Eval, Or
|
||||
from trytond.tools import Cache, safe_eval
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.backend import TableHandler
|
||||
from trytond.tools import reduce_ids
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
|
||||
class Trigger(ModelSQL, ModelView):
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
from threading import Lock
|
||||
import time
|
||||
from trytond.tools import Cache
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
|
||||
class Cacheable(object):
|
||||
|
||||
@@ -8,10 +8,10 @@ except ImportError:
|
||||
import md5
|
||||
import copy
|
||||
from trytond.model import Model
|
||||
from trytond.tools import Cache, safe_eval
|
||||
from trytond.modules import create_graph, get_module_list
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.pyson import PYSONEncoder, CONTEXT
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
def _find(tree, element):
|
||||
if element.tag == 'xpath':
|
||||
@@ -82,6 +82,7 @@ class ModelView(Model):
|
||||
ModelView.__modules_list = None
|
||||
|
||||
def _get_modules_list(self):
|
||||
from trytond.modules import create_graph, get_module_list
|
||||
if ModelView.__modules_list:
|
||||
return ModelView.__modules_list
|
||||
graph = create_graph(get_module_list())[0]
|
||||
|
||||
@@ -9,10 +9,11 @@ import traceback
|
||||
import logging
|
||||
import contextlib
|
||||
from trytond.backend import Database
|
||||
from trytond.tools import Cache
|
||||
import trytond.tools as tools
|
||||
from trytond.config import CONFIG
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
import trytond.convert as convert
|
||||
|
||||
OPJ = os.path.join
|
||||
MODULES_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
@@ -234,7 +235,7 @@ def load_module_graph(graph, pool, lang=None):
|
||||
models_to_update_history.add(model._name)
|
||||
|
||||
#Instanciate a new parser for the package:
|
||||
tryton_parser = tools.TrytondXmlHandler(pool=pool, module=module)
|
||||
tryton_parser = convert.TrytondXmlHandler(pool=pool, module=module)
|
||||
|
||||
for filename in package.datas.get('xml', []):
|
||||
filename = filename.replace('/', os.sep)
|
||||
@@ -302,7 +303,7 @@ def load_module_graph(graph, pool, lang=None):
|
||||
# Vacuum :
|
||||
while modules_todo:
|
||||
(module, to_delete) = modules_todo.pop()
|
||||
tools.post_import(pool, module, to_delete)
|
||||
convert.post_import(pool, module, to_delete)
|
||||
|
||||
|
||||
cursor.commit()
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
from __future__ import with_statement
|
||||
from trytond.pool import Pool
|
||||
from trytond import security
|
||||
from trytond.tools import Cache
|
||||
from trytond.backend import Database
|
||||
from trytond.config import CONFIG
|
||||
from trytond.version import VERSION
|
||||
from trytond.monitor import monitor
|
||||
from trytond.transaction import Transaction
|
||||
import traceback
|
||||
import logging
|
||||
import time
|
||||
@@ -21,6 +13,14 @@ except ImportError:
|
||||
import sha
|
||||
import base64
|
||||
import pydoc
|
||||
from trytond.pool import Pool
|
||||
from trytond import security
|
||||
from trytond.backend import Database
|
||||
from trytond.config import CONFIG
|
||||
from trytond.version import VERSION
|
||||
from trytond.monitor import monitor
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
|
||||
def dispatch(host, port, protocol, database_name, user, session, object_type,
|
||||
object_name, method, *args, **kargs):
|
||||
|
||||
@@ -22,10 +22,11 @@ from trytond.protocols.sslsocket import SSLSocket
|
||||
from trytond.config import CONFIG
|
||||
from trytond.security import login
|
||||
from trytond.version import PACKAGE, VERSION, WEBSITE
|
||||
from trytond.tools.misc import Cache, LocalDict
|
||||
from trytond.tools.misc import LocalDict
|
||||
from trytond.backend import Database
|
||||
from trytond.pool import Pool
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
_TRYTON_RELOAD = False
|
||||
domimpl = xml.dom.minidom.getDOMImplementation()
|
||||
|
||||
|
||||
+7
-6
@@ -3,12 +3,6 @@
|
||||
"User"
|
||||
from __future__ import with_statement
|
||||
import copy
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.wizard import Wizard
|
||||
from trytond.tools import Cache, safe_eval
|
||||
from trytond.backend import TableHandler
|
||||
from trytond.security import get_connections
|
||||
from trytond.transaction import Transaction
|
||||
import string
|
||||
import random
|
||||
try:
|
||||
@@ -16,6 +10,13 @@ try:
|
||||
except ImportError:
|
||||
hashlib = None
|
||||
import sha
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
from trytond.wizard import Wizard
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.backend import TableHandler
|
||||
from trytond.security import get_connections
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.cache import Cache
|
||||
from trytond.pyson import Eval, Greater
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
from misc import *
|
||||
from convert import *
|
||||
from datetime_strftime import *
|
||||
+3
-167
@@ -4,22 +4,18 @@
|
||||
"""
|
||||
Miscelleanous tools used by tryton
|
||||
"""
|
||||
import os, time, sys
|
||||
import inspect
|
||||
from trytond.config import CONFIG
|
||||
import os, sys
|
||||
import subprocess
|
||||
import zipfile
|
||||
from trytond.backend import Database
|
||||
from threading import Lock, local
|
||||
from threading import local
|
||||
import smtplib
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
import dis
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.config import CONFIG
|
||||
|
||||
def find_in_path(name):
|
||||
if os.name == "nt":
|
||||
@@ -172,166 +168,6 @@ def find_language_context(args, kargs=None):
|
||||
res = arg.get('language', 'en_US')
|
||||
return kargs.get('context', {}).get('language', res)
|
||||
|
||||
|
||||
class Cache(object):
|
||||
"""
|
||||
Use it as a decorator of the function you plan to cache
|
||||
Timeout: 0 = no timeout, otherwise in seconds
|
||||
"""
|
||||
_cache_instance = []
|
||||
_resets = {}
|
||||
_resets_lock = Lock()
|
||||
|
||||
def __init__(self, name, timeout=3600, max_len=1024):
|
||||
self.timeout = timeout
|
||||
self.max_len = max_len
|
||||
self._cache = {}
|
||||
self._cache_instance.append(self)
|
||||
self._name = name
|
||||
self._timestamp = None
|
||||
self._lock = Lock()
|
||||
|
||||
def __call__(self, function):
|
||||
arg_names = inspect.getargspec(function)[0]
|
||||
|
||||
def reset():
|
||||
dbname = Transaction().cursor.dbname
|
||||
Cache.reset(dbname, self._name)
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache[dbname] = {}
|
||||
finally:
|
||||
self._lock.release()
|
||||
return True
|
||||
|
||||
def call(model, *args, **kwargs):
|
||||
result = None
|
||||
find = False
|
||||
cursor = Transaction().cursor
|
||||
# Update named arguments with positional argument values
|
||||
kwargs_origin = kwargs.copy()
|
||||
kwargs.update(dict(zip(arg_names, args)))
|
||||
kwargs = kwargs.items()
|
||||
kwargs.sort()
|
||||
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache.setdefault(cursor.dbname, {})
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
lower = None
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if len(self._cache[cursor.dbname]) > self.max_len:
|
||||
mintime = time.time() - self.timeout
|
||||
for key in self._cache[cursor.dbname].keys():
|
||||
last_time = self._cache[cursor.dbname][key][1]
|
||||
if mintime > last_time:
|
||||
del self._cache[cursor.dbname][key]
|
||||
else:
|
||||
if not lower or lower[1] > last_time:
|
||||
lower = (key, last_time)
|
||||
if (len(self._cache[cursor.dbname]) > self.max_len
|
||||
and lower):
|
||||
del self._cache[cursor.dbname][lower[0]]
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
# Work out key as a tuple
|
||||
key = (id(model), Transaction().user, repr(Transaction().context),
|
||||
repr(kwargs))
|
||||
|
||||
# Check cache and return cached value if possible
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if key in self._cache[cursor.dbname]:
|
||||
(value, last_time) = self._cache[cursor.dbname][key]
|
||||
mintime = time.time() - self.timeout
|
||||
if self.timeout <= 0 or mintime <= last_time:
|
||||
self._cache[cursor.dbname][key] = (value,
|
||||
time.time())
|
||||
result = value
|
||||
find = True
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
if not find:
|
||||
# Work out new value, cache it and return it
|
||||
result = function(model, *args, **kwargs_origin)
|
||||
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache[cursor.dbname][key] = (result, time.time())
|
||||
finally:
|
||||
self._lock.release()
|
||||
return result
|
||||
|
||||
call.__doc__ = function.__doc__
|
||||
call.reset = reset
|
||||
return call
|
||||
|
||||
@staticmethod
|
||||
def clean(dbname):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
database = Database(dbname).connect()
|
||||
cursor = database.cursor()
|
||||
try:
|
||||
cursor.execute('SELECT "timestamp", "name" FROM ir_cache')
|
||||
timestamps = {}
|
||||
for timestamp, name in cursor.fetchall():
|
||||
timestamps[name] = timestamp
|
||||
finally:
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
for obj in Cache._cache_instance:
|
||||
if obj._name in timestamps:
|
||||
if not obj._timestamp or timestamps[obj._name] > obj._timestamp:
|
||||
obj._timestamp = timestamps[obj._name]
|
||||
obj._lock.acquire()
|
||||
try:
|
||||
obj._cache[dbname] = {}
|
||||
finally:
|
||||
obj._lock.release()
|
||||
|
||||
@staticmethod
|
||||
def reset(dbname, name):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
Cache._resets_lock.acquire()
|
||||
try:
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
Cache._resets[dbname].add(name)
|
||||
finally:
|
||||
Cache._resets_lock.release()
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
def resets(dbname):
|
||||
if not CONFIG['multi_server']:
|
||||
return
|
||||
database = Database(dbname).connect()
|
||||
cursor = database.cursor()
|
||||
Cache._resets_lock.acquire()
|
||||
Cache._resets.setdefault(dbname, set())
|
||||
try:
|
||||
for name in Cache._resets[dbname]:
|
||||
cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
|
||||
(name,))
|
||||
if cursor.fetchone():
|
||||
cursor.execute('UPDATE ir_cache SET "timestamp" = %s '\
|
||||
'WHERE name = %s', (datetime.datetime.now(), name))
|
||||
else:
|
||||
cursor.execute('INSERT INTO ir_cache ' \
|
||||
'("timestamp", "name") ' \
|
||||
'VALUES (%s, %s)', (datetime.datetime.now(), name))
|
||||
Cache._resets[dbname].clear()
|
||||
finally:
|
||||
cursor.commit()
|
||||
cursor.close()
|
||||
Cache._resets_lock.release()
|
||||
|
||||
def memoize(maxsize):
|
||||
"""
|
||||
Decorator to 'memoize' a function - caching its results with a
|
||||
|
||||
Reference in new issue
Block a user