Skip to content

Commit 9aabd3e

Browse files
Merge pull request #14 from MatthieuDartiailh/tests
Tests improvements
2 parents 19bf1ae + 1c185f9 commit 9aabd3e

26 files changed

+729
-86
lines changed

.coveragerc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[report]
2+
# Regexes for lines to exclude from consideration
3+
exclude_lines =
4+
# Have to re-enable the standard pragma
5+
pragma: no cover
6+
7+
# Don't complain if tests don't hit defensive assertion code:
8+
raise NotImplementedError

.travis.yml

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
language: python
2+
3+
# use container-based infrastructure
4+
sudo : false
5+
6+
python:
7+
- "2.7"
8+
- "3.3"
9+
- "3.4"
10+
11+
env:
12+
- PINT="N"
13+
- PINT="Y"
14+
15+
before_install:
16+
- REDIRECT_TO=/dev/stdout # change to /dev/null to silence Travis
17+
- wget -q http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh
18+
- chmod +x miniconda.sh
19+
- ./miniconda.sh -b -p ~/anaconda &> ${REDIRECT_TO}
20+
- export PATH=~/anaconda/bin:$PATH
21+
- conda update --yes --quiet conda &> ${REDIRECT_TO}
22+
23+
- SRC_DIR=$(pwd)
24+
- export ENV_NAME=travis
25+
26+
install:
27+
- conda create --yes -n $ENV_NAME python=$TRAVIS_PYTHON_VERSION pip > ${REDIRECT_TO};
28+
- source activate $ENV_NAME
29+
- conda install --yes --quiet future, pytest > ${REDIRECT_TO};
30+
- pip install -q pytest-cov, pytest-capturelog, funcsigs
31+
# Use develop version of Pint
32+
- if [ $PINT == 'Y' ]; then pip install https://github.com/hgrecco/pint/zipball/develop; fi
33+
- pip install -q coveralls
34+
35+
script:
36+
- cd ${SRC_DIR}
37+
- py.test tests -v --cov lantz_core
38+
39+
after_script:
40+
- coveralls --verbose

AUTHORS

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Lantz is developed and maintained by the LabPy team (https://github.com/LabPy)
2+
3+
Main members are :
4+
- Matthieu Dartiailh : <[email protected]>
5+
- Hernan Grecco
6+
- Alex Forencich
7+
- A. Arsenovic
8+
9+
Other contributors, listed alphabetically, are:
10+
11+
(If you think that your name belongs here, please let the maintainer know)

CHANGES

Whitespace-only changes.

lantz_core/__init__.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
# -*- coding: utf-8 -*-
22
"""
3-
lantz
4-
~~~~~
3+
lantz_core
4+
~~~~~~~~~~
55
66
An automation and instrumentation toolkit with a clean, well-designed and
77
consistent interface.
88
99
:copyright: 2015 by The Lantz Authors
1010
:license: BSD, see LICENSE for more details.
1111
"""
12+
from __future__ import (division, unicode_literals, print_function,
13+
absolute_import)
14+
15+
from .has_features import subsystem, channel, set_feat, set_action
16+
from .action import Action
17+
from .errors import LantzError
18+
from .limits import IntLimitsValidator, FloatLimitsValidator
19+
from .unit import set_unit_registry, get_unit_registry
20+
21+
__all__ = ['subsystem', 'channel', 'set_action', 'set_feat', 'Action',
22+
'LantzError', 'set_unit_registry', 'get_unit_registry',
23+
'IntLimitsValidator', 'FloatLimitsValidator']

lantz_core/base_driver.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,12 @@ def connected(self):
146146
80)
147147
raise NotImplementedError(message)
148148

149-
def __entry__(self):
149+
def __enter__(self):
150150
"""Context manager handling the connection to the instrument.
151151
152152
"""
153153
self.initialize()
154+
return self
154155

