[tryton-debian-vcs] tryton-proteus branch debian updated. debian/3.2.1-1-4-g6c34531

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Thu Oct 23 12:19:35 UTC 2014


The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/tryton-proteus.git;a=commitdiff;h=debian/3.2.1-1-4-g6c34531

commit 6c34531a9d032cca29744b7fb36753daa16df726
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Thu Oct 23 13:32:11 2014 +0200

    Releasing debian version 3.4.0-1.
    
    Signed-off-by: Mathias Behrle <mathiasb at m9s.biz>

diff --git a/debian/changelog b/debian/changelog
index 69a803c..f0ddd90 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+tryton-proteus (3.4.0-1) unstable; urgency=medium
+
+  * Adding actual upstream signing key.
+  * Updating to Standards-Version: 3.9.6, no changes needed.
+  * Merging upstream version 3.4.0.
+
+ -- Mathias Behrle <mathiasb at m9s.biz>  Tue, 21 Oct 2014 20:24:42 +0200
+
 tryton-proteus (3.2.1-1) unstable; urgency=medium
 
   * Updating signing key while using now plain .asc files instead of .pgp
commit 5a2dc7ee6bcaee7c294c682614cf3ab656020f6e
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Tue Oct 21 11:29:25 2014 +0200

    Merging upstream version 3.4.0.

diff --git a/CHANGELOG b/CHANGELOG
index 599fe1b..f46bf35 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,9 @@
-Version 3.2.1 - 2014-07-02
+Version 3.4.0 - 2014-10-20
 * Bug fixes (see mercurial logs for details)
+* New configuration syntax
+* Add Report
+* Explicitly set value of parent field
+* Add duplicate method
 
 Version 3.2.0 - 2014-04-21
 * Bug fixes (see mercurial logs for details)
diff --git a/PKG-INFO b/PKG-INFO
index d00840d..015002e 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: proteus
-Version: 3.2.1
+Version: 3.4.0
 Summary: Library to access Tryton server as a client
 Home-page: http://www.tryton.org/
 Author: Tryton
 Author-email: issue_tracker at tryton.org
 License: LGPL-3
-Download-URL: http://downloads.tryton.org/3.2/
+Download-URL: http://downloads.tryton.org/3.4/
 Description: proteus
         =======
         
@@ -20,18 +20,14 @@ Description: proteus
         Example of usage
         ----------------
         
-            >>> from proteus import config, Model, Wizard
+            >>> from proteus import config, Model, Wizard, Report
         
-        Creating a database
-        ~~~~~~~~~~~~~~~~~~~
+        Configuration
+        ~~~~~~~~~~~~~
         
         Configuration to connect to a sqlite memory database using trytond as module.
         
-            >>> config = config.set_trytond(':memory:', database_type='sqlite')
-        
-        When connecting to a database that doesn't exist, Proteus will create it.
-        If no database name was given, then Proteus will generate one. It will choose
-        ':memory': for 'sqlite' type otherwise `'test_%' % int(time.time())`.
+            >>> config = config.set_trytond('sqlite:///:memory:')
         
         Installing a module
         ~~~~~~~~~~~~~~~~~~~
@@ -108,6 +104,17 @@ Description: proteus
             >>> party.categories #doctest: +ELLIPSIS
             [proteus.Model.get('party.category')(...)]
         
+        Print party label
+        ~~~~~~~~~~~~~~~~~
+        
+        There is a label report on `Party`.
+        
+            >>> label = Report('party.label')
+        
+        The report is executed with a list of records and some extra data.
+        
+            >>> type_, data, print_, name = label.execute([party], {})
+        
         Support
         -------
         
diff --git a/README b/README
index 0b23a11..53fd3ed 100644
--- a/README
+++ b/README
@@ -11,18 +11,14 @@ See INSTALL
 Example of usage
 ----------------
 
-    >>> from proteus import config, Model, Wizard
+    >>> from proteus import config, Model, Wizard, Report
 
-Creating a database
-~~~~~~~~~~~~~~~~~~~
+Configuration
+~~~~~~~~~~~~~
 
 Configuration to connect to a sqlite memory database using trytond as module.
 
