gomyck-tools 1.3.2__py3-none-any.whl → 1.3.4__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.
ctools/cdebug.py ADDED
@@ -0,0 +1,143 @@
1
+ import subprocess
2
+ import sys
3
+ import threading
4
+ from datetime import datetime
5
+ from queue import Queue, Empty
6
+
7
+
8
+ class ProgramInterceptor:
9
+ def __init__(self, command):
10
+ self.command = command
11
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
12
+ self.log_filename = f"program_io_log_{timestamp}.txt"
13
+ self.process = None
14
+ self.log_queue = Queue()
15
+
16
+ def start(self):
17
+ # 启动子进程
18
+ self.process = subprocess.Popen(
19
+ self.command,
20
+ stdin=subprocess.PIPE,
21
+ stdout=subprocess.PIPE,
22
+ stderr=subprocess.PIPE,
23
+ bufsize=0,
24
+ universal_newlines=True
25
+ )
26
+
27
+ # 启动日志写入线程
28
+ log_thread = threading.Thread(target=self._write_log_thread, daemon=True)
29
+ log_thread.start()
30
+
31
+ # 记录初始信息
32
+ self._enqueue_log("header", f"Command: {' '.join(self.command)}")
33
+ self._enqueue_log("header", f"Start time: {datetime.now()}")
34
+ self._enqueue_log("header", "-" * 50)
35
+
36
+ # 启动输出转发线程
37
+ stdout_thread = threading.Thread(
38
+ target=self._forward_stream,
39
+ args=(self.process.stdout, sys.stdout, "stdout"),
40
+ daemon=True
41
+ )
42
+ stderr_thread = threading.Thread(
43
+ target=self._forward_stream,
44
+ args=(self.process.stderr, sys.stderr, "stderr"),
45
+ daemon=True
46
+ )
47
+
48
+ stdout_thread.start()
49
+ stderr_thread.start()
50
+
51
+ # 主线程处理输入转发
52
+ try:
53
+ while True:
54
+ if self.process.poll() is not None:
55
+ break
56
+
57
+ # 读取用户输入
58
+ try:
59
+ user_input = sys.stdin.readline()
60
+ if not user_input: # EOF
61
+ break
62
+
63
+ # 记录输入
64
+ self._enqueue_log("stdin", user_input)
65
+
66
+ # 转发到子进程
67
+ try:
68
+ self.process.stdin.write(user_input)
69
+ self.process.stdin.flush()
70
+ except (BrokenPipeError, OSError):
71
+ break
72
+
73
+ except KeyboardInterrupt:
74
+ break
75
+
76
+ finally:
77
+ # 清理工作
78
+ self.process.terminate()
79
+
80
+ # 等待所有输出处理完成
81
+ stdout_thread.join(timeout=1)
82
+ stderr_thread.join(timeout=1)
83
+
84
+ # 记录结束信息
85
+ self._enqueue_log("header", "-" * 50)
86
+ self._enqueue_log("header", f"End time: {datetime.now()}")
87
+ self._enqueue_log("header", f"Exit code: {self.process.returncode}")
88
+
89
+ # 等待日志写入完成
90
+ self.log_queue.put(None) # 结束信号
91
+ log_thread.join(timeout=2)
92
+
93
+ def _forward_stream(self, source, target, stream_name):
94
+ """转发数据流并记录"""
95
+ for line in iter(source.readline, ''):
96
+ # 转发到目标
97
+ target.write(line)
98
+ target.flush()
99
+
100
+ # 记录输出
101
+ self._enqueue_log(stream_name, line)
102
+
103
+ def _enqueue_log(self, stream_type, content):
104
+ """将日志内容放入队列"""
105
+ self.log_queue.put((stream_type, content))
106
+
107
+ def _write_log_thread(self):
108
+ """日志写入线程"""
109
+ with open(self.log_filename, 'w', encoding='utf-8') as log_file:
110
+ while True:
111
+ try:
112
+ item = self.log_queue.get(timeout=0.5)
113
+ if item is None: # 结束信号
114
+ break
115
+
116
+ stream_type, content = item
117
+
118
+ if stream_type == 'stderr': continue
119
+
120
+ if stream_type == "header":
121
+ log_file.write(content + "\n")
122
+ else:
123
+ direction = ">>>" if stream_type == "stdin" else "<<<"
124
+ log_file.write(f"{direction} {stream_type}: {content}")
125
+
126
+ log_file.flush()
127
+
128
+ except Empty:
129
+ if self.process.poll() is not None:
130
+ continue
131
+
132
+
133
+ def main():
134
+ if len(sys.argv) < 2:
135
+ print("Usage: cdebug.py <program> [args...]")
136
+ print("Example: cdebug.py python script.py arg1 arg2")
137
+ sys.exit(1)
138
+ interceptor = ProgramInterceptor(sys.argv[1:])
139
+ interceptor.start()
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()
ctools/cron_lite.py CHANGED
@@ -23,7 +23,7 @@ def demo1():
23
23
  def demo2(xx, fff):
