DrissionRecord 1.0.0__tar.gz

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.
@@ -0,0 +1,7 @@
1
+ from .byte_recorder import ByteRecorder
2
+ from .db_recorder import DBRecorder
3
+ from .recorder import Recorder
4
+ from .cell_style import CellStyle
5
+ from .tools import Col
6
+
7
+ __version__ = '1.0.0'
@@ -0,0 +1,193 @@
1
+ # -*- coding:utf-8 -*-
2
+ from abc import abstractmethod
3
+ from pathlib import Path
4
+ from threading import Lock
5
+ from time import sleep
6
+
7
+ from .setter import OriginalSetter, BaseSetter
8
+ from .tools import get_usable_path, make_valid_name, get_tables, make_final_data_simplify
9
+
10
+
11
+ class OriginalRecorder(object):
12
+ def __init__(self, path=None, cache_size=1000):
13
+ self._data = []
14
+ self._path = None
15
+ self._type = None
16
+ self._lock = Lock()
17
+ self._pause_add = False # 文件写入时暂停接收输入
18
+ self._pause_write = False # 标记文件正在被一个线程写入
19
+ self._setter = None
20
+ self._data_count = 0 # 已缓存数据的条数
21
+ self._file_exists = False
22
+ self._backup_path = 'backup'
23
+ self._backup_times = 0
24
+ self._backup_interval = 0 # 多少次就自动保存
25
+ self._backup_overwrite = False
26
+ self.show_msg = True
27
+ if path:
28
+ self.set.path(path)
29
+ self._cache = cache_size or 0
30
+
31
+ def __del__(self):
32
+ self.record()
33
+
34
+ @property
35
+ def set(self):
36
+ if self._setter is None:
37
+ self._setter = OriginalSetter(self)
38
+ return self._setter
39
+
40
+ @property
41
+ def cache_size(self):
42
+ return self._cache
43
+
44
+ @property
45
+ def path(self):
46
+ return self._path
47
+
48
+ @property
49
+ def type(self):
50
+ return self._type
51
+
52
+ @property
53
+ def data(self):
54
+ return self._data
55
+
56
+ def record(self):
57
+ if not self._data_count:
58
+ return self._path
59
+ if not self._path:
60
+ raise ValueError('保存路径为空。')
61
+
62
+ with self._lock:
63
+ if self._backup_interval and self._backup_times >= self._backup_interval:
64
+ self.backup(folder=self._backup_path, overwrite=self._backup_overwrite)
65
+
66
+ self._pause_add = True # 写入文件前暂缓接收数据
67
+ if self.show_msg:
68
+ print(f'{self.path} 开始写入文件,切勿关闭进程。')
69
+
70
+ Path(self.path).parent.mkdir(parents=True, exist_ok=True)
71
+ while True:
72
+ try:
73
+ while self._pause_write: # 等待其它线程写入结束
74
+ sleep(.02)
75
+
76
+ self._pause_write = True
77
+ self._record()
78
+ break
79
+
80
+ except PermissionError:
81
+ if self.show_msg:
82
+ print('\r文件被打开,保存失败,请关闭,程序会自动重试。', end='')
83
+
84
+ except Exception as e:
85
+ try:
86
+ with open('failed_data.txt', 'a+', encoding='utf-8') as f:
87
+ f.write(str(self.data) + '\n')
88
+ print('保存失败的数据已保存到failed_data.txt。')
89
+ except:
90
+ raise e
91
+ raise
92
+
93
+ finally:
94
+ self._pause_write = False
95
+
96
+ sleep(.3)
97
+
98
+ if self.show_msg:
99
+ print(f'{self.path} 写入文件结束。')
100
+ self.clear()
101
+ self._pause_add = False
102
+
103
+ if self._backup_interval:
104
+ self._backup_times += 1
105
+ self._file_exists = True
106
+ return self._path
107
+
108
+ def clear(self):
109
+ self._data.clear()
110
+ self._data_count = 0
111
+
112
+ def backup(self, folder=None, name=None, overwrite=None):
113
+ src_path = Path(self._path)
114
+ if not self._file_exists:
115
+ if not src_path.exists():
116
+ return ''
117
+ self._file_exists = True
118
+
119
+ if overwrite is None:
120
+ overwrite = self._backup_overwrite
121
+ folder = Path(folder if folder else self._backup_path)
122
+ folder.mkdir(parents=True, exist_ok=True)
123
+ if not name:
124
+ name = src_path.name
125
+ elif not name.endswith(src_path.suffix):
126
+ name = f'{name}{src_path.suffix}'
127
+ path = folder / make_valid_name(name)
128
+ if not overwrite and path.exists():
129
+ from datetime import datetime
130
+ name = f'{path.stem}_{datetime.now().strftime("%Y%m%d%H%M%S")}{path.suffix}'
131
+ path = get_usable_path(folder / name)
132
+
133
+ from shutil import copy
134
+ copy(self._path, path)
135
+ self._backup_times = 0
136
+ return str(path.absolute())
137
+
138
+ @abstractmethod
139
+ def add_data(self, data):
140
+ pass
141
+
142
+ @abstractmethod
143
+ def _record(self):
144
+ pass
145
+
146
+
147
+ class BaseRecorder(OriginalRecorder):
148
+ def __init__(self, path=None, cache_size=None):
149
+ super().__init__(path, cache_size)
150
+ self._before = []
151
+ self._after = []
152
+ self._encoding = 'utf-8'
153
+ self._table = None
154
+ self._make_final_data = make_final_data_simplify
155
+ self._auto_new_header = False
156
+
157
+ @property
158
+ def set(self):
159
+ if self._setter is None:
160
+ self._setter = BaseSetter(self)
161
+ return self._setter
162
+
163
+ @property
164
+ def before(self):
165
+ return self._before
166
+
167
+ @property
168
+ def after(self):
169
+ return self._after
170
+
171
+ @property
172
+ def table(self):
173
+ return self._table
174
+
175
+ @property
176
+ def tables(self):
177
+ if self._type != 'xlsx':
178
+ raise TypeError('只有xlsx格式能使用tables属性。')
179
+ if not self._path:
180
+ raise RuntimeError('未指定文件路径。')
181
+ return get_tables(self._path)
182
+
183
+ @property
184
+ def encoding(self):
185
+ return self._encoding
186
+
187
+ @abstractmethod
188
+ def add_data(self, data, table=None):
189
+ pass
190
+
191
+ @abstractmethod
192
+ def _record(self):
193
+ pass
@@ -0,0 +1,143 @@
1
+ # -*- coding:utf-8 -*-
2
+ from abc import abstractmethod
3
+ from pathlib import Path
4
+ from threading import Lock
5
+ from typing import Union, Optional, Callable
6
+
7
+ from .setter import OriginalSetter, BaseSetter
8
+
9
+
10
+ class OriginalRecorder(object):
11
+ """记录器的基类"""
12
+ _cache: int = ...
13
+ _path: Optional[str] = ...
14
+ _type: Optional[str] = ...
15
+ _data: list = ...
16
+ _lock: Lock = ...
17
+ _pause_add: bool = ...
18
+ _pause_write: bool = ...
19
+ _setter: Optional[OriginalSetter] = ...
20
+ _data_count: int = ...
21
+ _file_exists: bool = ...
22
+ _backup_path: str = ...
23
+ _backup_times: int = ...
24
+ _backup_interval: int = ...
25
+ _backup_overwrite: bool = ...
26
+ show_msg: bool = ...
27
+
28
+ def __init__(self,
29
+ path: Union[str, Path, None] = None,
30
+ cache_size: int = None) -> None:
31
+ """
32
+ :param path: 保存的文件路径
33
+ :param cache_size: 每接收多少条记录写入文件,0为不自动写入
34
+ """
35
+ ...
36
+
37
+ def __del__(self) -> None:
38
+ """对象关闭时把剩下的数据写入文件"""
39
+ ...
40
+
41
+ @property
42
+ def set(self) -> OriginalSetter:
43
+ """返回用于设置属性的对象"""
44
+ ...
45
+
46
+ @property
47
+ def cache_size(self) -> int:
48
+ """返回缓存大小"""
49
+ ...
50
+
51
+ @property
52
+ def path(self) -> str:
53
+ """返回文件路径"""
54
+ ...
55
+
56
+ @property
57
+ def type(self) -> str:
58
+ """返回文件类型"""
59
+ ...
60
+
61
+ @property
62
+ def data(self) -> Union[dict, list]:
63
+ """返回当前保存在缓存的数据"""
64
+ ...
65
+
66
+ def record(self) -> str:
67
+ """记录数据,返回文件路径"""
68
+ ...
69
+
70
+ def clear(self) -> None:
71
+ """清空缓存中的数据"""
72
+ ...
73
+
74
+ def backup(self,
75
+ folder: Union[str, Path, None] = None,
76
+ name: str = None,
77
+ overwrite: bool = None) -> str:
78
+ """把当前文件备份到指定路径
79
+ :param folder: 文件夹路径,为None使用内置路径(初始 'backup')
80
+ :param name: 保存的文件名,可不含后缀,为None使用内置路径文件名
81
+ :param overwrite: 是否覆盖同名文件,为False时每次备份文件名添加当前时间,为None使用内置设置
82
+ """
83
+ ...
84
+
85
+ @abstractmethod
86
+ def add_data(self, data):
87
+ ...
88
+
89
+ @abstractmethod
90
+ def _record(self):
91
+ ...
92
+
93
+
94
+ class BaseRecorder(OriginalRecorder):
95
+ """Recorder和DBRecorder的父类"""
96
+ _encoding: str = ...
97
+ _before: Optional[list] = ...
98
+ _after: Optional[list] = ...
99
+ _table: Optional[str] = ...
100
+ _setter: BaseSetter = ...
101
+ _make_final_data: Callable = ...
102
+ _auto_new_header: bool = ...
103
+
104
+ def __init__(self, path: Union[None, str, Path] = None, cache_size: int = None) -> None:
105
+ """
106
+ :param path: 保存的文件路径
107
+ :param cache_size: 每接收多少条记录写入文件,0为不自动写入
108
+ """
109
+ ...
110
+
111
+ @property
112
+ def set(self) -> BaseSetter:
113
+ """返回用于设置属性的对象"""
114
+ ...
115
+
116
+ @property
117
+ def before(self) -> Union[dict, list]:
118
+ """返回当前before内容"""
119
+ ...
120
+
121
+ @property
122
+ def after(self) -> Union[dict, list]:
123
+ """返回当前after内容"""
124
+ ...
125
+
126
+ @property
127
+ def table(self) -> Optional[str]:
128
+ """返回当前使用的表名"""
129
+ ...
130
+
131
+ @property
132
+ def tables(self) -> list:
133
+ """返回所有表名"""
134
+ ...
135
+
136
+ @property
137
+ def encoding(self) -> str:
138
+ """返回csv文件使用的编码格式"""
139
+ ...
140
+
141
+ @abstractmethod
142
+ def _record(self):
143
+ ...
@@ -0,0 +1,42 @@
1
+ # -*- coding:utf-8 -*-
2
+ from pathlib import Path
3
+ from time import sleep
4
+
5
+ from .base import OriginalRecorder
6
+
7
+
8
+ class ByteRecorder(OriginalRecorder):
9
+ __END = (0, 2)
10
+
11
+ def __init__(self, path=None, cache_size=1000):
12
+ super().__init__(path, cache_size)
13
+ self._type = 'byte'
14
+
15
+ def add_data(self, data, seek=None):
16
+ while self._pause_add: # 等待其它线程写入结束
17
+ sleep(.02)
18
+
19
+ if not isinstance(data, bytes):
20
+ raise TypeError('只能接受bytes类型数据。')
21
+ if seek is not None and not (isinstance(seek, int) and seek >= 0):
22
+ raise ValueError('seek参数只能接受None或大于等于0的整数。')
23
+
24
+ self._data.append((data, seek))
25
+ self._data_count += 1
26
+
27
+ if 0 < self.cache_size <= self._data_count:
28
+ self.record()
29
+
30
+ def _record(self):
31
+ if not self._file_exists and not Path(self.path).exists():
32
+ with open(self.path, 'wb'):
33
+ pass
34
+
35
+ with open(self.path, 'rb+') as f:
36
+ previous = None
37
+ for i in self._data:
38
+ loc = ByteRecorder.__END if i[1] is None else (i[1], 0)
39
+ if not (previous == loc == ByteRecorder.__END):
40
+ f.seek(loc[0], loc[1])
41
+ previous = loc
42
+ f.write(i[0])
@@ -0,0 +1,33 @@
1
+ # -*- coding:utf-8 -*-
2
+ from pathlib import Path
3
+ from typing import Union, Optional
4
+
5
+ from .base import OriginalRecorder
6
+
7
+
8
+ class ByteRecorder(OriginalRecorder):
9
+ __END: tuple = ...
10
+ data: list = ...
11
+
12
+ def __init__(self,
13
+ path: Union[None, str, Path] = None,
14
+ cache_size: int = 1000):
15
+ """用于记录字节数据的工具
16
+ :param path: 保存的文件路径
17
+ :param cache_size: 每接收多少条记录写入文件,0为不自动写入
18
+ """
19
+ ...
20
+
21
+ def add_data(self,
22
+ data: bytes,
23
+ seek: int = None) -> None:
24
+ """添加一段二进制数据
25
+ :param data: bytes类型数据
26
+ :param seek: 在文件中的位置,None表示最后
27
+ :return: None
28
+ """
29
+ ...
30
+
31
+ def _record(self) -> None:
32
+ """记录数据到文件"""
33
+ ...