-    >>> config = config.set_trytond(':memory:', database_type='sqlite')
-
-When connecting to a database that doesn't exist, Proteus will create it.
-If no database name was given, then Proteus will generate one. It will choose
-':memory': for 'sqlite' type otherwise `'test_%' % int(time.time())`.
+    >>> config = config.set_trytond('sqlite:///:memory:')
 
 Installing a module
 ~~~~~~~~~~~~~~~~~~~
@@ -99,6 +95,17 @@ Append it to categories of the party
     >>> party.categories #doctest: +ELLIPSIS
     [proteus.Model.get('party.category')(...)]
 
+Print party label
+~~~~~~~~~~~~~~~~~
+
+There is a label report on `Party`.
+
+    >>> label = Report('party.label')
+
+The report is executed with a list of records and some extra data.
+
+    >>> type_, data, print_, name = label.execute([party], {})
+
 Support
 -------
 
diff --git a/proteus.egg-info/PKG-INFO b/proteus.egg-info/PKG-INFO
index d00840d..015002e 100644
--- a/proteus.egg-info/PKG-INFO
+++ b/proteus.egg-info/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 1.1
 Name: proteus
-Version: 3.2.1
+Version: 3.4.0
 Summary: Library to access Tryton server as a client
 Home-page: http://www.tryton.org/
 Author: Tryton
 Author-email: issue_tracker at tryton.org
 License: LGPL-3
-Download-URL: http://downloads.tryton.org/3.2/
+Download-URL: http://downloads.tryton.org/3.4/
 Description: proteus
         =======
         
@@ -20,18 +20,14 @@ Description: proteus
         Example of usage
         ----------------
         
-            >>> from proteus import config, Model, Wizard
+            >>> from proteus import config, Model, Wizard, Report
         
-        Creating a database
-        ~~~~~~~~~~~~~~~~~~~
+        Configuration
+        ~~~~~~~~~~~~~
         
         Configuration to connect to a sqlite memory database using trytond as module.
         
-            >>> config = config.set_trytond(':memory:', database_type='sqlite')
-        
-        When connecting to a database that doesn't exist, Proteus will create it.
-        If no database name was given, then Proteus will generate one. It will choose
-        ':memory': for 'sqlite' type otherwise `'test_%' % int(time.time())`.
+            >>> config = config.set_trytond('sqlite:///:memory:')
         
         Installing a module
         ~~~~~~~~~~~~~~~~~~~
@@ -108,6 +104,17 @@ Description: proteus
             >>> party.categories #doctest: +ELLIPSIS
             [proteus.Model.get('party.category')(...)]
         
+        Print party label
+        ~~~~~~~~~~~~~~~~~
+        
+        There is a label report on `Party`.
+        
+            >>> label = Report('party.label')
+        
+        The report is executed with a list of records and some extra data.
+        
+            >>> type_, data, print_, name = label.execute([party], {})
+        
         Support
         -------
         
diff --git a/proteus.egg-info/SOURCES.txt b/proteus.egg-info/SOURCES.txt
index 1eef609..b19dc88 100644
--- a/proteus.egg-info/SOURCES.txt
+++ b/proteus.egg-info/SOURCES.txt
@@ -15,7 +15,9 @@ proteus.egg-info/requires.txt
 proteus.egg-info/top_level.txt
 proteus.egg-info/zip-safe
 proteus/tests/__init__.py
+proteus/tests/common.py
 proteus/tests/test_config.py
 proteus/tests/test_context.py
 proteus/tests/test_model.py
+proteus/tests/test_report.py
 proteus/tests/test_wizard.py
\ No newline at end of file
diff --git a/proteus.egg-info/requires.txt b/proteus.egg-info/requires.txt
index e8c47c8..232d2df 100644
--- a/proteus.egg-info/requires.txt
+++ b/proteus.egg-info/requires.txt
@@ -4,7 +4,7 @@ python-dateutil
 cdecimal
 
 [trytond]
-trytond >= 3.2, < 3.3
+trytond >= 3.4, < 3.5
 
 [simplejson]
 simplejson
