Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
337e53d099 | ||
|
|
2cbb0fec5b | ||
|
|
dccc7198f4 |
+9
-7
@@ -9,7 +9,7 @@
|
|||||||
> (Windows 10/11 x64,免安装、无需 Python,**内置 OpenOCD 工具链**)。
|
> (Windows 10/11 x64,免安装、无需 Python,**内置 OpenOCD 工具链**)。
|
||||||
> - **要改代码/重新打包**:见 §5、§6。
|
> - **要改代码/重新打包**:见 §5、§6。
|
||||||
>
|
>
|
||||||
> 界面与构建方式对齐 DLPilot 项目(CustomTkinter 深色双栏仪表盘 +
|
> 界面与构建方式对齐 DLPilot 项目(CustomTkinter 深色卡片仪表盘 +
|
||||||
> PyInstaller onefile + Gitea Release 发布)。更新:2026-09-14
|
> PyInstaller onefile + Gitea Release 发布)。更新:2026-09-14
|
||||||
|
|
||||||
## 1. 功能总览
|
## 1. 功能总览
|
||||||
@@ -46,13 +46,15 @@ DAPLinkFlash/
|
|||||||
|
|
||||||
## 3. GUI 使用
|
## 3. GUI 使用
|
||||||
|
|
||||||
主窗口为双栏仪表盘:
|
主窗口为单列仪表盘,补齐/拼接经顶栏按钮弹出独立窗口:
|
||||||
|
|
||||||
```
|
```
|
||||||
左列 右列
|
顶栏 DAPLink Flash [补齐 (Pad)] [拼接 (Merge)] 关于 Dark/Light/System
|
||||||
┌ 探针与工具链 [自动定位] ┐ ┌ 补齐 (Pad) — Y-Modem 对齐 ┐
|
┌ 探针与工具链 [自动定位] ┐
|
||||||
├ 芯片与固件(型号/固件/基地址)┤ ├ 拼接 (Merge) — BL+APP+标志 ┤
|
├ 芯片与固件(型号/固件/基地址) ┤
|
||||||
└ 操作: 检测/烧录/擦除/读取/复位/停止 ┘ └ 运行日志 — OpenOCD 输出 ┘
|
├ 运行日志 — OpenOCD 输出 ┤
|
||||||
|
└ 操作: 检测/烧录/擦除/读取/复位/停止 ┘
|
||||||
|
弹窗 补齐 (Pad) / 拼接 (Merge) — 非模态, 配置与主窗口共享
|
||||||
```
|
```
|
||||||
|
|
||||||
1. **选工具链**:默认自动定位(内置 → 本机 `D:/toolchain` → PATH),左卡右上
|
1. **选工具链**:默认自动定位(内置 → 本机 `D:/toolchain` → PATH),左卡右上
|
||||||
@@ -62,7 +64,7 @@ DAPLinkFlash/
|
|||||||
3. **选固件**:点"浏览"选 BIN/HEX/ELF;BIN 确认基地址(完整镜像 `0x08000000`,
|
3. **选固件**:点"浏览"选 BIN/HEX/ELF;BIN 确认基地址(完整镜像 `0x08000000`,
|
||||||
单独 APP `0x08002000`);
|
单独 APP `0x08002000`);
|
||||||
4. **点"检测设备"**确认链路通 → **点"烧录固件"**;输出实时显示在右下日志区;
|
4. **点"检测设备"**确认链路通 → **点"烧录固件"**;输出实时显示在右下日志区;
|
||||||
5. **固件工具**(右列):
|
5. **固件工具**(顶栏按钮弹出的独立窗口,非模态):
|
||||||
- 补齐:选输入 BIN 后输出名自动带出(`*_pad.bin`),对齐默认 1K,点"执行补齐";
|
- 补齐:选输入 BIN 后输出名自动带出(`*_pad.bin`),对齐默认 1K,点"执行补齐";
|
||||||
- 拼接:选 Bootloader 与 APP 后输出名自动带出(`*_BL.bin`),地址按典型布局
|
- 拼接:选 Bootloader 与 APP 后输出名自动带出(`*_BL.bin`),地址按典型布局
|
||||||
预填,点"执行拼接"。
|
预填,点"执行拼接"。
|
||||||
|
|||||||
+77
-54
@@ -33,7 +33,7 @@ BOLD = ('Microsoft YaHei UI', 13, 'bold')
|
|||||||
TITLE_FONT = ('Microsoft YaHei UI', 18, 'bold')
|
TITLE_FONT = ('Microsoft YaHei UI', 18, 'bold')
|
||||||
|
|
||||||
APP_NAME = 'DAPLink Flash'
|
APP_NAME = 'DAPLink Flash'
|
||||||
APP_VERSION = '2.0.0'
|
APP_VERSION = '2.1.0'
|
||||||
REPO_URL = 'https://gitea.hulk.wang/hulk/gd32e230f8_firmware_merge_tool'
|
REPO_URL = 'https://gitea.hulk.wang/hulk/gd32e230f8_firmware_merge_tool'
|
||||||
|
|
||||||
OK_BLUE = '#3B8ED0'
|
OK_BLUE = '#3B8ED0'
|
||||||
@@ -73,8 +73,8 @@ class App(ctk.CTk):
|
|||||||
'hulk.DAPLinkFlash')
|
'hulk.DAPLinkFlash')
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self.geometry('1360x880')
|
self.geometry('960x950')
|
||||||
self.minsize(1200, 780)
|
self.minsize(860, 800)
|
||||||
ic = get_icon_path()
|
ic = get_icon_path()
|
||||||
if ic:
|
if ic:
|
||||||
try:
|
try:
|
||||||
@@ -99,6 +99,8 @@ class App(ctk.CTk):
|
|||||||
self.proc = None
|
self.proc = None
|
||||||
self._poll_job = None
|
self._poll_job = None
|
||||||
self._op_buttons = []
|
self._op_buttons = []
|
||||||
|
self.v_pad_result = tk.StringVar(value='')
|
||||||
|
self.v_merge_result = tk.StringVar(value='')
|
||||||
|
|
||||||
self._build()
|
self._build()
|
||||||
self._log(f'{APP_NAME} v{APP_VERSION} — OpenOCD 烧录 · 固件补齐 · 镜像拼接')
|
self._log(f'{APP_NAME} v{APP_VERSION} — OpenOCD 烧录 · 固件补齐 · 镜像拼接')
|
||||||
@@ -138,8 +140,13 @@ class App(ctk.CTk):
|
|||||||
top = ctk.CTkFrame(self, fg_color='transparent')
|
top = ctk.CTkFrame(self, fg_color='transparent')
|
||||||
top.pack(fill='x', padx=12, pady=(10, 2))
|
top.pack(fill='x', padx=12, pady=(10, 2))
|
||||||
ctk.CTkLabel(top, text=APP_NAME, font=TITLE_FONT).pack(side='left')
|
ctk.CTkLabel(top, text=APP_NAME, font=TITLE_FONT).pack(side='left')
|
||||||
ctk.CTkLabel(top, text='GD32 固件烧录 · 补齐 · 镜像拼接',
|
ctk.CTkLabel(top, text='GD32 固件烧录',
|
||||||
text_color='gray60').pack(side='left', padx=10, pady=(6, 0))
|
text_color='gray60').pack(side='left', padx=10, pady=(6, 0))
|
||||||
|
ctk.CTkButton(top, text='补齐 (Pad)', width=88, height=26,
|
||||||
|
command=self._open_pad_dialog).pack(side='left', padx=(16, 6),
|
||||||
|
pady=(4, 0))
|
||||||
|
ctk.CTkButton(top, text='拼接 (Merge)', width=100, height=26,
|
||||||
|
command=self._open_merge_dialog).pack(side='left', pady=(4, 0))
|
||||||
seg = ctk.CTkSegmentedButton(top, values=['Dark', 'Light', 'System'],
|
seg = ctk.CTkSegmentedButton(top, values=['Dark', 'Light', 'System'],
|
||||||
command=self._set_appearance)
|
command=self._set_appearance)
|
||||||
seg.set('System')
|
seg.set('System')
|
||||||
@@ -152,15 +159,9 @@ class App(ctk.CTk):
|
|||||||
|
|
||||||
body = ctk.CTkFrame(self, fg_color='transparent')
|
body = ctk.CTkFrame(self, fg_color='transparent')
|
||||||
body.pack(fill='both', expand=True, padx=12, pady=(4, 12))
|
body.pack(fill='both', expand=True, padx=12, pady=(4, 12))
|
||||||
body.grid_columnconfigure(0, weight=1, uniform='col')
|
body.grid_rowconfigure(2, weight=1) # 运行日志吃掉剩余高度
|
||||||
body.grid_columnconfigure(1, weight=1, uniform='col')
|
body.grid_columnconfigure(0, weight=1)
|
||||||
body.grid_rowconfigure(0, weight=1)
|
self._build_main(body)
|
||||||
left = ctk.CTkFrame(body, fg_color='transparent')
|
|
||||||
left.grid(row=0, column=0, sticky='nsew')
|
|
||||||
right = ctk.CTkFrame(body, fg_color='transparent')
|
|
||||||
right.grid(row=0, column=1, sticky='nsew', padx=(10, 0))
|
|
||||||
self._build_left(left)
|
|
||||||
self._build_right(right)
|
|
||||||
|
|
||||||
status = ctk.CTkFrame(self, fg_color='transparent')
|
status = ctk.CTkFrame(self, fg_color='transparent')
|
||||||
status.pack(fill='x', padx=14, pady=(0, 8))
|
status.pack(fill='x', padx=14, pady=(0, 8))
|
||||||
@@ -169,9 +170,9 @@ class App(ctk.CTk):
|
|||||||
ctk.CTkLabel(status, text=f'v{APP_VERSION}',
|
ctk.CTkLabel(status, text=f'v{APP_VERSION}',
|
||||||
text_color='gray60').pack(side='right')
|
text_color='gray60').pack(side='right')
|
||||||
|
|
||||||
# -- 左列: 探针与工具链 / 芯片与固件 / 操作 --
|
# -- 主列: 探针与工具链 / 芯片与固件 / 运行日志 / 操作 --
|
||||||
def _build_left(self, col):
|
def _build_main(self, col):
|
||||||
col.grid_rowconfigure(2, weight=1) # 操作卡片吸底
|
col.grid_rowconfigure(2, weight=1) # 日志卡片吃掉剩余高度
|
||||||
col.grid_columnconfigure(0, weight=1)
|
col.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
self.v_toolchain = tk.StringVar(
|
self.v_toolchain = tk.StringVar(
|
||||||
@@ -218,8 +219,15 @@ class App(ctk.CTk):
|
|||||||
checkbox_width=18, checkbox_height=18).pack(
|
checkbox_width=18, checkbox_height=18).pack(
|
||||||
side='left', padx=(0, 14))
|
side='left', padx=(0, 14))
|
||||||
|
|
||||||
|
card, c = self._card(col, '运行日志 — OpenOCD 输出与固件工具结果',
|
||||||
|
button='清空', command=self._clear_log)
|
||||||
|
card.grid(row=2, column=0, sticky='nsew', pady=(10, 0))
|
||||||
|
self.txt_log = ctk.CTkTextbox(c, font=MONO)
|
||||||
|
self.txt_log.pack(fill='both', expand=True)
|
||||||
|
self.txt_log.configure(state='disabled')
|
||||||
|
|
||||||
card, c = self._card(col, '操作')
|
card, c = self._card(col, '操作')
|
||||||
card.grid(row=2, column=0, sticky='sew', pady=(10, 0))
|
card.grid(row=3, column=0, sticky='ew', pady=(10, 0))
|
||||||
grid = ctk.CTkFrame(c, fg_color='transparent')
|
grid = ctk.CTkFrame(c, fg_color='transparent')
|
||||||
grid.pack(fill='x')
|
grid.pack(fill='x')
|
||||||
grid.grid_columnconfigure((0, 1, 2), weight=1, uniform='op')
|
grid.grid_columnconfigure((0, 1, 2), weight=1, uniform='op')
|
||||||
@@ -235,18 +243,30 @@ class App(ctk.CTk):
|
|||||||
command=self._on_stop, state='disabled')
|
command=self._on_stop, state='disabled')
|
||||||
self.btn_stop.grid(row=1, column=2, sticky='ew', padx=3, pady=3)
|
self.btn_stop.grid(row=1, column=2, sticky='ew', padx=3, pady=3)
|
||||||
|
|
||||||
# -- 右列: 补齐 / 拼接 / 日志 --
|
# ============================================================
|
||||||
def _build_right(self, col):
|
# 固件工具弹窗: 补齐 / 拼接
|
||||||
col.grid_rowconfigure(2, weight=1) # 日志卡片吃掉剩余高度
|
# ============================================================
|
||||||
col.grid_columnconfigure(0, weight=1)
|
def _center_dialog(self, dlg, w, h):
|
||||||
|
"""弹窗叠在主窗口中上部"""
|
||||||
|
dlg.update_idletasks()
|
||||||
|
x = self.winfo_rootx() + max(0, (self.winfo_width() - w) // 2)
|
||||||
|
y = self.winfo_rooty() + max(0, (self.winfo_height() - h) // 4)
|
||||||
|
dlg.geometry(f'{w}x{h}+{x}+{y}')
|
||||||
|
|
||||||
card, c = self._card(col, '补齐 (Pad) — 尾部补 0xFF 到页边界, '
|
def _open_pad_dialog(self):
|
||||||
'适配 Bootloader Y-Modem 整包接收')
|
dlg = ctk.CTkToplevel(self)
|
||||||
card.grid(row=0, column=0, sticky='ew')
|
dlg.title('固件工具 — 补齐 (Pad)')
|
||||||
self._field(c, '输入 BIN', self.v['pad_in'], self._browse_pad_in)
|
dlg.resizable(False, False)
|
||||||
self._field(c, '输出 BIN', self.v['pad_out'], self._browse_pad_out)
|
dlg.transient(self)
|
||||||
row = ctk.CTkFrame(c, fg_color='transparent')
|
box = ctk.CTkFrame(dlg, fg_color='transparent')
|
||||||
row.pack(fill='x', pady=(6, 0))
|
box.pack(fill='both', expand=True, padx=18, pady=(14, 12))
|
||||||
|
ctk.CTkLabel(box, text='补齐 (Pad)', font=BOLD, anchor='w').pack(fill='x')
|
||||||
|
ctk.CTkLabel(box, text='尾部补 0xFF 到页边界, 适配 Bootloader Y-Modem 整包接收',
|
||||||
|
text_color='gray60', anchor='w').pack(fill='x', pady=(0, 6))
|
||||||
|
self._field(box, '输入 BIN', self.v['pad_in'], self._browse_pad_in)
|
||||||
|
self._field(box, '输出 BIN', self.v['pad_out'], self._browse_pad_out)
|
||||||
|
row = ctk.CTkFrame(box, fg_color='transparent')
|
||||||
|
row.pack(fill='x', pady=(8, 0))
|
||||||
ctk.CTkLabel(row, text='对齐:', width=40, anchor='e').pack(side='left')
|
ctk.CTkLabel(row, text='对齐:', width=40, anchor='e').pack(side='left')
|
||||||
ctk.CTkComboBox(row, width=86, state='readonly',
|
ctk.CTkComboBox(row, width=86, state='readonly',
|
||||||
values=['1024', '2048', '4096'],
|
values=['1024', '2048', '4096'],
|
||||||
@@ -256,17 +276,27 @@ class App(ctk.CTk):
|
|||||||
).pack(side='left', padx=(4, 12))
|
).pack(side='left', padx=(4, 12))
|
||||||
ctk.CTkButton(row, text='执行补齐', width=96,
|
ctk.CTkButton(row, text='执行补齐', width=96,
|
||||||
command=self._on_pad).pack(side='left')
|
command=self._on_pad).pack(side='left')
|
||||||
self.v_pad_result = tk.StringVar(value='')
|
ctk.CTkLabel(box, textvariable=self.v_pad_result, text_color=OK_BLUE,
|
||||||
ctk.CTkLabel(c, textvariable=self.v_pad_result, text_color=OK_BLUE,
|
justify='left', anchor='w').pack(fill='x', pady=(8, 0))
|
||||||
justify='left', anchor='w').pack(fill='x', pady=(4, 0))
|
ctk.CTkLabel(box, text='Bootloader 的 Y-Modem 按 1K 整包接收, OTA 升级包发送前先补齐',
|
||||||
|
text_color='gray50', anchor='w').pack(fill='x', pady=(2, 0))
|
||||||
|
self._center_dialog(dlg, 680, 300)
|
||||||
|
|
||||||
card, c = self._card(col, '拼接 (Merge) — Bootloader + APP + 有效标志 → 整片镜像')
|
def _open_merge_dialog(self):
|
||||||
card.grid(row=1, column=0, sticky='ew', pady=(10, 0))
|
dlg = ctk.CTkToplevel(self)
|
||||||
self._field(c, 'Bootloader', self.v['bootloader'], self._browse_bl)
|
dlg.title('固件工具 — 拼接 (Merge)')
|
||||||
self._field(c, 'Application', self.v['app'], self._browse_app)
|
dlg.resizable(False, False)
|
||||||
self._field(c, '输出文件', self.v['merge_out'], self._browse_merge_out)
|
dlg.transient(self)
|
||||||
row = ctk.CTkFrame(c, fg_color='transparent')
|
box = ctk.CTkFrame(dlg, fg_color='transparent')
|
||||||
row.pack(fill='x', pady=(6, 0))
|
box.pack(fill='both', expand=True, padx=18, pady=(14, 12))
|
||||||
|
ctk.CTkLabel(box, text='拼接 (Merge)', font=BOLD, anchor='w').pack(fill='x')
|
||||||
|
ctk.CTkLabel(box, text='Bootloader + APP + 有效标志 → 整片 Flash 镜像',
|
||||||
|
text_color='gray60', anchor='w').pack(fill='x', pady=(0, 6))
|
||||||
|
self._field(box, 'Bootloader', self.v['bootloader'], self._browse_bl)
|
||||||
|
self._field(box, 'Application', self.v['app'], self._browse_app)
|
||||||
|
self._field(box, '输出文件', self.v['merge_out'], self._browse_merge_out)
|
||||||
|
row = ctk.CTkFrame(box, fg_color='transparent')
|
||||||
|
row.pack(fill='x', pady=(8, 0))
|
||||||
for label, key, w in (('APP 地址', 'app_addr', 104),
|
for label, key, w in (('APP 地址', 'app_addr', 104),
|
||||||
('标志地址', 'flag_addr', 104),
|
('标志地址', 'flag_addr', 104),
|
||||||
('Flash', 'merge_flash', 84),
|
('Flash', 'merge_flash', 84),
|
||||||
@@ -274,22 +304,14 @@ class App(ctk.CTk):
|
|||||||
ctk.CTkLabel(row, text=label + ':', anchor='e').pack(side='left')
|
ctk.CTkLabel(row, text=label + ':', anchor='e').pack(side='left')
|
||||||
ctk.CTkEntry(row, width=w, textvariable=self.v[key]
|
ctk.CTkEntry(row, width=w, textvariable=self.v[key]
|
||||||
).pack(side='left', padx=(4, 10))
|
).pack(side='left', padx=(4, 10))
|
||||||
ctk.CTkButton(row, text='执行拼接', width=96,
|
ctk.CTkButton(box, text='执行拼接', width=96,
|
||||||
command=self._on_merge).pack(side='left')
|
command=self._on_merge).pack(anchor='w', pady=(8, 0))
|
||||||
self.v_merge_result = tk.StringVar(value='')
|
ctk.CTkLabel(box, textvariable=self.v_merge_result, text_color=OK_BLUE,
|
||||||
ctk.CTkLabel(c, textvariable=self.v_merge_result, text_color=OK_BLUE,
|
justify='left', anchor='w').pack(fill='x', pady=(2, 0))
|
||||||
justify='left', anchor='w').pack(fill='x', pady=(4, 0))
|
ctk.CTkLabel(box, text='典型布局: 0x08000000 Bootloader 8KB | 0x08002000 APP | '
|
||||||
ctk.CTkLabel(c, text='典型布局: 0x08000000 Bootloader 8KB | 0x08002000 APP | '
|
'0x0800FFFC 有效标志 0xEEEEEEEE | Flash 64KB',
|
||||||
'0x0800FFFC 有效标志 0xEEEEEEEE | Flash 64KB',
|
text_color='gray50', anchor='w').pack(fill='x', pady=(2, 0))
|
||||||
text_color='gray50', justify='left', anchor='w',
|
self._center_dialog(dlg, 720, 420)
|
||||||
).pack(fill='x', pady=(2, 0))
|
|
||||||
|
|
||||||
card, c = self._card(col, '运行日志 — OpenOCD 输出与固件工具结果',
|
|
||||||
button='清空', command=self._clear_log)
|
|
||||||
card.grid(row=2, column=0, sticky='nsew', pady=(10, 0))
|
|
||||||
self.txt_log = ctk.CTkTextbox(c, font=MONO)
|
|
||||||
self.txt_log.pack(fill='both', expand=True)
|
|
||||||
self.txt_log.configure(state='disabled')
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 输入行辅助
|
# 输入行辅助
|
||||||
@@ -515,6 +537,7 @@ class App(ctk.CTk):
|
|||||||
else:
|
else:
|
||||||
self._log('[失败] %s (退出码 %d)' % (title, code))
|
self._log('[失败] %s (退出码 %d)' % (title, code))
|
||||||
self._set_status('%s 失败' % title)
|
self._set_status('%s 失败' % title)
|
||||||
|
self.proc = None # 释放占用, 允许下一次操作
|
||||||
self._busy(False)
|
self._busy(False)
|
||||||
self._save()
|
self._save()
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
|
|||||||
@@ -58,11 +58,13 @@ def git_remote_info():
|
|||||||
class Api:
|
class Api:
|
||||||
def __init__(self, base, owner, repo, token, insecure):
|
def __init__(self, base, owner, repo, token, insecure):
|
||||||
self.api = f'{base}/api/v1/repos/{owner}/{repo}'
|
self.api = f'{base}/api/v1/repos/{owner}/{repo}'
|
||||||
|
self.token = token
|
||||||
self.headers = {'Authorization': f'token {token}'}
|
self.headers = {'Authorization': f'token {token}'}
|
||||||
self.ctx = ssl._create_unverified_context() if insecure else None
|
self.ctx = ssl._create_unverified_context() if insecure else None
|
||||||
|
|
||||||
def request(self, method, path, data=None, headers=None, retries=4):
|
def request(self, method, path, data=None, headers=None, retries=4):
|
||||||
"""发请求; 实例 TLS 间歇性抽风(SSL UNEXPECTED_EOF), 连接级错误自动重试"""
|
"""发请求; 实例 TLS 间歇性抽风(SSL UNEXPECTED_EOF), 连接级错误自动重试,
|
||||||
|
并改用 curl -k 兜底(urllib 的握手在该实例上可能持续失败)"""
|
||||||
last = None
|
last = None
|
||||||
for attempt in range(retries):
|
for attempt in range(retries):
|
||||||
req = urllib.request.Request(self.api + path, data=data, method=method,
|
req = urllib.request.Request(self.api + path, data=data, method=method,
|
||||||
@@ -75,9 +77,39 @@ class Api:
|
|||||||
return e.code, json.loads(e.read() or b'{}')
|
return e.code, json.loads(e.read() or b'{}')
|
||||||
except (urllib.error.URLError, ssl.SSLError, OSError) as e:
|
except (urllib.error.URLError, ssl.SSLError, OSError) as e:
|
||||||
last = e
|
last = e
|
||||||
|
try:
|
||||||
|
status, body = self._curl(method, path, data, headers)
|
||||||
|
if status > 0:
|
||||||
|
return status, body
|
||||||
|
except Exception as ce:
|
||||||
|
last = ce
|
||||||
time.sleep(1.0 + attempt)
|
time.sleep(1.0 + attempt)
|
||||||
raise RuntimeError(f'Gitea 请求连续 {retries} 次失败: {last}')
|
raise RuntimeError(f'Gitea 请求连续 {retries} 次失败: {last}')
|
||||||
|
|
||||||
|
def _curl(self, method, path, data=None, headers=None):
|
||||||
|
"""curl 兜底: 返回 (status, json/None); 解析失败返回 (0, None) 交回上层重试"""
|
||||||
|
cmd = ['curl', '-k', '-s', '-X', method, '-w', '\n%{http_code}']
|
||||||
|
if data is not None:
|
||||||
|
cmd += ['--data-binary', '@-']
|
||||||
|
for key, value in self.headers.items():
|
||||||
|
cmd += ['-H', f'{key}: {value}']
|
||||||
|
for key, value in (headers or {}).items():
|
||||||
|
cmd += ['-H', f'{key}: {value}']
|
||||||
|
cmd.append(self.api + path)
|
||||||
|
proc = subprocess.run(cmd, input=data, capture_output=True, timeout=600)
|
||||||
|
out = proc.stdout.decode('utf-8', 'replace')
|
||||||
|
body, _, code = out.rpartition('\n')
|
||||||
|
try:
|
||||||
|
status = int(code.strip())
|
||||||
|
except ValueError:
|
||||||
|
return 0, None
|
||||||
|
if not body.strip():
|
||||||
|
return status, None
|
||||||
|
try:
|
||||||
|
return status, json.loads(body)
|
||||||
|
except ValueError:
|
||||||
|
return status, {'raw': body}
|
||||||
|
|
||||||
def get_json(self, path):
|
def get_json(self, path):
|
||||||
status, data = self.request('GET', path)
|
status, data = self.request('GET', path)
|
||||||
return data if status == 200 else None
|
return data if status == 200 else None
|
||||||
|
|||||||
Reference in New Issue
Block a user