QuLab 2.10.10__cp313-cp313-macosx_10_13_universal2.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 (107) hide show
  1. qulab/__init__.py +33 -0
  2. qulab/__main__.py +4 -0
  3. qulab/cli/__init__.py +0 -0
  4. qulab/cli/commands.py +30 -0
  5. qulab/cli/config.py +170 -0
  6. qulab/cli/decorators.py +28 -0
  7. qulab/dicttree.py +523 -0
  8. qulab/executor/__init__.py +5 -0
  9. qulab/executor/analyze.py +188 -0
  10. qulab/executor/cli.py +434 -0
  11. qulab/executor/load.py +563 -0
  12. qulab/executor/registry.py +185 -0
  13. qulab/executor/schedule.py +543 -0
  14. qulab/executor/storage.py +615 -0
  15. qulab/executor/template.py +259 -0
  16. qulab/executor/utils.py +194 -0
  17. qulab/expression.py +827 -0
  18. qulab/fun.cpython-313-darwin.so +0 -0
  19. qulab/monitor/__init__.py +1 -0
  20. qulab/monitor/__main__.py +8 -0
  21. qulab/monitor/config.py +41 -0
  22. qulab/monitor/dataset.py +77 -0
  23. qulab/monitor/event_queue.py +54 -0
  24. qulab/monitor/mainwindow.py +234 -0
  25. qulab/monitor/monitor.py +115 -0
  26. qulab/monitor/ploter.py +123 -0
  27. qulab/monitor/qt_compat.py +16 -0
  28. qulab/monitor/toolbar.py +265 -0
  29. qulab/scan/__init__.py +2 -0
  30. qulab/scan/curd.py +221 -0
  31. qulab/scan/models.py +554 -0
  32. qulab/scan/optimize.py +76 -0
  33. qulab/scan/query.py +387 -0
  34. qulab/scan/record.py +603 -0
  35. qulab/scan/scan.py +1166 -0
  36. qulab/scan/server.py +450 -0
  37. qulab/scan/space.py +213 -0
  38. qulab/scan/utils.py +234 -0
  39. qulab/storage/__init__.py +0 -0
  40. qulab/storage/__main__.py +51 -0
  41. qulab/storage/backend/__init__.py +0 -0
  42. qulab/storage/backend/redis.py +204 -0
  43. qulab/storage/base_dataset.py +352 -0
  44. qulab/storage/chunk.py +60 -0
  45. qulab/storage/dataset.py +127 -0
  46. qulab/storage/file.py +273 -0
  47. qulab/storage/models/__init__.py +22 -0
  48. qulab/storage/models/base.py +4 -0
  49. qulab/storage/models/config.py +28 -0
  50. qulab/storage/models/file.py +89 -0
  51. qulab/storage/models/ipy.py +58 -0
  52. qulab/storage/models/models.py +88 -0
  53. qulab/storage/models/record.py +161 -0
  54. qulab/storage/models/report.py +22 -0
  55. qulab/storage/models/tag.py +93 -0
  56. qulab/storage/storage.py +95 -0
  57. qulab/sys/__init__.py +2 -0
  58. qulab/sys/chat.py +688 -0
  59. qulab/sys/device/__init__.py +3 -0
  60. qulab/sys/device/basedevice.py +255 -0
  61. qulab/sys/device/loader.py +86 -0
  62. qulab/sys/device/utils.py +79 -0
  63. qulab/sys/drivers/FakeInstrument.py +68 -0
  64. qulab/sys/drivers/__init__.py +0 -0
  65. qulab/sys/ipy_events.py +125 -0
  66. qulab/sys/net/__init__.py +0 -0
  67. qulab/sys/net/bencoder.py +205 -0
  68. qulab/sys/net/cli.py +169 -0
  69. qulab/sys/net/dhcp.py +543 -0
  70. qulab/sys/net/dhcpd.py +176 -0
  71. qulab/sys/net/kad.py +1142 -0
  72. qulab/sys/net/kcp.py +192 -0
  73. qulab/sys/net/nginx.py +194 -0
  74. qulab/sys/progress.py +190 -0
  75. qulab/sys/rpc/__init__.py +0 -0
  76. qulab/sys/rpc/client.py +0 -0
  77. qulab/sys/rpc/exceptions.py +96 -0
  78. qulab/sys/rpc/msgpack.py +1052 -0
  79. qulab/sys/rpc/msgpack.pyi +41 -0
  80. qulab/sys/rpc/router.py +35 -0
  81. qulab/sys/rpc/rpc.py +412 -0
  82. qulab/sys/rpc/serialize.py +139 -0
  83. qulab/sys/rpc/server.py +29 -0
  84. qulab/sys/rpc/socket.py +29 -0
  85. qulab/sys/rpc/utils.py +25 -0
  86. qulab/sys/rpc/worker.py +0 -0
  87. qulab/sys/rpc/zmq_socket.py +227 -0
  88. qulab/tools/__init__.py +0 -0
  89. qulab/tools/connection_helper.py +39 -0
  90. qulab/typing.py +2 -0
  91. qulab/utils.py +95 -0
  92. qulab/version.py +1 -0
  93. qulab/visualization/__init__.py +188 -0
  94. qulab/visualization/__main__.py +71 -0
  95. qulab/visualization/_autoplot.py +464 -0
  96. qulab/visualization/plot_circ.py +319 -0
  97. qulab/visualization/plot_layout.py +408 -0
  98. qulab/visualization/plot_seq.py +242 -0
  99. qulab/visualization/qdat.py +152 -0
  100. qulab/visualization/rot3d.py +23 -0
  101. qulab/visualization/widgets.py +86 -0
  102. qulab-2.10.10.dist-info/METADATA +110 -0
  103. qulab-2.10.10.dist-info/RECORD +107 -0
  104. qulab-2.10.10.dist-info/WHEEL +5 -0
  105. qulab-2.10.10.dist-info/entry_points.txt +2 -0
  106. qulab-2.10.10.dist-info/licenses/LICENSE +21 -0
  107. qulab-2.10.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,255 @@
