소스 코드 : https://github.com/volfpeter/htmy
문서 및 예 : https://volfpeter.github.io/htmy
htmy비동기 , 순수한 파이썬 렌더링 엔진.
ErrorBoundary 구성 요소.패키지는 PYPI에서 사용할 수 있으며 다음과 같이 설치할 수 있습니다.
$ pip install htmy 렌더링 엔진 자체에서 내장 구성 요소에 이르기까지 전체 라이브러리는 몇 가지 간단한 프로토콜과 소수의 간단한 유틸리티 클래스를 중심으로 구축됩니다. 즉, 라이브러리의 모든 것을 쉽게 사용자 정의, 확장 또는 교체 할 수 있습니다. 예, 렌더링 엔진조차도. 나머지 부품은 예상대로 계속 작동합니다.
또한 라이브러리는 메타 클래스 또는 디스크립터와 같은 고급 파이썬 기능에 의존하지 않습니다. 복잡한 기본 클래스 등도 없습니다. 주니어 엔지니어조차도 htmy 로 구축 된 응용 프로그램을 이해, 개발 및 디버깅 할 수 있습니다.
동기화 또는 비동기 htmy(context: Context) -> Component 요소가있는 모든 클래스는 htmy 구성 요소 (기술적으로 HTMYComponentType )입니다. 문자열은 또한 구성 요소, HTMYComponentType 또는 문자열 객체의 목록 또는 튜플입니다.
이 메소드 이름을 사용하면 다른 도구와의 이름 충돌을 두려워하지 않고 비즈니스 객체 ( TypedDicts 또는 pydantic 모델에서 ORM 클래스로)를 구성 요소로 변환 할 수 있습니다.
Async 지원을 사용하면 구성 요소에서 데이터를로드하거나 Async 비즈니스 로직을 바로 실행할 수 있습니다. 이렇게하면 경우에 따라 작성하는 데 필요한 보일러 플레이트의 양을 줄이고 렌더링 및 비 렌더링 논리를 어떤 방식 으로든 분리 할 수 있습니다.
예:
from dataclasses import dataclass
from htmy import Component , Context , html
@ dataclass ( frozen = True , kw_only = True , slots = True )
class User :
username : str
name : str
email : str
async def is_admin ( self ) -> bool :
return False
class UserRow ( User ):
async def htmy ( self , context : Context ) -> Component :
role = "admin" if await self . is_admin () else "restricted"
return html . tr (
html . td ( self . username ),
html . td ( self . name ),
html . td ( html . a ( self . email , href = f"mailto: { self . email } " )),
html . td ( role )
)
@ dataclass ( frozen = True , kw_only = True , slots = True )
class UserRows :
users : list [ User ]
def htmy ( self , context : Context ) -> Component :
# Note that a list is returned here. A list or tuple of `HTMYComponentType | str` objects is also a component.
return [ UserRow ( username = u . username , name = u . name , email = u . email ) for u in self . users ]
user_table = html . table (
UserRows (
users = [
User ( username = "Foo" , name = "Foo" , email = "[email protected]" ),
User ( username = "Bar" , name = "Bar" , email = "[email protected]" ),
]
)
) htmy 또한 동기화 또는 비동기 my_component(props: MyProps, context: Context) -> Component 함수에 사용할 수있는 @component 데코레이터를 제공하여 구성 요소로 변환 ( props 유형을 보존).
위와 동일한 예는 다음과 같지만 기능 구성 요소가 있습니다.
from dataclasses import dataclass
from htmy import Component , Context , component , html
@ dataclass ( frozen = True , kw_only = True , slots = True )
class User :
username : str
name : str
email : str
async def is_admin ( self ) -> bool :
return False
@ component
async def user_row ( user : User , context : Context ) -> Component :
# The first argument of function components is their "props", the data they need.
# The second argument is the rendering context.
role = "admin" if await user . is_admin () else "restricted"
return html . tr (
html . td ( user . username ),
html . td ( user . name ),
html . td ( html . a ( user . email , href = f"mailto: { user . email } " )),
html . td ( role )
)
@ component
def user_rows ( users : list [ User ], context : Context ) -> Component :
# Nothing to await in this component, so it's sync.
# Note that we only pass the "props" to the user_row() component (well, function component wrapper).
# The context will be passed to the wrapper during rendering.
return [ user_row ( user ) for user in users ]
user_table = html . table (
user_rows (
[
User ( username = "Foo" , name = "Foo" , email = "[email protected]" ),
User ( username = "Bar" , name = "Bar" , email = "[email protected]" ),
]
)
) htmy HTML 및 기타 사용 사례를위한 풍부한 내장 유틸리티 및 구성 요소 세트를 보유하고 있습니다.
html 모듈 : 완전한 기준선 HTML 태그 세트.md : MarkdownParser 유틸리티 및 로딩, 구문 분석, 변환 및 Markdown 컨텐츠를위한 MD 구성 요소.i18n : 비동기, JSON 기반 국제화 유틸리티.BaseTag , TagWithProps , Tag , WildcardTag : 사용자 정의 XML 태그를위한 기본 클래스.ErrorBoundary , Fragment , SafeStr , WithContext : 오류 처리를위한 유틸리티, 구성 요소 포장지, 컨텍스트 제공 업체 및 서식.Snippet : 파일 시스템에서 문서 스 니펫을로드 및 사용자 정의하기위한 유틸리티 클래스.etree.ETreeConverter : 사용자 정의 HTMY 구성 요소를 지원하여 XML을 구성 요소 트리로 변환하는 유틸리티. htmy.HTMY 라이브러리의 내장 된 기본 렌더러입니다.
Fastapi와 같은 비동기 웹 프레임 워크에서 라이브러리를 사용하는 경우 이미 비동기 환경에 있으므로 구성 요소를 간단히 렌더링 할 수 있습니다 await HTMY().render(my_root_component)
로컬 스크립트 또는 CLI와 같은 동기화 환경에서 렌더러를 실행하려는 경우 먼저 Async 작업에 렌더러를 래핑하고 asyncio.run() 로 해당 작업을 실행해야합니다.
import asyncio
from htmy import HTMY , html
async def render_page () -> None :
page = (
html . DOCTYPE . html ,
html . html (
html . body (
html . h1 ( "Hello World!" ),
html . p ( "This page was rendered by " , html . code ( "htmy" )),
),
)
)
result = await HTMY (). render ( page )
print ( result )
if __name__ == "__main__" :
asyncio . run ( render_page ()) 위의 코드 예제에서 볼 수 있듯이 모든 구성 요소에는 context: Context 인 인수가 있습니다. 컨텍스트는 "prop thrown"없이 구성 요소의 전체 하위 트리와 데이터를 공유하는 방법입니다.
컨텍스트 (기술적으로 Mapping )는 전적으로 렌더러가 관리합니다. 컨텍스트 제공자 구성 요소 (동기화 또는 Async가있는 모든 클래스 htmy_context() -> Context 메소드) 컨텍스트에 새 데이터를 추가하여 하위 트리의 구성 요소를 사용할 수있게하며 구성 요소는 컨텍스트에서 필요한 것을 간단히 가져갈 수 있습니다.
상황에 대한 내용에 대한 제한이 없으며, 예를 들어 현재 사용자, UI 기본 설정, 테마 또는 구성 요소가 제공하는 등 애플리케이션에 필요한 경우 사용될 수 있습니다. 실제로, 내장 구성 요소는 태그 속성 이름과 값 형식을 사용자 정의 할 수 있도록 Formatter 컨텍스트에서 컨텍스트에서 얻을 수 있습니다.
다음은 컨텍스트 제공 업체 및 소비자 구현 예입니다.
import asyncio
from htmy import HTMY , Component , ComponentType , Context , component , html
class UserContext :
def __init__ ( self , * children : ComponentType , username : str , theme : str ) -> None :
self . _children = children
self . username = username
self . theme = theme
def htmy_context ( self ) -> Context :
# Context provider implementation.
return { UserContext : self }
def htmy ( self , context : Context ) -> Component :
# Context providers must also be components, as they just
# wrap some children components in their context.
return self . _children
@ classmethod
def from_context ( cls , context : Context ) -> "UserContext" :
user_context = context [ cls ]
if isinstance ( user_context , UserContext ):
return user_context
raise TypeError ( "Invalid user context." )
@ component
def welcome_page ( text : str , context : Context ) -> Component :
# Get user information from the context.
user = UserContext . from_context ( context )
return (
html . DOCTYPE . html ,
html . html (
html . body (
html . h1 ( text , html . strong ( user . username )),
data_theme = user . theme ,
),
),
)
async def render_welcome_page () -> None :
page = UserContext (
welcome_page ( "Welcome back " ),
username = "John" ,
theme = "dark" ,
)
result = await HTMY (). render ( page )
print ( result )
if __name__ == "__main__" :
asyncio . run ( render_welcome_page ()) 물론 ContextAware 과 같은 내장 컨텍스트 관련 유틸리티 또는 WithContext 플레이트 코드가 적은 편리하고 입력 된 컨텍스트 사용을 위해 내장 컨텍스트 관련 유틸리티에 의존 할 수 있습니다.
앞에서 언급했듯이 내장형 Formatter 클래스는 태그 속성 이름 및 값 형식을 담당합니다. 이 클래스를 확장하거나 새로운 규칙을 인스턴스에 추가 한 다음 HTMY 또는 HTMY.render() 또는 컨텍스트 제공자 구성 요소에서 직접 사용자 정의 인스턴스를 컨텍스트에 추가하여 내장 형식 형식의 동작을 완전히 무시하거나 확장 할 수 있습니다.
기본 태그 속성 서식 규칙입니다.
_ > - )의 대시로 변환됩니다. 예를 들어 data_theme="dark" 는 data-theme="dark" 로 변환되지만 _data_theme="dark" 렌더링 된 텍스트에서 data_theme="dark" 로 끝납니다. 더 중요한 것은 class_="text-danger" , _class="text-danger" , _class__="text-danger" 이 모두 class="text-danger" 으로 변환되고 _for="my-input" 또는 for_="my_input" for="my-input" 됩니다.bool 속성 값은 문자열로 변환됩니다 ( "true" 및 "false" ).XBool.true 속성 값은 빈 문자열로 변환되고 XBool.false 값이 건너 뜁니다 (속성 이름 만 렌더링).date 및 datetime 속성 값은 ISO 문자열로 변환됩니다. ErrorBoundary 구성 요소는 HTTP 오류를 제기하는 대신 응용 프로그램이 우아하게 실패하지 않으려면 (예 : 오류 메시지를 표시) 유용합니다.
오류 경계는 구성 요소 구성 요소 하위 트리를 감습니다. 렌더러가 ErrorBoundary 구성 요소를 만나면 랩핑 된 컨텐츠를 렌더링하려고합니다. ErrorBoundary 의 Subtree의 어느 시점에서든 예외로 렌더링이 실패하면 렌더러는 자동으로 ErrorBoundary 의 fallback 속성에 할당 된 구성 요소로 다시 떨어집니다.
선택적으로, 오류 경계가 처리 할 수있는 오류를 정의하여 오류 처리를 잘 제어 할 수 있습니다.
일반적으로, 일부 비동기 호출을 기다려야하는 경우 구성 요소는 비동기가되어야합니다.
구성 요소가 잠재적으로 "장기 실행"동기 호출을 실행하는 경우, 작업자 스레드에 호출을 기다리는 것을 위임하는 것이 좋습니다 (따라서 구성 요소를 비동기로 만듭니다). 예를 들어 anyio 의 to_thread 유틸리티, starlette 's (또는 fastapi 's) run_in_threadpool() 등으로 수행 할 수 있습니다. 여기서 목표는 성능 문제로 이어질 수 있으므로 Asyncio 이벤트 루프를 차단하지 않는 것입니다.
다른 모든 경우에 동기화 구성 요소를 사용하는 것이 가장 좋습니다.
Fastapi :
스펙트럼의 한쪽 끝에는 서버 (Python) 및 클라이언트 (JavaScript) 응용 프로그램을 전체 상태 관리 및 동기화와 동기화 (경우에 따라 추가 JavaScript) 패키지와 결합하는 완전한 응용 프로그램 프레임 워크가 있습니다. 가장 인기있는 예는 반사, Nicegui, Reactpy 및 Fastui입니다.
이러한 프레임 워크의 주요 이점은 빠른 응용 프로그램 프로토 타이핑과 매우 편리한 개발자 경험입니다 (적어도 프레임 워크의 내장 기능 세트에 머무르는 한). 이와 대가로, 그들은 구성 요소에서 프론트 엔드 툴링 및 상태 관리에 이르기까지 매우 복잡하고, 기본 엔지니어링은 매우 복잡하고, 배포 및 스케일링은 어렵거나 비용이 많이들 수 있으며, 마이그레이션하기가 어려울 수 있습니다. 이러한 경고에도 불구하고 내부 도구 및 응용 프로그램 프로토 타이핑에 매우 적합한 선택이 될 수 있습니다.
스펙트럼의 다른 쪽 끝 (일반 렌더링 엔진)은 Jinja 템플릿 엔진에 의해 지배되며, 이는 오랫동안 안전하고 주변에있을 것입니다. Jinja의 주요 단점은 우수한 IDE 지원이없고, 정적 코드 분석 지원의 완전한 부족 및 (주관적으로) 추악한 구문이 부족하다는 것입니다.
그런 다음 중간 지하를 목표로하는 도구가 있습니다. 일반적으로 전체 응용 프로그램 프레임 워크의 대부분의 이점과 단점을 제공하면서 상태 관리, 클라이언트 서버 커뮤니케이션 및 사용자가 일정 수준의 HTMX 지원을 통해 해결할 수있는 동적 UI 업데이트를 떠나는 도구가 있습니다. 이 그룹에는 fasthtml 및 ludic과 같은 라이브러리가 포함되어 있습니다.
htmy 의 주요 목표는 가능한 한 간단 하고 유지 관리 가능 하며 사용자 정의 할 수있는 비동기 의 순수한 파이썬 렌더링 엔진이되는 동시에 복잡하고 유지 관리 가능한 응용 프로그램을 만들기위한 모든 빌딩 블록을 제공하는 것입니다.
라이브러리는 의존성을 최소화하는 것을 목표로합니다. 현재 다음의 종속성이 필요합니다.
anyio : 비동기 파일 작업 및 네트워킹 용.async-lru : 비동기 캐싱 용.markdown : Markdown Parsing의 경우. 라인 및 서식에는 ruff 사용하고 정적 코드 분석을위한 mypy 및 테스트를위한 pytest 사용하십시오.
이 문서는 mkdocs-material 및 mkdocstrings 로 구축되었습니다.
더 많은 문서, 예제, 코드 및 테스트를 포함하여 모든 기여를 환영합니다. 심지어 질문도 있습니다.
패키지는 MIT 라이센스 조건에 따라 오픈 소스입니다.