qcodes 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. qcodes/__init__.py +65 -0
  2. qcodes/actions.py +207 -0
  3. qcodes/config/__init__.py +2 -0
  4. qcodes/config/config.py +396 -0
  5. qcodes/config/qcodesrc.json +11 -0
  6. qcodes/config/qcodesrc_schema.json +55 -0
  7. qcodes/data/__init__.py +0 -0
  8. qcodes/data/data_array.py +520 -0
  9. qcodes/data/data_set.py +803 -0
  10. qcodes/data/format.py +309 -0
  11. qcodes/data/gnuplot_format.py +366 -0
  12. qcodes/data/hdf5_format.py +385 -0
  13. qcodes/data/io.py +266 -0
  14. qcodes/data/location.py +166 -0
  15. qcodes/data/manager.py +163 -0
  16. qcodes/instrument/__init__.py +0 -0
  17. qcodes/instrument/base.py +630 -0
  18. qcodes/instrument/function.py +134 -0
  19. qcodes/instrument/ip.py +236 -0
  20. qcodes/instrument/metaclass.py +45 -0
  21. qcodes/instrument/mock.py +278 -0
  22. qcodes/instrument/parameter.py +1001 -0
  23. qcodes/instrument/remote.py +529 -0
  24. qcodes/instrument/server.py +189 -0
  25. qcodes/instrument/sweep_values.py +286 -0
  26. qcodes/instrument/visa.py +198 -0
  27. qcodes/instrument_drivers/AlazarTech/ATS.py +1001 -0
  28. qcodes/instrument_drivers/AlazarTech/ATS9870.py +266 -0
  29. qcodes/instrument_drivers/AlazarTech/ATS_acquisition_controllers.py +143 -0
  30. qcodes/instrument_drivers/AlazarTech/__init__.py +0 -0
  31. qcodes/instrument_drivers/QuTech/IVVI.py +390 -0
  32. qcodes/instrument_drivers/QuTech/__init__.py +0 -0
  33. qcodes/instrument_drivers/__init__.py +0 -0
  34. qcodes/instrument_drivers/agilent/Agilent_34400A.py +155 -0
  35. qcodes/instrument_drivers/agilent/E8527D.py +79 -0
  36. qcodes/instrument_drivers/agilent/HP33210A.py +104 -0
  37. qcodes/instrument_drivers/agilent/__init__.py +0 -0
  38. qcodes/instrument_drivers/agilent/test_suite.py +96 -0
  39. qcodes/instrument_drivers/ithaco/Ithaco_1211.py +110 -0
  40. qcodes/instrument_drivers/ithaco/__init__.py +0 -0
  41. qcodes/instrument_drivers/oxford/__init__.py +0 -0
  42. qcodes/instrument_drivers/oxford/mercuryiPS.py +358 -0
  43. qcodes/instrument_drivers/oxford/triton.py +227 -0
  44. qcodes/instrument_drivers/rigol/DG4000.py +591 -0
  45. qcodes/instrument_drivers/rigol/__init__.py +0 -0
  46. qcodes/instrument_drivers/rohde_schwarz/SGS100A.py +99 -0
  47. qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +154 -0
  48. qcodes/instrument_drivers/rohde_schwarz/__init__.py +0 -0
  49. qcodes/instrument_drivers/signal_hound/USB_SA124B.py +567 -0
  50. qcodes/instrument_drivers/signal_hound/__init__.py +0 -0
  51. qcodes/instrument_drivers/stanford_research/SR560.py +116 -0
  52. qcodes/instrument_drivers/stanford_research/SR830.py +314 -0
  53. qcodes/instrument_drivers/stanford_research/SR865.py +305 -0
  54. qcodes/instrument_drivers/stanford_research/__init__.py +0 -0
  55. qcodes/instrument_drivers/tektronix/AWG5014.py +1228 -0
  56. qcodes/instrument_drivers/tektronix/AWG520.py +659 -0
  57. qcodes/instrument_drivers/tektronix/Keithley_2000.py +195 -0
  58. qcodes/instrument_drivers/tektronix/Keithley_2600.py +83 -0
  59. qcodes/instrument_drivers/tektronix/Keithley_2700.py +395 -0
  60. qcodes/instrument_drivers/tektronix/__init__.py +0 -0
  61. qcodes/instrument_drivers/test.py +94 -0
  62. qcodes/instrument_drivers/weinschel/Weinschel_8320.py +21 -0
  63. qcodes/instrument_drivers/weinschel/__init__.py +0 -0
  64. qcodes/instrument_drivers/weinschel/test_suite.py +27 -0
  65. qcodes/loops.py +1009 -0
  66. qcodes/measure.py +155 -0
  67. qcodes/plots/__init__.py +0 -0
  68. qcodes/plots/base.py +280 -0
  69. qcodes/plots/colors.py +135 -0
  70. qcodes/plots/pyqtgraph.py +384 -0
  71. qcodes/plots/qcmatplotlib.py +206 -0
  72. qcodes/process/__init__.py +0 -0
  73. qcodes/process/helpers.py +60 -0
  74. qcodes/process/qcodes_process.py +72 -0
  75. qcodes/process/server.py +395 -0
  76. qcodes/process/stream_queue.py +152 -0
  77. qcodes/station.py +178 -0
  78. qcodes/test.py +119 -0
  79. qcodes/tests/__init__.py +0 -0
  80. qcodes/tests/common.py +7 -0
  81. qcodes/tests/data_mocks.py +173 -0
  82. qcodes/tests/instrument_mocks.py +327 -0
  83. qcodes/tests/py35_syntax.py +13 -0
  84. qcodes/tests/test_combined_par.py +156 -0
  85. qcodes/tests/test_command.py +120 -0
  86. qcodes/tests/test_config.py +222 -0
  87. qcodes/tests/test_data.py +668 -0
  88. qcodes/tests/test_deferred_operations.py +105 -0
  89. qcodes/tests/test_driver_testcase.py +58 -0
  90. qcodes/tests/test_format.py +414 -0
  91. qcodes/tests/test_hdf5formatter.py +333 -0
  92. qcodes/tests/test_helpers.py +630 -0
  93. qcodes/tests/test_instrument.py +981 -0
  94. qcodes/tests/test_instrument_server.py +156 -0
  95. qcodes/tests/test_json.py +48 -0
  96. qcodes/tests/test_location_provider.py +109 -0
  97. qcodes/tests/test_loop.py +825 -0
  98. qcodes/tests/test_measure.py +49 -0
  99. qcodes/tests/test_metadata.py +49 -0
  100. qcodes/tests/test_multiprocessing.py +471 -0
  101. qcodes/tests/test_nested_attrs.py +100 -0
  102. qcodes/tests/test_parameter.py +151 -0
  103. qcodes/tests/test_plots.py +44 -0
  104. qcodes/tests/test_sweep_values.py +159 -0
  105. qcodes/tests/test_validators.py +482 -0
  106. qcodes/tests/test_visa.py +184 -0
  107. qcodes/utils/__init__.py +0 -0
  108. qcodes/utils/command.py +176 -0
  109. qcodes/utils/deferred_operations.py +276 -0
  110. qcodes/utils/helpers.py +417 -0
  111. qcodes/utils/metadata.py +42 -0
  112. qcodes/utils/nested_attrs.py +177 -0
  113. qcodes/utils/reload_code.py +98 -0
  114. qcodes/utils/threading.py +72 -0
  115. qcodes/utils/timing.py +89 -0
  116. qcodes/utils/validators.py +393 -0
  117. qcodes/version.py +1 -0
  118. qcodes/widgets/__init__.py +0 -0
  119. qcodes/widgets/display.py +47 -0
  120. qcodes/widgets/widgets.css +77 -0
  121. qcodes/widgets/widgets.js +237 -0
  122. qcodes/widgets/widgets.py +215 -0
  123. qcodes-0.1.0.dist-info/DESCRIPTION.rst +165 -0
  124. qcodes-0.1.0.dist-info/METADATA +194 -0
  125. qcodes-0.1.0.dist-info/RECORD +128 -0
  126. qcodes-0.1.0.dist-info/WHEEL +5 -0
  127. qcodes-0.1.0.dist-info/metadata.json +1 -0
  128. qcodes-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,49 @@