155156
def __exit__(self, exc_type, exc_value, traceback):
156157
"""Context manager handling the connection to the instrument.

lantz_core/features/enumerable.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ class Enumerable(Feature):
2525
Permitted values for the property.
2626
2727
"""
28-
def __init__(self, getter=None, setter=None, values=(), get_format='',
28+
def __init__(self, getter=None, setter=None, values=(), extract='',
2929
retries=0, checks=None, discard=None):
30-
super(Enumerable, self).__init__(getter, setter, get_format, retries,
30+
super(Enumerable, self).__init__(getter, setter, extract, retries,
3131
checks, discard)
3232
self.values = set(values)
3333
self.creation_kwargs['values'] = values

lantz_core/features/feature.py

+24-14
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,6 @@ def __init__(self, getter=None, setter=None, extract='', retries=0,
9898
self._set if setter is not None else None,
9999
self._del)
100100

101-
self.modify_behavior('post_set', self.check_operation,
102-
('operation', 'prepend'), True)
103-
104101
if checks:
105102
self._build_checkers(checks)
106103
if discard:
@@ -243,7 +240,7 @@ def post_set(self, driver, value, i_value, response):
243240
Raised if the driver detects an issue.
244241
245242
"""
246-
self.check_operation(self, driver, value, i_value, response)
243+
self.check_operation(driver, value, i_value, response)
247244

248245
def check_operation(self, driver, value, i_value, response):
249246
"""Check the instrument operated correctly.
@@ -274,7 +271,7 @@ def check_operation(self, driver, value, i_value, response):
274271
mess += ':' + str(details)
275272
else:
276273
mess += '.'
277-
raise LantzError(mess.format(self._name, value, i_value))
274+
raise LantzError(mess.format(self.name, value, i_value))
278275

279276
def discard_cache(self, driver, value, i_value, response):
280277
"""Empty the cache of the specified values.
@@ -333,19 +330,29 @@ def modify_behavior(self, method_name, custom_method, specifiers=(),
333330
method_name : unicode
334331
Name of the Feature behavior which should be modified.
335332
336-
custom_method : MethodType
337-
Method to use when customizing the feature behavior.
333+
custom_method : callable|None
334+
Method to use when customizing the feature behavior, or None when
335+
removing a customization.
338336
339337
specifiers : tuple, optional
340338
Tuple used to determine how the method should be used. If ommitted
341339
the method will simply replace the existing behavior otherwise
342340
it will be used to update the MethodComposer in the adequate
343341
fashion. For get and set MethodComposers are not used and no matter
344342
this value the method will replace the existing behavior.
343+
ex : ('custom', 'add_after', 'old')
344+
345+
internal : bool, optional
346+
Private flag used to indicate that this method is used for internal
347+
purposes and that the modification makes no sense to remember as
348+
this won't have to be copied by copy_custom_behaviors.
345349
346350
"""
347351
# Make the method a method of the Feature.
348-
m = wrap_custom_feat_method(custom_method, self)
352+
# The if clause handles the case of 'remove' for which passing None
353+
# should work
354+
m = (wrap_custom_feat_method(custom_method, self) if custom_method
355+
else None)
349356

350357
# In the absence of specifiers or for get and set we simply replace the
351358
# method.
@@ -363,13 +370,14 @@ def modify_behavior(self, method_name, custom_method, specifiers=(),
363370
# In case of non internal modifications (ie unrelated to Feature
364371
# initialisation) we keep a description of what has been done to be
365372
# able to copy those behaviors. If a method already existed we assume
366-
# it was meaningful and add it in the composer under the id 'custom'.
373+
# it was meaningful and add it in the composer under the id 'old'.
367374
if not internal:
368375
if method_name not in self._customs:
369376
self._customs[method_name] = OrderedDict()
370377
elif not isinstance(self._customs[method_name], OrderedDict):
371-
composer.prepend('old', self._customs[method_name])
372-
self._customs[method_name] = {'custom': (m, 'prepend')}
378+
old = self._customs[method_name]
379+
composer.prepend('old', old)
380+
self._customs[method_name] = OrderedDict(old=(old, 'prepend'))
373381

374382
# We now update the composer.
375383
composer_method_name = specifiers[1]
@@ -420,6 +428,7 @@ def copy_custom_behaviors(self, feat):
420428
"""
421429
# Loop on methods which are affected by mofifiers.
422430
for meth_name, modifiers in feat._customs.items():
431+
print(meth_name)
423432
if isinstance(modifiers, MethodType):
424433
self.modify_behavior(meth_name, modifiers)
425434
continue
@@ -436,16 +445,17 @@ def copy_custom_behaviors(self, feat):
436445
aux = {'add_after': 'append', 'add_before': 'prepend'}
437446
self.modify_behavior(meth_name, modifier[0],
438447
(custom, aux[modifier[1]]))
448+
439449
# Otherwise we check whether or not the anchor exists and if
440450
# not try to find the most meaningfull one.
441451
else:
442452
our_names = method._names
443-
if custom in our_names:
453+
if modifier[2] in our_names:
444454
self.modify_behavior(meth_name, modifier[0],
445455
(custom, modifier[1],
446456
modifier[2]))
447457
else:
448-
feat_names = getattr(self, meth_name)._names
458+
feat_names = getattr(feat, meth_name)._names
449459
# For add after we try to find an entry existing in
450460
# both feature going backward (we will prepend at the
451461
# worst), for add before we go forward (we will append
@@ -464,7 +474,7 @@ def copy_custom_behaviors(self, feat):
464474

465475
if shift != 0:
466476
op = 'prepend' if shift == -1 else 'append'
467-
self.modify_behavior(meth_name, modifiers[0],
477+
self.modify_behavior(meth_name, modifier[0],
468478
(custom, op))
469479

470480
def _build_checkers(self, checks):

lantz_core/features/limits_validated.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ class LimitsValidated(Feature):
2929
provided it is used to retrieve the range from the driver at runtime.
3030
3131
"""
32-
def __init__(self, getter=None, setter=None, limits=None, get_format='',
32+
def __init__(self, getter=None, setter=None, limits=None, extract='',
3333
retries=0, checks=None, discard=None):
34-
Feature.__init__(self, getter, setter, get_format,
34+
Feature.__init__(self, getter, setter, extract,
3535
retries, checks, discard)
3636
if limits:
3737
if isinstance(limits, AbstractLimitsValidator):

lantz_core/features/util.py

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class MethodsComposer(object):
6868
be called in the right order while allowing fancy insertion based on method
6969
id.
7070
71+
Notes
72+
-----
73+
Method ids must be unique and duplicate names are removed without warning.
74+
7175
"""
7276
__slots__ = ('_names', '_methods')
7377

lantz_core/has_features.py

+13-32
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from types import FunctionType
1919
from inspect import cleandoc, getsourcelines, currentframe
2020
from itertools import chain
21-
from textwrap import fill
2221
from abc import ABCMeta
2322
from collections import defaultdict
2423

@@ -123,7 +122,7 @@ def __init__(self, bases=()):
123122

124123
def __setattr__(self, name, value):
125124
if isinstance(value, _subpart):
126-
value._parent_ = self
125+
object.__setattr__(value, '_parent_', self)
127126
object.__setattr__(self, name, value)
128127

129128
def __call__(self, func):
@@ -324,9 +323,6 @@ def __new__(meta, name, bases, dct):
324323
for key, value in dct.items():
325324

326325
if isinstance(value, _subpart):
327-
if key in subparts:
328-
msg = 'Attempt to redeclare subpart {}'
329-
raise KeyError(msg.format(key))
330326
value._name_ = key
331327
subparts[key] = value
332328

@@ -621,7 +617,7 @@ def clear_cache(self, subsystems=True, channels=True, features=None):
621617
for ch in getattr(self, chs):
622618
ch.clear_cache(subsystems)
623619

624-
def check_cache(self, subsystems=True, channels=True, properties=None):
620+
def check_cache(self, subsystems=True, channels=True, features=None):
625621
"""Return the value of the cache of the object.
626622
627623
The cache values for the subsystems and channels are not accessible.
@@ -634,8 +630,8 @@ def check_cache(self, subsystems=True, channels=True, properties=None):
634630
channels : bool, optional
635631
Whether or not to include the channels caches. This argument is
636632
used only if properties is None.
637-
properties : iterable of str, optional
638-
Name of the properties whose cache should be cleared. All caches
633+
features : iterable of str, optional
634+
Name of the features whose cache should be cleared. All caches
639635
will be cleared if not specified.
640636
641637
Returns
@@ -646,10 +642,10 @@ def check_cache(self, subsystems=True, channels=True, properties=None):
646642
647643
"""
648644
cache = {}
649-
if properties:
645+
if features:
650646
sss = defaultdict(list)
651647
chs = defaultdict(list)
652-
for name in properties:
648+
for name in features:
653649
if '.' in name:
654650
aux, n = name.split('.', 1)
655651
if aux in self.__subsystems__:
@@ -660,7 +656,7 @@ def check_cache(self, subsystems=True, channels=True, properties=None):
660656
cache[name] = self._cache[name]
661657

662658
for ss in sss:
663-
cache[ss] = getattr(self, ss).check_cache(properties=sss[ss])
659+
cache[ss] = getattr(self, ss).check_cache(features=sss[ss])
664660

665661
if self.__channels__:
666662
for ch in chs:
@@ -669,7 +665,7 @@ def check_cache(self, subsystems=True, channels=True, properties=None):
669665
channel_cont = getattr(self, ch)
670666
for ch_id in channel_cont.available:
671667
chan = channel_cont[ch_id]
672-
ch_cache[ch_id] = chan.check_cache(properties=chs[ch])
668+
ch_cache[ch_id] = chan.check_cache(features=chs[ch])
673669
else:
674670
cache = self._cache.copy()
675671
if subsystems:
@@ -693,7 +689,7 @@ def declared_limits(self):
693689
Ranges are considered declared as soon as a getter has been defined.
694690
695691
"""
696-
return self.__ranges__
692+
return self.__limits__
697693

698694
def get_limits(self, limits_id):
699695
"""Access the limits object matching the definition.
@@ -736,13 +732,7 @@ def reopen_connection(self):
736732
"""Reopen the connection to the instrument.
737733
738734
"""
739-
message = fill(cleandoc(
740-
'''This method is used to reopen a connection whose state
741-
is suspect, for example the last message sent did not
742-
go through, and should be implemented by classes
743-
subclassing HasFeatures'''),
744-
80)
745-
raise NotImplementedError(message)
735+
raise NotImplementedError()
746736

747737
def default_get_feature(self, feat, cmd, *args, **kwargs):
748738
"""Method used by default by the Feature to retrieve a value from an
@@ -762,10 +752,7 @@ def default_get_feature(self, feat, cmd, *args, **kwargs):
762752
state.
763753
764754
"""
765-
mess = fill(cleandoc('''Method used by default by the Feature to
766-
retrieve a value from an instrument. Should be implemented by
767-
classes subclassing HasFeatures.'''), 80)
768-
raise NotImplementedError(mess)
755+
raise NotImplementedError()
769756

770757
def default_set_feature(self, feat, cmd, *args, **kwargs):
771758
"""Method used by default by the Feature to set an instrument value.
@@ -784,10 +771,7 @@ def default_set_feature(self, feat, cmd, *args, **kwargs):
784771
state.
785772
786773
"""
787-
mess = fill(cleandoc('''Method used by default by the Feature to
788-
set an instrument value. Should be implemented by
789-
classes subclassing HasFeatures'''), 80)
790-
raise NotImplementedError(mess)
774+
raise NotImplementedError()
791775

792776
def default_check_operation(self, feat, value, i_value, state=None):
793777
"""Method used by default by the Feature to check the instrument
@@ -813,10 +797,7 @@ def default_check_operation(self, feat, value, i_value, state=None):
813797
something should always be returned.
814798
815799
"""
816-
mess = fill(cleandoc('''Method used by default by the Feature to
817-
check the instrument operation. Should be implemented by
818-
classes subclassing HasFeatures.'''), 80)
819-
raise NotImplementedError(mess)
800+
raise NotImplementedError()
820801

821802

822803
AbstractHasFeatures.register(HasFeatures)

0 commit comments

Comments
 (0)