python3-openEuler 0.0.1__tar.gz → 0.0.3__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.

Potentially problematic release.


This version of python3-openEuler might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python3-openEuler
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: used to get and deal information about openEuler os
5
5
  Home-page: https://gitee.com/devops_dev/openEuler
6
6
  Author: redrose2100
@@ -0,0 +1,74 @@
1
+ from openEuler.openEuler import OpenEuler
2
+ import logging
3
+ from typing import Optional, Union
4
+
5
+ # 初始化包专属日志器(名称与包绑定,避免冲突)
6
+ logger = logging.getLogger(__name__)
7
+
8
+ # 核心:设置默认日志级别为 INFO(全局生效)
9
+ DEFAULT_LOG_LEVEL = logging.INFO
10
+ logger.setLevel(DEFAULT_LOG_LEVEL)
11
+
12
+ # 禁止日志向上传播(避免干扰用户根日志器)
13
+ logger.propagate = False
14
+
15
+ # 定义默认日志格式(包含关键上下文信息)
16
+ DEFAULT_FORMATTER = logging.Formatter(
17
+ fmt="[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s",
18
+ datefmt="%Y-%m-%d %H:%M:%S"
19
+ )
20
+
21
+ def _add_default_handler(level: int = DEFAULT_LOG_LEVEL):
22
+ """
23
+ 为日志器添加默认控制台Handler(仅在无Handler时添加,避免重复输出)
24
+
25
+ Args:
26
+ level: Handler的日志级别(默认与logger级别一致)
27
+ """
28
+ if not logger.handlers:
29
+ # 输出到stdout(而非stderr,避免与错误信息混淆)
30
+ console_handler = logging.StreamHandler()
31
+ console_handler.setLevel(level) # Handler级别可独立控制
32
+ console_handler.setFormatter(DEFAULT_FORMATTER)
33
+ logger.addHandler(console_handler)
34
+
35
+
36
+ def get_logger(
37
+ custom_formatter: Optional[logging.Formatter] = None,
38
+ level: Optional[Union[int, str]] = None
39
+ ) -> logging.Logger:
40
+ """
41
+ 获取包的日志器,支持自定义格式和级别
42
+
43
+ Args:
44
+ custom_formatter: 自定义日志格式(如不指定则使用默认格式)
45
+ level: 日志级别(可选,如 logging.DEBUG 或 "DEBUG",优先级高于默认)
46
+
47
+ Returns:
48
+ 配置好的日志器实例
49
+ """
50
+ # 确保默认Handler存在(首次调用时初始化)
51
+ _add_default_handler()
52
+
53
+ # 动态调整日志级别(支持字符串级别,如 "DEBUG",更友好)
54
+ if level is not None:
55
+ # 若传入字符串级别(如 "DEBUG"),转换为logging常量
56
+ if isinstance(level, str):
57
+ level = logging.getLevelName(level.upper())
58
+ logger.setLevel(level)
59
+ # 同步调整所有Handler的级别(避免Handler级别低于logger导致的日志丢失)
60
+ for handler in logger.handlers:
61
+ handler.setLevel(level)
62
+
63
+ # 应用用户自定义格式
64
+ if custom_formatter:
65
+ for handler in logger.handlers:
66
+ handler.setFormatter(custom_formatter)
67
+
68
+ return logger
69
+
70
+
71
+ # 初始化时触发一次默认Handler添加(确保用户首次使用日志即有输出)
72
+ _add_default_handler()
73
+
74
+ __all__ = ["OpenEuler", "get_logger"]
@@ -0,0 +1,270 @@
1
+ import re
2
+ import logging
3
+ import requests
4
+ from bs4 import BeautifulSoup
5
+ import traceback
6
+ import functools
7
+ import inspect
8
+ from typing import Callable, Any
9
+ from typing import List, Dict, Any, Optional
10
+
11
+ import requests.exceptions
12
+ from typing import Generator, Optional,Tuple
13
+
14
+ log=logging.getLogger(__name__)
15
+
16
+ def enter_and_leave_function(func: Callable) -> Callable:
17
+ """
18
+ 函数调用日志装饰器:
19
+ 1. 记录函数入参、调用位置
20
+ 2. 正常执行时记录返回值
21
+ 3. 异常时记录完整堆栈(含函数内具体报错行数)
22
+ """
23
+
24
+ @functools.wraps(func) # 保留原函数元信息(如 __name__、__doc__)
25
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
26
+ # 获取函数定义的文件路径和行号(基础位置信息)
27
+ func_def_file = inspect.getsourcefile(func) or "unknown_file"
28
+ func_def_file = func_def_file.split("/")[-1]
29
+ func_def_line = inspect.getsourcelines(func)[1] if func_def_file != "unknown_file" else "unknown_line"
30
+ log.info(
31
+ f"[{func_def_file}: {func_def_line}]"
32
+ f"[{func.__name__}()]"
33
+ f"| args={args}, kwargs={kwargs}"
34
+ )
35
+
36
+ try:
37
+ result = func(*args, **kwargs)
38
+ log.info(
39
+ f"[{func_def_file}: {func_def_line}]"
40
+ f" finish run function {func.__name__}(), return value is: {result} "
41
+ )
42
+ return result
43
+
44
+ except Exception as e:
45
+ error_traceback = traceback.format_exc()
46
+
47
+ log.error(
48
+ f"[{func_def_file}: {func_def_line}]"
49
+ f"failed to run function {func.__name__}() :Failed. "
50
+ f"| error_type:{type(e).__name__} "
51
+ f"| error_message:{str(e)} "
52
+ f"| full_stack_trace:\n{error_traceback}",
53
+ exc_info=False # 已手动捕获堆栈,避免 logging 重复打印
54
+ )
55
+ raise # 重新抛出异常,不中断原异常链路
56
+
57
+ return wrapper
58
+
59
+ class Gitee():
60
+ def __init__(self):
61
+ self.__base_url= "https://gitee.com/api/v5"
62
+ self.__access_token="aa6cb32539129acf5605793f91a1588c"
63
+
64
+ def get_branches_list_by_repo(self,repo_name,owner_name):
65
+ """
66
+ 获取仓库的所有分支
67
+ :param repo_name: 仓库名称
68
+ :param owner_name: 仓库所属空间地址(企业、组织或个人的地址
69
+ :return:
70
+ """
71
+ url = f"{self.__base_url}/repos/{owner_name}/{repo_name}/branches"
72
+ page=1
73
+ parameters={
74
+ "access_token":self.__access_token,
75
+ "repo":repo_name,
76
+ "owner":owner_name,
77
+ "sort":"name",
78
+ "direction":"asc",
79
+ "page":page,
80
+ "per_page":10
81
+ }
82
+ headers={
83
+ "Content-Type":"application/json",
84
+ "Accept":"application/json",
85
+ "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"
86
+ }
87
+ branches=[]
88
+ while True:
89
+ response=requests.get(url,params=parameters,headers=headers)
90
+ if response.status_code==200:
91
+ data=response.json()
92
+ for branch in data:
93
+ branches.append(branch["name"])
94
+ page+=1
95
+ parameters["page"]=page
96
+ if len(data)==0:
97
+ return branches
98
+ else:
99
+ log.error(f"request url is {url}, parameters is {parameters},headers is {headers} failed, response status code is {response.status_code}")
100
+ return branches
101
+
102
+ def get_repo_name_and_repo_html_url_by_org(self,org_name):
103
+ log.info(f"begin to get openEuler repo names and repo html urls by org {org_name}...")
104
+ url = f"{self.__base_url}/orgs/{org_name}/repos"
105
+ page=1
106
+ parameters={
107
+ "access_token":"aa6cb32539129acf5605793f91a1588c",
108
+ "org":org_name,
109
+ "page":page,
110
+ "per_page":10,
111
+ "type":"all"
112
+ }
113
+ headers={
114
+ "Content-Type":"application/json",
115
+ "Accept":"application/json",
116
+ "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"
117
+ }
118
+ page=1
119
+ log.info(f"begin to request url is {url}, parameters is {parameters},headers is {headers}...")
120
+ while True:
121
+ response=requests.get(url,params=parameters,headers=headers)
122
+ if response.status_code==200:
123
+ data=response.json()
124
+ for repo in data:
125
+ yield repo["name"],repo["html_url"]
126
+ page+=1
127
+ parameters["page"]=page
128
+ if len(data)==0:
129
+ break
130
+ else:
131
+ log.error(f"request url is {url}, parameters is {parameters},headers is {headers} failed, response status code is {response.status_code}")
132
+ break
133
+
134
+ class OpenEuler():
135
+ def __init__(self):
136
+ pass
137
+
138
+ def get_openEuler_repo_names_and_urls(
139
+ self,
140
+ os_version: str
141
+ ) -> Generator[Tuple[str, str], None, None]:
142
+ """
143
+ 从 Gitee 的 src-openEuler 组织中筛选出包含指定 openEuler 版本分支的仓库信息。
144
+
145
+ 函数通过调用 Gitee 相关接口,遍历 src-openEuler 组织下的所有仓库,
146
+ 检查仓库是否存在与目标 openEuler 版本匹配的分支,若存在则返回该仓库的名称和 HTML 地址。
147
+
148
+ Args:
149
+ os_version: 目标 openEuler 版本号(如 "24.03-LTS-SP2"),用于匹配仓库分支
150
+
151
+ Yields:
152
+ Generator[Tuple[str, str], None, None]:
153
+ 迭代返回符合条件的仓库信息元组:
154
+ - 第一个元素:仓库名称(如 "kernel")
155
+ - 第二个元素:仓库的 HTML 访问地址(如 "https://gitee.com/src-openEuler/kernel")
156
+
157
+ Notes:
158
+ 依赖 Gitee 类的以下方法:
159
+ - get_repo_name_and_repo_html_url_by_org(org_name: str): 用于获取指定组织下所有仓库的名称和 HTML 地址
160
+ - get_branches_list_by_repo(repo_name: str, org_name: str): 用于获取指定仓库的所有分支名称列表
161
+ """
162
+ # 初始化 Gitee 接口操作实例
163
+ log.info("正在初始化 Gitee 接口操作实例...")
164
+ gitee = Gitee()
165
+
166
+ # 遍历 src-openEuler 组织下的所有仓库(名称 + HTML 地址)
167
+ for repo_name, repo_url in gitee.get_repo_name_and_repo_html_url_by_org("src-openEuler"):
168
+ log.info(f"正在检查仓库: {repo_name},地址: {repo_url}")
169
+
170
+ # 获取当前仓库的所有分支列表
171
+ branches = gitee.get_branches_list_by_repo(repo_name, "src-openEuler")
172
+ # 处理无分支的异常情况
173
+ if not branches:
174
+ log.warning(f"仓库 {repo_name}({repo_url})未发现任何分支,已跳过")
175
+ continue
176
+
177
+ # 检查目标版本分支是否存在,存在则返回该仓库信息
178
+ branch = f"openEuler-{os_version}"
179
+ if branch in branches:
180
+ log.info(f"仓库 {repo_name}({repo_url})已找到目标版本分支 {branch}")
181
+ yield repo_name, repo_url
182
+
183
+ def get_openEuler_everything_pkgs(
184
+ self, os_version: str, os_arch: str
185
+ ) -> Generator[str, None, None]:
186
+ """
187
+ 从 openEuler everything 源页面迭代 以迭代方式返回指定版本、架构的所有 RPM 包完整名称。
188
+
189
+ Args:
190
+ os_version: openEuler 版本号(如 "24.03-LTS-SP2")
191
+ os_arch: 系统架构(如 "x86_64", "aarch64")
192
+
193
+ Yields:
194
+ str: RPM 包完整名称(如 "zvbi-devel-0.2.44-1.oe2403sp2.x86_64.rpm")
195
+
196
+ Raises:
197
+ RuntimeError: 网络请求失败(如超时、404、500 等)
198
+ ValueError: 页面解析失败(未找到任何 .rpm 包)
199
+ """
200
+ base_url_template = "https://dl-cdn.openeuler.openatom.cn/openEuler-{os_version}/everything/{os_arch}/Packages/"
201
+ timeout = 15
202
+ headers = {
203
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
204
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
205
+ }
206
+
207
+ target_url = base_url_template.format(os_version=os_version, os_arch=os_arch)
208
+
209
+ try:
210
+ with requests.Session() as session:
211
+ session.mount('https://', requests.adapters.HTTPAdapter(
212
+ pool_connections=10,
213
+ pool_maxsize=10,
214
+ max_retries=3
215
+ ))
216
+ response = session.get(
217
+ url=target_url,
218
+ headers=headers,
219
+ timeout=timeout,
220
+ allow_redirects=True
221
+ )
222
+ response.raise_for_status()
223
+ html_content = response.text
224
+
225
+ except requests.exceptions.RequestException as e:
226
+ raise RuntimeError(f"获取页面失败!URL: {target_url}, 错误: {str(e)}") from e
227
+
228
+ soup = BeautifulSoup(html_content, "html.parser")
229
+ # 精确匹配.rpm链接,排除父目录和空链接
230
+ pkg_links = soup.find_all(
231
+ "a",
232
+ href=lambda href: isinstance(href, str) and href.endswith(".rpm") and not href.startswith('../')
233
+ )
234
+
235
+ if not pkg_links:
236
+ raise ValueError(f"页面解析失败!URL: {target_url}, 未找到任何 .rpm 包")
237
+
238
+ for link in pkg_links:
239
+ full_pkg_name = link.get("href", "").strip()
240
+ if full_pkg_name: # 过滤空字符串
241
+ yield full_pkg_name
242
+
243
+
244
+
245
+ if __name__ == "__main__":
246
+ # 初始化获取器
247
+ # oe = OpenEuler()
248
+ # log.info("正在初始化 Gitee 模块...")
249
+ # repos_generator = oe.get_openEuler_repo_names_and_urls(
250
+ # os_version="24.03-LTS-SP2"
251
+ # )
252
+ # log.info("正在获取 openEuler 24.03-LTS-SP2 x86_64 架构的仓库信息...")
253
+ # count=0
254
+ # for repo_name, repo_url in repos_generator:
255
+ # log.info(f"正在处理仓库: {repo_name},地址: {repo_url}")
256
+ # count+=1
257
+ # print(f"{repo_name}:{repo_url}")
258
+ # log.info("共获取到 %d 个仓库" % count)
259
+ # 示例:获取 openEuler 24.03-LTS-SP2 x86_64 架构的所有包(迭代打印前 10 个)
260
+ # pkg_generator = oe.get_openEuler_everything_pkgs(
261
+ # os_version="24.03-LTS-SP1",
262
+ # os_arch="x86_64"
263
+ # )
264
+ # count=0
265
+ # for name in pkg_generator:
266
+ # count+=1
267
+ # print(f"{name}")
268
+ # print("共获取到 %d 个软件包" % count)
269
+ pass
270
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python3-openEuler
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: used to get and deal information about openEuler os
5
5
  Home-page: https://gitee.com/devops_dev/openEuler
