调整版本并做测试
This commit is contained in:
401
pyenjoy/template/Engine.py
Normal file
401
pyenjoy/template/Engine.py
Normal file
@@ -0,0 +1,401 @@
|
||||
#!/usr/bin/env python3.9
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
JFinal Engine - Template Engine Main Class
|
||||
"""
|
||||
|
||||
from typing import Dict, Optional, Set, Type, Any, Callable
|
||||
from .EngineConfig import EngineConfig
|
||||
from .Env import Env
|
||||
from .Template import Template
|
||||
from .stat.Parser import Parser
|
||||
from .stat.ast.Stat import Stat
|
||||
from .source.ISourceFactory import ISourceFactory
|
||||
from .source.ClassPathSourceFactory import ClassPathSourceFactory
|
||||
from .source.StringSource import StringSource
|
||||
from .io.WriterBuffer import WriterBuffer
|
||||
from .io.EncoderFactory import EncoderFactory
|
||||
from .io.JdkEncoderFactory import JdkEncoderFactory
|
||||
from .stat.Compressor import Compressor
|
||||
from .expr.ast.FieldKit import FieldKit
|
||||
from .expr.ast.FieldKeyBuilder import FieldKeyBuilder
|
||||
from .expr.ast.MethodKit import MethodKit
|
||||
from .Directive import Directive
|
||||
from .stat.OutputDirectiveFactory import OutputDirectiveFactory
|
||||
from ..kit.SyncWriteMap import SyncWriteMap
|
||||
from ..kit.StrKit import StrKit
|
||||
from ..kit.HashKit import HashKit
|
||||
|
||||
class Engine:
|
||||
"""Template Engine - Main class for JFinal Template Engine"""
|
||||
|
||||
MAIN_ENGINE_NAME = "main"
|
||||
|
||||
_MAIN_ENGINE: 'Engine' = None
|
||||
_engine_map: Dict[str, 'Engine'] = {}
|
||||
|
||||
def __init__(self, engine_name: str = None):
|
||||
"""
|
||||
Initialize engine
|
||||
|
||||
Args:
|
||||
engine_name: Engine name (optional)
|
||||
"""
|
||||
self._name = engine_name or "NO_NAME"
|
||||
self._dev_mode = True
|
||||
self._cache_string_template = False
|
||||
self._config = EngineConfig()
|
||||
self._source_factory = self._config.source_factory
|
||||
self._template_cache = SyncWriteMap(2048, 0.5)
|
||||
|
||||
@classmethod
|
||||
def use(cls, engine_name: str = None) -> 'Engine':
|
||||
"""
|
||||
Get engine instance
|
||||
|
||||
Args:
|
||||
engine_name: Engine name (optional, uses main engine if not specified)
|
||||
|
||||
Returns:
|
||||
Engine instance
|
||||
"""
|
||||
if engine_name is None:
|
||||
if cls._MAIN_ENGINE is None:
|
||||
cls._MAIN_ENGINE = cls(cls.MAIN_ENGINE_NAME)
|
||||
cls._engine_map[cls.MAIN_ENGINE_NAME] = cls._MAIN_ENGINE
|
||||
return cls._MAIN_ENGINE
|
||||
|
||||
return cls._engine_map.get(engine_name)
|
||||
|
||||
@classmethod
|
||||
def create(cls, engine_name: str, configurator: Callable[['Engine'], None] = None) -> 'Engine':
|
||||
"""
|
||||
Create new engine with name
|
||||
|
||||
Args:
|
||||
engine_name: Engine name
|
||||
configurator: Configuration callback
|
||||
|
||||
Returns:
|
||||
New engine instance
|
||||
"""
|
||||
if StrKit.is_blank(engine_name):
|
||||
raise ValueError("Engine name cannot be blank")
|
||||
|
||||
engine_name = engine_name.strip()
|
||||
if engine_name in cls._engine_map:
|
||||
raise ValueError(f"Engine already exists: {engine_name}")
|
||||
|
||||
new_engine = cls(engine_name)
|
||||
cls._engine_map[engine_name] = new_engine
|
||||
|
||||
if configurator:
|
||||
configurator(new_engine)
|
||||
|
||||
return new_engine
|
||||
|
||||
@classmethod
|
||||
def create_if_absent(cls, engine_name: str, configurator: Callable[['Engine'], None] = None) -> 'Engine':
|
||||
"""
|
||||
Create engine if absent
|
||||
|
||||
Args:
|
||||
engine_name: Engine name
|
||||
configurator: Configuration callback
|
||||
|
||||
Returns:
|
||||
Engine instance
|
||||
"""
|
||||
ret = cls._engine_map.get(engine_name)
|
||||
if ret is None:
|
||||
# Double-check locking
|
||||
if engine_name not in cls._engine_map:
|
||||
ret = cls.create(engine_name)
|
||||
if configurator:
|
||||
configurator(ret)
|
||||
return ret
|
||||
|
||||
@classmethod
|
||||
def remove(cls, engine_name: str) -> Optional['Engine']:
|
||||
"""
|
||||
Remove engine by name
|
||||
|
||||
Args:
|
||||
engine_name: Engine name
|
||||
|
||||
Returns:
|
||||
Removed engine or None
|
||||
"""
|
||||
removed = cls._engine_map.pop(engine_name, None)
|
||||
if removed and cls.MAIN_ENGINE_NAME == removed.name:
|
||||
cls._MAIN_ENGINE = None
|
||||
return removed
|
||||
|
||||
@classmethod
|
||||
def set_main_engine(cls, engine: 'Engine'):
|
||||
"""
|
||||
Set main engine
|
||||
|
||||
Args:
|
||||
engine: Engine to set as main
|
||||
"""
|
||||
if engine is None:
|
||||
raise ValueError("Engine cannot be null")
|
||||
|
||||
engine._name = cls.MAIN_ENGINE_NAME
|
||||
cls._engine_map[cls.MAIN_ENGINE_NAME] = engine
|
||||
cls._MAIN_ENGINE = engine
|
||||
|
||||
def get_template(self, file_name: str) -> Template:
|
||||
"""
|
||||
Get template by file name
|
||||
|
||||
Args:
|
||||
file_name: Template file name
|
||||
|
||||
Returns:
|
||||
Template instance
|
||||
"""
|
||||
template = self._template_cache.get(file_name)
|
||||
|
||||
if template is None:
|
||||
template = self._build_template_by_source_factory(file_name)
|
||||
self._template_cache[file_name] = template
|
||||
elif self._dev_mode and template.is_modified():
|
||||
template = self._build_template_by_source_factory(file_name)
|
||||
self._template_cache[file_name] = template
|
||||
|
||||
return template
|
||||
|
||||
def _build_template_by_source_factory(self, file_name: str) -> Template:
|
||||
"""Build template using source factory"""
|
||||
source = self._source_factory.get_source(
|
||||
self._config.base_template_path,
|
||||
file_name,
|
||||
self._config.encoding
|
||||
)
|
||||
|
||||
return self._build_template(source, file_name)
|
||||
|
||||
def get_template_by_string(self, content: str, cache: bool = False) -> Template:
|
||||
"""
|
||||
Get template by string content
|
||||
|
||||
Args:
|
||||
content: Template content
|
||||
cache: Whether to cache the template
|
||||
|
||||
Returns:
|
||||
Template instance
|
||||
"""
|
||||
if not cache:
|
||||
source = StringSource(content, False)
|
||||
return self._build_template(source, None)
|
||||
|
||||
cache_key = HashKit.md5(content)
|
||||
template = self._template_cache.get(cache_key)
|
||||
|
||||
if template is None:
|
||||
source = StringSource(content, cache_key)
|
||||
template = self._build_template(source, cache_key)
|
||||
self._template_cache[cache_key] = template
|
||||
elif self._dev_mode and template.is_modified():
|
||||
source = StringSource(content, cache_key)
|
||||
template = self._build_template(source, cache_key)
|
||||
self._template_cache[cache_key] = template
|
||||
|
||||
return template
|
||||
|
||||
def _build_template(self, source, cache_key: str = None) -> Template:
|
||||
"""Build template from source"""
|
||||
env = Env(self._config)
|
||||
|
||||
if self._dev_mode:
|
||||
env.add_source(source)
|
||||
|
||||
parser = Parser(env, source.get_content(), cache_key)
|
||||
stat = parser.parse()
|
||||
|
||||
return Template(env, stat)
|
||||
|
||||
def add_shared_function(self, file_name: str) -> 'Engine':
|
||||
"""Add shared function from file"""
|
||||
self._config.add_shared_function(file_name)
|
||||
return self
|
||||
|
||||
def add_shared_function_by_string(self, content: str) -> 'Engine':
|
||||
"""Add shared function from string"""
|
||||
self._config.add_shared_function_by_content(content)
|
||||
return self
|
||||
|
||||
def add_shared_object(self, name: str, obj: Any) -> 'Engine':
|
||||
"""Add shared object"""
|
||||
self._config.add_shared_object(name, obj)
|
||||
return self
|
||||
|
||||
def remove_shared_object(self, name: str) -> 'Engine':
|
||||
"""Remove shared object"""
|
||||
self._config.remove_shared_object(name)
|
||||
return self
|
||||
|
||||
def add_directive(self, directive_name: str, directive_class: Type[Directive],
|
||||
keep_line_blank: bool = False) -> 'Engine':
|
||||
"""Add custom directive"""
|
||||
self._config.add_directive(directive_name, directive_class, keep_line_blank)
|
||||
return self
|
||||
|
||||
def remove_directive(self, directive_name: str) -> 'Engine':
|
||||
"""Remove directive"""
|
||||
self._config.remove_directive(directive_name)
|
||||
return self
|
||||
|
||||
def add_shared_method(self, obj: Any) -> 'Engine':
|
||||
"""Add shared method from object"""
|
||||
self._config.add_shared_method(obj)
|
||||
return self
|
||||
|
||||
def add_shared_method_from_class(self, clazz: Type) -> 'Engine':
|
||||
"""Add shared method from class"""
|
||||
self._config.add_shared_method_from_class(clazz)
|
||||
return self
|
||||
|
||||
def add_shared_static_method(self, clazz: Type) -> 'Engine':
|
||||
"""Add shared static method from class"""
|
||||
self._config.add_shared_static_method(clazz)
|
||||
return self
|
||||
|
||||
def remove_shared_method(self, method_name: str) -> 'Engine':
|
||||
"""Remove shared method by name"""
|
||||
self._config.remove_shared_method(method_name)
|
||||
return self
|
||||
|
||||
def remove_template_cache(self, cache_key: str):
|
||||
"""Remove template cache by key"""
|
||||
self._template_cache.pop(cache_key, None)
|
||||
|
||||
def remove_all_template_cache(self):
|
||||
"""Remove all template cache"""
|
||||
self._template_cache.clear()
|
||||
|
||||
@property
|
||||
def dev_mode(self) -> bool:
|
||||
"""Get dev mode setting"""
|
||||
return self._dev_mode
|
||||
|
||||
@dev_mode.setter
|
||||
def dev_mode(self, value: bool):
|
||||
"""Set dev mode"""
|
||||
self._dev_mode = value
|
||||
self._config.dev_mode = value
|
||||
if self._dev_mode:
|
||||
self.remove_all_template_cache()
|
||||
|
||||
@property
|
||||
def cache_string_template(self) -> bool:
|
||||
"""Get cache string template setting"""
|
||||
return self._cache_string_template
|
||||
|
||||
@cache_string_template.setter
|
||||
def cache_string_template(self, value: bool):
|
||||
"""Set cache string template"""
|
||||
self._cache_string_template = value
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Get engine name"""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def config(self) -> EngineConfig:
|
||||
"""Get engine configuration"""
|
||||
return self._config
|
||||
|
||||
@property
|
||||
def source_factory(self) -> ISourceFactory:
|
||||
"""Get source factory"""
|
||||
return self._source_factory
|
||||
|
||||
@source_factory.setter
|
||||
def source_factory(self, value: ISourceFactory):
|
||||
"""Set source factory"""
|
||||
self._config.source_factory = value
|
||||
self._source_factory = value
|
||||
|
||||
def set_to_class_path_source_factory(self) -> 'Engine':
|
||||
"""Set to class path source factory"""
|
||||
self.source_factory = ClassPathSourceFactory()
|
||||
return self
|
||||
|
||||
@property
|
||||
def base_template_path(self) -> Optional[str]:
|
||||
"""Get base template path"""
|
||||
return self._config.base_template_path
|
||||
|
||||
@base_template_path.setter
|
||||
def base_template_path(self, value: str):
|
||||
"""Set base template path"""
|
||||
self._config.base_template_path = value
|
||||
|
||||
@property
|
||||
def encoding(self) -> str:
|
||||
"""Get encoding"""
|
||||
return self._config.encoding
|
||||
|
||||
@encoding.setter
|
||||
def encoding(self, value: str):
|
||||
"""Set encoding"""
|
||||
self._config.encoding = value
|
||||
|
||||
@property
|
||||
def date_pattern(self) -> str:
|
||||
"""Get date pattern"""
|
||||
return self._config.date_pattern
|
||||
|
||||
@date_pattern.setter
|
||||
def date_pattern(self, value: str):
|
||||
"""Set date pattern"""
|
||||
self._config.date_pattern = value
|
||||
|
||||
def set_compressor(self, compressor: Compressor) -> 'Engine':
|
||||
"""Set compressor"""
|
||||
self._config._compressor = compressor
|
||||
return self
|
||||
|
||||
def set_compressor_on(self, separator: str = '\n') -> 'Engine':
|
||||
"""Set compressor on"""
|
||||
self._config._compressor = Compressor(separator)
|
||||
return self
|
||||
|
||||
def set_encoder_factory(self, encoder_factory: EncoderFactory) -> 'Engine':
|
||||
"""Set encoder factory"""
|
||||
self._config.writer_buffer.set_encoder_factory(encoder_factory)
|
||||
self._config.writer_buffer.set_encoding(self._config.encoding)
|
||||
return self
|
||||
|
||||
def set_to_jdk_encoder_factory(self) -> 'Engine':
|
||||
"""Set to JDK encoder factory"""
|
||||
self.set_encoder_factory(JdkEncoderFactory())
|
||||
return self
|
||||
|
||||
def set_buffer_size(self, buffer_size: int) -> 'Engine':
|
||||
"""Set buffer size"""
|
||||
self._config.writer_buffer.set_buffer_size(buffer_size)
|
||||
return self
|
||||
|
||||
def set_reentrant_buffer_size(self, reentrant_buffer_size: int) -> 'Engine':
|
||||
"""Set reentrant buffer size"""
|
||||
self._config.writer_buffer.set_reentrant_buffer_size(reentrant_buffer_size)
|
||||
return self
|
||||
|
||||
@property
|
||||
def template_cache_size(self) -> int:
|
||||
"""Get template cache size"""
|
||||
return len(self._template_cache)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Template Engine: {self._name}"
|
||||
|
||||
|
||||
# Initialize main engine
|
||||
Engine._MAIN_ENGINE = Engine(Engine.MAIN_ENGINE_NAME)
|
||||
Engine._engine_map[Engine.MAIN_ENGINE_NAME] = Engine._MAIN_ENGINE
|
||||
Reference in New Issue
Block a user