\ No newline at end of file
diff --git a/proteus/__init__.py b/proteus/__init__.py
index 6c8d8ab..2f2b0f8 100644
--- a/proteus/__init__.py
+++ b/proteus/__init__.py
@@ -3,8 +3,8 @@
 '''
 A library to access Tryton's models like a client.
 '''
-__version__ = "3.2.1"
-__all__ = ['Model', 'Wizard']
+__version__ = "3.4.0"
+__all__ = ['Model', 'Wizard', 'Report']
 import sys
 try:
     import cdecimal
@@ -26,23 +26,32 @@ _MODELS = threading.local()
 
 class _EvalEnvironment(dict):
     'Dictionary for evaluation'
-    def __init__(self, parent):
+    def __init__(self, parent, eval_type='eval'):
         super(_EvalEnvironment, self).__init__()
         self.parent = parent
+        assert eval_type in ('eval', 'on_change')
+        self.eval_type = eval_type
 
     def __getitem__(self, item):
         if item == '_parent_' + self.parent._parent_name \
-                and self.parent.parent:
-            return _EvalEnvironment(self.parent.parent)
-        return self.parent._get_eval()[item]
+                and self.parent._parent:
+            return _EvalEnvironment(self.parent._parent,
+                eval_type=self.eval_type)
+        if self.eval_type == 'eval':
+            return self.parent._get_eval()[item]
+        else:
+            return self.parent._get_on_change_values(fields=[item])[item]
 
     def __getattr__(self, item):
-        return self.__getitem__(item)
+        try:
+            return self.__getitem__(item)
+        except KeyError:
+            raise AttributeError(item)
 
     def get(self, item, default=None):
         try:
-            return self.__getattr__(item)
-        except:
+            return self.__getitem__(item)
+        except KeyError:
             pass
         return super(_EvalEnvironment, self).get(item, default)
 
@@ -55,7 +64,13 @@ class _EvalEnvironment(dict):
     __repr__ = __str__
 
     def __contains__(self, item):
-        return item in self.parent._fields
+        if item == '_parent_' + self.parent._parent_name \
+                and self.parent._parent:
+            return True
+        if self.eval_type == 'eval':
+            return item in self.parent._get_eval()
+        else:
+            return item in self.parent._fields
 
 
 class FieldDescriptor(object):
@@ -97,23 +112,16 @@ class CharDescriptor(FieldDescriptor):
     default = None
 
     def __set__(self, instance, value):
-        assert isinstance(value, basestring) or value in (None, False)
+        assert isinstance(value, basestring) or value is None
         super(CharDescriptor, self).__set__(instance, value or '')
 
-    def __get__(self, instance, owner):
-        value = super(CharDescriptor, self).__get__(instance, owner)
-        if value is False:
-            value = None
-        return value
-
 
 class BinaryDescriptor(FieldDescriptor):
     default = None
 
     def __set__(self, instance, value):
-        assert (isinstance(value, (basestring, buffer))
-            or value in (None, False))
-        super(BinaryDescriptor, self).__set__(instance, value or '')
+        assert isinstance(value, (basestring, buffer)) or value is None
+        super(BinaryDescriptor, self).__set__(instance, value)
 
 
 class IntegerDescriptor(FieldDescriptor):
@@ -143,8 +151,6 @@ class NumericDescriptor(FieldDescriptor):
 class ReferenceDescriptor(FieldDescriptor):
     def __get__(self, instance, owner):
         value = super(ReferenceDescriptor, self).__get__(instance, owner)
-        if instance._parent_name == self.name:
-            value = instance._parent
         if isinstance(value, basestring):
             model_name, id = value.split(',', 1)
             if model_name:
@@ -158,7 +164,6 @@ class ReferenceDescriptor(FieldDescriptor):
         if isinstance(value, basestring):
             assert value.startswith(',')
         elif isinstance(value, Model):
-            assert value.id > 0 and not value._changed
             assert value._config == instance._config
         super(ReferenceDescriptor, self).__set__(instance, value)
 
@@ -172,25 +177,25 @@ class DateDescriptor(FieldDescriptor):
         return value
 
     def __set__(self, instance, value):
