hdpip 0.0.5.post1__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,28 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ [... full GPL-3.0 license text ...]
14
+
15
+ Copyright (C) 2025 寒冬利刃
16
+
17
+ This program is free software: you can redistribute it and/or modify
18
+ it under the terms of the GNU General Public License as published by
19
+ the Free Software Foundation, either version 3 of the License, or
20
+ (at your option) any later version.
21
+
22
+ This program is distributed in the hope that it will be useful,
23
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ GNU General Public License for more details.
26
+
27
+ You should have received a copy of the GNU General Public License
28
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,29 @@
1
+ # 寒冬pip HDpip
2
+
3
+ ## 一个基于maliang的**pip GUI**
4
+
5
+ ## **A pip GUI** based on maliang
6
+
7
+ ## 安装
8
+
9
+ ## Install
10
+
11
+ ```shell
12
+ pip install HDpip
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ ## Use
18
+
19
+ ```shell
20
+ HDpip
21
+ ```
22
+
23
+ 或者
24
+
25
+ or
26
+
27
+ ```shell
28
+ hdpip
29
+ ```
@@ -0,0 +1,11 @@
1
+ """
2
+ - HDpip: A pip GUI based on maliang
3
+ - Copyright © 2025 寒冬利刃.
4
+ - License: GPL-3
5
+ """
6
+
7
+ version = "0.0.5.post1"
8
+ author = "寒冬利刃"
9
+ copyright = "Copyright © 2025 寒冬利刃."
10
+
11
+ from . import core, gui
@@ -0,0 +1,12 @@
1
+ """
2
+ - HDpip: A pip GUI based on maliang
3
+ - Copyright © 2025 寒冬利刃.
4
+ - License: GPL-3
5
+
6
+ 本模块是本包核心。
7
+ """
8
+
9
+ from . import data
10
+ from . import pip_api
11
+ from . import system
12
+ from . import util
@@ -0,0 +1,437 @@
1
+ """
2
+ - HDpip: A pip GUI based on maliang
3
+ - Copyright © 2025 寒冬利刃.
4
+ - License: GPL-3
5
+
6
+ Data 数据系统。
7
+ """
8
+
9
+ import copy
10
+ import json
11
+ import pathlib
12
+ import shutil
13
+ import traceback
14
+ from typing import *
15
+ from typing_extensions import overload
16
+
17
+ try:
18
+ from . import system
19
+ except ImportError:
20
+ import system
21
+
22
+ class Data():
23
+ """
24
+ 接受一个`.json`文件(使用`open`函数打开文件,并使用`load`函数加载。),生成一个数据类。
25
+
26
+ 但是,您应该如此获得数据:
27
+
28
+ ```
29
+ d = Data()
30
+ d.open("data.json")
31
+ d.load() #这是必须的,因为在open后不会自动运行load函数。
32
+ print(d["a"][0])
33
+ ```
34
+
35
+ 您可以便捷地使用`==`运算符判断相等或使用`+`运算符合并数据,还支持事件管理,可以注册回调函数来监听事件。
36
+
37
+ 实际上,由于我写了点神奇的代码,以下写法也可行:
38
+
39
+ ```
40
+ d["a", 0]
41
+ ```
42
+
43
+ 等同于:
44
+
45
+ ```
46
+ d["a"][0]
47
+ ```
48
+ """
49
+
50
+ def __init__(self):
51
+ self.event_list = []
52
+ self.file = {}
53
+ self.data = None
54
+
55
+ def open(self, file: str | pathlib.Path, encoding: str = "utf-8") -> dict[str, str]:
56
+ """
57
+ 绑定一个`.json`文件,且返回绑定的文件字典。
58
+
59
+ :param self: `Data`类
60
+ :param file: 一个指向`.json`文件的路径,如`data.json`
61
+ :type file: str | pathlib.Path
62
+ :param encoding: 编码字符串,如`utf-8`
63
+ :type encoding: str
64
+ :return: 文件字典
65
+ :rtype: dict[str, str]
66
+ """
67
+
68
+ file = str(pathlib.Path(file).resolve())
69
+ self.file = {"file": file, "encoding": encoding}
70
+ self.notifyEvent("open", self.file)
71
+ return self.file
72
+
73
+ def load(self) -> list | dict:
74
+ """
75
+ 加载`.json`文件的数据至数据类并返回。
76
+
77
+ :param self: `Data`类
78
+ :return: 数据
79
+ :rtype: list | dict
80
+ """
81
+
82
+ with open(**self.file, mode = "r") as f:
83
+ self.data = json.load(f)
84
+ self.notifyEvent("load", {"data": self.data})
85
+ return self.data
86
+
87
+ def save(self) -> list | dict:
88
+ """
89
+ 保存`.json`文件的数据至文件并返回。
90
+
91
+ :param self: `Data`类
92
+ :return: 数据
93
+ :rtype: list | dict
94
+ """
95
+
96
+ with open(**self.file, mode = "w") as f:
97
+ json.dump(self.data, f)
98
+ self.notifyEvent("save", {"data": self.data})
99
+ return self.data
100
+
101
+ def __iter__(self):
102
+ return self.data.__iter__()
103
+
104
+ def __next__(self):
105
+ return self.data.__next__()
106
+
107
+ @overload
108
+ def __getitem__(self, key: str | int): ...
109
+ @overload
110
+ def __getitem__(self, key: tuple | list): ...
111
+ def __getitem__(self, key: str | int | tuple | list):
112
+ if isinstance(key, str | int):
113
+ result = value = self.data.__getitem__(key)
114
+ elif isinstance(key, tuple | list):
115
+ result = self.data
116
+ for i in key:
117
+ result = result.__getitem__(i)
118
+ value = result
119
+ self.notifyEvent("__getitem__", {"key": key, "value": value})
120
+ return result
121
+
122
+ @overload
123
+ def __setitem__(self, key: str | int, value: Any): ...
124
+ @overload
125
+ def __setitem__(self, key: tuple | list, value: Any): ...
126
+ def __setitem__(self, key: str | int | tuple | list, value: Any):
127
+ if isinstance(key, str | int):
128
+ old_value = self.data.__getitem__(key) or None
129
+ result = self.data.__setitem__(key, value)
130
+ elif isinstance(key, tuple | list):
131
+ result = self.data
132
+ for i in range(0, len(key)):
133
+ if i == len(key) - 1:
134
+ old_value = result.__getitem__(key[i]) or None
135
+ result.__setitem__(key[i], value)
136
+ else:
137
+ result = result.__getitem__(key[i])
138
+ self.notifyEvent("__setitem__", {"key": key, "value": value, "old_value": old_value})
139
+ return result
140
+
141
+ @overload
142
+ def __delitem__(self, key: str | int): ...
143
+ @overload
144
+ def __delitem__(self, key: tuple | list): ...
145
+ def __delitem__(self, key: str | int | tuple | list):
146
+ if isinstance(key, str | int):
147
+ old_value = self.data[key] or None
148
+ result = old_value = self.data.__delitem__(key)
149
+ elif isinstance(key, tuple | list):
150
+ result = self.data
151
+ for i in range(0, len(key)):
152
+ if i == len(key) - 1:
153
+ old_value = result.__getitem__(key[i]) or None
154
+ result.__delitem__(key[i])
155
+ else:
156
+ result = result.__getitem__(key[i])
157
+ self.notifyEvent("__delitem__", {"key": key, "old_value": old_value})
158
+ return result
159
+
160
+ def __eq__(self, value):
161
+ return self.file == value.file and self.data == self.data
162
+
163
+ def __add__(self, value: list | dict):
164
+ result = copy.deepcopy(self)
165
+ if isinstance(value, Data):
166
+ value = value.data
167
+ if isinstance(self.data, list) and isinstance(value, list):
168
+ result.data = self.data + value
169
+ elif isinstance(self.data, dict) and isinstance(value, dict):
170
+ result.data.update(value)
171
+ else:
172
+ raise TypeError(f"本Data类存取的数据为{type(self.data).__name__}类型,但您尝试合并一个{type(value).__name__}类型!")
173
+ self.notifyEvent("__add__", {"value": value, "result": result})
174
+ return result
175
+
176
+ def __iadd__(self, value: list | dict):
177
+ if isinstance(value, Data):
178
+ value = value.data
179
+ if isinstance(self.data, list) and isinstance(value, list):
180
+ self.data += value
181
+ elif isinstance(self.data, dict) and isinstance(value, dict):
182
+ self.data.update(value)
183
+ else:
184
+ raise TypeError(f"本Data类存取的数据为{type(self.data).__name__}类型,但您尝试合并一个{type(value).__name__}类型!")
185
+ self.notifyEvent("__iadd__", {"value": value})
186
+ return self
187
+
188
+ def registerEvent(self, callback: Callable[[str, dict[str, Any]], Any]):
189
+ """
190
+ 注册事件回调函数。
191
+
192
+ :param callback: 回调函数,接收两个参数:(`event_type`, `event_data`)
193
+ :type callback: Callable[[str, dict[str, Any]], Any]
194
+
195
+ **event_type**
196
+
197
+ `open`, `load`, `save`, `__getitem__`, `__setitem__`, `__delitem__`, `__add__`
198
+
199
+ **event_data**
200
+
201
+ 根据不同的事件类型,event_data包含不同的数据:
202
+
203
+ - `open`:
204
+ ```
205
+ {
206
+ "file": str, # 文件路径
207
+ "encoding": str # 编码格式
208
+ }
209
+ ```
210
+
211
+ - `load`:
212
+ ```
213
+ {
214
+ "data": dict | list # 加载的数据
215
+ }
216
+ ```
217
+
218
+ - `save`:
219
+ ```
220
+ {
221
+ "data": dict | list # 保存的数据
222
+ }
223
+ ```
224
+
225
+ - `__getitem__`:
226
+ ```
227
+ {
228
+ "key": str | int, # 访问的键
229
+ "value": Any # 获取的值
230
+ }
231
+ ```
232
+
233
+ - `__setitem__`:
234
+ ```
235
+ {
236
+ "key": str | int, # 设置的键
237
+ "value": Any, # 新设置的值
238
+ "old_value": Any # 原来的值(如果存在)
239
+ }
240
+ ```
241
+
242
+ - `__delitem__`:
243
+ ```
244
+ {
245
+ "key": str | int, # 删除的键
246
+ "old_value": Any # 被删除的值(如果存在)
247
+ }
248
+ ```
249
+
250
+ - `__add__`:
251
+ ```
252
+ {
253
+ "value": dict | list | Data, # 被合并的数据
254
+ "result": Data # 合并后的结果
255
+ }
256
+ ```
257
+ """
258
+
259
+ if callback not in self.event_list:
260
+ self.event_list.append(callback)
261
+
262
+ def unregisterEvent(self, callback: Callable[[str, dict[str, Any]], Any]):
263
+ """
264
+ 注销事件回调函数。
265
+
266
+ :param callback: 要注销的回调函数
267
+ :type callback: Callable[[str, dict[str, Any]], Any]
268
+ """
269
+
270
+ if callback in self.event_list:
271
+ self.event_list.remove(callback)
272
+
273
+ def notifyEvent(
274
+ self,
275
+ event_type: Literal[
276
+ "open",
277
+ "load",
278
+ "save",
279
+ "__getitem__",
280
+ "__setitem__",
281
+ "__delitem__",
282
+ "__add__"
283
+ ],
284
+ event_data: dict[str, Any]
285
+ ):
286
+ """
287
+ 通知所有事件。
288
+
289
+ :param event_type: 事件类型
290
+ :type event_type: Literal["open", "load", "save", "\\_\\_getitem\\_\\_", "\\_\\_setitem\\_\\_", "\\_\\_delitem\\_\\_", "\\_\\_add\\_\\_"]
291
+ :param event_data: 事件数据
292
+ :type event_data: dict[str, Any]
293
+ """
294
+
295
+ for observer in self.event_list[:]:
296
+ try:
297
+ observer(event_type, event_data)
298
+ except Exception as error:
299
+ traceback.print_exception(error)
300
+
301
+ class DataManager():
302
+ def importSetting(self, path: str | pathlib.Path) -> None:
303
+ """
304
+ 导入设置。
305
+
306
+ :param path: 路径
307
+ :type path: str | pathlib.Path
308
+ """
309
+
310
+ path = pathlib.Path(path).resolve()
311
+ shutil.copy(path, self.custom_setting)
312
+ self.setting.load()
313
+
314
+ def exportSetting(self, path: str | pathlib.Path) -> None:
315
+ """
316
+ 导出设置。
317
+
318
+ :param path: 路径
319
+ :type path: str | pathlib.Path
320
+ """
321
+
322
+ path = pathlib.Path(path).resolve()
323
+ shutil.copy(self.custom_setting, path)
324
+
325
+ def generateLanguageDict(self) -> dict[str, pathlib.Path]:
326
+ """
327
+ 生成语言字典。
328
+
329
+ :return: 语言字典
330
+ :rtype: dict[str: Path]
331
+ """
332
+
333
+ default_language_list = list(self.default_language_dir.iterdir())
334
+ custom_language_list = list(self.custom_language_dir.iterdir())
335
+ language_list = default_language_list + custom_language_list
336
+ language_dict = {}
337
+ for i in language_list:
338
+ language_dict[i.stem] = i
339
+ return language_dict
340
+
341
+ def getLanguage(self, language_code: str) -> None:
342
+ """
343
+ 通过语言代码获取语言数据。
344
+
345
+ :param language_code: 语言代码
346
+ :type language_code: str
347
+ """
348
+
349
+ try:
350
+ self.language.open(self.language_dict[language_code])
351
+ self.language.load()
352
+ except KeyError:
353
+ raise FileNotFoundError(f"未找到语言代码为{language_code}的语言文件!")
354
+
355
+ def importLanguage(self, path: str | pathlib.Path) -> None:
356
+ """
357
+ 导入语言。
358
+
359
+ :param path: 路径
360
+ :type path: str | pathlib.Path
361
+ """
362
+
363
+ path = pathlib.Path(path).resolve()
364
+ shutil.copy(path, self.custom_language_dir / path.name)
365
+ self.language_dict = self.generateLanguageDict()
366
+
367
+ def isInited(self) -> bool:
368
+ """
369
+ 返回是否已经初始化。
370
+
371
+ :return: 是否已经初始化
372
+ :rtype: bool
373
+ """
374
+
375
+ if not self.custom_setting.is_file():
376
+ return False
377
+ if not self.custom_language_dir.is_dir():
378
+ return False
379
+ return True
380
+
381
+ def onLanguageChange(self, event_type: str, event_data: dict):
382
+ if event_type == "__setitem__" and event_data["key"] == "language":
383
+ self.getLanguage(event_data["value"])
384
+ elif event_type == "load":
385
+ self.getLanguage(self.setting["language"])
386
+
387
+ def __init__(self):
388
+ self.default_setting = (system.getBaseDir() / "setting" / "global.json").resolve()
389
+ self.custom_setting = (system.getPythonPath().parent / "HDpip" / "setting.json").resolve()
390
+ self.language_code_dict = {
391
+ "en": "English",
392
+ "zh-CN": "简体中文",
393
+ "zh-TW": "繁體中文",
394
+ "ko": "한국어",
395
+ "fr": "Français",
396
+ "de": "Deutsch",
397
+ "es": "Español",
398
+ "ru": "Русский",
399
+ "ar": "العربية",
400
+ "hi": "हिन्दी"
401
+ }
402
+ self.default_language_dir = (system.getBaseDir() / "language").resolve()
403
+ self.custom_language_dir = (system.getPythonPath().parent / "HDpip" / "language").resolve()
404
+
405
+ def init(self, must = False):
406
+ """
407
+ 设置基本数据并初始化。
408
+
409
+ :param must: 是否强制初始化(*这将会覆盖用户数据!*)
410
+ :type must: bool
411
+ """
412
+
413
+ if must or not self.isInited():
414
+ self.custom_setting.parent.mkdir(exist_ok = True)
415
+ shutil.copy(self.default_setting, self.custom_setting)
416
+ self.custom_language_dir.mkdir(exist_ok = True)
417
+
418
+ self.setting = Data()
419
+ self.setting.open(self.custom_setting)
420
+ self.setting.load()
421
+
422
+ self.language_dict = self.generateLanguageDict()
423
+ self.language = Data()
424
+ self.getLanguage(self.setting["language"])
425
+ self.setting.registerEvent(self.onLanguageChange)
426
+
427
+ def isBelongedToHDpip(path: pathlib.Path) -> bool:
428
+ """
429
+ 判断一个路径是否属于HDpip。
430
+
431
+ :param path: 路径
432
+ :type path: pathlib.Path
433
+ :return: 结果
434
+ :rtype: bool
435
+ """
436
+
437
+ return any(path.is_relative_to(p) for p in [system.getBaseDir(), system.getPythonPath().parent / "HDpip"])