QuLab 2.10.10__cp313-cp313-win_amd64.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.cp313-win_amd64.pyd +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,161 @@
1
+ from datetime import date, datetime
2
+ from typing import Sequence
3
+
4
+ import dill
5
+ from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
6
+ from sqlalchemy.orm import Session, relationship
7
+
8
+ from .base import Base
9
+ from .config import Config
10
+ from .tag import get_object_with_tags, has_tags, tag
11
+
12
+
13
+ @has_tags
14
+ class Record(Base):
15
+ __tablename__ = 'records'
16
+
17
+ id = Column(Integer, primary_key=True)
18
+ ctime = Column(DateTime, default=datetime.utcnow)
19
+ mtime = Column(DateTime, default=datetime.utcnow)
20
+ atime = Column(DateTime, default=datetime.utcnow)
21
+ name = Column(String)
22
+ datafile_id = Column(Integer, ForeignKey('files.id'))
23
+ configfile_id = Column(Integer, ForeignKey('files.id'))
24
+ meta_id = Column(Integer, ForeignKey('files.id'))
25
+
26
+ datafile = relationship("File", foreign_keys=[datafile_id])
27
+ configfile = relationship("File", foreign_keys=[configfile_id])
28
+ metafile = relationship("File", foreign_keys=[meta_id])
29
+
30
+ @property
31
+ def data(self) -> dict:
32
+ result = dill.loads(self.datafile.read())
33
+ result['meta'] = self.meta
34
+ result['meta']['config'] = self.config
35
+ return result
36
+
37
+ @data.setter
38
+ def data(self, data: dict):
39
+ meta = data.pop('meta', {})
40
+ self.meta = meta
41
+ self.datafile.write(dill.dumps(data))
42
+
43
+ @property
44
+ def config(self) -> dict:
45
+ return dill.loads(self.configfile.read())
46
+
47
+ @config.setter
48
+ def config(self, data: dict):
49
+ self.configfile.write(dill.dumps(data))
50
+
51
+ @property
52
+ def meta(self) -> dict:
53
+ meta = dill.loads(self.metafile.read())
54
+ meta['id'] = self.id
55
+ meta['name'] = self.name
56
+ meta['ctime'] = self.ctime
57
+ meta['mtime'] = self.mtime
58
+ meta['atime'] = self.atime
59
+ return meta
60
+
61
+ @meta.setter
62
+ def meta(self, data: dict):
63
+ if 'config' in data:
64
+ config = data.pop('config', {})
65
+ self.config = config
66
+ self.metafile.write(dill.dumps(data))
67
+
68
+ def export(self, path):
69
+ with open(path, 'wb') as f:
70
+ f.write(self.data)
71
+
72
+ def __init__(self, name: str, data: bytes) -> None:
73
+ self.name = name
74
+ self.data = data
75
+
76
+ def __str__(self) -> str:
77
+ return self.name
78
+
79
+ def __repr__(self) -> str:
80
+ return f'Record({self.name!r})'
81
+
82
+
83
+ def query_record(
84
+ session: Session,
85
+ offset: int = 0,
86
+ limit: int = 10,
87
+ name: str | None = None,
88
+ tags: Sequence[str] = (),
89
+ before: datetime | date | None = None,
90
+ after: datetime | date | None = None) -> tuple[int, dict, dict]:
91
+ from waveforms.dicttree import foldDict
92
+
93
+ local_tm = datetime.fromtimestamp(0)
94
+ utc_tm = datetime.utcfromtimestamp(0)
95
+ tz_offset = local_tm - utc_tm
96
+ table = {'header': ['ID', 'Name', 'tags', 'created time'], 'body': []}
97
+ name_lst = sorted(
98
+ set([
99
+ n for n, *_ in get_object_with_tags(session.query(Record.name),
100
+ Record, *tags).all()
101
+ ]))
102
+ name_tr = foldDict(dict([(name, None) for name in name_lst]))
103
+
104
+ query = get_object_with_tags(session, Record, *tags)
105
+
106
+ if name is not None:
107
+ if name.endswith('*'):
108
+ query = query.filter(Record.name.like(name[:-1] + '%'))
109
+ else:
110
+ query = query.filter(Record.name == name)
111
+ if before is not None:
112
+ if isinstance(before, date):
113
+ before = datetime(before.year, before.month, before.day)
114
+ query = query.filter(Record.ctime <= before - tz_offset)
115
+ if after is not None:
116
+ if isinstance(after, date):
117
+ after = datetime(after.year, after.month, after.day)
118
+ query = query.filter(Record.ctime >= after - tz_offset)
119
+ total = query.count()
120
+ for r in query.order_by(Record.ctime.desc()).limit(limit).offset(offset):
121
+ tags = sorted([t.text for t in r.tags])
122
+ ctime = r.ctime + tz_offset
123
+ row = [r.id, r.name, tags, ctime]
124
+ table['body'].append(row)
125
+
126
+ return total, name_tr, table
127
+
128
+
129
+ def update_tags(session: Session,
130
+ record_id: int,
131
+ tags: Sequence[str],
132
+ append: bool = False):
133
+ record = session.get(Record, record_id)
134
+ if record is None:
135
+ return False
136
+ if append:
137
+ old = [t.text for t in record.tags]
138
+ for t in old:
139
+ if t not in tags:
140
+ tags.append(t)
141
+ record.tags = [tag(session, t) for t in tags]
142
+ try:
143
+ session.commit()
144
+ except Exception:
145
+ session.rollback()
146
+ return False
147
+ return True
148
+
149
+
150
+ def remove_tags(session: Session, record_id: int, tags: Sequence[str]):
151
+ record = session.get(Record, record_id)
152
+ if record is None:
153
+ return False
154
+ old = [t.text for t in record.tags]
155
+ record.tags = [tag(session, t) for t in old if t not in tags]
156
+ try:
157
+ session.commit()
158
+ except Exception:
159
+ session.rollback()
160
+ return False
161
+ return True
@@ -0,0 +1,22 @@
1
+ from .base import Base
2
+ from .tag import Tag, has_tags
3
+ from sqlalchemy import Column, DateTime, ForeignKey, Integer, String
4
+ from sqlalchemy.orm import relationship
5
+ from datetime import datetime
6
+
7
+ @has_tags
8
+ class Report(Base):
9
+ __tablename__ = 'reports'
10
+
11
+ id = Column(Integer, primary_key=True)
12
+ ctime = Column(DateTime, default=datetime.utcnow)
13
+ mtime = Column(DateTime, default=datetime.utcnow)
14
+
15
+ name = Column(String)
16
+ datafile_id = Column(Integer, ForeignKey('files.id'))
17
+ configfile_id = Column(Integer, ForeignKey('files.id'))
18
+ meta_id = Column(Integer, ForeignKey('files.id'))
19
+
20
+ datafile = relationship("File", foreign_keys=[datafile_id])
21
+ configfile = relationship("File", foreign_keys=[configfile_id])
22
+ metafile = relationship("File", foreign_keys=[meta_id])
@@ -0,0 +1,93 @@
1
+ from typing import Type
2
+
3
+ from sqlalchemy import Column, ForeignKey, Integer, String, Table
4
+ from sqlalchemy.orm import Query, Session, aliased, relationship
5
+ from sqlalchemy.orm.exc import NoResultFound
6
+
7
+ from . import Base
8
+
9
+
10
+ class Tag(Base):
11
+ __tablename__ = 'tags'
12
+
13
+ id = Column(Integer, primary_key=True)
14
+ name = Column(String, unique=True)
15
+
16
+ def __init__(self, name: str) -> None:
17
+ self.name = name
18
+
19
+ def __str__(self) -> str:
20
+ return self.name
21
+
22
+ def __repr__(self) -> str:
23
+ return f'Tag({self.name!r})'
24
+
25
+
26
+ def has_tags(cls: Type[Base]) -> Type[Base]:
27
+ table = Table(
28
+ f'{cls.__tablename__}_tags', Base.metadata,
29
+ Column('item_id',
30
+ ForeignKey(f'{cls.__tablename__}.id'),
31
+ primary_key=True),
32
+ Column('tag_id', ForeignKey('tags.id'), primary_key=True))
33
+
34
+ cls.tags = relationship("Tag", secondary=table, backref=cls.__tablename__)
35
+
36
+ def add_tag(self, tag: Tag):
37
+ if tag not in self.tags:
38
+ self.tags.append(tag)
39
+
40
+ def remove_tag(self, tag: Tag):
41
+ if tag in self.tags:
42
+ self.tags.remove(tag)
43
+
44
+ cls.add_tag = add_tag
45
+ cls.remove_tag = remove_tag
46
+
47
+ return cls
48
+
49
+
50
+ def tag(session: Session, tag_text: str) -> Tag:
51
+ """Get a tag from the database or create a new if not exists."""
52
+ try:
53
+ return session.query(Tag).filter(Tag.text == tag_text).one()
54
+ except NoResultFound:
55
+ tag = Tag(text=tag_text)
56
+ return tag
57
+
58
+
59
+ def get_object_with_tags(session: Session, cls: Type[Base],
60
+ *tags: str) -> Query:
61
+ """
62
+ Query objects with the given tags.
63
+
64
+ Parameters
65
+ ----------
66
+ session : :class:`sqlalchemy.orm.Session`
67
+ The database session.
68
+ cls : :class:`sqlalchemy.orm.Mapper`
69
+ The object class.
70
+ tags : str
71
+ The tags.
72
+
73
+ Returns
74
+ -------
75
+ :class:`sqlalchemy.orm.Query`
76
+ The query.
77
+ """
78
+ if isinstance(session, Query):
79
+ q = session
80
+ else:
81
+ q = session.query(cls)
82
+ if not hasattr(cls, 'tags'):
83
+ return []
84
+
85
+ aliase = {tag: aliased(Tag) for tag in tags}
86
+
87
+ for tag, a in aliase.items():
88
+ q = q.join(a, cls.tags)
89
+ if '*' in tag:
90
+ q = q.filter(a.text.like(tag.replace('*', '%')))
91
+ else:
92
+ q = q.filter(a.text == tag)
93
+ return q
@@ -0,0 +1,95 @@
1
+ import shutil
2
+ from abc import ABC, abstractmethod
3
+ from datetime import date, datetime
4
+ from pathlib import Path
5
+ from typing import Any
6
+ from uuid import UUID, uuid1, uuid5
7
+
8
+ from .file import load
9
+
10
+
11
+ class BaseStorage(ABC):
12
+
13
+ @abstractmethod
14
+ def _create_dataset(self) -> UUID:
15
+ """Create a new dataset and return its id"""
16
+ pass
17
+
18
+ @abstractmethod
19
+ def _delet_dataset(self, id: UUID):
20
+ """Delete a dataset"""
21
+ pass
22
+
23
+ @abstractmethod
24
+ def _dataset_getitem(self, id: UUID, key: str):
25
+ """Get a dataset item"""
26
+ pass
27
+
28
+ @abstractmethod
29
+ def _dataset_setitem(self, id: UUID, key: str, value: Any):
30
+ """Set a dataset item"""
31
+ pass
32
+
33
+ @abstractmethod
34
+ def _dataset_delitem(self, id: UUID, key: str):
35
+ """Delete a dataset item"""
36
+ pass
37
+
38
+ @abstractmethod
39
+ def _dataset_keys(self, id: UUID):
40
+ """Get dataset keys"""
41
+ pass
42
+
43
+ @abstractmethod
44
+ def _dataset_append(self, id: UUID, dataframe: dict[str, Any]):
45
+ """Append a dataframe to a dataset"""
46
+ pass
47
+
48
+ @abstractmethod
49
+ def _dataset_item_append(self, id: UUID, key: str, value: Any):
50
+ """Append a value to a dataset item"""
51
+ pass
52
+
53
+
54
+ class Storage(BaseStorage):
55
+
56
+ def __init__(self, base: Path | str):
57
+ if isinstance(base, str):
58
+ base = Path(base)
59
+ self.base = base
60
+ self.namespace = uuid5(UUID('f89f735a-791e-5a43-9ba6-f28d58601544'),
61
+ base.as_posix())
62
+
63
+ def clear(self):
64
+ shutil.rmtree(self.base)
65
+ self.base.mkdir(parents=True)
66
+
67
+ def uuid(self,
68
+ name: str | None = None,
69
+ namespace: UUID | None = None,
70
+ seq: int = 0) -> UUID:
71
+ if name is None:
72
+ name = str(uuid1())
73
+ if namespace is None:
74
+ return uuid5(self.namespace, f"{name}{seq}")
75
+ else:
76
+ return uuid5(namespace, f"{name}{seq}")
77
+
78
+ def uuid_to_path(self, uuid: UUID) -> Path:
79
+ return self.base / uuid.hex[:2] / uuid.hex[2:4] / uuid.hex[4:]
80
+
81
+ def create_dataset(self, title: str, tags: list[str] = []):
82
+ from .dataset import Dataset
83
+ id = self.uuid()
84
+ return Dataset(id, self)
85
+
86
+ def get_dataset(self, id: UUID):
87
+ from .dataset import Dataset
88
+ return Dataset(id, self)
89
+
90
+ def remove_dataset(self, id: UUID):
91
+ shutil.rmtree(self.uuid_to_path(id))
92
+
93
+ def query(self, title_pattern: str, tags: list[str],
94
+ before: datetime | date, after: datetime | date):
95
+ pass
qulab/sys/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from .device import (BaseDevice, VisaDevice, action, create_device, delete,
2
+ exclude, get, post, set)