1
+ import copy
2
+ import itertools
3
+ import logging
4
+ import re
5
+ import string
6
+ from collections import defaultdict
7
+ from functools import partial
8
+ from typing import Any, Callable, Literal, NamedTuple
9
+
10
+ log = logging.getLogger(__name__)
11
+
12
+ Decorator = Callable[[Callable], Callable]
13
+
14
+ _buildin_set = set
15
+
16
+
17
+ class Source():
18
+ pass
19
+
20
+
21
+ class Sink():
22
+ pass
23
+
24
+
25
+ def action(key: str,
26
+ method: Literal['get', 'set', 'post', 'delete'] = 'get',
27
+ **kwds) -> Decorator:
28
+
29
+ if any(c in key for c in ",()[]<>"):
30
+ raise ValueError('Invalid key: ' + key)
31
+
32
+ def decorator(func):
33
+ func.__action__ = key, method, kwds
34
+ return func
35
+
36
+ return decorator
37
+
38
+
39
+ def get(key: str, **kwds) -> Decorator:
40
+ return action(key, 'get', **kwds)
41
+
42
+
43
+ def set(key: str, **kwds) -> Decorator:
44
+ return action(key, 'set', **kwds)
45
+
46
+
47
+ def post(key: str, **kwds) -> Decorator:
48
+ return action(key, 'post', **kwds)
49
+
50
+
51
+ def delete(key: str, **kwds) -> Decorator:
52
+ return action(key, 'delete', **kwds)
53
+
54
+
55
+ class _Exclusion(NamedTuple):
56
+ keys: list
57
+
58
+
59
+ def exclude(sections: list):
60
+ return _Exclusion(sections)
61
+
62
+
63
+ def _add_action(attrs: dict, key: str, method: str, func: Callable, doc: dict,
64
+ sections: dict) -> None:
65
+ try:
66
+ mapping = attrs[f'__{method}_actions__']
67
+ except KeyError:
68
+ raise ValueError('Invalid method: ' + method)
69
+ arguments = re.findall(r'\{(\w+)\}', key)
70
+ doc[method][key] = func.__doc__
71
+ matrix = {}
72
+ for arg in arguments:
73
+ if (arg not in attrs and arg not in sections or arg in sections
74
+ and arg not in attrs and isinstance(sections[arg], _Exclusion)
75
+ or arg in sections and arg in attrs
76
+ and isinstance(sections[arg], _Exclusion) and len(
77
+ _buildin_set(attrs[arg]) -
78
+ _buildin_set(sections[arg].keys)) == 0):
79
+ raise ValueError(
80
+ f'Undefined section: {arg!r} in @action({key!r}, {method!r})')
81
+ if arg in sections and not isinstance(sections[arg], _Exclusion):
82
+ matrix[arg] = sections[arg]
83
+ else:
84
+ if arg in sections:
85
+ matrix[arg] = [
86
+ k for k in attrs[arg] if k not in sections[arg].keys
87
+ ]
88
+ else:
89
+ matrix[arg] = attrs[arg]
90
+ for values in itertools.product(*[matrix[arg] for arg in arguments]):
91
+ kwds = dict(zip(arguments, values))
92
+ # mapping[key.format(**kwds)] = partial(func, **kwds)
93
+ mapping[string.Template(key).substitute(kwds)] = partial(func, **kwds)
94
+
95
+
96
+ def _build_docs(mapping: dict, attrs: dict) -> str:
97
+ docs = []
98
+ for key, doc in mapping.items():
99
+ if not doc:
100
+ doc = "No documentation."
101
+ docs.append(f"key = \"{key}\"")
102
+ lines = doc.strip().split('\n')
103
+ docs.extend(lines)
104
+ docs.append("")
105
+ return '\n'.join(docs)
106
+
107
+
108
+ class DeviceMeta(type):
109
+
110
+ def __new__(cls, name, bases, attrs):
111
+ doc = defaultdict(dict)
112
+ for method in ['get', 'set', 'post', 'delete']:
113
+ attrs.setdefault(f'__{method}_actions__', {})
114
+ for attr in attrs.values():
115
+ if hasattr(attr, '__action__'):
116
+ key, method, kwds = attr.__action__
117
+ _add_action(attrs, key, method, attr, doc, kwds)
118
+ new_class = super().__new__(cls, name, bases, attrs)
119
+
120
+ for method in ['get', 'set', 'post', 'delete']:
121
+ getattr(new_class,
122
+ method).__doc__ = f"{method.upper()}\n\n" + _build_docs(
123
+ doc[method], attrs)
124
+ return new_class
125
+
126
+
127
+ class BaseDevice(metaclass=DeviceMeta):
128
+ __log__ = None
129
+
130
+ def __init__(self, address: str = None, **options):
131
+ self._state = {}
132
+ self._status = "NOT CONNECTED"
133
+ self.address = address
134
+ self.options = options
135
+
136
+ def __del__(self):
137
+ try:
138
+ self.close()
139
+ except Exception:
140
+ pass
141
+
142
+ @property
143
+ def log(self):
144
+ if self.__log__ is None:
145
+ self.__log__ = logging.getLogger(
146
+ f"{self.__class__.__module__}.{self.__class__.__name__}")
147
+ return self.__log__
148
+
149
+ @property
150
+ def state(self):
151
+ return copy.deepcopy(self._state)
152
+
153
+ def get_state(self, key: str, default=None) -> Any:
154
+ return self._state.get(key, default)
155
+
156
+ @property
157
+ def status(self):
158
+ return self._status
159
+
160
+ def open(self) -> None:
161
+ self._status = "CONNECTED"
162
+
163
+ def close(self) -> None:
164
+ self._status = "NOT CONNECTED"
165
+ self._state.clear()
166
+
167
+ def reset(self) -> None:
168
+ self._state.clear()
169
+
170
+ def get(self, key: str, default: Any = None) -> Any:
171
+ self.log.info(f'Get {key!r}')
172
+ if key in self.__get_actions__:
173
+ result = self.__get_actions__[key](self)
174
+ self._state[key] = result
175
+ return result
176
+ else:
177
+ return self._state.get(key, default)
178
+
179
+ def set(self, key: str, value: Any = None) -> None:
180
+ self.log.info(f'Set {key!r} = {value!r}')
181
+ self.__set_actions__[key](self, value)
182
+ self._state[key] = value
183
+
184
+ def post(self, key: str, value: Any = None) -> Any:
185
+ self.log.info(f'Post {key!r} = {value!r}')
186
+ return self.__post_actions__[key](self, value)
187
+
188
+ def delete(self, key: str) -> None:
189
+ self.log.info(f'Delete {key!r}')
190
+ self.__delete_actions__[key](self)
191
+ del self._state[key]
192
+
193
+ def __repr__(self) -> str:
194
+ return f'{self.__class__.__name__}({self.address!r})'
195
+
196
+
197
+ class VisaDevice(BaseDevice):
198
+
199
+ error_query = 'SYST:ERR?'
200
+
201
+ def open(self) -> None:
202
+ import pyvisa
203
+ kwds = self.options.copy()
204
+ if 'backend' in kwds:
205
+ rm = pyvisa.ResourceManager(kwds.pop('backend'))
206
+ else:
207
+ rm = pyvisa.ResourceManager()
208
+ self.resource = rm.open_resource(self.address, **kwds)
209
+ self.errors = []
210
+ super().open()
211
+
212
+ def close(self) -> None:
213
+ super().close()
214
+ self.resource.close()
215
+
216
+ def reset(self) -> None:
217
+ super().reset()
218
+ self.resource.write('*RST')
219
+
220
+ def check_error(self):
221
+ if self.error_query:
222
+ while True:
223
+ error = self.resource.query(self.error_query)
224
+ error_code = int(error.split(',')[0])
225
+ if error_code == 0:
226
+ break
227
+ self.errors.append(error)
228
+
229
+ @get('idn')
230
+ def get_idn(self) -> str:
231
+ """Get instrument identification."""
232
+ return self.resource.query('*IDN?')
233
+
234
+ @get('opc')
235
+ def get_opc(self) -> bool:
236
+ """Get operation complete."""
237
+ return bool(int(self.resource.query('*OPC?')))
238
+
239
+ @get('errors')
240
+ def get_errors(self) -> list[str]:
241
+ """Get error queue."""
242
+ self.check_error()
243
+ errors = self.errors
244
+ self.errors = []
245
+ return errors
246
+
247
+ @set('timeout')
248
+ def set_timeout(self, value: float) -> None:
249
+ """Set timeout in seconds."""
250
+ self.resource.timeout = round(value * 1000)
251
+
252
+ @get('timeout')
253
+ def get_timeout(self) -> float:
254
+ """Get timeout in seconds."""
255
+ return self.resource.timeout / 1000
@@ -0,0 +1,86 @@
1
+ import importlib
2
+ from pathlib import Path
3
+ from typing import Type
4
+
5
+ from .basedevice import BaseDevice
6
+
7
+ path = {}
8
+
9
+
10
+ def _make_device(module, args, kwds) -> BaseDevice:
11
+ importlib.reload(module)
12
+ try:
13
+ return module.device(*args, **kwds)
14
+ except AttributeError:
15
+ return module.Device(*args, **kwds)
16
+
17
+
18
+ def create_device_from_file(filepath: str | Path, package_name: str, args,
19
+ kwds) -> BaseDevice:
20
+ """
21
+ Create a device from a file.
22
+
23
+ Parameters
24
+ ----------
25
+ filepath : str | Path
26
+ The path to the driver file.
27
+ package_name : str
28
+ The name of the package in which .filepath is located.
29
+ args : tuple
30
+ The positional arguments to pass to the device maker.
31
+ kwds : dict
32
+ The keyword arguments to pass to the device maker.
33
+ """
34
+ filepath = Path(filepath)
35
+ if not filepath.exists():
36
+ raise RuntimeError(f"File {filepath} does not exist")
37
+ module_name = f"{package_name}.{filepath.stem}"
38
+ spec = importlib.util.spec_from_file_location(module_name, filepath)
39
+ module = importlib.util.module_from_spec(spec)
40
+ return _make_device(module, args, kwds)
41
+
42
+
43
+ def create_device_from_module(module_name: str, args, kwds) -> BaseDevice:
44
+ """
45
+ Create a device from a module.
46
+
47
+ Parameters
48
+ ----------
49
+ module_name : str
50
+ The name of the module to import.
51
+ args : tuple
52
+ The positional arguments to pass to the device maker.
53
+ kwds : dict
54
+ The keyword arguments to pass to the device maker.
55
+ """
56
+ module = importlib.import_module(module_name)
57
+ return _make_device(module, args, kwds)
58
+
59
+
60
+ def create_device(driver_name: str, *args, **kwds) -> BaseDevice:
61
+ """
62
+ Create a device from a driver.
63
+
64
+ Parameters
65
+ ----------
66
+ driver_name : str
67
+ The name of the driver to use.
68
+ args : tuple
69
+ The positional arguments to pass to the device maker.
70
+ kwds : dict
71
+ The keyword arguments to pass to the device maker.
72
+ """
73
+ try:
74
+ for package_name, p in path.items():
75
+ try:
76
+ dev = create_device_from_file(
77
+ Path(p) / f"{driver_name}.py", package_name, args, kwds)
78
+ return dev
79
+ except:
80
+ pass
81
+
82
+ dev = create_device_from_module(f"qulab.sys.drivers.{driver_name}",
83
+ args, kwds)
84
+ return dev
85
+ except:
86
+ raise RuntimeError(f"Can not find driver {driver_name!r}")
@@ -0,0 +1,79 @@
1
+ import struct
2
+
3
+ import numpy as np
4
+
5
+ types = {
6
+ "b" : ( int, 'b'), "B" : ( int, 'B'),
7
+ "h" : ( int, 'h'), "H" : ( int, 'H'),
8
+ "i" : ( int, 'i'), "I" : ( int, 'I'),
9
+ "q" : ( int, 'q'), "Q" : ( int, 'Q'),
10
+ "f" : (float, 'f'), "d" : (float, 'd'),
11
+ "int8" : ( int, 'b'), "uint8" : ( int, 'B'),
12
+ "int16" : ( int, 'h'), "uint16" : ( int, 'H'),
13
+ "int32" : ( int, 'i'), "uint32" : ( int, 'I'),
14
+ "int64" : ( int, 'q'), "uint64" : ( int, 'Q'),
15
+ "float" : (float, 'f'), "double" : (float, 'd'),
16
+ "float32": (float, 'f'), "float64": (float, 'd')
17
+ } # yapf: disable
18
+
19
+
20
+ def IEEE_488_2_BinBlock(datalist, dtype="int16", is_big_endian=True):
21
+ """
22
+ 将一组数据打包成 IEEE 488.2 标准二进制块
23
+
24
+ Args:
25
+ datalist : 要打包的数字列表
26
+ dtype : 数据类型
27
+ endian : 字节序
28
+
29
+ Returns:
30
+ binblock, header
31
+ 二进制块, 以及其 'header'
32
+ """
33
+ if isinstance(datalist, bytes):
34
+ datablock = datalist
35
+ else:
36
+ datalist = np.asarray(datalist)
37
+ datalist.astype(types[dtype][0])
38
+ if is_big_endian:
39
+ endianc = '>'
40
+ else:
41
+ endianc = '<'
42
+ datablock = struct.pack(
43
+ '%s%d%s' % (endianc, len(datalist), types[dtype][1]), *datalist)
44
+ size = '%d' % len(datablock)
45
+ header = '#%d%s' % (len(size), size)
46
+
47
+ return header.encode() + datablock
48
+
49
+
50
+ def decode_IEEE_488_2_BinBlock(data, dtype="int16", is_big_endian=True):
51
+ """
52
+ 解析 IEEE 488.2 标准二进制块
53
+
54
+ Args:
55
+ data : 二进制块
56
+ dtype : 数据类型
57
+ endian : 字节序
58
+
59
+ Returns:
60
+ datalist
61
+ """
62
+ if isinstance(data, bytes):
63
+ header = data[:2]
64
+ size = int(data[2:2 + int(header[1:2])])
65
+ assert size == len(data) - 2 - int(header[1:2]), "data size error"
66
+ datablock = data[2 + int(header[1:2]):size + 2 + int(header[1:2])]
67
+ else:
68
+ raise ValueError("data must be bytes")
69
+
70
+ if is_big_endian:
71
+ endianc = '>'
72
+ else:
73
+ endianc = '<'
74
+ datalist = list(
75
+ struct.unpack(
76
+ '%s%d%s' % (endianc, size // struct.calcsize(types[dtype][1]),
77
+ types[dtype][1]), datablock))
78
+
79
+ return datalist
@@ -0,0 +1,68 @@
1
+ import numpy as np
2
+
3
+ from qulab.sys import BaseDevice, exclude, get, set
4
+
5
+
6
+ class Device(BaseDevice):
7
+
8
+ channel = [
9
+ 'X1', 'X2', 'X3', 'X4', 'Y1', 'Y2', 'Y3', 'Y4', 'Z1', 'Z2', 'Z3', 'Z4',
10
+ 'M1', 'M2'
11
+ ]
12
+
13
+ @get('IDN')
14
+ def get_idn(self) -> str:
15
+ return 'Fake Instrument'
16
+
17
+ @get('${channel_type}${channel_num}.SampleRate',
18
+ channel_type=['X', 'Y'],
19
+ channel_num=[1, 2, 3, 4])
20
+ def get_sample_rate_DDS(self, channel_type: str,
21
+ channel_num: int) -> float:
22
+ return 6e9
23
+
24
+ @get('${channel}.SampleRate', channel=['Z1', 'Z2', 'Z3', 'Z4'])
25
+ def get_sample_rate_Z(self, channel: str) -> float:
26
+ return 2e9
27
+
28
+ @get('${channel}.SampleRate', channel=['M1', 'M2'])
29
+ def get_sample_rate_AD(self, channel: str) -> float:
30
+ return 1e9
31
+
32
+ @set('${channel}.Vpp', channel=exclude(['M1', 'M2']))
33
+ def set_voltage(self, value: float, channel: str) -> None:
34
+ self.log.info(f'Set {channel} Vpp to {value}')
35
+
36
+ # @get('${channel}.Vpp', channel=exclude(['M1', 'M2']))
37
+ # def get_voltage(self, channel: str, default=0.0) -> float:
38
+ # return self._state.get(f'{channel}.Vpp', default)
39
+
40
+ @set('${channel}.Offset', channel=exclude(['M1', 'M2']))
41
+ def set_offset(
42
+ self,
43
+ value: float,
44
+ channel: str,
45
+ ) -> None:
46
+ self.log.info(f'Set {channel} offset to {value}')
47
+
48
+ # @get('${channel}.Offset', channel=exclude(['M1', 'M2']))
49
+ # def get_offset(self, channel: str, default=0.0) -> float:
50
+ # return self._state.get(f'{channel}.Offset', default)
51
+
52
+ @set('${channel}.Waveform', channel=exclude(['M1', 'M2']))
53
+ def set_waveform(self, value, channel: str) -> None:
54
+ self.log.info(f'Set {channel} waveform to {value!r}')
55
+
56
+ # @get('${channel}.Waveform', channel=exclude(['M1', 'M2']))
57
+ # def get_waveform(self, channel: str, default=None) -> str:
58
+ # return self._state.get(f'{channel}.Waveform', default)
59
+
60
+ @set('${channel}.Size', channel=['M1', 'M2'])
61
+ def set_size(self, value, channel: str) -> None:
62
+ self.log.info(f'Set {channel} size to {value!r}')
63
+
64
+ @get('${channel}.Trace', channel=['M1', 'M2'])
65
+ def get_random_data(self, channel: str) -> np.ndarray:
66
+ size = self._state.get(f'{channel}.Size', 1024)
67
+ shots = self._state.get(f'{channel}.Shots', 128)
68
+ return np.random.randn(shots, size)
File without changes
@@ -0,0 +1,125 @@
1
+ import contextlib
2
+ import time
3
+ from datetime import datetime
4
+
5
+ from .storage.crud import create_cell, create_notebook
6
+ from .storage.models import Notebook
7
+
8
+ __current_notebook_id = None
9
+
10
+
11
+ def _get_current_notebook(session):
12
+ global __current_notebook_id
13
+ if __current_notebook_id is None:
14
+ notebook = create_notebook(session, 'Untitled')
15
+ session.commit()
16
+ __current_notebook_id = notebook.id
17
+ else:
18
+ notebook = session.query(Notebook).filter(
19
+ Notebook.id == __current_notebook_id).one()
20
+ return notebook
21
+
22
+
23
+ def _get_current_cell(session):
24
+ notebook = _get_current_notebook(session)
25
+ if notebook.cells:
26
+ return notebook.cells[-1]
27
+ else:
28
+ return None
29
+
30
+
31
+ def _save_inputCells(session, inputCells=None):
32
+ notebook = _get_current_notebook(session)
33
+ aready_saved = len(notebook.cells)
34
+ if inputCells:
35
+ for cell in inputCells[aready_saved:]:
36
+ create_cell(session, notebook, cell)
37
+
38
+
39
+ def _update_inputCells(session):
40
+ cell = _get_current_cell(session)
41
+ if cell:
42
+ cell.ftime = datetime.utcnow()
43
+ session.add(cell)
44
+
45
+
46
+ __sessionmaker = None
47
+
48
+
49
+ @contextlib.contextmanager
50
+ def get_session():
51
+ global __session
52
+ if __sessionmaker is None:
53
+ raise RuntimeError('Sessionmaker is not set')
54
+ session = __sessionmaker()
55
+ try:
56
+ yield session
57
+ session.commit()
58
+ except:
59
+ session.rollback()
60
+
61
+
62
+ def set_sessionmaker(sessionmaker):
63
+ global __sessionmaker
64
+ __sessionmaker = sessionmaker
65
+
66
+
67
+ def get_current_notebook(session=None):
68
+ try:
69
+ if session is None:
70
+ with get_session() as session:
71
+ return _get_current_notebook(session)
72
+ else:
73
+ return _get_current_notebook(session)
74
+ except RuntimeError:
75
+ return None
76
+
77
+
78
+ def get_current_cell_id(session=None):
79
+ try:
80
+ if session is None:
81
+ with get_session() as session:
82
+ return _get_current_cell(session).id
83
+ else:
84
+ return _get_current_cell(session).id
85
+ except RuntimeError as e:
86
+ return None
87
+
88
+
89
+ def get_inputCells():
90
+ try:
91
+ from IPython import get_ipython
92
+
93
+ return get_ipython().user_ns['In']
94
+ except Exception as e:
95
+ # raise e
96
+ return ['']
97
+
98
+
99
+ def autosave_cells(ipython):
100
+ try:
101
+ with get_session() as session:
102
+ _save_inputCells(session, get_inputCells())
103
+ except:
104
+ pass
105
+
106
+
107
+ def update_timestamp(ipython):
108
+ try:
109
+ with get_session() as session:
110
+ _update_inputCells(session)
111
+ except:
112
+ pass
113
+
114
+
115
+ def setup_ipy_events():
116
+ try:
117
+ from IPython import get_ipython
118
+
119
+ ipython = get_ipython()
120
+
121
+ if ipython is not None:
122
+ ipython.events.register('pre_run_cell', autosave_cells)
123
+ ipython.events.register('post_run_cell', update_timestamp)
124
+ except:
125
+ pass
File without changes