6
6
  Author: redrose2100
@@ -10,7 +10,7 @@ except:
10
10
 
11
11
  setup(
12
12
  name="python3-openEuler",
13
- version="0.0.1",
13
+ version="0.0.3",
14
14
  description="used to get and deal information about openEuler os",
15
15
  long_description=long_desc,
16
16
  long_description_content_type="text/markdown",
@@ -1 +0,0 @@
1
- from openEuler.openEuler import OpenEuler
@@ -1,85 +0,0 @@
1
- import requests
2
- from bs4 import BeautifulSoup
3
- from log import log
4
- from typing import Generator, Optional
5
-
6
- class OpenEuler():
7
- def __init__(self):
8
- pass
9
-
10
- def get_openEuler_everything_pkgs(
11
- self, os_version: str, os_arch: str
12
- ) -> Generator[str, None, None]:
13
- """
14
- 从 openEuler everything 源页面获取指定版本、架构的所有软件包列表,以迭代方式返回。
15
-
16
- Args:
17
- os_version: openEuler 版本号(如 "24.03-LTS-SP2")
18
- os_arch: 系统架构(如 "x86_64", "aarch64")
19
-
20
- Yields:
21
- str: 软件包名称(如 "a2ps-4.14-36.oe2403.x86_64.rpm")
22
-
23
- Raises:
24
- requests.exceptions.RequestException: 网络请求失败(如超时、404、500 等)
25
- ValueError: 页面解析失败(未找到任何 .rpm 包)
26
- """
27
- # 基础 URL 模板(openEuler everything 源固定路径)
28
- base_url_template = "https://dl-cdn.openeuler.openatom.cn/openEuler-{os_version}/everything/{os_arch}/Packages/"
29
- # 请求超时时间(避免长期阻塞)
30
- timeout = 15
31
- # 请求头(模拟浏览器,避免部分服务器拦截)
32
- headers = {
33
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
34
- }
35
- # 1. 构造目标页面 URL
36
- target_url = base_url_template.format(
37
- os_version=os_version, os_arch=os_arch
38
- )
39
- try:
40
- # 2. 发送 GET 请求获取页面内容(允许重定向,部分 CDN 可能跳转)
41
- response = requests.get(
42
- url=target_url,
43
- headers=headers,
44
- timeout=timeout,
45
- allow_redirects=True
46
- )
47
- # 检查响应状态码(非 200 视为请求失败)
48
- response.raise_for_status()
49
- html_content = response.text
50
-
51
- except requests.exceptions.RequestException as e:
52
- raise RuntimeError(
53
- f"获取页面失败!URL: {target_url}, 错误: {str(e)}"
54
- ) from e
55
-
56
- # 3. 解析 HTML 提取 .rpm 包链接
57
- soup = BeautifulSoup(html_content, "html.parser") # 使用标准 HTML 解析器(无需额外依赖)
58
- # 页面中软件包以 <a> 标签存在,href 属性为包名,且文本与 href 一致
59
- pkg_links = soup.find_all("a", href=lambda href: href and href.endswith(".rpm"))
60
-
61
- if not pkg_links:
62
- raise ValueError(
63
- f"页面解析失败!URL: {target_url}, 未找到任何 .rpm 格式的软件包"
64
- )
65
-
66
- # 4. 迭代返回软件包名称(通过 yield 实现生成器)
67
- for link in pkg_links:
68
- pkg_name = link.get("href") # 从 href 属性获取包名(避免文本空格问题)
69
- if pkg_name: # 双重校验(防止空链接)
70
- yield pkg_name
71
-
72
- if __name__ == "__main__":
73
- # 初始化获取器
74
- oe = OpenEuler()
75
- # 示例:获取 openEuler 24.03-LTS-SP2 x86_64 架构的所有包(迭代打印前 10 个)
76
- pkg_generator = oe.get_openEuler_everything_pkgs(
77
- os_version="24.03-LTS-SP2",
78
- os_arch="x86_64"
79
- )
80
- count=0
81
- for pkg in pkg_generator:
82
- count+=1
83
- print(pkg)
84
- print("共获取到 %d 个软件包" % count)
85
-