half-sample 0.1.18__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.
- half_sample-0.1.18/PKG-INFO +15 -0
- half_sample-0.1.18/README.md +20 -0
- half_sample-0.1.18/cpp_build/sample.exe +0 -0
- half_sample-0.1.18/half_sample.egg-info/PKG-INFO +15 -0
- half_sample-0.1.18/half_sample.egg-info/SOURCES.txt +11 -0
- half_sample-0.1.18/half_sample.egg-info/dependency_links.txt +1 -0
- half_sample-0.1.18/half_sample.egg-info/top_level.txt +1 -0
- half_sample-0.1.18/sample/__init__.py +3 -0
- half_sample-0.1.18/sample/process.py +154 -0
- half_sample-0.1.18/sample/result.py +120 -0
- half_sample-0.1.18/sample/sample.py +97 -0
- half_sample-0.1.18/setup.cfg +4 -0
- half_sample-0.1.18/setup.py +18 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: half_sample
|
|
3
|
+
Version: 0.1.18
|
|
4
|
+
Summary: sample data and analysis
|
|
5
|
+
Home-page: https://github.com/KD-Group/Half.Sample
|
|
6
|
+
Author: kunde
|
|
7
|
+
Author-email: gzkunde@163.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
|
|
14
|
+
UNKNOWN
|
|
15
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Half.Sample
|
|
2
|
+
|
|
3
|
+
[](https://ci.appveyor.com/project/Wingsgo/half-sample)
|
|
4
|
+
|
|
5
|
+
Sample, C++ Program in Half Application with Python Module
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
Python Requirements:
|
|
10
|
+
|
|
11
|
+
1. st
|
|
12
|
+
2. typing
|
|
13
|
+
|
|
14
|
+
Others:
|
|
15
|
+
|
|
16
|
+
1. C++11 environment
|
|
17
|
+
2. scons
|
|
18
|
+
|
|
19
|
+
You can also refer to the Travis-CI config file: `.travis.yml`, or Appveyor config file: `appveyor.yml`.
|
|
20
|
+
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: half-sample
|
|
3
|
+
Version: 0.1.18
|
|
4
|
+
Summary: sample data and analysis
|
|
5
|
+
Home-page: https://github.com/KD-Group/Half.Sample
|
|
6
|
+
Author: kunde
|
|
7
|
+
Author-email: gzkunde@163.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
|
|
14
|
+
UNKNOWN
|
|
15
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
cpp_build/sample.exe
|
|
4
|
+
half_sample.egg-info/PKG-INFO
|
|
5
|
+
half_sample.egg-info/SOURCES.txt
|
|
6
|
+
half_sample.egg-info/dependency_links.txt
|
|
7
|
+
half_sample.egg-info/top_level.txt
|
|
8
|
+
sample/__init__.py
|
|
9
|
+
sample/process.py
|
|
10
|
+
sample/result.py
|
|
11
|
+
sample/sample.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sample
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
import subprocess
|
|
6
|
+
import multiprocessing
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger('library')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def process(target, args=(), finished=None):
|
|
13
|
+
p = multiprocessing.Process(target=target, args=args)
|
|
14
|
+
p.start()
|
|
15
|
+
|
|
16
|
+
def check_finished():
|
|
17
|
+
p.join()
|
|
18
|
+
finished()
|
|
19
|
+
|
|
20
|
+
threading.Thread(target=check_finished).start()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Process:
|
|
24
|
+
def __init__(self, cmd):
|
|
25
|
+
self.cmd = cmd
|
|
26
|
+
self.p = None
|
|
27
|
+
self._max_retries = 1
|
|
28
|
+
self._start_process()
|
|
29
|
+
|
|
30
|
+
def set_max_retry(self, max_retries):
|
|
31
|
+
self._max_retries = max_retries
|
|
32
|
+
|
|
33
|
+
def get_max_retry(self):
|
|
34
|
+
return self._max_retries
|
|
35
|
+
|
|
36
|
+
def _start_process(self):
|
|
37
|
+
"""启动或重启子进程"""
|
|
38
|
+
if self.p is not None:
|
|
39
|
+
# 如果旧进程还在运行,先终止它
|
|
40
|
+
try:
|
|
41
|
+
if self.p.poll() is None: # 进程仍在运行
|
|
42
|
+
self.p.terminate()
|
|
43
|
+
self.p.wait(timeout=3)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
logger.warning(f"failed to poll: {e}")
|
|
46
|
+
try:
|
|
47
|
+
self.p.kill()
|
|
48
|
+
self.p.wait()
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.warning(f"failed to kill: {e}")
|
|
51
|
+
pass
|
|
52
|
+
# 启动新进程
|
|
53
|
+
self.p = subprocess.Popen(self.cmd,
|
|
54
|
+
shell=False,
|
|
55
|
+
creationflags=0x00000008 if sys.platform == "win32" else 0,
|
|
56
|
+
stdin=subprocess.PIPE,
|
|
57
|
+
stdout=subprocess.PIPE,
|
|
58
|
+
stderr=subprocess.PIPE)
|
|
59
|
+
|
|
60
|
+
def _is_process_alive(self):
|
|
61
|
+
"""检查进程是否存活"""
|
|
62
|
+
if self.p is None:
|
|
63
|
+
return False
|
|
64
|
+
return self.p.poll() is None
|
|
65
|
+
|
|
66
|
+
def _ensure_process_alive(self):
|
|
67
|
+
"""确保进程存活,如果崩溃则重启"""
|
|
68
|
+
if not self._is_process_alive():
|
|
69
|
+
logger.warning(f"subprocess [{self.cmd}] crash, restarting...")
|
|
70
|
+
self._start_process()
|
|
71
|
+
logger.warning(f"subprocess [{self.cmd}] restart completed")
|
|
72
|
+
|
|
73
|
+
def write_line(self, line: str):
|
|
74
|
+
"""写入一行数据,自动处理进程重启"""
|
|
75
|
+
for attempt in range(self._max_retries):
|
|
76
|
+
try:
|
|
77
|
+
self._ensure_process_alive()
|
|
78
|
+
self.p.stdin.write(line.encode())
|
|
79
|
+
self.p.stdin.write('\n'.encode())
|
|
80
|
+
self.p.stdin.flush()
|
|
81
|
+
return # 成功写入,退出重试循环
|
|
82
|
+
except (BrokenPipeError, OSError) as e:
|
|
83
|
+
if attempt < self._max_retries - 1:
|
|
84
|
+
logger.warning(f"write line failed (attempted {attempt + 1}/{self._max_retries}): {e}")
|
|
85
|
+
self._start_process() # 重启进程
|
|
86
|
+
time.sleep(0.1) # 短暂等待新进程启动
|
|
87
|
+
else:
|
|
88
|
+
raise RuntimeError(f"无法向子进程写入数据,已重试{self._max_retries}次: {e}")
|
|
89
|
+
|
|
90
|
+
def read_line(self) -> str:
|
|
91
|
+
"""读取一行数据,自动处理进程重启"""
|
|
92
|
+
for attempt in range(self._max_retries):
|
|
93
|
+
try:
|
|
94
|
+
self._ensure_process_alive()
|
|
95
|
+
line = self.p.stdout.readline()
|
|
96
|
+
if not line: # EOF,进程可能已终止
|
|
97
|
+
if not self._is_process_alive():
|
|
98
|
+
raise RuntimeError("子进程已终止")
|
|
99
|
+
return ""
|
|
100
|
+
return line.decode().replace('\r\n', '').replace('\n', '')
|
|
101
|
+
except (OSError, RuntimeError) as e:
|
|
102
|
+
if attempt < self._max_retries - 1:
|
|
103
|
+
logger.warning(f"read line failed (attempted {attempt + 1}/{self._max_retries}): {e}")
|
|
104
|
+
self._start_process() # 重启进程
|
|
105
|
+
time.sleep(0.1) # 短暂等待新进程启动
|
|
106
|
+
else:
|
|
107
|
+
raise RuntimeError(f"无法从子进程读取数据,已重试{self._max_retries}次: {e}")
|
|
108
|
+
|
|
109
|
+
def read_until(self, until):
|
|
110
|
+
"""读取直到指定字符串,自动处理进程重启"""
|
|
111
|
+
lines = []
|
|
112
|
+
|
|
113
|
+
for attempt in range(self._max_retries):
|
|
114
|
+
try:
|
|
115
|
+
while True:
|
|
116
|
+
line = self.read_line()
|
|
117
|
+
if line == until:
|
|
118
|
+
return '\n'.join(lines)
|
|
119
|
+
elif not line:
|
|
120
|
+
break
|
|
121
|
+
else:
|
|
122
|
+
lines.append(line)
|
|
123
|
+
except (RuntimeError, OSError) as e:
|
|
124
|
+
if attempt < self._max_retries - 1:
|
|
125
|
+
logger.warning(f"read_until failed (attempted {attempt + 1}/{self._max_retries}): {e}")
|
|
126
|
+
self._start_process() # 重启进程
|
|
127
|
+
lines = [] # 清空已读取的行
|
|
128
|
+
time.sleep(0.1)
|
|
129
|
+
else:
|
|
130
|
+
raise RuntimeError(f"read_until 失败,已重试{self._max_retries}次: {e}")
|
|
131
|
+
|
|
132
|
+
return '\n'.join(lines)
|
|
133
|
+
|
|
134
|
+
def terminate(self):
|
|
135
|
+
"""终止子进程"""
|
|
136
|
+
if self.p is not None and self.p.poll() is None:
|
|
137
|
+
try:
|
|
138
|
+
self.p.terminate()
|
|
139
|
+
self.p.wait(timeout=3)
|
|
140
|
+
except subprocess.TimeoutExpired as e:
|
|
141
|
+
logger.warning(f"Timeout expired while trying to terminate [{self.cmd}]: {e}")
|
|
142
|
+
try:
|
|
143
|
+
self.p.kill()
|
|
144
|
+
self.p.wait()
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(f"Failed to kill [{self.cmd}]: {e}")
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.warning(f"Failed to terminate [{self.cmd}]: {e}")
|
|
149
|
+
|
|
150
|
+
def get_return_code(self):
|
|
151
|
+
"""获取进程返回码"""
|
|
152
|
+
if self.p is not None:
|
|
153
|
+
return self.p.poll()
|
|
154
|
+
return None
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Result:
|
|
5
|
+
ErrorMessageMapper = {
|
|
6
|
+
# 基础状态
|
|
7
|
+
"success": "成功",
|
|
8
|
+
|
|
9
|
+
# 用户操作错误
|
|
10
|
+
"command_not_found": "命令不存在",
|
|
11
|
+
"sampler_not_found": "采样器不存在",
|
|
12
|
+
"now_in_measuring": "正在测量中",
|
|
13
|
+
"file_not_found": "文件未找到",
|
|
14
|
+
|
|
15
|
+
# 采样错误
|
|
16
|
+
"voltage_not_enough": "电压不足",
|
|
17
|
+
"wave_not_found": "未找到波形",
|
|
18
|
+
"appropriate_wave_not_found": "未找到合适波形",
|
|
19
|
+
|
|
20
|
+
# 警告类错误
|
|
21
|
+
"warning_intr_not_available": "中断资源不可用",
|
|
22
|
+
"warning_param_out_of_range": "参数超出范围",
|
|
23
|
+
"warning_prop_value_out_of_range": "属性值超出范围",
|
|
24
|
+
"warning_prop_value_not_spted": "属性值不支持",
|
|
25
|
+
"warning_prop_value_conflict": "属性值状态冲突",
|
|
26
|
+
"warning_vrg_of_group_not_same": "通道组量程不一致",
|
|
27
|
+
|
|
28
|
+
# 系统级错误
|
|
29
|
+
"error_handle_not_valid": "句柄无效",
|
|
30
|
+
"error_param_out_of_range": "参数超出范围",
|
|
31
|
+
"error_param_not_spted": "参数不支持",
|
|
32
|
+
"error_param_fmt_unexpted": "参数格式异常",
|
|
33
|
+
"error_memory_not_enough": "内存不足",
|
|
34
|
+
"error_buffer_is_null": "缓冲区为空",
|
|
35
|
+
"error_buffer_too_small": "缓冲区过小",
|
|
36
|
+
"error_data_len_exceed_limit": "数据长度超限",
|
|
37
|
+
"error_func_not_spted": "功能不支持",
|
|
38
|
+
"error_event_not_spted": "事件不支持",
|
|
39
|
+
"error_prop_not_spted": "属性不支持",
|
|
40
|
+
"error_prop_read_only": "属性只读",
|
|
41
|
+
"error_prop_value_conflict": "属性值冲突",
|
|
42
|
+
"error_prop_value_out_of_range": "属性值超限",
|
|
43
|
+
"error_prop_value_not_spted": "属性值不支持",
|
|
44
|
+
"error_privilege_not_held": "权限未持有",
|
|
45
|
+
"error_privilege_not_available": "权限不可用",
|
|
46
|
+
"error_driver_not_found": "驱动未找到",
|
|
47
|
+
"error_driver_ver_mismatch": "驱动版本不匹配",
|
|
48
|
+
"error_driver_count_exceed_limit": "驱动数量超限",
|
|
49
|
+
"error_device_not_opened": "设备未打开",
|
|
50
|
+
"error_device_not_exist": "设备不存在",
|
|
51
|
+
"error_device_unrecognized": "设备未识别",
|
|
52
|
+
"error_config_data_lost": "配置数据丢失",
|
|
53
|
+
"error_func_not_inited": "功能未初始化",
|
|
54
|
+
"error_func_busy": "功能忙",
|
|
55
|
+
"error_intr_not_available": "中断不可用",
|
|
56
|
+
"error_dma_not_available": "DMA通道不可用",
|
|
57
|
+
"error_device_io_time_out": "设备IO超时",
|
|
58
|
+
"error_signature_not_match": "签名不匹配",
|
|
59
|
+
"error_func_conflict_with_bfd_ai": "功能与缓冲AI冲突",
|
|
60
|
+
"error_vrg_not_available_in_se_mode": "单端模式量程不可用",
|
|
61
|
+
"error_undefined": "未定义错误"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def __init__(self):
|
|
65
|
+
self.error = False
|
|
66
|
+
self.message = ''
|
|
67
|
+
|
|
68
|
+
self.sampler_name = ''
|
|
69
|
+
self.measuring = False
|
|
70
|
+
self.success = False
|
|
71
|
+
self.with_magnetic = False
|
|
72
|
+
|
|
73
|
+
self.sampling_interval = 0.0 # us
|
|
74
|
+
self.wave_interval = 0.0 # us
|
|
75
|
+
self.waveforms_per_sample = 0.0 # 采集卡每次采样时能得到的最大波形数量,可能采集到部分波形,因此为double类型,例如0.5
|
|
76
|
+
self.sampling_time = 1 # 采样次数,当要求的波形数量大于采集卡单次最大采样点数的时,进行多次采样直到能采集到要求的波形
|
|
77
|
+
self.sampling_length_per_sample = 0 # 采集卡单次采样点数
|
|
78
|
+
self.waveform_length = 0 # 一个完整波形的点数(包括上升沿和下降沿)
|
|
79
|
+
self.valid_length = 0 # 有效的波形点数(仅包含上升沿部分)
|
|
80
|
+
self.number_of_waveforms = 0 # 波形平均次数
|
|
81
|
+
self.wave = []
|
|
82
|
+
self.time_line = []
|
|
83
|
+
self.estimate = []
|
|
84
|
+
|
|
85
|
+
self.tau = 0.0
|
|
86
|
+
self.tau_b = 0.0
|
|
87
|
+
self.w = 0.0
|
|
88
|
+
self.b = 0.0
|
|
89
|
+
self.loss = 0.0
|
|
90
|
+
|
|
91
|
+
self.v0 = 0.0
|
|
92
|
+
self.v_inf = 0.0
|
|
93
|
+
|
|
94
|
+
self.mock_tau = 0.0
|
|
95
|
+
self.mock_v0 = 0.0
|
|
96
|
+
self.mock_v_inf = 0.0
|
|
97
|
+
self.mock_noise = 0.0
|
|
98
|
+
|
|
99
|
+
self.rho = 0.0
|
|
100
|
+
self.miu = 0.0
|
|
101
|
+
self.carrier = 0.0
|
|
102
|
+
self.corrected_rho = 0.0
|
|
103
|
+
self.corrected_miu = 0.0
|
|
104
|
+
self.corrected_carrier = 0.0
|
|
105
|
+
|
|
106
|
+
def process(self):
|
|
107
|
+
self.time_line = [self.wave_interval * i for i in range(len(self.wave))]
|
|
108
|
+
|
|
109
|
+
if self.success:
|
|
110
|
+
tau, w, b = self.tau, self.w, self.b
|
|
111
|
+
self.estimate = [w * math.exp(t / -tau) + b for t in self.time_line]
|
|
112
|
+
|
|
113
|
+
self.v0, self.v_inf = b + w, b
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def chinese_message(self) -> str:
|
|
117
|
+
try:
|
|
118
|
+
return self.ErrorMessageMapper[self.message]
|
|
119
|
+
except Exception as e:
|
|
120
|
+
return self.message
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
from . import Process
|
|
5
|
+
from . import Result
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Sampler:
|
|
9
|
+
@property
|
|
10
|
+
def execution_path(self) -> str:
|
|
11
|
+
main_path = os.path.join(os.path.dirname(__file__), '..')
|
|
12
|
+
|
|
13
|
+
# build if SConstruct file exists
|
|
14
|
+
if os.path.exists(os.path.join(main_path, 'SConstruct')):
|
|
15
|
+
if os.system('cd {} && scons'.format(main_path)) != 0:
|
|
16
|
+
raise self.Error("Compile C++ Driver Error")
|
|
17
|
+
|
|
18
|
+
# try to use cpp_build/sample.exe when developing
|
|
19
|
+
execution_name = 'sample.exe'
|
|
20
|
+
if not os.path.exists(execution_name):
|
|
21
|
+
execution_name = os.path.join(main_path, 'cpp_build', execution_name)
|
|
22
|
+
if os.path.exists(execution_name):
|
|
23
|
+
return os.path.abspath(execution_name)
|
|
24
|
+
|
|
25
|
+
# try to find sample.exe in system path when release
|
|
26
|
+
if shutil.which("sample.exe"):
|
|
27
|
+
return shutil.which("sample.exe")
|
|
28
|
+
|
|
29
|
+
raise self.Error('Sample Driver Not Found')
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def p(self) -> Process:
|
|
33
|
+
p = getattr(self, 'p_', None)
|
|
34
|
+
if p is not None:
|
|
35
|
+
return p
|
|
36
|
+
|
|
37
|
+
p = Process(self.execution_path)
|
|
38
|
+
setattr(self, 'p_', p)
|
|
39
|
+
return p
|
|
40
|
+
|
|
41
|
+
def communicate(self, command: str, executor: Process = None) -> Result:
|
|
42
|
+
result = Result()
|
|
43
|
+
try:
|
|
44
|
+
executor = executor or self.p
|
|
45
|
+
executor.write_line(command)
|
|
46
|
+
lines = executor.read_until('EOF')
|
|
47
|
+
if lines:
|
|
48
|
+
exec(lines, result.__dict__)
|
|
49
|
+
except Exception as e:
|
|
50
|
+
result.error = True
|
|
51
|
+
result.message = str(e)
|
|
52
|
+
raise self.Error("{}: {}".format(result.message, result.chinese_message))
|
|
53
|
+
|
|
54
|
+
if result.error:
|
|
55
|
+
raise self.Error("{}: {}".format(result.message, result.chinese_message))
|
|
56
|
+
|
|
57
|
+
return result
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def is_measuring(self) -> bool:
|
|
61
|
+
return self.communicate('is_measuring').measuring
|
|
62
|
+
|
|
63
|
+
def measure(self, number_of_waveforms: int, emitting_frequency: float, auto_mode: bool = False) -> Result:
|
|
64
|
+
return self.communicate("to_measure {} {:.2f} {}".format(number_of_waveforms, emitting_frequency, auto_mode))
|
|
65
|
+
|
|
66
|
+
def dump(self, filename: str, number_of_waveforms: int, emitting_frequency: float,
|
|
67
|
+
auto_mode: bool = False) -> Result:
|
|
68
|
+
return self.communicate(
|
|
69
|
+
"to_dump {} {} {:.2f} {}".format(filename, number_of_waveforms, emitting_frequency, auto_mode))
|
|
70
|
+
|
|
71
|
+
def process(self, filename: str) -> Result:
|
|
72
|
+
return self.communicate("to_process {}".format(filename))
|
|
73
|
+
|
|
74
|
+
def set_sampler(self, sampler_name: str) -> Result:
|
|
75
|
+
return self.communicate("set_sampler {}".format(sampler_name))
|
|
76
|
+
|
|
77
|
+
def get_sampler(self) -> Result:
|
|
78
|
+
return self.communicate("get_sampler")
|
|
79
|
+
|
|
80
|
+
def set_sampler_value(self, key: str, value: float) -> Result:
|
|
81
|
+
return self.communicate("set_sampler_value {} {}".format(key, value))
|
|
82
|
+
|
|
83
|
+
def get_sampler_value(self, key: str) -> Result:
|
|
84
|
+
return self.communicate("get_sampler_value {}".format(key))
|
|
85
|
+
|
|
86
|
+
def query(self) -> Result:
|
|
87
|
+
result = self.communicate("to_query")
|
|
88
|
+
result.process()
|
|
89
|
+
if not result.success and result.message == "success":
|
|
90
|
+
result.message = "error_undefined"
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
class Error(RuntimeError):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
sampler = Sampler()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import setuptools
|
|
2
|
+
|
|
3
|
+
setuptools.setup(
|
|
4
|
+
name="half_sample",
|
|
5
|
+
version="0.1.18",
|
|
6
|
+
author="kunde",
|
|
7
|
+
author_email="gzkunde@163.com",
|
|
8
|
+
description="sample data and analysis",
|
|
9
|
+
long_description="",
|
|
10
|
+
url="https://github.com/KD-Group/Half.Sample",
|
|
11
|
+
packages=["sample"],
|
|
12
|
+
classifiers=(
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
),
|
|
17
|
+
data_files=[('cpp_build/sample.exe')]
|
|
18
|
+
)
|