9.3 KiB
ArgsConvert 与 ConditionConvert 技术文档
语言: Python
编码: UTF-8
模块路径:appPublic.argsconvert(假设)
功能描述: 提供字符串模板变量替换和条件性文本块解析的工具类。
概述
本模块包含两个核心类:
ArgsConvert:用于在字符串、列表或字典中查找并替换特定格式的占位符(如%{...}%),支持表达式求值。ConditionConvert:用于处理带有开始/结束标记的条件性文本块(如$<tag>$...$</tag>$),根据命名空间中的值决定是否保留内容。
这两个类广泛适用于模板渲染、动态配置生成、条件输出等场景。
安装依赖
无需外部依赖,仅需标准库及以下内部模块:
re: 正则表达式处理appPublic.dictObject.DictObject: 可属性访问的字典对象appPublic.registerfunction.rfrun: 允许在eval中调用注册函数
类说明
一、ConvertException
class ConvertException(Exception):
pass
自定义异常类,用于在转换过程中抛出错误,例如标签不匹配、语法错误等。
二、ArgsConvert
功能
对字符串、列表、字典中的占位符进行变量替换。支持嵌套结构和简单表达式计算。
初始化
def __init__(self, preString, subfixString, coding='utf-8')
| 参数 | 类型 | 描述 |
|---|---|---|
preString |
str | 占位符前缀,如 "%{" |
subfixString |
str | 占位符后缀,如 "}%" |
coding |
str | 编码方式,默认为 'utf-8'(未实际使用) |
⚠️ 注意:虽然参数名为
coding,但当前代码中并未真正用于编码转换。
示例:
AC = ArgsConvert('%{', '}%')
表示将形如 %{var_name}% 的占位符替换成对应的变量值。
方法
1. convert(obj, namespace, default='')
递归地将对象中的占位符替换为命名空间中的值。
- 支持类型:字符串、列表、字典
- 返回新对象,原对象不变
| 参数 | 类型 | 描述 |
|---|---|---|
obj |
any | 要转换的对象(str / list / dict) |
namespace |
dict | 变量命名空间(提供变量值) |
default |
any 或 callable | 若变量不存在时返回的默认值;若为可调用,则传入变量名作为参数 |
返回值:转换后的对象
行为说明:
- 字符串 → 替换占位符
- 列表 → 遍历每个元素递归转换
- 字典 → 构造
DictObject并递归转换每个值
2. findAllVariables(src)
从字符串中提取所有匹配的占位符内的变量名(不含前后缀)
| 参数 | 类型 | 描述 |
|---|---|---|
src |
str | 源字符串 |
返回值:list[str],变量名列表
3. getVarName(vs)
从完整占位符字符串中提取变量表达式(去掉前后缀)
| 参数 | 类型 | 描述 |
|---|---|---|
vs |
str | 完整的占位符,如 %{a + b}% |
返回值:str,中间部分,如 "a + b"
4. getVarValue(var, namespace, default)
获取变量的实际值,优先尝试 eval(var, namespace),失败则 fallback 到 namespace.get(var) 或 default
| 参数 | 类型 | 描述 |
|---|---|---|
var |
str | 表达式或变量名 |
namespace |
dict | 命名空间 |
default |
any 或 callable | 默认值策略 |
执行顺序:
- 使用
eval(var, ns)计算表达式(安全风险需注意) - 失败时尝试
ns.get(var) - 再失败时:
- 若
default是 callable,调用default(var) - 否则返回
default
- 若
✅ 支持复杂表达式:
%{d['a']+'(rr)'}%、%{len(mylist)}%
5. convertString(s, namespace, default)
处理单个字符串中的占位符替换。
算法逻辑:
- 找出所有匹配的占位符
- 对每个占位符:
- 提取变量名
- 获取其值
- 如果非字符串,转为字符串
- 用该值替换原占位符
- 特殊优化:如果整个字符串就是单个占位符(如
%{a}%),直接返回值本身(可能非字符串)
三、ConditionConvert
功能
实现基于标签对的条件文本块渲染。只有当标签内变量为真时,才保留其中内容。
支持嵌套结构(通过栈管理),类似简易模板引擎的 if 语句。
初始化
def __init__(self, pString='$<', sString='>$', coding='utf-8')
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
pString |
str | '$<' |
开始标签前缀 |
sString |
str | '>$' |
结束标签后缀 |
coding |
str | 'utf-8' |
编码设置(未实际使用) |
示例:
cc = ConditionConvert()
识别如下格式:
$<cond1>$ ... $</cond1>$
方法
1. convert(obj, namespace)
递归处理对象中的条件块。
支持:字符串、列表、字典
返回转换后的结果。
2. getVarName(vs)
同 ArgsConvert,去除前后缀得到变量名。
特别地,若以 / 开头表示是闭合标签。
3. getVarValue(var, namespace)
尝试用 eval(var, namespace) 求值,否则 .get(var, None)
4. convertList(parts, namespace)
核心方法:处理由正则分割的字符串片段列表,实现条件判断与嵌套控制。
使用 self.buffer1 作为标签栈记录开启的标签名。
逻辑流程:
- 遍历每个片段:
- 若不是标签 → 添加到结果
- 若是开启标签(如
$<abc>$)→ 压栈,进入子块 - 若是关闭标签(如
$</abc>$)→ 出栈校验,求值决定是否保留子块内容
- 若最终栈非空 → 抛出
ConvertException(标签未闭合)
返回值:(result_str, remaining_list) —— 已处理的结果和剩余待处理部分
5. convertUnicode(s, namespace) 和 convertString(...)
分别处理 Unicode 和普通字符串,统一调用 convertList 实现。
使用示例
示例 1:ArgsConvert 基础用法
ns = {
'a': 12,
'b': 'of',
'c': 'abc',
'是': 'is',
'd': {
'a': 'doc',
'b': 'gg'
}
}
AC = ArgsConvert('%{','}%')
s1 = "%{a}% is a number,%{d['b']}% is %{是}% undefined,%{c}% is %{d['a']+'(rr)'}% string"
print(AC.convert(s1, ns))
# 输出: "12 is a number,gg is is undefined,abc is doc(rr) string"
复杂结构转换
argdict = {
'my': ['this is a descrciption %{b}%', 123, 'ereg%{a}%,%{c}%'],
'b': s1
}
converted = AC.convert(argdict, ns)
# 所有嵌套层级中的 %{...}% 都会被正确替换
示例 2:ConditionConvert 条件渲染
cc = ConditionConvert()
s2 = "Begin $<abc>$this is $<ba>$ba = 100 $</ba>$condition out$</abc>$ end"
result = cc.convert(s2, {'ba': 23})
print(result)
# 输出: "Begin this is ba = 100 condition out end"
# 因为 'ba' 存在且非 False/null,所以内容保留
SQL 模板构建
sql_template = """
select * from RPT_BONDRATINGS
where 1=1
$<rtype>$and ratingtype=${rtype}$$</rtype>$
$<bond>$and bond_id = ${bond}$$</bond>$
"""
result = cc.convert(sql_template, {'bond': '943', 'rtype': '1'})
# 输出:
"""
select * from RPT_BONDRATINGS
where 1=1
and ratingtype=1
and bond_id = 943
"""
💡 注:
${}在此仅为占位符形式,实际仍由ArgsConvert处理更合适,此处演示结合潜力。
安全注意事项
⚠️ 严重警告:
getVarValue() 使用了 eval(),存在潜在的安全风险!
- 不应将用户输入直接放入占位符表达式
- 建议限制命名空间权限,避免泄露敏感函数
- 推荐替代方案:使用
ast.literal_eval或自定义表达式解析器
已知限制
| 项目 | 说明 |
|---|---|
| 编码参数无作用 | coding 参数未被实际使用 |
eval 安全问题 |
直接执行任意表达式,需谨慎使用 |
| 标签必须严格匹配 | <a><b></a></b> 会报错 |
| 不支持 else 分支 | 仅支持 if-like 结构 |
| 性能 | 每次 split + findall,大文本效率较低 |
设计建议(改进方向)
| 改进建议 | 说明 |
|---|---|
| 引入沙箱机制 | 限制 eval 可访问的内置函数 |
| 支持更多语法 | 如 $<not cond>$...$</not>$ |
使用 AST 解析代替 eval |
更安全地处理表达式 |
| 添加缓存编译正则 | 提升性能 |
| 支持异步命名空间解析 | 更灵活的数据源 |
总结
| 类名 | 用途 | 适用场景 |
|---|---|---|
ArgsConvert |
模板变量替换 | 日志模板、邮件内容、动态配置 |
ConditionConvert |
条件文本渲染 | 动态 SQL、HTML 模板、报告生成 |
两者配合可构建轻量级模板引擎,适合内部系统快速开发。
附录:正则表达式详解
ArgsConvert 的 re1
ps = '\\%\\{'
ss = '\\}\\%'
re1 = ps + r'.*?' + ss # 非贪婪匹配任意字符
匹配:%{任意内容}%
原注释中更复杂的版本被注释掉,当前使用通配模式。
ConditionConvert 的 re1
pS = '\\$\\<'
sS = '\\>\\$'
pattern = '(' + pS + '/?' + '[_a-zA-Z_\\u4e00-\\u9fa5][...]*' + sS + ')'
匹配:
$<tag_name>$$</tag_name>$
标签名支持中文、字母、数字、下划线及常见符号。
📌 作者: Unknown
📅 最后更新: 2025年4月5日
📚 适用版本: Python 3.x