v2.3.0: info 新增开发包/传输请求字段 (纯只读 E071)

- info 输出新增「开发包」(packageRef)、「传输请求」(纯只读查 E071)

- 优雅降级: 无请求/查询失败/非独立传输对象, 不中断 info

- bump 2.2.1 -> 2.3.0

- pack 从源码同步 (含 parser/transport 最新代码)
This commit is contained in:
WuRangyu
2026-08-06 11:57:36 +08:00
parent 607445b338
commit 02bc2c5165
7 changed files with 381 additions and 193 deletions
+1 -1
View File
@@ -2,4 +2,4 @@
sap-cli — 基于 SAP ADT REST API 的 ABAP 开发对象管理工具
"""
__version__ = "2.2.1"
__version__ = "2.3.0"
+2
View File
@@ -158,6 +158,8 @@ CDS View:
tr_release.add_argument("--corr_nr", required=True, help="传输请求编号")
tr_objects = transport_sub.add_parser("objects", help="列出传输请求中的对象")
tr_objects.add_argument("--corr_nr", required=True, help="传输请求编号")
tr_create = transport_sub.add_parser("create", help="新建传输请求")
tr_create.add_argument("--description", required=True, help="传输请求描述")
# ── check (ATC 代码检查) ──
check_parser = subparsers.add_parser("check", help="ATC 代码检查")
+39 -1
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import argparse
import logging
import os
import re
import xml.etree.ElementTree as ET
from sapcli.client import ADTClient
@@ -18,7 +19,7 @@ from sapcli.exceptions import (
SapCliError,
)
from sapcli.manifest import Manifest, ManifestEntry
from sapcli.types import get_type_config, parse_object_name
from sapcli.types import get_r3tr_object_code, get_type_config, parse_object_name
DDIC_TYPES = {"domain", "dataelement", "table", "structure", "tabletype"}
@@ -401,6 +402,30 @@ def _update_manifest_after_sync(
logger.warning("更新清单失败: %s", e)
def _query_transport_request(
client: ADTClient, obj_type: str, display_name: str
) -> str:
"""查对象当前所在(未释放)的传输请求号。纯只读查 E071;失败优雅降级。"""
r3tr_code = get_r3tr_object_code(obj_type)
if not r3tr_code:
return "—(非独立传输对象)"
obj_name_upper = display_name.upper().split("/")[-1]
if not re.fullmatch(r"[A-Za-z0-9_/$]+", obj_name_upper):
return "(对象名非法,跳过)"
try:
sql = (
f"SELECT trkorr FROM e071 WHERE pgmid='R3TR' "
f"AND object='{r3tr_code}' AND obj_name='{obj_name_upper}' "
f"UP TO 10 ROWS"
)
result = client.query_table_data(sql, max_rows=10)
trkorrs = sorted({row[0] for row in result.get("rows", []) if row and row[0]})
return ", ".join(trkorrs) if trkorrs else ""
except Exception:
logger.debug("查询 E071 传输请求失败", exc_info=True)
return "(无法获取)"
def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
"""查询对象元数据信息。"""
obj_type: str = args.type
@@ -469,6 +494,14 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
info_responsible = root.attrib.get(f"{{{ns['adtcore']}}}responsible", "")
info_language = root.attrib.get(f"{{{ns['adtcore']}}}masterLanguage", "")
# 开发包:解析对象 XML 自带的 packageRef 子元素
pkg_ref = root.find(f".//{{{ns['adtcore']}}}packageRef")
info_package = (
pkg_ref.attrib.get(f"{{{ns['adtcore']}}}name", "")
if pkg_ref is not None
else ""
)
if info_version == "active":
status_icon = "✓ 已激活"
elif info_version == "inactive":
@@ -476,6 +509,9 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
else:
status_icon = f"{info_version}"
# 传输请求号:纯只读查 E071(不在对象元数据 XML 中)
corr_display = _query_transport_request(client, obj_type, parsed.display_name)
print(f" ✓ 查询成功\n")
print(f" {'' * 50}")
print(f" 名称: {info_name}")
@@ -484,6 +520,8 @@ def cmd_info(args: argparse.Namespace, client: ADTClient) -> None:
print(f" 状态: {status_icon}")
print(f" 负责人: {info_responsible}")
print(f" 语言: {info_language}")
print(f" 开发包: {info_package or '(未指定)'}")
print(f" 传输请求: {corr_display}")
print(f" 创建者: {info_created_by}")
print(f" 修改者: {info_changed_by}")
print(f" 修改时间: {info_changed_at}")
+26 -1
View File
@@ -21,12 +21,15 @@ def cmd_transport(args: argparse.Namespace, client: ADTClient) -> None:
_transport_release(args, client)
elif action == "objects":
_transport_objects(args, client)
elif action == "create":
_transport_create(args, client)
else:
print(" 用法: sap-cli transport [list|info|release|objects]")
print(" 用法: sap-cli transport [list|info|release|objects|create]")
print(" list — 列出可修改的传输请求")
print(" info — 查看传输请求详情")
print(" release — 释放传输请求")
print(" objects — 列出传输请求中的对象")
print(" create — 新建传输请求 (--description)")
def _transport_list(args: argparse.Namespace, client: ADTClient) -> None:
@@ -107,6 +110,28 @@ def _transport_release(args: argparse.Namespace, client: ADTClient) -> None:
print(f" ✗ 释放失败,请检查传输请求状态")
def _transport_create(args: argparse.Namespace, client: ADTClient) -> None:
"""新建传输请求。"""
description: str = args.description
print("=" * 60)
print(" SAP 新建传输请求")
print("=" * 60)
print(f" 描述: {description}")
print(f"\n → 正在创建...")
try:
new_nr = client.create_transport_request(description)
except Exception as e:
print(f" ✗ 创建失败: {e}")
return
if new_nr:
print(f"\n ✓ 传输请求已创建: {new_nr}")
else:
print(f"\n ✗ 创建失败(未返回编号),请检查系统或描述合法性")
def _transport_objects(args: argparse.Namespace, client: ADTClient) -> None:
"""列出传输请求中的对象。"""
corr_nr: str = args.corr_nr
+29
View File
@@ -227,6 +227,35 @@ def all_type_keys() -> list[str]:
return list(_OBJECT_TYPE_REGISTRY.keys())
# sap-cli 类型 → SAP 传输系统 R3TR 对象代码(用于查 E071 表)。
# None 表示该类型非独立 R3TR 对象,查 E071 无意义。
_R3TR_OBJECT_CODE: dict[str, str | None] = {
"report": "PROG",
"class": "CLAS",
"interface": "INTF",
"function": None, # 函数模块属函数组(FUGR),非独立 R3TR
"functiongroup": "FUGR",
"domain": "DOMA",
"dataelement": "DTEL",
"table": "TABL",
"structure": "TABL",
"tabletype": "TTYP",
"include": None, # Include 属所属程序,非独立 R3TR
"cdsview": "DDLS",
"messageclass": "MSAG",
"view": "VIEW",
"searchhelp": "SHLP",
"lockobject": "ENQU",
"dcl": "DCLS",
"ddlX": "DDLX",
}
def get_r3tr_object_code(key: str) -> str | None:
"""返回对象类型对应的 SAP R3TR 传输对象代码;无独立代码返回 None。"""
return _R3TR_OBJECT_CODE.get(key)
@dataclass(frozen=True)
class ParsedName:
obj_uri: str