From 31be9d0e97c32900c4c1ca1c63ad5da64a9753c2 Mon Sep 17 00:00:00 2001 From: mrzhou Date: Fri, 27 Feb 2026 17:10:54 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=89=88=E6=9C=AC=E5=B9=B6?= =?UTF-8?q?=E5=81=9A=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MANIFEST.in | 2 +- README.md | 2 +- README.py | 12 +- {py_enjoy => pyenjoy}/__init__.py | 2 +- {py_enjoy => pyenjoy}/kit/Func.py | 0 {py_enjoy => pyenjoy}/kit/HashKit.py | 0 {py_enjoy => pyenjoy}/kit/JavaKeyword.py | 0 {py_enjoy => pyenjoy}/kit/Kv.py | 0 {py_enjoy => pyenjoy}/kit/Okv.py | 0 {py_enjoy => pyenjoy}/kit/Prop.py | 0 {py_enjoy => pyenjoy}/kit/PropKit.py | 0 {py_enjoy => pyenjoy}/kit/ReflectKit.py | 0 {py_enjoy => pyenjoy}/kit/StrKit.py | 0 {py_enjoy => pyenjoy}/kit/SyncWriteMap.py | 0 {py_enjoy => pyenjoy}/kit/TimeKit.py | 0 {py_enjoy => pyenjoy}/kit/TypeKit.py | 0 {py_enjoy => pyenjoy}/proxy/ProxyClass.py | 0 .../proxy/ProxyClassLoader.py | 0 {py_enjoy => pyenjoy}/proxy/ProxyCompiler.py | 0 {py_enjoy => pyenjoy}/template/Directive.py | 0 {py_enjoy => pyenjoy}/template/Engine.py | 0 .../template/EngineConfig.py | 0 {py_enjoy => pyenjoy}/template/Env.py | 0 {py_enjoy => pyenjoy}/template/Template.py | 0 .../template/TemplateException.py | 0 .../template/expr/__init__.py | 0 .../template/expr/ast/Arith.py | 0 .../template/expr/ast/Compare.py | 0 .../template/expr/ast/Const.py | 0 .../template/expr/ast/Expr.py | 0 .../template/expr/ast/FieldKeyBuilder.py | 0 .../template/expr/ast/FieldKit.py | 0 .../template/expr/ast/MethodKit.py | 0 .../template/expr/ast/SharedMethodKit.py | 0 .../template/expr/ast/__init__.py | 0 .../template/ext/__init__.py | 0 .../template/ext/directive/CallDirective.py | 0 .../template/ext/directive/DateDirective.py | 0 .../template/ext/directive/EscapeDirective.py | 0 .../template/ext/directive/NumberDirective.py | 0 .../template/ext/directive/RandomDirective.py | 0 .../template/ext/directive/RenderDirective.py | 0 .../template/ext/directive/StringDirective.py | 0 .../template/ext/directive/__init__.py | 0 .../ext/sharedmethod/SharedMethodLib.py | 0 .../template/ext/sharedmethod/__init__.py | 0 .../template/io/EncoderFactory.py | 0 .../template/io/JdkEncoder.py | 0 .../template/io/JdkEncoderFactory.py | 0 .../template/io/WriterBuffer.py | 0 {py_enjoy => pyenjoy}/template/io/__init__.py | 0 .../template/source/ClassPathSource.py | 0 .../template/source/ClassPathSourceFactory.py | 0 .../template/source/FileSource.py | 0 .../template/source/FileSourceFactory.py | 0 .../template/source/ISourceFactory.py | 0 .../template/source/StringSource.py | 0 .../template/source/__init__.py | 0 .../template/stat/Compressor.py | 0 .../template/stat/OutputDirectiveFactory.py | 0 {py_enjoy => pyenjoy}/template/stat/Parser.py | 104 +++++++++++++++++- {py_enjoy => pyenjoy}/template/stat/Scope.py | 0 .../template/stat/ast/Define.py | 0 .../template/stat/ast/For.py | 28 ++++- .../template/stat/ast/Output.py | 0 .../template/stat/ast/Stat.py | 0 .../template/stat/ast/Text.py | 0 .../template/stat/ast/__init__.py | 0 render_example.py | 89 +++++++++++++++ setup.py | 10 +- templates/test.html | 23 ++++ test.py | 14 +-- test_file_template.py | 98 +++++++++++++++++ test_file_template_newline.py | 104 ++++++++++++++++++ test_filters.py | 66 +++++++++++ test_null_coalescing.py | 64 +++++++++++ test_user_scenario.py | 86 +++++++++++++++ 77 files changed, 679 insertions(+), 25 deletions(-) rename {py_enjoy => pyenjoy}/__init__.py (97%) rename {py_enjoy => pyenjoy}/kit/Func.py (100%) rename {py_enjoy => pyenjoy}/kit/HashKit.py (100%) rename {py_enjoy => pyenjoy}/kit/JavaKeyword.py (100%) rename {py_enjoy => pyenjoy}/kit/Kv.py (100%) rename {py_enjoy => pyenjoy}/kit/Okv.py (100%) rename {py_enjoy => pyenjoy}/kit/Prop.py (100%) rename {py_enjoy => pyenjoy}/kit/PropKit.py (100%) rename {py_enjoy => pyenjoy}/kit/ReflectKit.py (100%) rename {py_enjoy => pyenjoy}/kit/StrKit.py (100%) rename {py_enjoy => pyenjoy}/kit/SyncWriteMap.py (100%) rename {py_enjoy => pyenjoy}/kit/TimeKit.py (100%) rename {py_enjoy => pyenjoy}/kit/TypeKit.py (100%) rename {py_enjoy => pyenjoy}/proxy/ProxyClass.py (100%) rename {py_enjoy => pyenjoy}/proxy/ProxyClassLoader.py (100%) rename {py_enjoy => pyenjoy}/proxy/ProxyCompiler.py (100%) rename {py_enjoy => pyenjoy}/template/Directive.py (100%) rename {py_enjoy => pyenjoy}/template/Engine.py (100%) rename {py_enjoy => pyenjoy}/template/EngineConfig.py (100%) rename {py_enjoy => pyenjoy}/template/Env.py (100%) rename {py_enjoy => pyenjoy}/template/Template.py (100%) rename {py_enjoy => pyenjoy}/template/TemplateException.py (100%) rename {py_enjoy => pyenjoy}/template/expr/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/Arith.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/Compare.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/Const.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/Expr.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/FieldKeyBuilder.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/FieldKit.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/MethodKit.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/SharedMethodKit.py (100%) rename {py_enjoy => pyenjoy}/template/expr/ast/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/ext/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/CallDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/DateDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/EscapeDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/NumberDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/RandomDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/RenderDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/StringDirective.py (100%) rename {py_enjoy => pyenjoy}/template/ext/directive/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/ext/sharedmethod/SharedMethodLib.py (100%) rename {py_enjoy => pyenjoy}/template/ext/sharedmethod/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/io/EncoderFactory.py (100%) rename {py_enjoy => pyenjoy}/template/io/JdkEncoder.py (100%) rename {py_enjoy => pyenjoy}/template/io/JdkEncoderFactory.py (100%) rename {py_enjoy => pyenjoy}/template/io/WriterBuffer.py (100%) rename {py_enjoy => pyenjoy}/template/io/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/source/ClassPathSource.py (100%) rename {py_enjoy => pyenjoy}/template/source/ClassPathSourceFactory.py (100%) rename {py_enjoy => pyenjoy}/template/source/FileSource.py (100%) rename {py_enjoy => pyenjoy}/template/source/FileSourceFactory.py (100%) rename {py_enjoy => pyenjoy}/template/source/ISourceFactory.py (100%) rename {py_enjoy => pyenjoy}/template/source/StringSource.py (100%) rename {py_enjoy => pyenjoy}/template/source/__init__.py (100%) rename {py_enjoy => pyenjoy}/template/stat/Compressor.py (100%) rename {py_enjoy => pyenjoy}/template/stat/OutputDirectiveFactory.py (100%) rename {py_enjoy => pyenjoy}/template/stat/Parser.py (69%) rename {py_enjoy => pyenjoy}/template/stat/Scope.py (100%) rename {py_enjoy => pyenjoy}/template/stat/ast/Define.py (100%) rename {py_enjoy => pyenjoy}/template/stat/ast/For.py (74%) rename {py_enjoy => pyenjoy}/template/stat/ast/Output.py (100%) rename {py_enjoy => pyenjoy}/template/stat/ast/Stat.py (100%) rename {py_enjoy => pyenjoy}/template/stat/ast/Text.py (100%) rename {py_enjoy => pyenjoy}/template/stat/ast/__init__.py (100%) create mode 100644 render_example.py create mode 100644 templates/test.html create mode 100644 test_file_template.py create mode 100644 test_file_template_newline.py create mode 100644 test_filters.py create mode 100644 test_null_coalescing.py create mode 100644 test_user_scenario.py diff --git a/MANIFEST.in b/MANIFEST.in index 0f4ef16..7352f35 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include README.md include LICENSE -recursive-include py_enjoy * +recursive-include pyenjoy * diff --git a/README.md b/README.md index d10d661..42d2606 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# py_enjoy +# py-enjoy jfinal-enjoy的 python3.9+ 实现 diff --git a/README.py b/README.py index 3083da9..be6d96d 100644 --- a/README.py +++ b/README.py @@ -9,7 +9,7 @@ This is a Python 3.9+ implementation of the JFinal Template Engine (Enjoy). Project Structure ----------------- -py_enjoy/ +pyenjoy/ ├── __init__.py # Main package initialization ├── kit/ # Utility classes │ ├── StrKit.py # String utilities @@ -93,19 +93,19 @@ Installation ```bash # Clone or download the project -cd py_enjoy +cd pyenjoy # No installation required, just add to PYTHONPATH import sys -sys.path.append('/path/to/py_enjoy') +sys.path.append('/path/to/pyenjoy') ``` Quick Start ----------- ```python -from py_enjoy.template.Engine import Engine -from py_enjoy.kit.Kv import Kv +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv # Get the template engine engine = Engine.use() @@ -145,7 +145,7 @@ data = Kv.of("items", [ ### Custom Directives ```python -from py_enjoy.template.Directive import Directive +from pyenjoy.template.Directive import Directive class UpperDirective(Directive): def exec(self, env, scope, writer): diff --git a/py_enjoy/__init__.py b/pyenjoy/__init__.py similarity index 97% rename from py_enjoy/__init__.py rename to pyenjoy/__init__.py index 3706e16..b99ec7a 100644 --- a/py_enjoy/__init__.py +++ b/pyenjoy/__init__.py @@ -4,7 +4,7 @@ py_enjoy - Python implementation of JFinal Template Engine """ -__version__ = "5.2.2" +__version__ = "1.0.1" __author__ = "James Zhan 詹波 (original), Python port by mrzhou@miw.cn" __license__ = "Apache License 2.0" diff --git a/py_enjoy/kit/Func.py b/pyenjoy/kit/Func.py similarity index 100% rename from py_enjoy/kit/Func.py rename to pyenjoy/kit/Func.py diff --git a/py_enjoy/kit/HashKit.py b/pyenjoy/kit/HashKit.py similarity index 100% rename from py_enjoy/kit/HashKit.py rename to pyenjoy/kit/HashKit.py diff --git a/py_enjoy/kit/JavaKeyword.py b/pyenjoy/kit/JavaKeyword.py similarity index 100% rename from py_enjoy/kit/JavaKeyword.py rename to pyenjoy/kit/JavaKeyword.py diff --git a/py_enjoy/kit/Kv.py b/pyenjoy/kit/Kv.py similarity index 100% rename from py_enjoy/kit/Kv.py rename to pyenjoy/kit/Kv.py diff --git a/py_enjoy/kit/Okv.py b/pyenjoy/kit/Okv.py similarity index 100% rename from py_enjoy/kit/Okv.py rename to pyenjoy/kit/Okv.py diff --git a/py_enjoy/kit/Prop.py b/pyenjoy/kit/Prop.py similarity index 100% rename from py_enjoy/kit/Prop.py rename to pyenjoy/kit/Prop.py diff --git a/py_enjoy/kit/PropKit.py b/pyenjoy/kit/PropKit.py similarity index 100% rename from py_enjoy/kit/PropKit.py rename to pyenjoy/kit/PropKit.py diff --git a/py_enjoy/kit/ReflectKit.py b/pyenjoy/kit/ReflectKit.py similarity index 100% rename from py_enjoy/kit/ReflectKit.py rename to pyenjoy/kit/ReflectKit.py diff --git a/py_enjoy/kit/StrKit.py b/pyenjoy/kit/StrKit.py similarity index 100% rename from py_enjoy/kit/StrKit.py rename to pyenjoy/kit/StrKit.py diff --git a/py_enjoy/kit/SyncWriteMap.py b/pyenjoy/kit/SyncWriteMap.py similarity index 100% rename from py_enjoy/kit/SyncWriteMap.py rename to pyenjoy/kit/SyncWriteMap.py diff --git a/py_enjoy/kit/TimeKit.py b/pyenjoy/kit/TimeKit.py similarity index 100% rename from py_enjoy/kit/TimeKit.py rename to pyenjoy/kit/TimeKit.py diff --git a/py_enjoy/kit/TypeKit.py b/pyenjoy/kit/TypeKit.py similarity index 100% rename from py_enjoy/kit/TypeKit.py rename to pyenjoy/kit/TypeKit.py diff --git a/py_enjoy/proxy/ProxyClass.py b/pyenjoy/proxy/ProxyClass.py similarity index 100% rename from py_enjoy/proxy/ProxyClass.py rename to pyenjoy/proxy/ProxyClass.py diff --git a/py_enjoy/proxy/ProxyClassLoader.py b/pyenjoy/proxy/ProxyClassLoader.py similarity index 100% rename from py_enjoy/proxy/ProxyClassLoader.py rename to pyenjoy/proxy/ProxyClassLoader.py diff --git a/py_enjoy/proxy/ProxyCompiler.py b/pyenjoy/proxy/ProxyCompiler.py similarity index 100% rename from py_enjoy/proxy/ProxyCompiler.py rename to pyenjoy/proxy/ProxyCompiler.py diff --git a/py_enjoy/template/Directive.py b/pyenjoy/template/Directive.py similarity index 100% rename from py_enjoy/template/Directive.py rename to pyenjoy/template/Directive.py diff --git a/py_enjoy/template/Engine.py b/pyenjoy/template/Engine.py similarity index 100% rename from py_enjoy/template/Engine.py rename to pyenjoy/template/Engine.py diff --git a/py_enjoy/template/EngineConfig.py b/pyenjoy/template/EngineConfig.py similarity index 100% rename from py_enjoy/template/EngineConfig.py rename to pyenjoy/template/EngineConfig.py diff --git a/py_enjoy/template/Env.py b/pyenjoy/template/Env.py similarity index 100% rename from py_enjoy/template/Env.py rename to pyenjoy/template/Env.py diff --git a/py_enjoy/template/Template.py b/pyenjoy/template/Template.py similarity index 100% rename from py_enjoy/template/Template.py rename to pyenjoy/template/Template.py diff --git a/py_enjoy/template/TemplateException.py b/pyenjoy/template/TemplateException.py similarity index 100% rename from py_enjoy/template/TemplateException.py rename to pyenjoy/template/TemplateException.py diff --git a/py_enjoy/template/expr/__init__.py b/pyenjoy/template/expr/__init__.py similarity index 100% rename from py_enjoy/template/expr/__init__.py rename to pyenjoy/template/expr/__init__.py diff --git a/py_enjoy/template/expr/ast/Arith.py b/pyenjoy/template/expr/ast/Arith.py similarity index 100% rename from py_enjoy/template/expr/ast/Arith.py rename to pyenjoy/template/expr/ast/Arith.py diff --git a/py_enjoy/template/expr/ast/Compare.py b/pyenjoy/template/expr/ast/Compare.py similarity index 100% rename from py_enjoy/template/expr/ast/Compare.py rename to pyenjoy/template/expr/ast/Compare.py diff --git a/py_enjoy/template/expr/ast/Const.py b/pyenjoy/template/expr/ast/Const.py similarity index 100% rename from py_enjoy/template/expr/ast/Const.py rename to pyenjoy/template/expr/ast/Const.py diff --git a/py_enjoy/template/expr/ast/Expr.py b/pyenjoy/template/expr/ast/Expr.py similarity index 100% rename from py_enjoy/template/expr/ast/Expr.py rename to pyenjoy/template/expr/ast/Expr.py diff --git a/py_enjoy/template/expr/ast/FieldKeyBuilder.py b/pyenjoy/template/expr/ast/FieldKeyBuilder.py similarity index 100% rename from py_enjoy/template/expr/ast/FieldKeyBuilder.py rename to pyenjoy/template/expr/ast/FieldKeyBuilder.py diff --git a/py_enjoy/template/expr/ast/FieldKit.py b/pyenjoy/template/expr/ast/FieldKit.py similarity index 100% rename from py_enjoy/template/expr/ast/FieldKit.py rename to pyenjoy/template/expr/ast/FieldKit.py diff --git a/py_enjoy/template/expr/ast/MethodKit.py b/pyenjoy/template/expr/ast/MethodKit.py similarity index 100% rename from py_enjoy/template/expr/ast/MethodKit.py rename to pyenjoy/template/expr/ast/MethodKit.py diff --git a/py_enjoy/template/expr/ast/SharedMethodKit.py b/pyenjoy/template/expr/ast/SharedMethodKit.py similarity index 100% rename from py_enjoy/template/expr/ast/SharedMethodKit.py rename to pyenjoy/template/expr/ast/SharedMethodKit.py diff --git a/py_enjoy/template/expr/ast/__init__.py b/pyenjoy/template/expr/ast/__init__.py similarity index 100% rename from py_enjoy/template/expr/ast/__init__.py rename to pyenjoy/template/expr/ast/__init__.py diff --git a/py_enjoy/template/ext/__init__.py b/pyenjoy/template/ext/__init__.py similarity index 100% rename from py_enjoy/template/ext/__init__.py rename to pyenjoy/template/ext/__init__.py diff --git a/py_enjoy/template/ext/directive/CallDirective.py b/pyenjoy/template/ext/directive/CallDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/CallDirective.py rename to pyenjoy/template/ext/directive/CallDirective.py diff --git a/py_enjoy/template/ext/directive/DateDirective.py b/pyenjoy/template/ext/directive/DateDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/DateDirective.py rename to pyenjoy/template/ext/directive/DateDirective.py diff --git a/py_enjoy/template/ext/directive/EscapeDirective.py b/pyenjoy/template/ext/directive/EscapeDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/EscapeDirective.py rename to pyenjoy/template/ext/directive/EscapeDirective.py diff --git a/py_enjoy/template/ext/directive/NumberDirective.py b/pyenjoy/template/ext/directive/NumberDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/NumberDirective.py rename to pyenjoy/template/ext/directive/NumberDirective.py diff --git a/py_enjoy/template/ext/directive/RandomDirective.py b/pyenjoy/template/ext/directive/RandomDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/RandomDirective.py rename to pyenjoy/template/ext/directive/RandomDirective.py diff --git a/py_enjoy/template/ext/directive/RenderDirective.py b/pyenjoy/template/ext/directive/RenderDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/RenderDirective.py rename to pyenjoy/template/ext/directive/RenderDirective.py diff --git a/py_enjoy/template/ext/directive/StringDirective.py b/pyenjoy/template/ext/directive/StringDirective.py similarity index 100% rename from py_enjoy/template/ext/directive/StringDirective.py rename to pyenjoy/template/ext/directive/StringDirective.py diff --git a/py_enjoy/template/ext/directive/__init__.py b/pyenjoy/template/ext/directive/__init__.py similarity index 100% rename from py_enjoy/template/ext/directive/__init__.py rename to pyenjoy/template/ext/directive/__init__.py diff --git a/py_enjoy/template/ext/sharedmethod/SharedMethodLib.py b/pyenjoy/template/ext/sharedmethod/SharedMethodLib.py similarity index 100% rename from py_enjoy/template/ext/sharedmethod/SharedMethodLib.py rename to pyenjoy/template/ext/sharedmethod/SharedMethodLib.py diff --git a/py_enjoy/template/ext/sharedmethod/__init__.py b/pyenjoy/template/ext/sharedmethod/__init__.py similarity index 100% rename from py_enjoy/template/ext/sharedmethod/__init__.py rename to pyenjoy/template/ext/sharedmethod/__init__.py diff --git a/py_enjoy/template/io/EncoderFactory.py b/pyenjoy/template/io/EncoderFactory.py similarity index 100% rename from py_enjoy/template/io/EncoderFactory.py rename to pyenjoy/template/io/EncoderFactory.py diff --git a/py_enjoy/template/io/JdkEncoder.py b/pyenjoy/template/io/JdkEncoder.py similarity index 100% rename from py_enjoy/template/io/JdkEncoder.py rename to pyenjoy/template/io/JdkEncoder.py diff --git a/py_enjoy/template/io/JdkEncoderFactory.py b/pyenjoy/template/io/JdkEncoderFactory.py similarity index 100% rename from py_enjoy/template/io/JdkEncoderFactory.py rename to pyenjoy/template/io/JdkEncoderFactory.py diff --git a/py_enjoy/template/io/WriterBuffer.py b/pyenjoy/template/io/WriterBuffer.py similarity index 100% rename from py_enjoy/template/io/WriterBuffer.py rename to pyenjoy/template/io/WriterBuffer.py diff --git a/py_enjoy/template/io/__init__.py b/pyenjoy/template/io/__init__.py similarity index 100% rename from py_enjoy/template/io/__init__.py rename to pyenjoy/template/io/__init__.py diff --git a/py_enjoy/template/source/ClassPathSource.py b/pyenjoy/template/source/ClassPathSource.py similarity index 100% rename from py_enjoy/template/source/ClassPathSource.py rename to pyenjoy/template/source/ClassPathSource.py diff --git a/py_enjoy/template/source/ClassPathSourceFactory.py b/pyenjoy/template/source/ClassPathSourceFactory.py similarity index 100% rename from py_enjoy/template/source/ClassPathSourceFactory.py rename to pyenjoy/template/source/ClassPathSourceFactory.py diff --git a/py_enjoy/template/source/FileSource.py b/pyenjoy/template/source/FileSource.py similarity index 100% rename from py_enjoy/template/source/FileSource.py rename to pyenjoy/template/source/FileSource.py diff --git a/py_enjoy/template/source/FileSourceFactory.py b/pyenjoy/template/source/FileSourceFactory.py similarity index 100% rename from py_enjoy/template/source/FileSourceFactory.py rename to pyenjoy/template/source/FileSourceFactory.py diff --git a/py_enjoy/template/source/ISourceFactory.py b/pyenjoy/template/source/ISourceFactory.py similarity index 100% rename from py_enjoy/template/source/ISourceFactory.py rename to pyenjoy/template/source/ISourceFactory.py diff --git a/py_enjoy/template/source/StringSource.py b/pyenjoy/template/source/StringSource.py similarity index 100% rename from py_enjoy/template/source/StringSource.py rename to pyenjoy/template/source/StringSource.py diff --git a/py_enjoy/template/source/__init__.py b/pyenjoy/template/source/__init__.py similarity index 100% rename from py_enjoy/template/source/__init__.py rename to pyenjoy/template/source/__init__.py diff --git a/py_enjoy/template/stat/Compressor.py b/pyenjoy/template/stat/Compressor.py similarity index 100% rename from py_enjoy/template/stat/Compressor.py rename to pyenjoy/template/stat/Compressor.py diff --git a/py_enjoy/template/stat/OutputDirectiveFactory.py b/pyenjoy/template/stat/OutputDirectiveFactory.py similarity index 100% rename from py_enjoy/template/stat/OutputDirectiveFactory.py rename to pyenjoy/template/stat/OutputDirectiveFactory.py diff --git a/py_enjoy/template/stat/Parser.py b/pyenjoy/template/stat/Parser.py similarity index 69% rename from py_enjoy/template/stat/Parser.py rename to pyenjoy/template/stat/Parser.py index e7a4d89..943dc2c 100644 --- a/py_enjoy/template/stat/Parser.py +++ b/pyenjoy/template/stat/Parser.py @@ -274,8 +274,108 @@ class SimpleExprList: # Replace 'for.' with '_for.' in the expression expr_str = expr_str.replace('for.', '_for.') - # Evaluate the expression - return eval(expr_str, {}, wrapped_vars) + # Remove newline characters from expression + expr_str = expr_str.replace('\n', '').replace('\r', '') + + # Split expression by | to handle filters + expr_parts = expr_str.split('|') + main_expr = expr_parts[0].strip() + + # Evaluate main expression first + if '??' in main_expr: + # Custom evaluation for expressions with ?? operator + def evaluate_null_coalescing(expr): + if '??' not in expr: + try: + return eval(expr, {}, wrapped_vars) + except (NameError, AttributeError): + return None + + # Process from right to left for nested ?? + idx = expr.rfind('??') + + # Find left expression + left_expr = expr[:idx].strip() + right_expr = expr[idx+2:].strip() + + # Evaluate left part + left_val = evaluate_null_coalescing(left_expr) + + if left_val is not None: + return left_val + else: + # Evaluate right part + return evaluate_null_coalescing(right_expr) + + result = evaluate_null_coalescing(main_expr) + else: + # Regular evaluation for expressions without ?? + try: + result = eval(main_expr, {}, wrapped_vars) + except (NameError, AttributeError): + result = None + + # Apply filters + for filter_part in expr_parts[1:]: + filter_part = filter_part.strip() + if not filter_part: + continue + + # Parse filter name and arguments + if '(' in filter_part: + filter_name = filter_part[:filter_part.find('(')].strip() + # Extract arguments + args_str = filter_part[filter_part.find('(')+1:filter_part.rfind(')')].strip() + # Simple argument parsing - split by commas, ignoring commas inside quotes + args = [] + current_arg = '' + in_quotes = False + quote_char = '' + for char in args_str: + if char in "'\"" and (not in_quotes or char == quote_char): + if not in_quotes: + in_quotes = True + quote_char = char + else: + in_quotes = False + current_arg += char + elif char == ',' and not in_quotes: + args.append(current_arg.strip()) + current_arg = '' + else: + current_arg += char + if current_arg: + args.append(current_arg.strip()) + else: + filter_name = filter_part.strip() + args = [] + + # Apply filter + if filter_name == 'join': + # Join filter implementation + if len(args) > 0: + # Remove quotes from separator if present + sep = args[0] + if (sep.startswith('"') and sep.endswith('"')) or (sep.startswith("'") and sep.endswith("'")): + sep = sep[1:-1] + else: + sep = '' + + if isinstance(result, (list, tuple)): + result = sep.join(str(item) for item in result) + else: + result = str(result) + elif filter_name == 'upper': + # Upper case filter + result = str(result).upper() + elif filter_name == 'lower': + # Lower case filter + result = str(result).lower() + elif filter_name == 'strip': + # Strip whitespace filter + result = str(result).strip() + + return result except Exception as e: # Handle evaluation errors gracefully return f"Error evaluating expression '{self._expr_str}': {e}" diff --git a/py_enjoy/template/stat/Scope.py b/pyenjoy/template/stat/Scope.py similarity index 100% rename from py_enjoy/template/stat/Scope.py rename to pyenjoy/template/stat/Scope.py diff --git a/py_enjoy/template/stat/ast/Define.py b/pyenjoy/template/stat/ast/Define.py similarity index 100% rename from py_enjoy/template/stat/ast/Define.py rename to pyenjoy/template/stat/ast/Define.py diff --git a/py_enjoy/template/stat/ast/For.py b/pyenjoy/template/stat/ast/For.py similarity index 74% rename from py_enjoy/template/stat/ast/For.py rename to pyenjoy/template/stat/ast/For.py index 139c63d..f0e8c07 100644 --- a/py_enjoy/template/stat/ast/For.py +++ b/pyenjoy/template/stat/ast/For.py @@ -28,15 +28,39 @@ class For(Stat): def exec(self, env: Env, scope: Scope, writer) -> None: """ Execute for loop - + Args: env: Template environment scope: Execution scope writer: Output writer """ try: + # Wrap dict values to allow attribute access + def wrap_dict(d): + """Wrap a dict to allow attribute access""" + if isinstance(d, dict): + # Create a wrapper that allows both dot access and bracket access + class DictWrapper: + def __init__(self, data): + self.__dict__ = data + def __getitem__(self, key): + return data[key] + return DictWrapper(d) + return d + + # Create wrapped vars similar to SimpleExprList.eval() + wrapped_vars = {} + for key, value in scope._data.items(): + if isinstance(value, dict): + wrapped_vars[key] = wrap_dict(value) + elif isinstance(value, list): + # Wrap dictionaries in lists + wrapped_vars[key] = [wrap_dict(item) for item in value] + else: + wrapped_vars[key] = value + # Get the iterable from the expression - iterable = eval(self._iter_expr, {}, scope._data.copy()) + iterable = eval(self._iter_expr, {}, wrapped_vars) # Convert iterable to list for easier manipulation items = list(iterable) diff --git a/py_enjoy/template/stat/ast/Output.py b/pyenjoy/template/stat/ast/Output.py similarity index 100% rename from py_enjoy/template/stat/ast/Output.py rename to pyenjoy/template/stat/ast/Output.py diff --git a/py_enjoy/template/stat/ast/Stat.py b/pyenjoy/template/stat/ast/Stat.py similarity index 100% rename from py_enjoy/template/stat/ast/Stat.py rename to pyenjoy/template/stat/ast/Stat.py diff --git a/py_enjoy/template/stat/ast/Text.py b/pyenjoy/template/stat/ast/Text.py similarity index 100% rename from py_enjoy/template/stat/ast/Text.py rename to pyenjoy/template/stat/ast/Text.py diff --git a/py_enjoy/template/stat/ast/__init__.py b/pyenjoy/template/stat/ast/__init__.py similarity index 100% rename from py_enjoy/template/stat/ast/__init__.py rename to pyenjoy/template/stat/ast/__init__.py diff --git a/render_example.py b/render_example.py new file mode 100644 index 0000000..bb38d91 --- /dev/null +++ b/render_example.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Example demonstrating how to use the render function +""" + +from pyenjoy.template.Engine import Engine + +# Initialize the engine (global instance) +engine = Engine.use() + +# Configure the engine (optional) +engine.base_template_path = "./templates" # Set base template directory +engine.encoding = "utf-8" # Set template encoding +engine.dev_mode = True # Enable auto-reload for development + +def render(page, data): + """ + Render a template file with provided data + + Args: + page: Template file name (relative to base_template_path) + data: Dictionary with template data + + Returns: + Rendered template string + """ + template = engine.get_template(page) + result = template.render_to_string(data) + return result + +def render_string(template_content, data): + """ + Render a template string with provided data + + Args: + template_content: Template content as string + data: Dictionary with template data + + Returns: + Rendered template string + """ + template = engine.get_template_by_string(template_content) + result = template.render_to_string(data) + return result + +# Example usage +if __name__ == "__main__": + # Example 1: Render from file + print("Example 1: Render from file") + print("=" * 50) + try: + # Create a simple template file for testing + import os + # os.makedirs("./templates", exist_ok=True) + # with open("./templates/test.html", "w", encoding="utf-8") as f: + # f.write("Hello, #(name)!") + + # Render the template + data = {"name": "World", "items": [ + {"id": 1, "title": "文章1", "author": "张三", "tags": ["cdms", "管理系统"], "create_time": "2023-01-01"}, + {"id": 2, "title": "文章2", "author": "李四", "tags": ["python"], "create_time": "2023-02-01"} + ]} + result = render("test.html", data) + print(f"Rendered result: {result}") + + # Clean up + # os.remove("./templates/test.html") + # os.rmdir("./templates") + except Exception as e: + print(f"Error: {e}") + + # Example 2: Render from string + print("\nExample 2: Render from string") + print("=" * 50) + template_content = "Name: #(name), Age: #(age??'未知')" + data = {"name": "张三"} + result = render_string(template_content, data) + print(f"Rendered result: {result}") + + # Example 3: Render with filters + print("\nExample 3: Render with filters") + print("=" * 50) + template_content = "Tags: #(tags | join(', '))" + data = {"tags": ["cdms", "管理系统"]} + result = render_string(template_content, data) + print(f"Rendered result: {result}") + + print("\nAll examples completed!") diff --git a/setup.py b/setup.py index 367fde3..80d7b40 100644 --- a/setup.py +++ b/setup.py @@ -8,14 +8,14 @@ with open(os.path.join(os.path.dirname(__file__), 'README.md'), 'r', encoding='u long_description = f.read() setup( - name='py_enjoy', - version='5.2.2', - description='Python implementation of JFinal Template Engine', + name='py-enjoy', + version='1.0.1', + description='JFinal-Enjoy Python 3.9+ 实现,基于5.2.2版本', long_description=long_description, long_description_content_type='text/markdown', author='James Zhan 詹波 (original), Python port by mrzhou@miw.cn', author_email='mrzhou@miw.cn', - url='https://github.com/yourusername/py_enjoy', # 请替换为实际的GitHub仓库URL + url='https://git.miw.cn/mrzhou/py_enjoy.git', # 请替换为实际的GitHub仓库URL license='Apache-2.0', packages=find_packages(), include_package_data=True, @@ -33,5 +33,5 @@ setup( 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Text Processing :: Markup', ], - keywords='template engine, jfinal, python', + keywords='template engine, jfinal, enjoy, python', ) diff --git a/templates/test.html b/templates/test.html new file mode 100644 index 0000000..da224d0 --- /dev/null +++ b/templates/test.html @@ -0,0 +1,23 @@ +Hello, #(name.join("====="))! + +#for(item in items) + + + +#(for.index):::#(item.id) + +#(item.title) + +#(item.author) + +#(item.tags | join(", ")) + +#(item.create_time) + + + + + + + +#end \ No newline at end of file diff --git a/test.py b/test.py index b4caea2..3d391c5 100644 --- a/test.py +++ b/test.py @@ -16,7 +16,7 @@ def test_kit_classes(): print("Testing kit classes...") # Test StrKit - from py_enjoy.kit.StrKit import StrKit + from pyenjoy.kit.StrKit import StrKit assert StrKit.first_char_to_lower_case("Hello") == "hello" assert StrKit.first_char_to_upper_case("hello") == "Hello" assert StrKit.is_blank(" ") == True @@ -25,7 +25,7 @@ def test_kit_classes(): print("✓ StrKit tests passed") # Test HashKit - from py_enjoy.kit.HashKit import HashKit + from pyenjoy.kit.HashKit import HashKit md5_result = HashKit.md5("test") assert len(md5_result) == 32 assert HashKit.slow_equals(b"test", b"test") == True @@ -33,7 +33,7 @@ def test_kit_classes(): print("✓ HashKit tests passed") # Test Kv - from py_enjoy.kit.Kv import Kv + from pyenjoy.kit.Kv import Kv kv = Kv.of("name", "John").set("age", 25) assert kv.get("name") == "John" assert kv.get_int("age") == 25 @@ -41,7 +41,7 @@ def test_kit_classes(): print("✓ Kv tests passed") # Test Prop - from py_enjoy.kit.Prop import Prop + from pyenjoy.kit.Prop import Prop # Create a test properties file test_content = """ name=John @@ -61,8 +61,8 @@ def test_template_engine(): """Test template engine""" print("\nTesting template engine...") - from py_enjoy.template.Engine import Engine - from py_enjoy.kit.Kv import Kv + from pyenjoy.template.Engine import Engine + from pyenjoy.kit.Kv import Kv engine = Engine.use() @@ -85,7 +85,7 @@ def test_type_conversions(): """Test type conversions""" print("\nTesting type conversions...") - from py_enjoy.kit.TypeKit import TypeKit + from pyenjoy.kit.TypeKit import TypeKit assert TypeKit.to_int("123") == 123 assert TypeKit.to_float("3.14") == 3.14 diff --git a/test_file_template.py b/test_file_template.py new file mode 100644 index 0000000..2988bac --- /dev/null +++ b/test_file_template.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Test template from file functionality +""" + +import os +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv + +def test_file_template(): + """Test template from file""" + print("Testing template from file...") + + # Create a test template file + test_dir = "./test_templates" + os.makedirs(test_dir, exist_ok=True) + + # Test case 1: Basic template + test_file1 = os.path.join(test_dir, "test1.txt") + with open(test_file1, "w", encoding="utf-8") as f: + f.write("Hello, #(name)!") + + # Test case 2: Template with null coalescing + test_file2 = os.path.join(test_dir, "test2.txt") + with open(test_file2, "w", encoding="utf-8") as f: + f.write("Data: #(data??'未知')") + + # Test case 3: Template with multiple expressions + test_file3 = os.path.join(test_dir, "test3.txt") + with open(test_file3, "w", encoding="utf-8") as f: + f.write("Name: #(name), Age: #(age??'未知'), City: #(city??'未知')") + + # Create engine and configure base template path + engine = Engine.use() + engine.base_template_path = os.getcwd() + + # Test cases + test_cases = [ + # (file_path, data, expected_result, description) + (test_file1, {"name": "World"}, "Hello, World!", "Basic template"), + (test_file2, {"data": None}, "Data: 未知", "Null coalescing with None"), + (test_file2, {"data": "测试数据"}, "Data: 测试数据", "Null coalescing with value"), + (test_file2, {}, "Data: 未知", "Null coalescing with undefined variable"), + (test_file3, {"name": "张三", "age": 30}, "Name: 张三, Age: 30, City: 未知", "Multiple expressions"), + ] + + passed = 0 + failed = 0 + + for i, (file_path, data, expected, description) in enumerate(test_cases): + try: + print(f"\nTest {i+1}: {description}") + print(f"File: {file_path}") + print(f"Data: {data}") + + # Get template + template = engine.get_template(file_path) + + # Render template + result = template.render_to_string(Kv(data)) + + print(f"Expected: '{expected}'") + print(f"Got: '{result}'") + + if result == expected: + print("✓ Passed") + passed += 1 + else: + print("✗ Failed") + failed += 1 + + # Debug: Check content + print(f"\nDebug info:") + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + print(f"File content: '{content}'") + print(f"Content length: {len(content)}") + print(f"Content repr: {repr(content)}") + + except Exception as e: + print(f"✗ Error: {e}") + failed += 1 + + print(f"\nSummary: {passed} passed, {failed} failed") + + # Clean up + for file_path in [test_file1, test_file2, test_file3]: + if os.path.exists(file_path): + os.remove(file_path) + if os.path.exists(test_dir): + os.rmdir(test_dir) + + return passed == len(test_cases) + +if __name__ == "__main__": + success = test_file_template() + exit(0 if success else 1) diff --git a/test_file_template_newline.py b/test_file_template_newline.py new file mode 100644 index 0000000..4a62855 --- /dev/null +++ b/test_file_template_newline.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Test template from file with newlines functionality +""" + +import os +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv + +def test_file_template_newline(): + """Test template from file with newlines""" + print("Testing template from file with newlines...") + + # Create a test template file + test_dir = "./test_templates" + os.makedirs(test_dir, exist_ok=True) + + # Test case 1: Template with expression containing newlines + test_file1 = os.path.join(test_dir, "test_newline.txt") + with open(test_file1, "w", encoding="utf-8") as f: + f.write("Name: #(name),\nAge: #(age),\nCity: #(city??'未知')") + + # Test case 2: Template with multi-line expression (using triple quotes) + test_file2 = os.path.join(test_dir, "test_multiline_expr.txt") + with open(test_file2, "w", encoding="utf-8") as f: + f.write("""Result: #(data ?? + '未知')""") + + # Test case 3: Template with complex multi-line expression + test_file3 = os.path.join(test_dir, "test_complex_multiline.txt") + with open(test_file3, "w", encoding="utf-8") as f: + f.write("""Details: #(user.name ?? '匿名') - + #(user.age ?? '未知') - + #(user.city ?? '未知')""") + + # Test case 4: Template with single expression on multiple lines + test_file4 = os.path.join(test_dir, "test_single_multiline.txt") + with open(test_file4, "w", encoding="utf-8") as f: + f.write("Value: #(\n data \n ?? \n 'default'\n)") + + # Create engine and configure base template path + engine = Engine.use() + engine.base_template_path = os.getcwd() + + # Test cases + test_cases = [ + # (file_path, data, expected_result, description) + (test_file1, {"name": "张三", "age": 30}, "Name: 张三,\nAge: 30,\nCity: 未知", "Template with newlines"), + (test_file4, {"data": None}, "Value: default", "Single expression on multiple lines"), + (test_file4, {"data": "测试"}, "Value: 测试", "Single expression with value on multiple lines"), + ] + + passed = 0 + failed = 0 + + for i, (file_path, data, expected, description) in enumerate(test_cases): + try: + print(f"\nTest {i+1}: {description}") + print(f"File: {file_path}") + print(f"Data: {data}") + + # Get template + template = engine.get_template(file_path) + + # Render template + result = template.render_to_string(Kv(data)) + + print(f"Expected: '{expected}'") + print(f"Got: '{result}'") + + if result == expected: + print("✓ Passed") + passed += 1 + else: + print("✗ Failed") + failed += 1 + + # Debug: Check content + print(f"\nDebug info:") + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + print(f"File content: '{content}'") + print(f"Content length: {len(content)}") + print(f"Content repr: {repr(content)}") + + except Exception as e: + print(f"✗ Error: {e}") + failed += 1 + + print(f"\nSummary: {passed} passed, {failed} failed") + + # Clean up + for file_path in [test_file1, test_file2, test_file3, test_file4]: + if os.path.exists(file_path): + os.remove(file_path) + if os.path.exists(test_dir): + os.rmdir(test_dir) + + return passed == len(test_cases) + +if __name__ == "__main__": + success = test_file_template_newline() + exit(0 if success else 1) diff --git a/test_filters.py b/test_filters.py new file mode 100644 index 0000000..b9879d3 --- /dev/null +++ b/test_filters.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Test template filters functionality +""" + +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv + +def test_filters(): + """Test template filters""" + print("Testing template filters...") + + engine = Engine.use() + + # Test cases + test_cases = [ + # Template, data, expected result, description + ("#(tags | join(', '))", {"tags": ["cdms", "管理系统"]}, "cdms, 管理系统", "Basic join filter"), + ("#(tags | join(';'))", {"tags": ["cdms", "管理系统"]}, "cdms;管理系统", "Join with custom separator"), + ("#(tags | join)", {"tags": ["cdms", "管理系统"]}, "cdms管理系统", "Join without separator"), + ("#(title | upper)", {"title": "test"}, "TEST", "Upper case filter"), + ("#(title | lower)", {"title": "TEST"}, "test", "Lower case filter"), + ("#(title | strip)", {"title": " test "}, "test", "Strip whitespace filter"), + # Multiple filters + ("#(title | upper | strip)", {"title": " test "}, "TEST", "Multiple filters"), + # Filter with null coalescing + ("#(tags??[] | join(', '))", {"tags": None}, "", "Join with null coalescing"), + # Complex scenario from user input + ("#for(item in data.items)#(item.tags | join(', '))#end", + {"data": {"items": [{"tags": ["cdms", "管理系统"]}]}}, + "cdms, 管理系统", "Complex scenario with for loop"), + ] + + passed = 0 + failed = 0 + + for i, (template_str, data, expected, description) in enumerate(test_cases): + try: + print(f"\nTest {i+1}: {description}") + print(f"Template: {template_str}") + print(f"Data: {data}") + + template = engine.get_template_by_string(template_str) + result = template.render_to_string(Kv(data)) + + print(f"Expected: '{expected}'") + print(f"Got: '{result}'") + + if result == expected: + print("✓ Passed") + passed += 1 + else: + print("✗ Failed") + failed += 1 + + except Exception as e: + print(f"✗ Error: {e}") + failed += 1 + + print(f"\nSummary: {passed} passed, {failed} failed") + return passed == len(test_cases) + +if __name__ == "__main__": + success = test_filters() + exit(0 if success else 1) diff --git a/test_null_coalescing.py b/test_null_coalescing.py new file mode 100644 index 0000000..92d5e7c --- /dev/null +++ b/test_null_coalescing.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Test null coalescing operator ?? functionality +""" + +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv + +def test_null_coalescing(): + """Test null coalescing operator""" + print("Testing null coalescing operator...") + + engine = Engine.use() + + # Test cases + test_cases = [ + # Template, data, expected result + ("#(data??'未知')", {"data": None}, "未知"), + ("#(data??'未知')", {"data": "测试"}, "测试"), + ("#(data??'未知')", {}, "未知"), + ("#(user.name??'匿名')", {"user": {"name": "张三"}}, "张三"), + ("#(user.name??'匿名')", {"user": {}}, "匿名"), + ("#(user.name??'匿名')", {}, "匿名"), + # Nested null coalescing + ("#(data1??data2??'默认值')", {"data1": None, "data2": None}, "默认值"), + ("#(data1??data2??'默认值')", {"data1": None, "data2": "中间值"}, "中间值"), + ("#(data1??data2??'默认值')", {"data1": "第一个值", "data2": "中间值"}, "第一个值"), + ] + + passed = 0 + failed = 0 + + for i, (template_str, data, expected) in enumerate(test_cases): + try: + template = engine.get_template_by_string(template_str) + result = template.render_to_string(Kv(data)) + if result == expected: + print(f"✓ Test {i+1}: Passed") + print(f" Template: {template_str}") + print(f" Data: {data}") + print(f" Result: {result}") + passed += 1 + else: + print(f"✗ Test {i+1}: Failed") + print(f" Template: {template_str}") + print(f" Data: {data}") + print(f" Expected: {expected}") + print(f" Got: {result}") + failed += 1 + except Exception as e: + print(f"✗ Test {i+1}: Error") + print(f" Template: {template_str}") + print(f" Data: {data}") + print(f" Error: {e}") + failed += 1 + print() + + print(f"Summary: {passed} passed, {failed} failed") + return passed == len(test_cases) + +if __name__ == "__main__": + success = test_null_coalescing() + exit(0 if success else 1) diff --git a/test_user_scenario.py b/test_user_scenario.py new file mode 100644 index 0000000..d8fae69 --- /dev/null +++ b/test_user_scenario.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3.9 +# -*- coding: utf-8 -*- +""" +Test the exact user scenario with real data and template +""" + +import os +from pyenjoy.template.Engine import Engine +from pyenjoy.kit.Kv import Kv + +def test_user_scenario(): + """Test the exact user scenario""" + print("Testing user scenario...") + + # User's data + data = { + 'items': [ + { + 'id': 'wiwInpwBRcYlH5JfMuby', + 'title': 'CDMS 内容数据管理系统2', + 'content': 'CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统', + 'tags': ['cdms', ' 管理系统'], + 'author': '赵钱孙', + 'create_time': '2026-02-27T15:37:43.207513', + 'update_time': '2026-02-27T15:37:43.207530', + 'status': 'active' + }, + { + 'id': 'wSwHnpwBRcYlH5Jf7-Zd', + 'title': 'CDMS 内容数据管理系统', + 'content': 'CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统CDMS 内容数据管理系统', + 'tags': ['cdms', ' 管理系统'], + 'author': '赵钱孙', + 'create_time': '2026-02-27T15:37:25.907558', + 'update_time': '2026-02-27T15:37:25.907576', + 'status': 'active' + } + ], + 'total': 2, + 'page': 1, + 'size': 10, + 'pages': 1 + } + + # User's template + template_content = """ + #for(item in data.items) + + #(item.id) + #(item.title) + #(item.author) + #(item.tags | join(", ")) + #(item.create_time) + + + + #end + """ + + # Create engine and test + engine = Engine.use() + + try: + template = engine.get_template_by_string(template_content) + result = template.render_to_string(Kv({"data": data})) + + print("\nTemplate rendering result:") + print("=" * 50) + print(result) + print("=" * 50) + print("\n✓ User scenario test passed!") + + # Verify the join filter worked correctly + if "cdms, 管理系统" in result: + print("✓ Join filter worked correctly!") + else: + print("✗ Join filter did not work as expected") + + return True + except Exception as e: + print(f"\n✗ User scenario test failed: {e}") + return False + +if __name__ == "__main__": + success = test_user_scenario() + exit(0 if success else 1)