def test_memory_cache_drop(self): "Test MemoryCache drop" cache.set('foo', 'bar') MemoryCache.drop(DB_NAME) self.assertEqual(cache.get('foo'), None)
def tearDown(self): MemoryCache.drop(DB_NAME)
# 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 time import unittest from trytond import backend, cache as cache_mod from trytond.cache import freeze, MemoryCache from trytond.tests.test_tryton import with_transaction, activate_module from trytond.tests.test_tryton import DB_NAME, USER from trytond.transaction import Transaction cache = MemoryCache('test.cache') cache_expire = MemoryCache('test.cache_expire', duration=1) class CacheTestCase(unittest.TestCase): "Test Cache" def testFreeze(self): "Test freeze" self.assertEqual(freeze([1, 2, 3]), (1, 2, 3)) self.assertEqual(freeze({ 'list': [1, 2, 3], }), frozenset([('list', (1, 2, 3))])) self.assertEqual( freeze({ 'dict': { 'inner dict': { 'list': [1, 2, 3], 'string': 'test',
class View(ModelSQL, ModelView): "View" __name__ = 'ir.ui.view' _rec_name = 'model' model = fields.Char('Model', select=True, states={ 'required': Eval('type').in_([None, 'tree', 'form', 'graph']), }) priority = fields.Integer('Priority', required=True, select=True) type = fields.Selection([ (None, ''), ('tree', 'Tree'), ('form', 'Form'), ('graph', 'Graph'), ('calendar', 'Calendar'), ('board', 'Board'), ], 'View Type', select=True, domain=[ If(Bool(Eval('inherit')), ('type', '=', None), ('type', '!=', None)), ], depends=['inherit']) data = fields.Text('Data') name = fields.Char('Name', states={ 'invisible': ~(Eval('module') & Eval('name')), }, depends=['module'], readonly=True) arch = fields.Function(fields.Text('View Architecture', states={ 'readonly': Bool(Eval('name')), }, depends=['name']), 'get_arch', setter='set_arch') inherit = fields.Many2One('ir.ui.view', 'Inherited View', select=True, ondelete='CASCADE') field_childs = fields.Char('Children Field', states={ 'invisible': Eval('type') != 'tree', }, depends=['type']) module = fields.Char('Module', states={ 'invisible': ~Eval('module'), }, readonly=True) domain = fields.Char('Domain', states={ 'invisible': ~Eval('inherit'), }, depends=['inherit']) # AKE : Force usage of MemoryCache for non serializable data _get_rng_cache = MemoryCache('ir_ui_view.get_rng') @classmethod def __setup__(cls): super(View, cls).__setup__() cls._error_messages.update({ 'invalid_xml': 'Invalid XML for view "%s".', }) cls._order.insert(0, ('priority', 'ASC')) cls._buttons.update({ 'show': { 'readonly': Eval('type') != 'form', }, }) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') table = TableHandler(cls, module_name) # Migration from 2.4 arch moved into data if table.column_exist('arch'): table.column_rename('arch', 'data') super(View, cls).__register__(module_name) # New instance to refresh definition table = TableHandler(cls, module_name) # Migration from 1.0 arch no more required table.not_null_action('arch', action='remove') # Migration from 2.4 model no more required table.not_null_action('model', action='remove') @staticmethod def default_priority(): return 16 @staticmethod def default_module(): return Transaction().context.get('module') or '' @classmethod @ModelView.button_action('ir.act_view_show') def show(cls, views): pass @classmethod def get_rng(cls, type_): key = (cls.__name__, type_) rng = cls._get_rng_cache.get(key, None) if rng is None: if sys.version_info < (3, ): filename = __file__.decode(sys.getfilesystemencoding()) else: filename = __file__ rng_name = os.path.join(os.path.dirname(filename), type_ + '.rng') with open(rng_name, 'rb') as fp: rng = etree.fromstring(fp.read()) cls._get_rng_cache.set(key, rng) return rng @property def rng_type(self): if self.inherit: return self.inherit.rng_type return self.type @classmethod def validate(cls, views): super(View, cls).validate(views) cls.check_xml(views) @classmethod def check_xml(cls, views): "Check XML" for view in views: if not view.arch: continue xml = view.arch.strip() if not xml: continue try: tree = etree.fromstring(xml) except Exception: # JCA : print faulty xml try: import pprint pprint.pprint(xml) except: print(xml) raise if hasattr(etree, 'RelaxNG'): validator = etree.RelaxNG(etree=cls.get_rng(view.rng_type)) if not validator.validate(tree): error_log = '\n'.join( map(str, validator.error_log.filter_from_errors())) logger.error('Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error('invalid_xml', (view.rec_name, ), error_log) root_element = tree.getroottree().getroot() # validate pyson attributes validates = { 'states': fields.states_validate, } def encode(element): for attr in ('states', 'domain', 'spell', 'colors'): if not element.get(attr): continue try: value = PYSONDecoder().decode(element.get(attr)) validates.get(attr, lambda a: True)(value) except Exception, e: error_log = '%s: <%s %s="%s"/>' % ( e, element.get('id') or element.get('name'), attr, element.get(attr)) logger.error('Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error('invalid_xml', (view.rec_name, ), error_log) for child in element: encode(child) encode(root_element)
class ActionReport(ActionMixin, ModelSQL, ModelView): "Action report" __name__ = 'ir.action.report' _action_name = 'report_name' model = fields.Char('Model') report_name = fields.Char('Internal Name', required=True) report = fields.Char("Path", states={ 'invisible': Eval('is_custom', False), }, depends=['is_custom']) report_content_custom = fields.Binary('Content') is_custom = fields.Function(fields.Boolean("Is Custom"), 'get_is_custom') report_content = fields.Function(fields.Binary( 'Content', filename='report_content_name'), 'get_report_content', setter='set_report_content') report_content_name = fields.Function( fields.Char('Content Name'), 'on_change_with_report_content_name') report_content_html = fields.Function(fields.Binary( "Content HTML", states={ 'invisible': ~Eval('template_extension').in_(['html', 'xhtml']), }, depends=['template_extension']), 'get_report_content_html', setter='set_report_content_html') action = fields.Many2One('ir.action', 'Action', required=True, ondelete='CASCADE') direct_print = fields.Boolean('Direct Print') single = fields.Boolean( "Single", help="Check if the template works only for one record.") translatable = fields.Boolean( "Translatable", help="Uncheck to disable translations for this report.") template_extension = fields.Selection([ ('odt', 'OpenDocument Text'), ('odp', 'OpenDocument Presentation'), ('ods', 'OpenDocument Spreadsheet'), ('odg', 'OpenDocument Graphics'), ('txt', 'Plain Text'), ('xml', 'XML'), ('html', 'HTML'), ('xhtml', 'XHTML'), ], string='Template Extension', required=True, translate=False) extension = fields.Selection( [ ('', ''), ('bib', 'BibTex'), ('bmp', 'Windows Bitmap'), ('csv', 'Text CSV'), ('dbf', 'dBase'), ('dif', 'Data Interchange Format'), ('doc', 'Microsoft Word 97/2000/XP'), ('doc6', 'Microsoft Word 6.0'), ('doc95', 'Microsoft Word 95'), ('docbook', 'DocBook'), ('docx', 'Microsoft Office Open XML Text'), ('docx7', 'Microsoft Word 2007 XML'), ('emf', 'Enhanced Metafile'), ('eps', 'Encapsulated PostScript'), ('gif', 'Graphics Interchange Format'), ('html', 'HTML Document'), ('jpg', 'Joint Photographic Experts Group'), ('met', 'OS/2 Metafile'), ('ooxml', 'Microsoft Office Open XML'), ('pbm', 'Portable Bitmap'), ('pct', 'Mac Pict'), ('pdb', 'AportisDoc (Palm)'), ('pdf', 'Portable Document Format'), ('pgm', 'Portable Graymap'), ('png', 'Portable Network Graphic'), ('ppm', 'Portable Pixelmap'), ('ppt', 'Microsoft PowerPoint 97/2000/XP'), ('psw', 'Pocket Word'), ('pwp', 'PlaceWare'), ('pxl', 'Pocket Excel'), ('ras', 'Sun Raster Image'), ('rtf', 'Rich Text Format'), ('latex', 'LaTeX 2e'), ('sda', 'StarDraw 5.0 (OpenOffice.org Impress)'), ('sdc', 'StarCalc 5.0'), ('sdc4', 'StarCalc 4.0'), ('sdc3', 'StarCalc 3.0'), ('sdd', 'StarImpress 5.0'), ('sdd3', 'StarDraw 3.0 (OpenOffice.org Impress)'), ('sdd4', 'StarImpress 4.0'), ('sdw', 'StarWriter 5.0'), ('sdw4', 'StarWriter 4.0'), ('sdw3', 'StarWriter 3.0'), ('slk', 'SYLK'), ('svg', 'Scalable Vector Graphics'), ('svm', 'StarView Metafile'), ('swf', 'Macromedia Flash (SWF)'), ('sxc', 'OpenOffice.org 1.0 Spreadsheet'), ('sxi', 'OpenOffice.org 1.0 Presentation'), ('sxd', 'OpenOffice.org 1.0 Drawing'), ('sxd3', 'StarDraw 3.0'), ('sxd5', 'StarDraw 5.0'), ('sxw', 'Open Office.org 1.0 Text Document'), ('text', 'Text Encoded'), ('tiff', 'Tagged Image File Format'), ('txt', 'Plain Text'), ('wmf', 'Windows Metafile'), ('xhtml', 'XHTML Document'), ('xls', 'Microsoft Excel 97/2000/XP'), ('xls5', 'Microsoft Excel 5.0'), ('xls95', 'Microsoft Excel 95'), ('xlsx', 'Microsoft Excel 2007/2010 XML'), ('xpm', 'X PixMap'), ], translate=False, string='Extension', help='Leave empty for the same as template, ' 'see LibreOffice documentation for compatible format.') module = fields.Char('Module', readonly=True, select=True) _template_cache = MemoryCache('ir.action.report.template', context=False) @classmethod def __register__(cls, module_name): super(ActionReport, cls).__register__(module_name) transaction = Transaction() cursor = transaction.connection.cursor() table = cls.__table_handler__(module_name) action_report = cls.__table__() # Migration from 3.4 remove report_name_module_uniq constraint table.drop_constraint('report_name_module_uniq') # Migration from 4.4 replace plain extension by txt cursor.execute( *action_report.update([action_report.extension], ['txt'], where=action_report.extension == 'plain')) @staticmethod def default_type(): return 'ir.action.report' @staticmethod def default_report_content(): return None @staticmethod def default_direct_print(): return False @classmethod def default_single(cls): return False @classmethod def default_translatable(cls): return True @staticmethod def default_template_extension(): return 'odt' @staticmethod def default_extension(): return '' @staticmethod def default_module(): return Transaction().context.get('module') or '' def get_is_custom(self, name): return bool(self.report_content_custom) @classmethod def get_report_content(cls, reports, name): contents = {} converter = fields.Binary.cast default = None format_ = Transaction().context.get('%s.%s' % (cls.__name__, name), '') if format_ == 'size': converter = len default = 0 for report in reports: data = getattr(report, name + '_custom') if not data and getattr(report, name[:-8]): try: with file_open(getattr(report, name[:-8]).replace('/', os.sep), mode='rb') as fp: data = fp.read() except Exception: data = None contents[report.id] = converter(data) if data else default return contents @classmethod def set_report_content(cls, records, name, value): cls.write(records, {'%s_custom' % name: value}) @classmethod def get_report_content_html(cls, reports, name): return cls.get_report_content(reports, name[:-5]) @classmethod def set_report_content_html(cls, reports, name, value): if value is not None: value = value.encode('utf-8') cls.set_report_content(reports, name[:-5], value) @fields.depends('name', 'template_extension') def on_change_with_report_content_name(self, name=None): return ''.join( filter(None, [self.name, os.extsep, self.template_extension])) @classmethod def get_pyson(cls, reports, name): pysons = {} field = name[6:] defaults = {} for report in reports: pysons[report.id] = (getattr(report, field) or defaults.get(field, 'null')) return pysons @classmethod def copy(cls, reports, default=None): if default is None: default = {} default = default.copy() default.setdefault('module', None) new_reports = [] for report in reports: if report.report: default['report_content'] = None default['report'] = None default['report_name'] = report.report_name new_reports.extend( super(ActionReport, cls).copy([report], default=default)) return new_reports @classmethod def write(cls, reports, values, *args): context = Transaction().context if 'module' in context: actions = iter((reports, values) + args) args = [] for reports, values in zip(actions, actions): values = values.copy() values['module'] = context['module'] args.extend((reports, values)) reports, values = args[:2] args = args[2:] cls._template_cache.clear() super(ActionReport, cls).write(reports, values, *args) def get_template_cached(self): return self._template_cache.get(self.id) def set_template_cached(self, template): self._template_cache.set(self.id, template)
class View(ModelSQL, ModelView): "View" __name__ = 'ir.ui.view' _rec_name = 'model' model = fields.Char('Model', select=True, states={ 'required': Eval('type').in_([None, 'tree', 'form', 'graph']), }) priority = fields.Integer('Priority', required=True, select=True) type = fields.Selection([ (None, ''), ('tree', 'Tree'), ('form', 'Form'), ('graph', 'Graph'), ('calendar', 'Calendar'), ('board', 'Board'), ], 'View Type', select=True, domain=[ If(Bool(Eval('inherit')), ('type', '=', None), ('type', '!=', None)), ], depends=['inherit']) data = fields.Text('Data') name = fields.Char('Name', states={ 'invisible': ~(Eval('module') & Eval('name')), }, depends=['module'], readonly=True) arch = fields.Function(fields.Text('View Architecture', states={ 'readonly': Bool(Eval('name')), }, depends=['name']), 'get_arch', setter='set_arch') inherit = fields.Many2One('ir.ui.view', 'Inherited View', select=True, ondelete='CASCADE') field_childs = fields.Char('Children Field', states={ 'invisible': Eval('type') != 'tree', }, depends=['type']) module = fields.Char('Module', states={ 'invisible': ~Eval('module'), }, readonly=True) domain = fields.Char('Domain', states={ 'invisible': ~Eval('inherit'), }, depends=['inherit']) # AKE : Force usage of MemoryCache for non serializable data _get_rng_cache = MemoryCache('ir_ui_view.get_rng') @classmethod def __setup__(cls): super(View, cls).__setup__() cls._error_messages.update({ 'invalid_xml': 'Invalid XML for view "%s".', }) cls._order.insert(0, ('priority', 'ASC')) cls._buttons.update({ 'show': { 'readonly': Eval('type') != 'form', 'depends': ['type'], }, }) @staticmethod def default_priority(): return 16 @staticmethod def default_module(): return Transaction().context.get('module') or '' @classmethod @ModelView.button_action('ir.act_view_show') def show(cls, views): pass @classmethod @memoize(10) def get_rng(cls, type_): key = (cls.__name__, type_) rng = cls._get_rng_cache.get(key) if rng is None: rng_name = os.path.join(os.path.dirname(__file__), type_ + '.rng') with open(rng_name, 'rb') as fp: rng = etree.fromstring(fp.read()) cls._get_rng_cache.set(key, rng) return rng @property def rng_type(self): if self.inherit: return self.inherit.rng_type return self.type @classmethod def validate(cls, views): super(View, cls).validate(views) cls.check_xml(views) @classmethod def check_xml(cls, views): "Check XML" for view in views: if not view.arch: continue xml = view.arch.strip() if not xml: continue try: tree = etree.fromstring(xml) except Exception: # JCA : print faulty xml try: import pprint pprint.pprint(xml) except: print(xml) raise if hasattr(etree, 'RelaxNG'): validator = etree.RelaxNG(etree=cls.get_rng(view.rng_type)) if not validator.validate(tree): error_log = '\n'.join( map(str, validator.error_log.filter_from_errors())) logger.error('Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error('invalid_xml', (view.rec_name, ), error_log) root_element = tree.getroottree().getroot() # validate pyson attributes validates = { 'states': fields.states_validate, } def encode(element): for attr in ('states', 'domain', 'spell', 'colors'): if not element.get(attr): continue try: value = PYSONDecoder().decode(element.get(attr)) validates.get(attr, lambda a: True)(value) except Exception as e: error_log = '%s: <%s %s="%s"/>' % ( e, element.get('id') or element.get('name'), attr, element.get(attr)) logger.error('Invalid XML view %s:\n%s\n%s', view.rec_name, error_log, xml) cls.raise_user_error('invalid_xml', (view.rec_name, ), error_log) for child in element: encode(child) encode(root_element) def get_arch(self, name): value = None if self.name and self.module: path = os.path.join(self.module, 'view', self.name + '.xml') try: with file_open(path, subdir='modules', mode='r', encoding='utf-8') as fp: value = fp.read() except IOError: pass if not value: value = self.data return value @classmethod def set_arch(cls, views, name, value): cls.write(views, {'data': value}) @classmethod def delete(cls, views): super(View, cls).delete(views) # Restart the cache ModelView._fields_view_get_cache.clear() @classmethod def create(cls, vlist): views = super(View, cls).create(vlist) # Restart the cache ModelView._fields_view_get_cache.clear() return views @classmethod def write(cls, views, values, *args): super(View, cls).write(views, values, *args) # Restart the cache ModelView._fields_view_get_cache.clear()