24
24
  print('hello world222', xx, fff)
25
25
 
26
- cron_lite.apply_cron_task('0/1 * * * * ? *', demo2, (123123123, 34534534))
26
+ cron_lite.reg_cron_task('0/1 * * * * ? *', demo2, (123123123, 34534534))
27
27
  print(123123)
28
28
 
29
29
  cron_lite.start_all()
@@ -35,13 +35,14 @@ class SchedulerMeta:
35
35
  status: bool = False
36
36
  event: sched.Event = None
37
37
  scheduler: sched.scheduler = None
38
+ cron_str: str = None
38
39
 
39
40
 
40
41
  scheduler_map: Dict[str, SchedulerMeta] = {} # {timer_task_name: SchedulerMeta}
41
42
  _switch = False
42
43
  _info_handler = print
43
44
  _error_handler = print
44
- _time_zone: Optional[pytz.BaseTzInfo] = None
45
+ _time_zone: Optional[pytz.BaseTzInfo] = pytz.timezone("Asia/Shanghai")
45
46
 
46
47
 
47
48
  def set_time_zone(time_zone_name: str):
@@ -79,7 +80,7 @@ def cron_task(cron_expr: str, task_name: str = None, till_time_stamp: int = None
79
80
  return deco
80
81
 
81
82
 
82
- def apply_cron_task(cron_expr, func, params, timer_task_name=None, till_time_stamp=None):
83
+ def reg_cron_task(cron_expr, func, params, timer_task_name=None, till_time_stamp=None):
83
84
  """
84
85
  cron_task decorator to register a function as crontab task
85
86
  :param func: task callback function
@@ -105,11 +106,6 @@ def apply_cron_task(cron_expr, func, params, timer_task_name=None, till_time_sta
105
106
  _register_next(task_name, wrapper, cron_expr, till_time_stamp)
106
107
 
107
108
  _register_next(task_name, wrapper, cron_expr, till_time_stamp, init=True)
108
- # 不使用 submit, 因为提交的任务, 不是 daemon 线程
109
- t = threading.Thread(target=_start, args=(timer_task_name, ))
110
- t.setDaemon(True)
111
- t.start()
112
- return t
113
109
 
114
110
  def start_all(spawn: bool = True, daemon: bool = True, info_handler=None, error_handler=None) -> Optional[threading.Thread]:
115
111
  """
@@ -130,8 +126,7 @@ def start_all(spawn: bool = True, daemon: bool = True, info_handler=None, error_
130
126
  if error_handler:
131
127
  _error_handler = error_handler
132
128
  if spawn:
133
- t = threading.Thread(target=_start)
134
- t.setDaemon(daemon)
129
+ t = threading.Thread(target=_start, daemon=daemon)
135
130
  t.start()
136
131
  return t
137
132
  else:
@@ -192,6 +187,7 @@ def _register_next(timer_task_name, base_func, cron_expr, till_time_stamp, init:
192
187
  scheduler_meta.timer_task_name = timer_task_name
193
188
  scheduler_meta.switch = True
194
189
  scheduler_meta.scheduler = sched.scheduler(time.time, time.sleep)
190
+ scheduler_meta.cron_str = cron_expr
195
191
  scheduler_map[timer_task_name] = scheduler_meta
196
192
  elif init:
197
193
  raise ValueError(f"task name: {timer_task_name} already exists!!!!!")
@@ -219,7 +215,6 @@ def _run_sched(scheduler_meta: SchedulerMeta):
219
215
  return
220
216
  time.sleep(0.5)
221
217
 
222
-
223
218
  def _start(taskName: str = None):
224
219
  global _switch
225
220
  _switch = True
@@ -227,9 +222,8 @@ def _start(taskName: str = None):
227
222
  taskList = []
228
223
  for timer_task_name, scheduler_meta in scheduler_map.items():
229
224
  if taskName is not None and timer_task_name != taskName: continue
230
- print("register job: ", timer_task_name)
231
- thread = threading.Thread(target=_run_sched, args=(scheduler_meta, ))
232
- thread.setDaemon(True)
225
+ print("register job: ", timer_task_name, ", cron: ", scheduler_meta.cron_str)
226
+ thread = threading.Thread(target=_run_sched, args=(scheduler_meta, ), daemon=True)
233
227
  thread.start()
234
228
  taskList.append(thread)
235
229
  for task in taskList: task.join()
@@ -237,7 +231,6 @@ def _start(taskName: str = None):
237
231
  _switch = False
238
232
  scheduler_map.clear()
239
233
 
240
-
241
234
  def _convert_cron(cron_expr):
242
235
  res_cron = ""
243
236
  cron_list = cron_expr.split(" ")
ctools/sys_log.py CHANGED
@@ -76,6 +76,9 @@ class GlobalLogger(object):
76
76
  def flush(self):
77
77
  pass
78
78
 
79
+ def fileno(self):
80
+ return sys.__stdout__.fileno()
81
+
79
82
 
80
83
  @call.init
81
84
  def _init_log() -> None:
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gomyck-tools
3
- Version: 1.3.2
3
+ Version: 1.3.4
4
4
  Summary: A tools collection for python development by hao474798383
5
5
  Author-email: gomyck <hao474798383@163.com>
6
- License: MIT
6
+ License-Expression: Apache-2.0
7
7
  Requires-Python: >=3.10
8
8
  Description-Content-Type: text/markdown
9
+ License-File: LICENSE
9
10
  Requires-Dist: jsonpickle~=3.4.2
10
11
  Requires-Dist: SQLAlchemy~=2.0.36
11
12
  Requires-Dist: chardet~=5.2.0
@@ -41,6 +42,7 @@ Requires-Dist: asyncpg>=0.28; extra == "full"
41
42
  Requires-Dist: pytest>=7.0; extra == "full"
42
43
  Requires-Dist: black>=24.0; extra == "full"
43
44
  Requires-Dist: mypy>=1.0; extra == "full"
45
+ Dynamic: license-file
44
46
 
45
47
  # Gomyck-Tools
46
48
 
@@ -9,6 +9,7 @@ ctools/bottle_webserver.py,sha256=l7t_sN4ayywD1sR0kzuhGioOuaqGR9VhJh7e6Gbd6aE,46
9
9
  ctools/bottle_websocket.py,sha256=zqCE1rGlMeC9oxFOULNd137IWIhdetq83Oq5OoH_zGI,1953
10
10
  ctools/browser_element_tools.py,sha256=IFR_tWu5on0LxhuC_4yT6EOjwCsC-juIoU8KQRDqR7E,9952
11
11
  ctools/call.py,sha256=BCr8wzt5qd70okv8IZn-9-EpjywleZgvA3u1vfZ_Kt8,1581
12
+ ctools/cdebug.py,sha256=4PWZxrUcfTOljK7xXSiQ9iXddYwsKKeq96RGUFNqNkg,3807
12
13
  ctools/cftp.py,sha256=SkHPDvKu58jnMnl68u5WxWEiFWsm2C0CGa5_GR_Obcw,2481
13
14
  ctools/cjson.py,sha256=d0RZ53b-M18OudRFGRtPCvvGofJcaLHdbNlTCemyJag,1365
14
15
  ctools/ckafka.py,sha256=EiiGCFkSbq8yRjQKVjc2_V114hKb8fJAVIOks_XbQ3w,5944
@@ -16,7 +17,7 @@ ctools/compile_tools.py,sha256=Nybh3vnkurIKnPnubdYzigjnzFu4GaTMKPvqFdibxmE,510
16
17
  ctools/console.py,sha256=EZumuyynwteKUhUxB_XoulHswDxHd75OQB34RiZ-OBM,1807
17
18
  ctools/coord_trans.py,sha256=pzIHxC4aLwvOF3eJG47Dda3vIq-Zp42xnu_FwILDflU,3951
18
19
  ctools/credis.py,sha256=sW7yDQvxa7B4dWvGwUH7GROq-7ElRMDhFT6g2C8ryfE,4522
19
- ctools/cron_lite.py,sha256=f9g7-64GsCxcAW-HUAvT6S-kooScl8zaJyqwHY-X_rE,8308
20
+ ctools/cron_lite.py,sha256=CUqdtO02VnYUWcw6t6Jr7v2dKKhx3_G7CDAtlWnirBE,8232
20
21
  ctools/ctoken.py,sha256=NZSBGF3lJajJFLRIZoeXmpp8h5cKM0dAH2weySgeORc,882
21
22
  ctools/cword.py,sha256=ZRzAFn96yjo-hAbZuGIm4DoBAL2y8tFySWZ5xbYgY6Q,857
22
23
  ctools/czip.py,sha256=8VQ420KgMF09U8McSXTkaAz0jd0Zzm6qazf3iJADQI4,4674
@@ -48,7 +49,7 @@ ctools/snow_id.py,sha256=hYinnRN-aOule4_9vfgXB7XnsU-56cIS3PhzAwWBc5E,2270
48
49
  ctools/str_diff.py,sha256=QUtXOfsRLTFozH_zByqsC39JeuG3eZtrwGVeLyaHYUI,429
49
50
  ctools/string_tools.py,sha256=itK59W4Ed4rQzuyHuioNgDRUcBlfb4ZoZnwmS9cJxiI,1887
50
51
  ctools/sys_info.py,sha256=NvKCuBlWHHiW4bDI4tYZUo3QusvODm1HlW6aAkrllnE,4248
51
- ctools/sys_log.py,sha256=oqb1S41LosdeZxtogFVgDk8R4sjiHhUeYJLCzHd728E,2805
52
+ ctools/sys_log.py,sha256=GO7wBCkF_R35WwLJ7x5lW8Dz0KeZExlbsE_yGspRH1Y,2861
52
53
  ctools/thread_pool.py,sha256=Mt60XMhs-nk-hbkPo8NA7wQ4RxRLZTk4X6vh5Wn3WEw,944
53
54
  ctools/upload_tools.py,sha256=sqe6K3ZWiyY58pFE5IO5mNaS1znnS7U4c4UqY8noED4,1068
54
55
  ctools/win_canvas.py,sha256=PAxI4i1jalfree9d1YG4damjc2EzaHZrgHZCTgk2GiM,2530
@@ -56,7 +57,8 @@ ctools/win_control.py,sha256=35f9x_ijSyc4ZDkcT32e9ZIhr_ffNxadynrQfFuIdYo,3489
56
57
  ctools/word_fill.py,sha256=xeo-P4DOjQUqd-o9XL3g66wQrE2diUPGwFywm8TdVyw,18210
57
58
  ctools/word_fill_entity.py,sha256=eX3G0Gy16hfGpavQSEkCIoKDdTnNgRRJrFvKliETZK8,985
58
59
  ctools/work_path.py,sha256=OmfYu-Jjg2huRY6Su8zJ_2EGFFhtBZFbobYTwbjJtG4,1817
59
- gomyck_tools-1.3.2.dist-info/METADATA,sha256=n0mq8Z9a0-nOrX3PWZ8JA6ILp79wX__r8foWccyIQB4,1682
60
- gomyck_tools-1.3.2.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
61
- gomyck_tools-1.3.2.dist-info/top_level.txt,sha256=-MiIH9FYRVKp1i5_SVRkaI-71WmF1sZSRrNWFU9ls3s,7
62
- gomyck_tools-1.3.2.dist-info/RECORD,,
60
+ gomyck_tools-1.3.4.dist-info/licenses/LICENSE,sha256=X25ypfH9E6VTht2hcO8k7LCSdHUcoG_ALQt80jdYZfY,547
61
+ gomyck_tools-1.3.4.dist-info/METADATA,sha256=2urHv5Qj9jvzPiYDOLJ19UQDEGeQK5KcTuflQMl_w4o,1744
62
+ gomyck_tools-1.3.4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
63
+ gomyck_tools-1.3.4.dist-info/top_level.txt,sha256=-MiIH9FYRVKp1i5_SVRkaI-71WmF1sZSRrNWFU9ls3s,7
64
+ gomyck_tools-1.3.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.0)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,13 @@
1
+ Copyright 2025 gomyck
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.