-        assert isinstance(value, datetime.date) or value in (None, False)
+        assert isinstance(value, datetime.date) or value is None
         super(DateDescriptor, self).__set__(instance, value)
 
 
 class DateTimeDescriptor(FieldDescriptor):
     def __set__(self, instance, value):
-        assert isinstance(value, datetime.datetime) or value in (None, False)
+        assert isinstance(value, datetime.datetime) or value is None
         super(DateTimeDescriptor, self).__set__(instance, value)
 
 
 class TimeDescriptor(FieldDescriptor):
     def __set__(self, instance, value):
-        assert isinstance(value, datetime.time) or value in (None, False)
+        assert isinstance(value, datetime.time) or value is None
         super(TimeDescriptor, self).__set__(instance, value)
 
 
 class DictDescriptor(FieldDescriptor):
     def __set__(self, instance, value):
-        assert isinstance(value, dict) or value in (None, False)
+        assert isinstance(value, dict) or value is None
         super(DictDescriptor, self).__set__(instance, value)
 
 
@@ -198,12 +203,8 @@ class Many2OneDescriptor(FieldDescriptor):
     def __get__(self, instance, owner):
         relation = Model.get(self.definition['relation'], instance._config)
         value = super(Many2OneDescriptor, self).__get__(instance, owner)
-        if instance._parent_name == self.name:
-            value = instance._parent
-        if isinstance(value, (int, long)) and value is not False:
+        if isinstance(value, (int, long)):
             value = relation(value)
-        elif not value:
-            value = None
         if self.name in instance._values:
             instance._values[self.name] = value
         return value
@@ -211,7 +212,6 @@ class Many2OneDescriptor(FieldDescriptor):
     def __set__(self, instance, value):
         assert isinstance(value, (Model, NoneType))
         if value:
-            assert value.id > 0 and not value._changed
             assert value._config == instance._config
         super(Many2OneDescriptor, self).__set__(instance, value)
 
@@ -342,7 +342,14 @@ class One2OneEvalDescriptor(Many2OneEvalDescriptor):
 
 class One2ManyEvalDescriptor(EvalDescriptor):
     def __get__(self, instance, owner):
-        return [x.id for x in getattr(instance, self.name)]
+        # Directly use _values to prevent infinite recursion with
+        # One2ManyDescriptor which could evaluate this field to decode the
+        # context
+        value = instance._values.get(self.name, [])
+        if isinstance(value, ModelList):
+            return [x.id for x in value]
+        else:
+            return value
 
 
 class Many2ManyEvalDescriptor(One2ManyEvalDescriptor):
@@ -411,7 +418,8 @@ class MetaModelFactory(object):
                 for field_name, definition in dict['_fields'].iteritems():
                     if field_name == 'id':
                         continue
-                    Descriptor = self.descriptors[definition['type']]
+                    Descriptor = self.descriptors.get(definition['type'],
+                        FieldDescriptor)
                     dict[field_name] = Descriptor(field_name, definition)
                     VDescriptor = self.value_descriptors.get(
                             definition['type'], ValueDescriptor)
@@ -469,27 +477,9 @@ class ModelList(list):
         ctx.update(decoder.decode(self.context) if self.context else {})
         return ctx
 
-    def append(self, record):
-        assert isinstance(record, Model)
-        if self.parent:
-            assert record._config == self.parent._config
-        elif self:
-            assert record._config == self[0]._config
-        assert record._parent is None
-        assert not record._parent_field_name
-        assert not record._parent_name
-        record._parent = self.parent
-        record._parent_field_name = self.parent_field_name
-        record._parent_name = self.parent_name
-        res = super(ModelList, self).append(record)
-        self._changed()
-        return res
-    append.__doc__ = list.append.__doc__
-
-    def extend(self, iterable):
-        iterable = list(iterable)
+    def __check(self, records):
         config = None
-        for record in iterable:
+        for record in records:
             assert isinstance(record, Model)
             if self.parent:
                 assert record._config == self.parent._config
@@ -499,13 +489,30 @@ class ModelList(list):
                 assert record._config == config
             else:
                 config = record._config
