From 7c610b68a13c271061d677ba09e0af1c61cdefc0 Mon Sep 17 00:00:00 2001 From: likeelli Date: Tue, 24 Feb 2026 16:41:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?--story=3D129997172=20=E3=80=90L3=E3=80=91t?= =?UTF-8?q?ccli=E5=9C=A8python=203.14=E7=8E=AF=E5=A2=83=E4=B8=8B=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=8A=A5=E9=94=99badly=20formed=20help=20string?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tccli/argparser.py | 109 ++++++++++++++++++++++++++++++---------- tccli/compatibility.py | 111 +++++++++++++++++++++++++++++++++++++++++ tccli/main.py | 17 +++++++ tccli/utils.py | 74 +++++++++++++++++++++------ 4 files changed, 272 insertions(+), 39 deletions(-) create mode 100644 tccli/compatibility.py diff --git a/tccli/argparser.py b/tccli/argparser.py index f162b517ba..380f42909d 100644 --- a/tccli/argparser.py +++ b/tccli/argparser.py @@ -8,6 +8,7 @@ from difflib import get_close_matches from gettext import gettext from tccli.error_msg import USAGE +from tccli.compatibility import PythonVersionChecker log = init("tccli.argparser") @@ -31,52 +32,110 @@ def choices(self, val): class BaseArgParser(argparse.ArgumentParser): Formatter = argparse.RawTextHelpFormatter - ChoicesPerLine = 2 + def __init__(self, *args, **kwargs): + # Python 3.14兼容性:确保帮助字符串格式正确 + kwargs.setdefault('formatter_class', self.Formatter) + kwargs.setdefault('add_help', False) + kwargs.setdefault('conflict_handler', 'resolve') + + # Python 3.14: 添加allow_abbrev=False以避免参数缩写问题 + if hasattr(argparse.ArgumentParser, 'allow_abbrev'): + kwargs.setdefault('allow_abbrev', False) + + super(BaseArgParser, self).__init__(*args, **kwargs) + def _check_value(self, action, value): + # 改进的错误消息格式化,避免Python 3.14兼容性问题 if action.choices is not None and value not in action.choices: msg = ['Invalid choice, valid choices are:\n'] args = (' '.join(sys.argv[1:])).split(value) - help_msg = "you can run `tccli %shelp` to see valid choices" % args[0] - log.error(USAGE + msg[0] + help_msg) + + # 改进的帮助消息生成 + help_msg = "you can run `tccli %s help` to see valid choices" % args[0] + + # 使用更安全的日志记录方式 + try: + log.error(USAGE + msg[0] + help_msg) + except Exception as e: + # Python 3.14兼容性:如果日志记录失败,直接输出到stderr + sys.stderr.write("Error: " + str(e) + "\n") + sys.stderr.write(USAGE + msg[0] + help_msg + "\n") + for i in range(len(action.choices))[::self.ChoicesPerLine]: current = [] for choice in action.choices[i:i + self.ChoicesPerLine]: - current.append('%-40s' % choice) + # 使用更安全的字符串格式化 + try: + current.append('%-40s' % choice) + except Exception: + current.append(str(choice).ljust(40)) msg.append(' | '.join(current)) + possible = get_close_matches(value, action.choices, cutoff=0.8) if possible: extra = ['\n\nInvalid choice: %r, maybe you meant:\n' % value] for word in possible: extra.append(' * %s' % word) msg.extend(extra) - msg.append("Invalid choice: The specified command or option is not found, " \ - "try lastest version by running `pip install --upgrade tccli`.") - raise argparse.ArgumentError(action, '\n'.join(msg)) + + msg.append("Invalid choice: The specified command or option is not found, " + "try latest version by running `pip install --upgrade tccli`.") + + # 使用更安全的错误消息构建 + error_msg = '\n'.join(msg) + raise argparse.ArgumentError(action, error_msg) def parse_known_args(self, args=None, namespace=None): - parsed, remaining = super(BaseArgParser, self).parse_known_args(args, namespace) - terminal_encoding = getattr(sys.stdin, 'encoding', 'utf-8') - if terminal_encoding is None or terminal_encoding == 'cp65001': - terminal_encoding = 'utf-8' - for arg, value in vars(parsed).items(): - if isinstance(value, six.binary_type): - setattr(parsed, arg, value.decode(terminal_encoding)) - elif isinstance(value, list): - encoded = [] - for v in value: - if isinstance(v, six.binary_type): - encoded.append(v.decode(terminal_encoding)) - else: - encoded.append(v) - setattr(parsed, arg, encoded) - return parsed, remaining - + # Python 3.14兼容性:改进参数解析 + try: + parsed, remaining = super(BaseArgParser, self).parse_known_args(args, namespace) + + # Python 3.14兼容性:改进编码处理 + terminal_encoding = getattr(sys.stdin, 'encoding', 'utf-8') + if terminal_encoding is None or terminal_encoding == 'cp65001': + terminal_encoding = 'utf-8' + + for arg, value in vars(parsed).items(): + if isinstance(value, six.binary_type): + setattr(parsed, arg, value.decode(terminal_encoding)) + elif isinstance(value, list): + encoded = [] + for v in value: + if isinstance(v, six.binary_type): + encoded.append(v.decode(terminal_encoding)) + else: + encoded.append(v) + setattr(parsed, arg, encoded) + + return parsed, remaining + except Exception as e: + # Python 3.14兼容性:改进异常处理 + if "badly formed help string" in str(e).lower(): + # 处理帮助字符串格式错误 + self._handle_badly_formed_help_string(e) + raise + + def _handle_badly_formed_help_string(self, exception): + """处理badly formed help string错误""" + # Python 3.14兼容性:简化帮助字符串格式 + if PythonVersionChecker.is_python_314_or_later(): + # 在Python 3.14中,简化帮助字符串格式 + self.formatter_class = argparse.HelpFormatter + def error(self, message): + # Python 3.14兼容性:改进错误处理 self.print_usage(sys.stderr) args = {'prog': self.prog, 'message': message} - self.exit(252, gettext('%(prog)s: error: %(message)s\n') % args) + + # 使用安全的字符串格式化 + try: + error_msg = gettext('%(prog)s: error: %(message)s\n') % args + except Exception: + error_msg = '%s: error: %s\n' % (self.prog, message) + + self.exit(252, error_msg) class CLIArgParser(BaseArgParser): diff --git a/tccli/compatibility.py b/tccli/compatibility.py new file mode 100644 index 0000000000..d60ce4360f --- /dev/null +++ b/tccli/compatibility.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +""" +Python版本兼容性模块 +处理Python 3.14及更高版本的兼容性问题 +""" + +import sys +import six +import warnings + + +class PythonVersionChecker: + """Python版本兼容性检查器""" + + @staticmethod + def get_python_version(): + """获取Python版本信息""" + return sys.version_info + + @staticmethod + def is_python_314_or_later(): + """检查是否为Python 3.14或更高版本""" + if six.PY2: + return False + return sys.version_info >= (3, 14) + + @staticmethod + def is_python_3(): + """检查是否为Python 3.x版本""" + return not six.PY2 + + @staticmethod + def check_compatibility(): + """检查并报告兼容性问题""" + version = PythonVersionChecker.get_python_version() + + if version >= (3, 14): + print("Info: Running on Python 3.14 or later") + print("Applying compatibility adjustments for argparse and string handling") + return False + return True + + @staticmethod + def apply_compatibility_fixes(): + """应用Python 3.14兼容性修复""" + if PythonVersionChecker.is_python_314_or_later(): + # Python 3.14特定修复 + # 忽略argparse相关的弃用警告 + warnings.filterwarnings("ignore", category=DeprecationWarning, module="argparse") + warnings.filterwarnings("ignore", category=FutureWarning, module="argparse") + + # 设置更安全的编码处理 + if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding is None: + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') + + if hasattr(sys.stderr, 'encoding') and sys.stderr.encoding is None: + import io + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + + @staticmethod + def safe_string_format(text, *args, **kwargs): + """安全的字符串格式化方法,处理Python 3.14兼容性问题""" + if text is None: + return "" + + try: + # 确保文本是字符串 + if isinstance(text, bytes): + text = text.decode('utf-8', errors='replace') + + # 安全的格式化 + if args or kwargs: + return text.format(*args, **kwargs) + else: + return text + except Exception as e: + # 如果格式化失败,返回原始文本 + return str(text) + + @staticmethod + def safe_help_string_generation(help_text): + """安全的帮助字符串生成方法""" + if help_text is None: + return "" + + # 处理Python 3.14中的字符串编码问题 + if PythonVersionChecker.is_python_314_or_later(): + # 确保字符串是unicode + if isinstance(help_text, bytes): + help_text = help_text.decode('utf-8', errors='replace') + + # 清理可能的问题字符 + help_text = help_text.replace('\x00', '') # 移除空字符 + help_text = help_text.strip() # 移除首尾空白 + + # 限制字符串长度,避免argparse处理过长的帮助字符串 + if len(help_text) > 1000: + help_text = help_text[:1000] + "..." + + return help_text + + +def apply_python_314_compatibility(): + """应用Python 3.14兼容性修复(全局函数)""" + PythonVersionChecker.apply_compatibility_fixes() + + +def check_python_version(): + """检查Python版本并返回兼容性状态""" + return PythonVersionChecker.check_compatibility() \ No newline at end of file diff --git a/tccli/main.py b/tccli/main.py index 872d371c19..c8718097c3 100644 --- a/tccli/main.py +++ b/tccli/main.py @@ -9,6 +9,9 @@ from tccli.log import init from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException +# Python 3.14兼容性:添加兼容性检查 +from tccli.compatibility import PythonVersionChecker + try: # cp65001 is utf-8 on windows platform # python2 doesn't support cp65001 codec @@ -36,6 +39,13 @@ def main(): try: + # Python 3.14兼容性:应用兼容性修复 + PythonVersionChecker.apply_compatibility_fixes() + + # 检查Python版本兼容性 + if PythonVersionChecker.is_python_314_or_later(): + log.info("Running on Python 3.14 or later - applying compatibility adjustments") + log.info("tccli %s" % ' '.join(sys.argv[1:])) CLICommand()() return 0 @@ -81,6 +91,13 @@ def main(): log.error(e) return 255 except Exception as e: + # Python 3.14兼容性:改进异常处理 + if "badly formed help string" in str(e).lower(): + # 处理帮助字符串格式错误 + sys.stderr.write("Error: Badly formed help string detected\n") + sys.stderr.write("This may be due to Python 3.14 compatibility issues.\n") + sys.stderr.write("Try running with simplified help: tccli help --simple\n") + sys.stderr.write("usage: %s\n" % USAGE) sys.stderr.write(str(e)) sys.stderr.write("\n") diff --git a/tccli/utils.py b/tccli/utils.py index a8dd3128f2..e9db82cf5f 100644 --- a/tccli/utils.py +++ b/tccli/utils.py @@ -35,29 +35,75 @@ def split_str_bk(pre, src, step): @staticmethod def split_str(pre, src, line_size): + """改进的帮助字符串分割方法,增强Python 3.14兼容性""" + if src is None: + return "" + + # 处理Python 3.14的字符串编码要求 if six.PY2: src = src.decode("utf-8") + else: + # Python 3.x: 确保字符串是unicode + if isinstance(src, bytes): + src = src.decode("utf-8") + dst = "" strlist = src.split("\n") + for s in strlist: + if not s.strip(): # 跳过空行 + dst += pre + "\n" + continue + lsize = 0 line = pre - for c in s: - line += c - if ord(c) < 256: - lsize += 1 - else: - lsize += 2 - if lsize >= line_size: - dst += (line + "\n") + words = s.split() + current_line_words = [] + + for word in words: + # 计算单词长度(考虑中英文字符) + word_length = sum(2 if ord(c) > 127 else 1 for c in word) + + # 如果当前行加上新单词会超出限制 + if lsize + word_length + (len(current_line_words) > 0) >= line_size: + # 输出当前行 + dst += line + " ".join(current_line_words) + "\n" + # 开始新行 line = pre - lsize = 0 - dst += (line + "\n") - dst += "\n" - if six.PY2: - dst = dst.encode("utf-8") + current_line_words = [word] + lsize = len(pre) + word_length + else: + current_line_words.append(word) + lsize += word_length + 1 # +1 for space + + # 输出最后一行 + if current_line_words: + dst += line + " ".join(current_line_words) + "\n" + return dst + @staticmethod + def safe_help_string_generation(help_text): + """安全的帮助字符串生成方法(兼容Python 3.14)""" + if help_text is None: + return "" + + # 处理Python 3.14中的字符串编码问题 + if not six.PY2: + # 确保字符串是unicode + if isinstance(help_text, bytes): + help_text = help_text.decode("utf-8", errors="replace") + + # 清理可能的问题字符 + help_text = help_text.replace('\x00', '') # 移除空字符 + help_text = help_text.strip() # 移除首尾空白 + + # 限制字符串长度,避免argparse处理过长的帮助字符串 + if len(help_text) > 1000: + help_text = help_text[:1000] + "..." + + return help_text + @staticmethod def is_valid_version(version): try: @@ -116,4 +162,4 @@ def is_bool(s): elif s.lower() == 'false': return True, False else: - return False, None + return False, None \ No newline at end of file From 5603603533807192d9f3783f6db3abfa75baaff6 Mon Sep 17 00:00:00 2001 From: likeelli Date: Thu, 19 Mar 2026 18:00:25 +0800 Subject: [PATCH 2/2] fix: escape % in help strings for Python 3.14 argparse compatibility Python 3.14's argparse._expand_help() performs '% params' string formatting on every help string when rendering help output. If the help text contains a bare '%' that is not a valid '%(name)s' specifier (e.g. '100%', URL-encoded '%3A', special-char docs like '% ^ & *'), a ValueError ('badly formed help string') is raised. Fix: add _escape_help_string() in argument.py that replaces every '%' with '%%' before the string is handed to argparse. This causes argparse's HelpFormatter to render the original '%' characters correctly, while eliminating the format error. The two documentation properties that feed help= are updated: - CustomArgument.documentation - CLIArgument.documentation Also reverts the misdirected patches from the previous commit (try/except wrappers in argparser.py, utils.py, main.py, and the new compatibility.py module) which did not address the root cause. --- tccli/argparser.py | 109 ++++++++++------------------------------ tccli/argument.py | 21 +++++++- tccli/compatibility.py | 111 ----------------------------------------- tccli/main.py | 17 ------- tccli/utils.py | 74 ++++++--------------------- 5 files changed, 58 insertions(+), 274 deletions(-) delete mode 100644 tccli/compatibility.py diff --git a/tccli/argparser.py b/tccli/argparser.py index 380f42909d..f162b517ba 100644 --- a/tccli/argparser.py +++ b/tccli/argparser.py @@ -8,7 +8,6 @@ from difflib import get_close_matches from gettext import gettext from tccli.error_msg import USAGE -from tccli.compatibility import PythonVersionChecker log = init("tccli.argparser") @@ -32,110 +31,52 @@ def choices(self, val): class BaseArgParser(argparse.ArgumentParser): Formatter = argparse.RawTextHelpFormatter - ChoicesPerLine = 2 - def __init__(self, *args, **kwargs): - # Python 3.14兼容性:确保帮助字符串格式正确 - kwargs.setdefault('formatter_class', self.Formatter) - kwargs.setdefault('add_help', False) - kwargs.setdefault('conflict_handler', 'resolve') - - # Python 3.14: 添加allow_abbrev=False以避免参数缩写问题 - if hasattr(argparse.ArgumentParser, 'allow_abbrev'): - kwargs.setdefault('allow_abbrev', False) - - super(BaseArgParser, self).__init__(*args, **kwargs) + ChoicesPerLine = 2 def _check_value(self, action, value): - # 改进的错误消息格式化,避免Python 3.14兼容性问题 if action.choices is not None and value not in action.choices: msg = ['Invalid choice, valid choices are:\n'] args = (' '.join(sys.argv[1:])).split(value) - - # 改进的帮助消息生成 - help_msg = "you can run `tccli %s help` to see valid choices" % args[0] - - # 使用更安全的日志记录方式 - try: - log.error(USAGE + msg[0] + help_msg) - except Exception as e: - # Python 3.14兼容性:如果日志记录失败,直接输出到stderr - sys.stderr.write("Error: " + str(e) + "\n") - sys.stderr.write(USAGE + msg[0] + help_msg + "\n") - + help_msg = "you can run `tccli %shelp` to see valid choices" % args[0] + log.error(USAGE + msg[0] + help_msg) for i in range(len(action.choices))[::self.ChoicesPerLine]: current = [] for choice in action.choices[i:i + self.ChoicesPerLine]: - # 使用更安全的字符串格式化 - try: - current.append('%-40s' % choice) - except Exception: - current.append(str(choice).ljust(40)) + current.append('%-40s' % choice) msg.append(' | '.join(current)) - possible = get_close_matches(value, action.choices, cutoff=0.8) if possible: extra = ['\n\nInvalid choice: %r, maybe you meant:\n' % value] for word in possible: extra.append(' * %s' % word) msg.extend(extra) - - msg.append("Invalid choice: The specified command or option is not found, " - "try latest version by running `pip install --upgrade tccli`.") - - # 使用更安全的错误消息构建 - error_msg = '\n'.join(msg) - raise argparse.ArgumentError(action, error_msg) + msg.append("Invalid choice: The specified command or option is not found, " \ + "try lastest version by running `pip install --upgrade tccli`.") + raise argparse.ArgumentError(action, '\n'.join(msg)) def parse_known_args(self, args=None, namespace=None): - # Python 3.14兼容性:改进参数解析 - try: - parsed, remaining = super(BaseArgParser, self).parse_known_args(args, namespace) - - # Python 3.14兼容性:改进编码处理 - terminal_encoding = getattr(sys.stdin, 'encoding', 'utf-8') - if terminal_encoding is None or terminal_encoding == 'cp65001': - terminal_encoding = 'utf-8' - - for arg, value in vars(parsed).items(): - if isinstance(value, six.binary_type): - setattr(parsed, arg, value.decode(terminal_encoding)) - elif isinstance(value, list): - encoded = [] - for v in value: - if isinstance(v, six.binary_type): - encoded.append(v.decode(terminal_encoding)) - else: - encoded.append(v) - setattr(parsed, arg, encoded) - - return parsed, remaining - except Exception as e: - # Python 3.14兼容性:改进异常处理 - if "badly formed help string" in str(e).lower(): - # 处理帮助字符串格式错误 - self._handle_badly_formed_help_string(e) - raise - - def _handle_badly_formed_help_string(self, exception): - """处理badly formed help string错误""" - # Python 3.14兼容性:简化帮助字符串格式 - if PythonVersionChecker.is_python_314_or_later(): - # 在Python 3.14中,简化帮助字符串格式 - self.formatter_class = argparse.HelpFormatter - + parsed, remaining = super(BaseArgParser, self).parse_known_args(args, namespace) + terminal_encoding = getattr(sys.stdin, 'encoding', 'utf-8') + if terminal_encoding is None or terminal_encoding == 'cp65001': + terminal_encoding = 'utf-8' + for arg, value in vars(parsed).items(): + if isinstance(value, six.binary_type): + setattr(parsed, arg, value.decode(terminal_encoding)) + elif isinstance(value, list): + encoded = [] + for v in value: + if isinstance(v, six.binary_type): + encoded.append(v.decode(terminal_encoding)) + else: + encoded.append(v) + setattr(parsed, arg, encoded) + return parsed, remaining + def error(self, message): - # Python 3.14兼容性:改进错误处理 self.print_usage(sys.stderr) args = {'prog': self.prog, 'message': message} - - # 使用安全的字符串格式化 - try: - error_msg = gettext('%(prog)s: error: %(message)s\n') % args - except Exception: - error_msg = '%s: error: %s\n' % (self.prog, message) - - self.exit(252, error_msg) + self.exit(252, gettext('%(prog)s: error: %(message)s\n') % args) class CLIArgParser(BaseArgParser): diff --git a/tccli/argument.py b/tccli/argument.py index e67ebeab6d..fdbfc74508 100644 --- a/tccli/argument.py +++ b/tccli/argument.py @@ -4,6 +4,23 @@ from tccli.loaders import CLI_BASE_TYPE +def _escape_help_string(text): + """Escape '%' characters in help strings for argparse compatibility. + + Python 3.14 changed argparse to raise a ValueError ("badly formed help + string") when help strings contain bare '%' characters that are not valid + '%(name)s' format specifiers. API documentation often contains literal + percent signs (e.g. "100%", URL-encoded strings like "%3A"). + + We replace every '%' with '%%' so that argparse's HelpFormatter renders + the original text unchanged, while no longer tripping over unrecognized + format specifiers. + """ + if not text: + return text + return text.replace('%', '%%') + + def str_to_bool(value): if isinstance(value, bool): return value @@ -143,7 +160,7 @@ def required(self, value): @property def documentation(self): - return self._help + return _escape_help_string(self._help) @property def cli_type(self): @@ -205,7 +222,7 @@ def required(self, value): @property def documentation(self): - return self._documentation + return _escape_help_string(self._documentation) @documentation.setter def documentation(self, value): diff --git a/tccli/compatibility.py b/tccli/compatibility.py deleted file mode 100644 index d60ce4360f..0000000000 --- a/tccli/compatibility.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Python版本兼容性模块 -处理Python 3.14及更高版本的兼容性问题 -""" - -import sys -import six -import warnings - - -class PythonVersionChecker: - """Python版本兼容性检查器""" - - @staticmethod - def get_python_version(): - """获取Python版本信息""" - return sys.version_info - - @staticmethod - def is_python_314_or_later(): - """检查是否为Python 3.14或更高版本""" - if six.PY2: - return False - return sys.version_info >= (3, 14) - - @staticmethod - def is_python_3(): - """检查是否为Python 3.x版本""" - return not six.PY2 - - @staticmethod - def check_compatibility(): - """检查并报告兼容性问题""" - version = PythonVersionChecker.get_python_version() - - if version >= (3, 14): - print("Info: Running on Python 3.14 or later") - print("Applying compatibility adjustments for argparse and string handling") - return False - return True - - @staticmethod - def apply_compatibility_fixes(): - """应用Python 3.14兼容性修复""" - if PythonVersionChecker.is_python_314_or_later(): - # Python 3.14特定修复 - # 忽略argparse相关的弃用警告 - warnings.filterwarnings("ignore", category=DeprecationWarning, module="argparse") - warnings.filterwarnings("ignore", category=FutureWarning, module="argparse") - - # 设置更安全的编码处理 - if hasattr(sys.stdout, 'encoding') and sys.stdout.encoding is None: - import io - sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') - - if hasattr(sys.stderr, 'encoding') and sys.stderr.encoding is None: - import io - sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') - - @staticmethod - def safe_string_format(text, *args, **kwargs): - """安全的字符串格式化方法,处理Python 3.14兼容性问题""" - if text is None: - return "" - - try: - # 确保文本是字符串 - if isinstance(text, bytes): - text = text.decode('utf-8', errors='replace') - - # 安全的格式化 - if args or kwargs: - return text.format(*args, **kwargs) - else: - return text - except Exception as e: - # 如果格式化失败,返回原始文本 - return str(text) - - @staticmethod - def safe_help_string_generation(help_text): - """安全的帮助字符串生成方法""" - if help_text is None: - return "" - - # 处理Python 3.14中的字符串编码问题 - if PythonVersionChecker.is_python_314_or_later(): - # 确保字符串是unicode - if isinstance(help_text, bytes): - help_text = help_text.decode('utf-8', errors='replace') - - # 清理可能的问题字符 - help_text = help_text.replace('\x00', '') # 移除空字符 - help_text = help_text.strip() # 移除首尾空白 - - # 限制字符串长度,避免argparse处理过长的帮助字符串 - if len(help_text) > 1000: - help_text = help_text[:1000] + "..." - - return help_text - - -def apply_python_314_compatibility(): - """应用Python 3.14兼容性修复(全局函数)""" - PythonVersionChecker.apply_compatibility_fixes() - - -def check_python_version(): - """检查Python版本并返回兼容性状态""" - return PythonVersionChecker.check_compatibility() \ No newline at end of file diff --git a/tccli/main.py b/tccli/main.py index c8718097c3..872d371c19 100644 --- a/tccli/main.py +++ b/tccli/main.py @@ -9,9 +9,6 @@ from tccli.log import init from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException -# Python 3.14兼容性:添加兼容性检查 -from tccli.compatibility import PythonVersionChecker - try: # cp65001 is utf-8 on windows platform # python2 doesn't support cp65001 codec @@ -39,13 +36,6 @@ def main(): try: - # Python 3.14兼容性:应用兼容性修复 - PythonVersionChecker.apply_compatibility_fixes() - - # 检查Python版本兼容性 - if PythonVersionChecker.is_python_314_or_later(): - log.info("Running on Python 3.14 or later - applying compatibility adjustments") - log.info("tccli %s" % ' '.join(sys.argv[1:])) CLICommand()() return 0 @@ -91,13 +81,6 @@ def main(): log.error(e) return 255 except Exception as e: - # Python 3.14兼容性:改进异常处理 - if "badly formed help string" in str(e).lower(): - # 处理帮助字符串格式错误 - sys.stderr.write("Error: Badly formed help string detected\n") - sys.stderr.write("This may be due to Python 3.14 compatibility issues.\n") - sys.stderr.write("Try running with simplified help: tccli help --simple\n") - sys.stderr.write("usage: %s\n" % USAGE) sys.stderr.write(str(e)) sys.stderr.write("\n") diff --git a/tccli/utils.py b/tccli/utils.py index e9db82cf5f..a8dd3128f2 100644 --- a/tccli/utils.py +++ b/tccli/utils.py @@ -35,75 +35,29 @@ def split_str_bk(pre, src, step): @staticmethod def split_str(pre, src, line_size): - """改进的帮助字符串分割方法,增强Python 3.14兼容性""" - if src is None: - return "" - - # 处理Python 3.14的字符串编码要求 if six.PY2: src = src.decode("utf-8") - else: - # Python 3.x: 确保字符串是unicode - if isinstance(src, bytes): - src = src.decode("utf-8") - dst = "" strlist = src.split("\n") - for s in strlist: - if not s.strip(): # 跳过空行 - dst += pre + "\n" - continue - lsize = 0 line = pre - words = s.split() - current_line_words = [] - - for word in words: - # 计算单词长度(考虑中英文字符) - word_length = sum(2 if ord(c) > 127 else 1 for c in word) - - # 如果当前行加上新单词会超出限制 - if lsize + word_length + (len(current_line_words) > 0) >= line_size: - # 输出当前行 - dst += line + " ".join(current_line_words) + "\n" - # 开始新行 - line = pre - current_line_words = [word] - lsize = len(pre) + word_length + for c in s: + line += c + if ord(c) < 256: + lsize += 1 else: - current_line_words.append(word) - lsize += word_length + 1 # +1 for space - - # 输出最后一行 - if current_line_words: - dst += line + " ".join(current_line_words) + "\n" - + lsize += 2 + if lsize >= line_size: + dst += (line + "\n") + line = pre + lsize = 0 + dst += (line + "\n") + dst += "\n" + if six.PY2: + dst = dst.encode("utf-8") return dst - @staticmethod - def safe_help_string_generation(help_text): - """安全的帮助字符串生成方法(兼容Python 3.14)""" - if help_text is None: - return "" - - # 处理Python 3.14中的字符串编码问题 - if not six.PY2: - # 确保字符串是unicode - if isinstance(help_text, bytes): - help_text = help_text.decode("utf-8", errors="replace") - - # 清理可能的问题字符 - help_text = help_text.replace('\x00', '') # 移除空字符 - help_text = help_text.strip() # 移除首尾空白 - - # 限制字符串长度,避免argparse处理过长的帮助字符串 - if len(help_text) > 1000: - help_text = help_text[:1000] + "..." - - return help_text - @staticmethod def is_valid_version(version): try: @@ -162,4 +116,4 @@ def is_bool(s): elif s.lower() == 'false': return True, False else: - return False, None \ No newline at end of file + return False, None