1
+ from unittest import TestCase
2
+ from datetime import datetime
3
+
4
+ from qcodes.instrument.parameter import ManualParameter
5
+ from qcodes.measure import Measure
6
+
7
+ from .instrument_mocks import MultiGetter
8
+
9
+
10
+ class TestMeasure(TestCase):
11
+ def setUp(self):
12
+ self.p1 = ManualParameter('P1', initial_value=1)
13
+
14
+ def test_simple_scalar(self):
15
+ data = Measure(self.p1).run_temp()
16
+
17
+ self.assertEqual(data.single_set.tolist(), [0])
18
+ self.assertEqual(data.P1.tolist(), [1])
19
+ self.assertEqual(len(data.arrays), 2, data.arrays)
20
+
21
+ self.assertNotIn('loop', data.metadata)
22
+
23
+ meta = data.metadata['measurement']
24
+ self.assertEqual(meta['__class__'], 'qcodes.measure.Measure')
25
+ self.assertEqual(len(meta['actions']), 1)
26
+ self.assertFalse(meta['background'])
27
+ self.assertFalse(meta['use_data_manager'])
28
+ self.assertFalse(meta['use_threads'])
29
+
30
+ ts_start = datetime.strptime(meta['ts_start'], '%Y-%m-%d %H:%M:%S')
31
+ ts_end = datetime.strptime(meta['ts_end'], '%Y-%m-%d %H:%M:%S')
32
+ self.assertGreaterEqual(ts_end, ts_start)
33
+
34
+ def test_simple_array(self):
35
+ data = Measure(MultiGetter(arr=(1.2, 3.4))).run_temp()
36
+
37
+ self.assertEqual(data.index0.tolist(), [0, 1])
38
+ self.assertEqual(data.arr.tolist(), [1.2, 3.4])
39
+ self.assertEqual(len(data.arrays), 2, data.arrays)
40
+
41
+ def test_array_and_scalar(self):
42
+ self.p1.set(42)
43
+ data = Measure(MultiGetter(arr=(5, 6)), self.p1).run_temp()
44
+
45
+ self.assertEqual(data.single_set.tolist(), [0])
46
+ self.assertEqual(data.P1.tolist(), [42])
47
+ self.assertEqual(data.index0.tolist(), [0, 1])
48
+ self.assertEqual(data.arr.tolist(), [5, 6])
49
+ self.assertEqual(len(data.arrays), 4, data.arrays)
@@ -0,0 +1,49 @@
1
+ from unittest import TestCase
2
+
3
+ from qcodes.utils.metadata import Metadatable
4
+
5
+
6
+ class TestMetadatable(TestCase):
7
+ def test_load(self):
8
+ m = Metadatable()
9
+ self.assertEqual(m.metadata, {})
10
+ m.load_metadata({1: 2, 3: 4})
11
+ self.assertEqual(m.metadata, {1: 2, 3: 4})
12
+ m.load_metadata({1: 5})
13
+ self.assertEqual(m.metadata, {1: 5, 3: 4})
14
+
15
+ def test_init(self):
16
+ with self.assertRaises(TypeError):
17
+ Metadatable(metadata={2: 3}, not_metadata={4: 5})
18
+
19
+ m = Metadatable(metadata={2: 3})
20
+ self.assertEqual(m.metadata, {2: 3})
21
+
22
+ class HasSnapshotBase(Metadatable):
23
+ def snapshot_base(self, update=False):
24
+ return {'cheese': 'gruyere'}
25
+
26
+ class HasSnapshot(Metadatable):
27
+ # Users shouldn't do this... but we'll test its behavior
28
+ # for completeness
29
+ def snapshot(self, update=False):
30
+ return {'fruit': 'kiwi'}
31
+
32
+ def test_snapshot(self):
33
+ m = Metadatable(metadata={6: 7})
34
+ self.assertEqual(m.snapshot_base(), {})
35
+ self.assertEqual(m.snapshot(), {'metadata': {6: 7}})
36
+ del m.metadata[6]
37
+ self.assertEqual(m.snapshot(), {})
38
+
39
+ sb = self.HasSnapshotBase(metadata={7: 8})
40
+ self.assertEqual(sb.snapshot_base(), {'cheese': 'gruyere'})
41
+ self.assertEqual(sb.snapshot(),
42
+ {'cheese': 'gruyere', 'metadata': {7: 8}})
43
+ del sb.metadata[7]
44
+ self.assertEqual(sb.snapshot(), sb.snapshot_base())
45
+
46
+ s = self.HasSnapshot(metadata={8: 9})
47
+ self.assertEqual(s.snapshot(), {'fruit': 'kiwi'})
48
+ self.assertEqual(s.snapshot_base(), {})
49
+ self.assertEqual(s.metadata, {8: 9})
@@ -0,0 +1,471 @@
1
+ from unittest import TestCase, skipIf
2
+ import time
3
+ import re
4
+ import sys
5
+ import multiprocessing as mp
6
+ from queue import Empty
7
+ from unittest.mock import patch
8
+
9
+ import qcodes
10
+ from qcodes.process.helpers import set_mp_method, kill_queue
11
+ from qcodes.process.qcodes_process import QcodesProcess
12
+ from qcodes.process.stream_queue import get_stream_queue, _SQWriter
13
+ from qcodes.process.server import ServerManager, RESPONSE_OK, RESPONSE_ERROR
14
+ import qcodes.process.helpers as qcmp
15
+ from qcodes.utils.helpers import in_notebook, LogCapture
16
+ from qcodes.utils.timing import calibrate
17
+
18
+ BREAK_SIGNAL = '~~BREAK~~'
19
+
20
+
21
+ class sqtest_echo:
22
+ def __init__(self, name, delay=0.01, has_q=True):
23
+ self.q_out = mp.Queue()
24
+ self.q_err = mp.Queue()
25
+ self.p = QcodesProcess(target=sqtest_echo_f,
26
+ args=(name, delay, self.q_out, self.q_err,
27
+ has_q),
28
+ name=name)
29
+ self.p.start()
30
+ self.delay = delay
31
+ self.resp_delay = delay * 2 + 0.03
32
+
33
+ def send_out(self, msg):
34
+ self.q_out.put(msg)
35
+ time.sleep(self.resp_delay)
36
+
37
+ def send_err(self, msg):
38
+ self.q_err.put(msg)
39
+ time.sleep(self.resp_delay)
40
+
41
+ def halt(self):
42
+ if not (hasattr(self, 'p') and self.p.is_alive()):
43
+ return
44
+ self.q_out.put(BREAK_SIGNAL)
45
+ self.p.join()
46
+ time.sleep(self.resp_delay)
47
+ for q in ['q_out', 'q_err']:
48
+ if hasattr(self, q):
49
+ queue = getattr(self, q)
50
+ kill_queue(queue)
51
+ kill_queue(queue) # repeat just to make sure it doesn't error
52
+
53
+ def __del__(self):
54
+ self.halt()
55
+
56
+
57
+ def sqtest_echo_f(name, delay, q_out, q_err, has_q):
58
+ while True:
59
+ time.sleep(delay)
60
+
61
+ if not q_out.empty():
62
+ out = q_out.get()
63
+
64
+ if out == BREAK_SIGNAL:
65
+ # now test that disconnect works, and reverts to
66
+ # regular stdout and stderr
67
+ if has_q:
68
+ try:
69
+ get_stream_queue().disconnect()
70
+ except RuntimeError:
71
+ pass
72
+ print('stdout ', end='', flush=True)
73
+ print('stderr ', file=sys.stderr, end='', flush=True)
74
+ break
75
+
76
+ print(out, end='', flush=True)
77
+
78
+ if not q_err.empty():
79
+ print(q_err.get(), file=sys.stderr, end='', flush=True)
80
+
81
+
82
+ def sqtest_exception():
83
+ raise RuntimeError('Boo!')
84
+
85
+
86
+ class TestMpMethod(TestCase):
87
+ def test_set_mp_method(self):
88
+ start_method = mp.get_start_method()
89
+ self.assertIn(start_method, ('fork', 'spawn', 'forkserver'))
90
+
91
+ # multiprocessing's set_start_method is NOT idempotent
92
+ with self.assertRaises(RuntimeError):
93
+ mp.set_start_method(start_method)
94
+
95
+ # but ours is
96
+ set_mp_method(start_method)
97
+
98
+ # it will still error on gibberish, but different errors depending
99
+ # on whether you force or not
100
+ with self.assertRaises(RuntimeError):
101
+ set_mp_method('spoon')
102
+ with self.assertRaises(ValueError):
103
+ set_mp_method('spoon', force=True)
104
+
105
+ # change the error we look for to test strange error handling
106
+ mp_err_normal = qcmp.MP_ERR
107
+ qcmp.MP_ERR = 'who cares?'
108
+ with self.assertRaises(RuntimeError):
109
+ set_mp_method('start_method')
110
+ qcmp.MP_ERR = mp_err_normal
111
+
112
+
113
+ class TestQcodesProcess(TestCase):
114
+ def setUp(self):
115
+ mp_stats = calibrate(quiet=True)
116
+ self.MP_START_DELAY = mp_stats['mp_start_delay']
117
+ self.MP_FINISH_DELAY = mp_stats['mp_finish_delay']
118
+ self.SLEEP_DELAY = mp_stats['sleep_delay']
119
+ self.BLOCKING_TIME = mp_stats['blocking_time']
120
+ self.sq = get_stream_queue()
121
+
122
+ @skipIf(getattr(qcodes, '_IN_NOTEBOOK', False),
123
+ 'called from notebook')
124
+ def test_not_in_notebook(self):
125
+ # below we'll patch this to True, but make sure that it's False
126
+ # in the normal test runner.
127
+ self.assertEqual(in_notebook(), False)
128
+
129
+ # and make sure that processes run this way do not use the queue
130
+ with self.sq.lock:
131
+ p = sqtest_echo('hidden', has_q=False)
132
+ time.sleep(self.MP_START_DELAY)
133
+ p.send_out('should go to stdout;')
134
+ p.send_err('should go to stderr;')
135
+ p.halt()
136
+
137
+ self.assertEqual(self.sq.get(), '')
138
+
139
+ @patch('qcodes.process.qcodes_process.in_notebook')
140
+ def test_qcodes_process_exception(self, in_nb_patch):
141
+ in_nb_patch.return_value = True
142
+
143
+ with self.sq.lock:
144
+ name = 'Hamlet'
145
+ p = QcodesProcess(target=sqtest_exception, name=name)
146
+
147
+ initial_outs = (sys.stdout, sys.stderr)
148
+
149
+ # normally you call p.start(), but for this test we want
150
+ # the function to actually run in the main process
151
+ # it will run the actual target, but will print the exception
152
+ # (to the queue) rather than raising it.
153
+ p.run()
154
+
155
+ # output streams are back to how they started
156
+ self.assertEqual((sys.stdout, sys.stderr), initial_outs)
157
+ time.sleep(0.01)
158
+ exc_text = self.sq.get()
159
+ # but we have the exception in the queue
160
+ self.maxDiff = None
161
+ self.assertGreaterEqual(exc_text.count(name + ' ERR'), 5)
162
+ self.assertEqual(exc_text.count('Traceback'), 1, exc_text)
163
+ self.assertEqual(exc_text.count('RuntimeError'), 2)
164
+ self.assertEqual(exc_text.count('Boo!'), 2)
165
+
166
+ @patch('qcodes.process.qcodes_process.in_notebook')
167
+ def test_qcodes_process(self, in_nb_patch):
168
+ in_nb_patch.return_value = True
169
+
170
+ queue_format = re.compile(
171
+ '^\[\d\d:\d\d:\d\d\.\d\d\d p\d( ERR)?\] [^\[\]]*$')
172
+
173
+ with self.sq.lock:
174
+ p1 = sqtest_echo('p1')
175
+ p2 = sqtest_echo('p2')
176
+ time.sleep(self.MP_START_DELAY + p1.delay + p2.delay)
177
+
178
+ self.assertEqual(self.sq.get(), '')
179
+
180
+ procNames = ['<{}>'.format(name)
181
+ for name in ('p1', 'p2')]
182
+
183
+ reprs = [repr(p) for p in mp.active_children()]
184
+ for name in procNames:
185
+ self.assertIn(name, reprs)
186
+
187
+ # test each individual stream to send several messages on same
188
+ # and different lines
189
+
190
+ for sender, label, term in ([[p1.send_out, 'p1] ', ''],
191
+ [p1.send_err, 'p1 ERR] ', '\n'],
192
+ [p2.send_out, 'p2] ', '\n'],
193
+ [p2.send_err, 'p2 ERR] ', '']]):
194
+ sender('row row ')
195
+ sender('row your boat\n')
196
+ sender('gently down ')
197
+ time.sleep(0.01)
198
+ data = [line for line in self.sq.get().split('\n') if line]
199
+ expected = [
200
+ label + 'row row row your boat',
201
+ label + 'gently down '
202
+ ]
203
+ # TODO - intermittent error here
204
+ self.assertEqual(len(data), len(expected), data)
205
+ for line, expected_line in zip(data, expected):
206
+ self.assertIsNotNone(queue_format.match(line), data)
207
+ self.assertEqual(line[14:], expected_line, data)
208
+
209
+ sender(' the stream' + term)
210
+ # no label/header as we're continuing the previous line
211
+ self.assertEqual(self.sq.get(), ' the stream' + term)
212
+
213
+ p1.send_out('marco')
214
+ p2.send_out('polo\n') # we don't see these single terminators
215
+ p1.send_out('marco\n') # when we change streams
216
+ p2.send_out('polo')
217
+ time.sleep(0.01)
218
+
219
+ data = self.sq.get().split('\n')
220
+ for line in data:
221
+ if line:
222
+ self.assertIsNotNone(queue_format.match(line))
223
+
224
+ data_msgs = [line[14:] for line in data]
225
+ expected = [
226
+ '',
227
+ 'p1] marco',
228
+ 'p2] polo',
229
+ 'p1] marco',
230
+ 'p2] polo'
231
+ ]
232
+ self.assertEqual(data_msgs, expected)
233
+
234
+ # Some OS's start more processes just for fun... so don't test
235
+ # that p1 and p2 are the only ones.
236
+ # self.assertEqual(len(reprs), 2, reprs)
237
+
238
+ p1.halt()
239
+ p2.halt()
240
+ # both p1 and p2 should have finished now, and ended.
241
+ reprs = [repr(p) for p in mp.active_children()]
242
+ for name in procNames:
243
+ self.assertNotIn(name, reprs)
244
+
245
+
246
+ class TestStreamQueue(TestCase):
247
+ def test_connection(self):
248
+ sq = get_stream_queue()
249
+ sq.connect('')
250
+ # TODO: do we really want double-connect to raise? or maybe
251
+ # only raise if the process name changes?
252
+ with self.assertRaises(RuntimeError):
253
+ sq.connect('')
254
+ sq.disconnect()
255
+ with self.assertRaises(RuntimeError):
256
+ sq.disconnect()
257
+
258
+ def test_del(self):
259
+ sq = get_stream_queue()
260
+ self.assertTrue(hasattr(sq, 'queue'))
261
+ self.assertTrue(hasattr(sq, 'lock'))
262
+ self.assertIsNotNone(sq.instance)
263
+
264
+ sq.__del__()
265
+
266
+ self.assertFalse(hasattr(sq, 'queue'))
267
+ self.assertFalse(hasattr(sq, 'lock'))
268
+ self.assertIsNone(sq.instance)
269
+
270
+ sq.__del__() # just to make sure it doesn't error
271
+
272
+ # this is basically tested in TestQcodesProcess, but the test happens
273
+ # in a subprocess so coverage doesn't know about it. Anyway, there are
274
+ # a few edge cases left that we have to test locally.
275
+ def test_sq_writer(self):
276
+ sq = get_stream_queue()
277
+ with sq.lock:
278
+ sq_clearer = _SQWriter(sq, 'Someone else')
279
+ sq_clearer.write('Boo!\n')
280
+
281
+ # apparently we need a little delay to make sure the queue
282
+ # properly appears populated.
283
+ time.sleep(0.01)
284
+
285
+ sq.get()
286
+ sq_name = 'A Queue'
287
+ sqw = _SQWriter(sq, sq_name)
288
+
289
+ # flush should exist, but does nothing
290
+ sqw.flush()
291
+
292
+ lines = [
293
+ 'Knock knock.\nWho\'s there?\n',
294
+ 'Interrupting cow.\n',
295
+ 'Interr-',
296
+ 'MOO!\n',
297
+ ''
298
+ ]
299
+
300
+ for line in lines:
301
+ sqw.write(line)
302
+
303
+ time.sleep(0.01)
304
+
305
+ # _SQWriter doesn't do any transformations to the messages, beyond
306
+ # adding a time string and the stream name
307
+ for line in lines:
308
+ if not line:
309
+ self.assertEqual(sq.queue.empty(), True)
310
+ continue
311
+
312
+ self.assertEqual(sq.queue.empty(), False)
313
+ timestr, stream_name, msg = sq.queue.get()
314
+ self.assertEqual(msg, line)
315
+ self.assertEqual(stream_name, sq_name)
316
+
317
+ # now test that even if the queue is unwatched the messages still
318
+ # go there. If we're feeling adventurous maybe we can test if
319
+ # something was actually printed.
320
+ sqw.MIN_READ_TIME = -1
321
+ new_message = 'should get printed\n'
322
+ sqw.write(new_message)
323
+
324
+ time.sleep(0.01)
325
+
326
+ self.assertEqual(sq.queue.empty(), False)
327
+ self.assertEqual(sq.queue.get()[2], new_message)
328
+
329
+ # test that an error in writing resets stdout and stderr
330
+ # nose uses its own stdout and stderr... so keep them (as we will
331
+ # force stdout and stderr to several other things) so we can put
332
+ # them back at the end
333
+ nose_stdout = sys.stdout
334
+ nose_stderr = sys.stderr
335
+ sys.stdout = _SQWriter(sq, 'mock_stdout')
336
+ sys.stderr = _SQWriter(sq, 'mock_stderr')
337
+ self.assertNotIn(sys.stdout, (sys.__stdout__, nose_stdout))
338
+ self.assertNotIn(sys.stderr, (sys.__stderr__, nose_stderr))
339
+
340
+ sqw.MIN_READ_TIME = 'not a number'
341
+ with self.assertRaises(TypeError):
342
+ sqw.write('trigger an error')
343
+
344
+ self.assertEqual(sys.stdout, sys.__stdout__)
345
+ self.assertEqual(sys.stderr, sys.__stderr__)
346
+
347
+ sys.stdout = nose_stdout
348
+ sys.stderr = nose_stderr
349
+ time.sleep(0.01)
350
+ sq.get()
351
+
352
+
353
+ class ServerManagerTest(ServerManager):
354
+ def _start_server(self):
355
+ # don't really start the server - we'll test its pieces separately,
356
+ # in the main process
357
+ pass
358
+
359
+
360
+ class EmptyServer:
361
+ def __init__(self, query_queue, response_queue, extras):
362
+ query_queue.put('why?')
363
+ response_queue.put(extras)
364
+
365
+
366
+ class CustomError(Exception):
367
+ pass
368
+
369
+
370
+ def delayed_put(queue, val, delay):
371
+ time.sleep(delay)
372
+ queue.put(val)
373
+
374
+
375
+ class TestServerManager(TestCase):
376
+ def check_error(self, manager, error_str, error_class):
377
+ manager._response_queue.put(RESPONSE_ERROR, error_str)
378
+ with self.assertRaises(error_class):
379
+ manager.ask('which way does the wind blow?')
380
+
381
+ def test_mechanics(self):
382
+ extras = 'super secret don\'t tell anyone'
383
+
384
+ sm = ServerManagerTest(name='test', server_class=EmptyServer,
385
+ shared_attrs=extras)
386
+ sm._run_server()
387
+
388
+ self.assertEqual(sm._query_queue.get(timeout=1), 'why?')
389
+ self.assertEqual(sm._response_queue.get(timeout=1), extras)
390
+
391
+ # builtin errors we propagate to the server
392
+ builtin_error_str = ('traceback\n lines\n and then\n'
393
+ ' OSError: your hard disk went floppy.')
394
+ sm._response_queue.put((RESPONSE_ERROR, builtin_error_str))
395
+ with self.assertRaises(OSError):
396
+ sm.ask('which way does the wind blow?')
397
+
398
+ # non-built-in errors we fall back on RuntimeError
399
+ custom_error_str = ('traceback\nlines\nand then\n'
400
+ 'CustomError: the Balrog is loose!')
401
+ extra_resp1 = 'should get tossed by the error checker'
402
+ extra_resp2 = 'so should this.'
403
+ sm._response_queue.put((RESPONSE_OK, extra_resp1))
404
+ sm._response_queue.put((RESPONSE_OK, extra_resp2))
405
+ sm._response_queue.put((RESPONSE_ERROR, custom_error_str))
406
+
407
+ # TODO: we have an intermittent failure below, but only when running
408
+ # the full test suite (including pyqt and matplotlib?), not if we
409
+ # run just this module, or at least not nearly as frequently.
410
+ time.sleep(0.2)
411
+
412
+ with LogCapture() as logs:
413
+ with self.assertRaises(RuntimeError):
414
+ sm.ask('something benign')
415
+ self.assertTrue(sm._response_queue.empty())
416
+ self.assertIn(extra_resp1, logs.value)
417
+ self.assertIn(extra_resp2, logs.value)
418
+
419
+ # extra responses to a query, only the last should be taken
420
+ extra_resp1 = 'boo!'
421
+ extra_resp2 = 'a barrel of monkeys!'
422
+ sm._response_queue.put((RESPONSE_OK, extra_resp1))
423
+ sm._response_queue.put((RESPONSE_OK, extra_resp2))
424
+ time.sleep(0.05)
425
+ p = mp.Process(target=delayed_put,
426
+ args=(sm._response_queue, (RESPONSE_OK, 42), 0.05))
427
+ p.start()
428
+
429
+ with LogCapture() as logs:
430
+ self.assertEqual(sm.ask('what is the answer'), 42)
431
+ self.assertIn(extra_resp1, logs.value)
432
+ self.assertIn(extra_resp2, logs.value)
433
+
434
+ # no response to a query
435
+ with self.assertRaises(Empty):
436
+ sm.ask('A sphincter says what?', timeout=0.05)
437
+
438
+ # test halting an unresponsive server
439
+ sm._server = mp.Process(target=time.sleep, args=(1000,))
440
+ sm._server.start()
441
+
442
+ self.assertIn(sm._server, mp.active_children())
443
+
444
+ with LogCapture() as logs:
445
+ sm.halt(0.01)
446
+ self.assertIn('ServerManager did not respond '
447
+ 'to halt signal, terminated', logs.value)
448
+
449
+ self.assertNotIn(sm._server, mp.active_children())
450
+
451
+ def test_pathological_edge_cases(self):
452
+ # kill_queue should never fail
453
+ kill_queue(None)
454
+
455
+ # and halt should ignore AssertionErrors, which arise in
456
+ # subprocesses when trying to kill a different subprocess
457
+ sm = ServerManagerTest(name='test', server_class=None)
458
+
459
+ class HorribleProcess:
460
+ def is_alive(self):
461
+ raise AssertionError
462
+
463
+ def write(self):
464
+ raise AssertionError
465
+
466
+ def join(self):
467
+ raise AssertionError
468
+
469
+ sm._server = HorribleProcess()
470
+
471
+ sm.halt()
@@ -0,0 +1,100 @@
1
+ from unittest import TestCase
2
+ from qcodes.utils.nested_attrs import NestedAttrAccess
3
+
4
+
5
+ class TestNestedAttrAccess(TestCase):
6
+ def test_simple(self):
7
+ obj = NestedAttrAccess()
8
+
9
+ # before setting attr1
10
+ self.assertEqual(obj.getattr('attr1', 99), 99)
11
+ with self.assertRaises(AttributeError):
12
+ obj.getattr('attr1')
13
+
14
+ with self.assertRaises(TypeError):
15
+ obj.setattr('attr1')
16
+
17
+ self.assertFalse(hasattr(obj, 'attr1'))
18
+
19
+ # set it to a value
20
+ obj.setattr('attr1', 98)
21
+ self.assertTrue(hasattr(obj, 'attr1'))
22
+
23
+ self.assertEqual(obj.getattr('attr1', 99), 98)
24
+ self.assertEqual(obj.getattr('attr1'), 98)
25
+
26
+ # then delete it
27
+ obj.delattr('attr1')
28
+
29
+ with self.assertRaises(AttributeError):
30
+ obj.delattr('attr1')
31
+
32
+ with self.assertRaises(AttributeError):
33
+ obj.getattr('attr1')
34
+
35
+ # make and call a method
36
+ def f(a, b=0):
37
+ return a + b
38
+
39
+ obj.setattr('m1', f)
40
+ self.assertEqual(obj.callattr('m1', 4, 1), 5)
41
+ self.assertEqual(obj.callattr('m1', 21, b=42), 63)
42
+
43
+ def test_nested(self):
44
+ obj = NestedAttrAccess()
45
+
46
+ self.assertFalse(hasattr(obj, 'd1'))
47
+
48
+ with self.assertRaises(TypeError):
49
+ obj.setattr('d1')
50
+
51
+ # set one attribute that creates nesting
52
+ obj.setattr('d1', {'a': {1: 2, 'l': [5, 6]}})
53
+
54
+ # can't nest inside a non-container
55
+ with self.assertRaises(TypeError):
56
+ obj.setattr('d1["a"][1]["secret"]', 42)
57
+
58
+ # get the whole dict
59
+ self.assertEqual(obj.getattr('d1'), {'a': {1: 2, 'l': [5, 6]}})
60
+ self.assertEqual(obj.getattr('d1', 55), {'a': {1: 2, 'l': [5, 6]}})
61
+
62
+ # get parts
63
+ self.assertEqual(obj.getattr('d1["a"]'), {1: 2, 'l': [5, 6]})
64
+ self.assertEqual(obj.getattr('d1["a"][1]'), 2)
65
+ self.assertEqual(obj.getattr('d1["a"][1]', 3), 2)
66
+ with self.assertRaises(KeyError):
67
+ obj.getattr('d1["b"]')
68
+
69
+ # add an attribute inside, then delete it again
70
+ obj.setattr('d1["a"][2]', 4)
71
+ self.assertEqual(obj.getattr('d1'), {'a': {1: 2, 2: 4, 'l': [5, 6]}})
72
+ obj.delattr('d1["a"][2]')
73
+ self.assertEqual(obj.getattr('d1'), {'a': {1: 2, 'l': [5, 6]}})
74
+ self.assertEqual(obj.d1, {'a': {1: 2, 'l': [5, 6]}})
75
+
76
+ # list access
77
+ obj.setattr('d1["a"]["l"][0]', 7)
78
+ obj.callattr('d1["a"]["l"].extend', [5, 3])
79
+ obj.delattr('d1["a"]["l"][1]')
80
+ # while we're at it test single quotes
81
+ self.assertEqual(obj.getattr("d1['a']['l'][1]"), 5)
82
+ self.assertEqual(obj.d1['a']['l'], [7, 5, 3])
83
+
84
+ def test_bad_attr(self):
85
+ obj = NestedAttrAccess()
86
+ obj.d = {}
87
+ # this one works
88
+ obj.setattr('d["x"]', 1)
89
+
90
+ bad_attrs = [
91
+ '', '.', '[', 'x.', '[]', # simply malformed
92
+ '.x' # don't put a dot at the start
93
+ '["hi"]', # can't set an item at the top level
94
+ 'd[x]', 'd["x]', 'd["x\']' # quoting errors
95
+ ]
96
+
97
+ for attr in bad_attrs:
98
+ with self.subTest(attr=attr):
99
+ with self.assertRaises(ValueError):
100
+ obj.setattr(attr, 1)