-        for record in iterable:
+        for record in records:
             assert record._parent is None
             assert not record._parent_field_name
             assert not record._parent_name
             record._parent = self.parent
             record._parent_field_name = self.parent_field_name
             record._parent_name = self.parent_name
+
+            # Set parent field to trigger on_change
+            if self.parent and self.parent_name in record._fields:
+                definition = record._fields[self.parent_name]
+                if definition['type'] in ('many2one', 'reference'):
+                    setattr(record, self.parent_name, self.parent)
+
+    def append(self, record):
+        self.__check([record])
+        res = super(ModelList, self).append(record)
+        self._changed()
+        return res
+    append.__doc__ = list.append.__doc__
+
+    def extend(self, iterable):
+        iterable = list(iterable)
+        self.__check(iterable)
         res = super(ModelList, self).extend(iterable)
         self._changed()
         return res
@@ -599,11 +606,9 @@ class Model(object):
                 getattr(self, field_name).extend(value)
             else:
                 if definition['type'] == 'many2one':
-                    if (isinstance(value, (int, long)) and value is not False):
+                    if isinstance(value, (int, long)):
                         relation = Model.get(definition['relation'])
                         value = relation(value)
-                    elif value is False:
-                        value = None
                 setattr(self, field_name, value)
     __init__.__doc__ = object.__init__.__doc__
 
@@ -692,6 +697,13 @@ class Model(object):
         self.reload()
         return True
 
+    @classmethod
+    def duplicate(cls, records, default=None):
+        'Duplicate the record'
+        ids = cls._proxy.copy([r.id for r in records], default,
+            cls._config.context)
+        return [cls(id) for id in ids]
+
     def click(self, button):
         'Click on button'
         self.save()
@@ -749,6 +761,7 @@ class Model(object):
             self._config.context))
 
     def _default_set(self, values):
+        fieldnames = []
         for field, value in values.iteritems():
             if '.' in field:
                 continue
@@ -767,6 +780,9 @@ class Model(object):
                     field)
             else:
                 self._values[field] = value
