[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