Skip to content

Commit 61c0004

Browse files
committed
Fixes and enhancements
1 parent 8fd9373 commit 61c0004

File tree

4 files changed

+62
-4
lines changed

4 files changed

+62
-4
lines changed

CHANGES.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@ Changelog
22
---------
33

44

5+
2.x (unreleased)
6+
~~~~~~~~~~~~~~~~
7+
8+
* Default location for the configuration file is the
9+
initial working directory.
10+
11+
* Enhanced syntax for method :meth:`RecordList.filtered`.
12+
E.g. instead of ``records.filtered(lambda r: r.type == 'active')``
13+
it's faster to use ``records.filtered(['type = active'])``.
14+
15+
* Support unary operators even for Python 3.
16+
17+
* Basic sequence operations on :class:`Env` instance.
18+
19+
520
2.1.7 (2019-03-20)
621
~~~~~~~~~~~~~~~~~~
722

odooly.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,12 @@ def _convert(node, _consts={'None': None, 'True': True, 'False': False}):
157157
return {_convert(k): _convert(v)
158158
for (k, v) in zip(node.keys, node.values)}
159159
if isinstance(node, _ast.Name) and node.id in _consts:
160-
return _consts[node.id] # Python <= 3.3
160+
return _consts[node.id] # Python <= 3.3
161+
if isinstance(node, _ast.UnaryOp): # Python >= 3
162+
if isinstance(node.op, _ast.USub):
163+
return -_convert(node.operand)
164+
if isinstance(node.op, _ast.UAdd):
165+
return +_convert(node.operand)
161166
raise ValueError('malformed or disallowed expression')
162167

163168

@@ -480,10 +485,30 @@ def __new__(cls, client, db_name=()):
480485
env._models = {}
481486
return env
482487

488+
def __contains__(self, name):
489+
"""Test wether the given model exists."""
490+
return name in self._model_names or name in self.models(name)
491+
483492
def __getitem__(self, name):
484493
"""Return the given :class:`Model`."""
485494
return self._get(name)
486495

496+
def __iter__(self):
497+
"""Return an iterator on model names."""
498+
return iter(self.models())
499+
500+
def __len__(self):
501+
"""Return the size of the model registry."""
502+
return len(self.models())
503+
504+
def __bool__(self):
505+
return True
506+
__nonzero__ = __bool__
507+
508+
__eq__ = object.__eq__
509+
__ne__ = object.__ne__
510+
__hash__ = object.__hash__
511+
487512
def __repr__(self):
488513
return "<Env '%s@%s'>" % (self.user.login if self.uid else '',
489514
self.db_name)
@@ -627,7 +652,7 @@ def __call__(self, user=None, password=None, context=None):
627652
return self
628653
env_key = json.dumps((uid, context), sort_keys=True)
629654
env = self._cache_get(env_key)
630-
if not env:
655+
if env is None:
631656
env = self._configure(uid, user, password, context)
632657
self._cache_set(env_key, env)
633658
return env
@@ -1570,9 +1595,15 @@ def mapped(self, func):
15701595
return vals
15711596

15721597
def filtered(self, func):
1573-
"""Select the records such that ``func(rec)`` is true."""
1598+
"""Select the records such that ``func(rec)`` is true.
1599+
1600+
As an alternative ``func`` can be a search domain (list)
1601+
to search among the records.
1602+
"""
15741603
if callable(func):
15751604
ids = [rec._idnames[0] for rec in self if func(rec)]
1605+
elif isinstance(func, list):
1606+
return self & self._model.search([('id', 'in', self.ids)] + func)
15761607
else:
15771608
ids = self[:]._filter(func.split('.')) if func else self._idnames
15781609
return BaseRecord(self._model, ids)
@@ -1922,7 +1953,7 @@ def main(interact=_interact):
19221953

19231954
(args, domain) = parser.parse_args()
19241955

1925-
Client._config_file = os.path.join(os.curdir, args.config or CONF_FILE)
1956+
Client._config_file = os.path.join(os.getcwd(), args.config or CONF_FILE)
19261957
if args.list_env:
19271958
print('Available settings: ' + ' '.join(read_config()))
19281959
return

tests/test_model.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,12 +1197,15 @@ def test_filtered(self):
11971197
[{'id': 42, 'foo_categ_id': False}],
11981198
[{'id': 88, 'foo_categ_id': [33, 'Categ 33']}],
11991199
[{'id': 33, 'flag3': 'OK'}],
1200+
1201+
[17],
12001202
]
12011203

12021204
ids1 = [42, 13, 17, 42, 112, 4, 7]
12031205
idns1 = [(42, 'qude'), (13, 'trz'), (17, 'dspt'), 42, (112, 'cdz'), False, 4, (7, 'spt')]
12041206
ids1_sorted = sorted(set(ids1) - {False})
12051207
records1 = m.browse(idns1)
1208+
domain1 = [('id', 'in', records1.ids), ('foo_categ_id.color', 'like', 'Magenta')]
12061209
self.assertEqual(records1.filtered('flag1'),
12071210
odooly.RecordList(m, [42, 42]))
12081211
self.assertEqual(records1.filtered('foo_categ_id'),
@@ -1221,6 +1224,9 @@ def test_filtered(self):
12211224
self.assertEqual(rec1.filtered('foo_categ_id.flag3'), m.browse([88]))
12221225
self.assertEqual(rec1.filtered(bool), m.browse([88]))
12231226

1227+
self.assertEqual(records1.filtered(['foo_categ_id.color like Magenta']),
1228+
odooly.RecordList(m, [17]))
1229+
12241230
self.assertRaises(TypeError, records1.filtered)
12251231

12261232
self.assertCalls(
@@ -1242,6 +1248,8 @@ def test_filtered(self):
12421248
OBJ('foo.bar', 'read', [42], ['foo_categ_id']),
12431249
OBJ('foo.bar', 'read', [88], ['foo_categ_id']),
12441250
OBJ('foo.categ', 'read', [33], ['flag3']),
1251+
1252+
OBJ('foo.bar', 'search', domain1),
12451253
)
12461254

12471255
records3 = m.browse([])

tests/test_util.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ def test_searchargs(self):
4747
self.assertEqual(searchargs((['ham<2'],)), ([('ham', '<', 2)],))
4848
self.assertEqual(searchargs((['ham<=2'],)), ([('ham', '<=', 2)],))
4949

50+
# Combine with unary operators
51+
self.assertEqual(searchargs((['ham=- 2'],)), ([('ham', '=', -2)],))
52+
self.assertEqual(searchargs((['ham<+ 2'],)), ([('ham', '<', 2)],))
53+
5054
# Operators rarely used
5155
self.assertEqual(searchargs((['status =like Running'],)),
5256
([('status', '=like', 'Running')],))

0 commit comments

Comments
 (0)