+            fieldnames.append(field)
+        for field in sorted(fieldnames):
+            self._on_change(field)
 
     def _get_eval(self):
         values = dict((x, getattr(self, '__%s_eval' % x))
@@ -774,23 +790,39 @@ class Model(object):
         values['id'] = self.id
         return values
 
-    def _get_on_change_value(self):
+    def _get_on_change_values(self, skip=None, fields=None):
         values = {'id': self.id}
-        for field, definition in self._fields.iteritems():
-            if field in self._values and field != 'id':
-                if definition['type'] == 'one2many':
-                    values[field] = [x._get_on_change_value()
-                        for x in getattr(self, field)]
-                else:
-                    values[field] = getattr(self, '__%s_eval' % field)
+        if fields:
+            definitions = ((f, self._fields[f]) for f in fields)
+        else:
+            definitions = self._fields.iteritems()
+        for field, definition in definitions:
+            if not fields:
+                if field == 'id' or (skip and field in skip):
+                    continue
+                if (self.id >= 0
+                        and (field not in self._values
+                            or field not in self._changed)):
+                    continue
+            if definition['type'] == 'one2many':
+                values[field] = [x._get_on_change_values(
+                        skip={definition.get('relation_field', '')})
+                    for x in getattr(self, field)]
+            elif (definition['type'] in ('many2one', 'reference')
+                    and self._parent_name == definition['name']
+                    and self._parent):
+                values[field] = self._parent._get_on_change_values(
+                    skip={self._parent_field_name})
+                if definition['type'] == 'reference':
+                    values[field] = (
+                        self._parent.__class__.__name__, values[field])
+            else:
+                values[field] = getattr(self, '__%s_eval' % field)
         return values
 
     def _on_change_args(self, args):
         res = {}
-        values = self._get_on_change_value()
-        if self._parent:
-            values['_parent_%s' % self._parent_name] = \
-                _EvalEnvironment(self._parent)
+        values = _EvalEnvironment(self, 'on_change')
         for arg in args:
             scope = values
             for i in arg.split('.'):
@@ -942,7 +974,7 @@ class Wizard(object):
                 # Filter only modified values
                 data = {self.form_state:
                     dict((k, v) for k, v in
-                        self.form._get_on_change_value().iteritems()
+                        self.form._get_on_change_values().iteritems()
                         if k in self.form._values)}
             else:
                 data = {}
@@ -965,3 +997,17 @@ class Wizard(object):
 
         if self.state == self.end_state:
             self._proxy.delete(self.session_id, self._config.context)
+
+
+class Report(object):
+    'Report class for Tryton reports'
+
+    def __init__(self, name, config=None, context=None):
+        super(Report, self).__init__()
+        self.name = name
+        self._config = config or proteus.config.get_config()
+        self._context = context or {}
+        self._proxy = self._config.get_proxy(name, type='report')
+
+    def execute(self, models, data):
+        return self._proxy.execute([m.id for m in models], data, self._context)
diff --git a/proteus/config.py b/proteus/config.py
index ebef61d..c01e451 100644
--- a/proteus/config.py
+++ b/proteus/config.py
@@ -9,7 +9,8 @@ import xmlrpclib
 import threading
 from decimal import Decimal
 import datetime
-import time
+import os
+import urlparse
 
 
 def dump_decimal(self, value, write):
@@ -50,6 +51,28 @@ xmlrpclib.Marshaller.dispatch[datetime.time] = dump_time
 xmlrpclib.Marshaller.dispatch[buffer] = dump_buffer
 
 
+class XMLRPCDecoder(object):
+
+    decoders = {}
+
+    @classmethod
+    def register(cls, klass, decoder):
+        assert klass not in cls.decoders
+        cls.decoders[klass] = decoder
+
+    def __call__(self, dct):
+        if dct.get('__class__') in self.decoders:
+            return self.decoders[dct['__class__']](dct)
+        return dct
+
+XMLRPCDecoder.register('date',
+    lambda dct: datetime.date(dct['year'], dct['month'], dct['day']))
+XMLRPCDecoder.register('time',
+    lambda dct: datetime.time(dct['hour'], dct['minute'], dct['second'],
+        dct['microsecond']))
+XMLRPCDecoder.register('Decimal', lambda dct: Decimal(dct['decimal']))
+
+
 def end_struct(self, data):
     mark = self._marks.pop()
     # map structs to Python dictionaries
@@ -57,14 +80,7 @@ def end_struct(self, data):
     items = self._stack[mark:]
     for i in range(0, len(items), 2):
         dct[xmlrpclib._stringify(items[i])] = items[i + 1]
-    if '__class__' in dct:
-        if dct['__class__'] == 'date':
-            dct = datetime.date(dct['year'], dct['month'], dct['day'])
-        elif dct['__class__'] == 'time':
-            dct = datetime.time(dct['hour'], dct['minute'], dct['second'],
-                dct['microsecond'])
-        elif dct['__class__'] == 'Decimal':
-            dct = Decimal(dct['decimal'])
+    dct = XMLRPCDecoder()(dct)
     self._stack[mark:] = [dct]
     self._value = 0
 
@@ -154,7 +170,7 @@ class _TrytondMethod(object):
                         for i in inst]
             if not rpc.readonly:
                 transaction.cursor.commit()
-        Cache.resets(self._config.database_name)
+            Cache.resets(self._config.database_name)
         return result
 
 
@@ -175,40 +191,32 @@ class TrytondProxy(object):
 class TrytondConfig(Config):
     'Configuration for trytond'
 
-    def __init__(self, database_name=None, user='admin', database_type=None,
-            language='en_US', password='', config_file=None):
+    def __init__(self, database=None, user='admin', language='en_US',
+            password='', config_file=os.environ.get('TRYTOND_CONFIG')):
         super(TrytondConfig, self).__init__()
-        from trytond.config import CONFIG
-        CONFIG.update_etc(config_file)
-        if database_type is not None:
-            CONFIG['db_type'] = database_type
+        if not database:
+            database = os.environ.get('TRYTOND_DATABASE_URI')
+        else:
+            os.environ['TRYTOND_DATABASE_URI'] = database
+        from trytond.config import config
+        config.update_etc(config_file)
         from trytond.pool import Pool
-        from trytond import backend
-        from trytond.protocols.dispatcher import create
         from trytond.cache import Cache
         from trytond.transaction import Transaction
-        self.database_type = CONFIG['db_type']
-        if database_name is None:
-            if self.database_type == 'sqlite':
-                database_name = ':memory:'
-            else:
-                database_name = 'test_%s' % int(time.time())
+        self.database = database
+        database_name = None
+        if database:
+            uri = urlparse.urlparse(database)
+            database_name = uri.path.strip('/')
+        if not database_name:
+            database_name = os.environ['DB_NAME']
         self.database_name = database_name
         self._user = user
         self.config_file = config_file
 
         Pool.start()
-
-        with Transaction().start(None, 0) as transaction:
-            cursor = transaction.cursor
-            databases = backend.get('Database').list(cursor)
-        if database_name not in databases:
-            create(database_name, CONFIG['admin_passwd'], language, password)
-
-        database_list = Pool.database_list()
         self.pool = Pool(database_name)
-        if database_name not in database_list:
-            self.pool.init()
+        self.pool.init()
 
         with Transaction().start(self.database_name, 0) as transaction:
             Cache.clean(database_name)
@@ -219,14 +227,13 @@ class TrytondConfig(Config):
                 ], limit=1)[0].id
             with transaction.set_user(self.user):
                 self._context = User.get_preferences(context_only=True)
-        Cache.resets(database_name)
+            Cache.resets(database_name)
     __init__.__doc__ = object.__init__.__doc__
 
     def __repr__(self):
         return "proteus.config.TrytondConfig"\
             "('%s', '%s', '%s', config_file=%s)"\
-            % (self.database_name, self._user, self.database_type,
-                self.config_file)
+            % (self.database, self._user, self.config_file)
     __repr__.__doc__ = object.__repr__.__doc__
 
     def __eq__(self, other):
@@ -234,12 +241,12 @@ class TrytondConfig(Config):
             raise NotImplementedError
         return (self.database_name == other.database_name
             and self._user == other._user
-            and self.database_type == other.database_type
+            and self.database == other.database
             and self.config_file == other.config_file)
 
     def __hash__(self):
         return hash((self.database_name, self._user,
-            self.database_type, self.config_file))
+            self.database, self.config_file))
 
     def get_proxy(self, name, type='model'):
         'Return Proxy class'
@@ -254,11 +261,11 @@ class TrytondConfig(Config):
         return methods
 
 
-def set_trytond(database_name=None, user='admin', database_type=None,
-        language='en_US', password='', config_file=None):
+def set_trytond(database=None, user='admin', language='en_US', password='',
+        config_file=None):
     'Set trytond package as backend'
-    _CONFIG.current = TrytondConfig(database_name, user, database_type,
-            language=language, password=password, config_file=config_file)
+    _CONFIG.current = TrytondConfig(database, user, language=language,
+        password=password, config_file=config_file)
     return _CONFIG.current
 
 
diff --git a/proteus/tests/common.py b/proteus/tests/common.py
new file mode 100644
index 0000000..15e1622
--- /dev/null
+++ b/proteus/tests/common.py
@@ -0,0 +1,18 @@
+#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 os
+from unittest import TestCase
+
+os.environ.setdefault('TRYTOND_DATABASE_URI', 'sqlite://')
+os.environ.setdefault('DB_NAME', ':memory:')
+from trytond.tests.test_tryton import db_exist, create_db
+
+from proteus import config
+
+
+class ProteusTestCase(TestCase):
+
+    def setUp(self):
+        if not db_exist():
+            create_db()
+        self.config = config.set_trytond()
diff --git a/proteus/tests/test_config.py b/proteus/tests/test_config.py
index 35433a3..2ad83f2 100644
--- a/proteus/tests/test_config.py
+++ b/proteus/tests/test_config.py
@@ -1,13 +1,10 @@
 #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 unittest import TestCase
 import proteus.config
+from .common import ProteusTestCase
 
 
-class TestConfig(TestCase):
-
-    def setUp(self):
-        proteus.config.set_trytond(database_type='sqlite')
+class TestConfig(ProteusTestCase):
 
     def test_proxy(self):
         config = proteus.config.get_config()
@@ -22,7 +19,7 @@ class TestConfig(TestCase):
 
     def test_trytond_config_eq(self):
         config1 = proteus.config.get_config()
-        proteus.config.set_trytond(database_type='sqlite')
+        proteus.config.set_trytond()
         config2 = proteus.config.get_config()
         self.assertEqual(config1, config2)
 
diff --git a/proteus/tests/test_context.py b/proteus/tests/test_context.py
index dc7faea..7cbacec 100644
--- a/proteus/tests/test_context.py
+++ b/proteus/tests/test_context.py
@@ -1,14 +1,9 @@
 # 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 .common import ProteusTestCase
 
-from unittest import TestCase
-from proteus import config
 
-
-class TestContext(TestCase):
-
-    def setUp(self):
-        self.config = config.set_trytond(database_type='sqlite')
+class TestContext(ProteusTestCase):
 
     def test_config(self):
         prev_ctx = self.config._context
diff --git a/proteus/tests/test_model.py b/proteus/tests/test_model.py
index 0a87cfc..f6aa197 100644
--- a/proteus/tests/test_model.py
+++ b/proteus/tests/test_model.py
@@ -1,13 +1,10 @@
 #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 unittest import TestCase
-from proteus import config, Model
+from proteus import Model
+from .common import ProteusTestCase
 
 
-class TestModel(TestCase):
-
-    def setUp(self):
-        config.set_trytond(database_type='sqlite')
+class TestModel(ProteusTestCase):
 
     def test_class_cache(self):
         User1 = Model.get('res.user')
@@ -39,7 +36,7 @@ class TestModel(TestCase):
         admin.create_uid = admin
         admin.create_uid = None
 
-        User(write_uid=False)
+        User(write_uid=None)
 
     def test_one2many(self):
         Group = Model.get('res.group')
@@ -278,6 +275,17 @@ class TestModel(TestCase):
         test.save()
         test.delete()
 
+    def test_duplicate(self):
+        User = Model.get('res.user')
+        test = User()
+        test.name = 'Test duplicate'
+        test.login = 'test duplicate'
+        test.save()
+        copy, = User.duplicate([test], {'name': 'Test copy'})
+        self.assertEqual(copy.name, 'Test copy')
+        self.assertEqual(copy.login, 'test duplicate (copy)')
+        self.assertNotEqual(copy.id, test.id)
+
     def test_on_change(self):
         Trigger = Model.get('ir.trigger')
 
diff --git a/proteus/tests/test_report.py b/proteus/tests/test_report.py
new file mode 100644
index 0000000..d11c7bc
--- /dev/null
+++ b/proteus/tests/test_report.py
@@ -0,0 +1,21 @@
+#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 unittest import TestCase
+from proteus import config, Report, Model
+from .common import ProteusTestCase
+
+
+class TestReport(ProteusTestCase):
+
+    def test_model_graph(self):
+        IrModel = Model.get('ir.model')
+        models = IrModel.find([])
+        data = {
+            'level': 1,
+            'filter': '',
+            }
+        report = Report('ir.model.graph')
+        type_, data, print_, name = report.execute(models, data)
+        self.assertEqual(type_, 'png')
+        self.assertEqual(print_, False)
+        self.assertEqual(name, 'Graph')
diff --git a/proteus/tests/test_wizard.py b/proteus/tests/test_wizard.py
index 86fb2a8..68b5226 100644
--- a/proteus/tests/test_wizard.py
+++ b/proteus/tests/test_wizard.py
@@ -1,13 +1,10 @@
 #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 unittest import TestCase
-from proteus import config, Wizard, Model
+from proteus import Wizard, Model
+from .common import ProteusTestCase
 
 
-class TestWizard(TestCase):
-
-    def setUp(self):
-        config.set_trytond(database_type='sqlite')
+class TestWizard(ProteusTestCase):
 
     def test_translation_clean(self):
         translation_clean = Wizard('ir.translation.clean')
-- 
tryton-proteus



More information about the tryton-debian-vcs mailing list