Filter import * from megasniff fields

This commit is contained in:
2025-07-12 00:51:05 +03:00
parent bacb1319aa
commit 0a471729e7
2 changed files with 98 additions and 97 deletions

View File

@@ -1,97 +1 @@
# Copyright (C) 2025 Shevchenko A
# SPDX-License-Identifier: LGPL-3.0-or-later
import importlib.resources
from collections.abc import Callable
from dataclasses import dataclass
from types import NoneType, UnionType
from typing import Optional, get_origin, get_args, Union, Annotated
import jinja2
from .utils import *
@dataclass
class RenderData:
argname: str
constrs: list[tuple[str, bool]] # typecall / use lookup table
typename: str
is_union: bool
is_optional: bool
default_option: Optional[str]
class SchemaInflatorGenerator:
templateLoader: jinja2.BaseLoader
templateEnv: jinja2.Environment
template: jinja2.Template
def __init__(self,
loader: Optional[jinja2.BaseLoader] = None,
convertor_template: str = 'inflator.jinja2'):
if loader is None:
template_path = importlib.resources.files('megasniff.templates')
loader = jinja2.FileSystemLoader(str(template_path))
self.templateLoader = loader
self.templateEnv = jinja2.Environment(loader=self.templateLoader)
self.template = self.templateEnv.get_template(convertor_template)
def schema_to_generator(self,
schema: type,
*,
_base_lookup_table: Optional[dict[str, Any]] = None) -> Callable[[dict[str, Any]], Any]:
# Я это написал, оно пока работает, и я не собираюсь это упрощать, сорян
type_hints = get_kwargs_type_hints(schema)
render_data = []
lookup_table = _base_lookup_table or {}
if schema.__name__ not in lookup_table.keys():
lookup_table[schema.__name__] = None
for argname, argtype in type_hints.items():
if argname in {'return', 'self'}:
continue
has_default, default_option = get_field_default(schema, argname)
argtypes = argtype,
type_origin = get_origin(argtype)
if any(map(lambda x: type_origin is x, [Union, UnionType, Optional, Annotated])):
argtypes = get_args(argtype)
if NoneType in argtypes or None in argtypes:
argtypes = tuple(filter(lambda x: x is not None and x is not NoneType, argtypes))
has_default = True
out_argtypes: list[tuple[str, bool]] = []
for argt in argtypes:
is_builtin = is_builtin_type(argt)
if not is_builtin and argt is not schema:
if argt.__name__ not in lookup_table.keys():
# если случилась циклическая зависимость, мы не хотим бексконечную рекурсию
lookup_table[argt.__name__] = self.schema_to_generator(argt, _base_lookup_table=lookup_table)
if argt is schema:
out_argtypes.append(('inflate', True))
else:
out_argtypes.append((argt.__name__, is_builtin))
render_data.append(
RenderData(argname, out_argtypes, repr(argtype), len(argtypes) > 1, has_default, default_option))
convertor_functext = self.template.render(conversions=render_data)
convertor_functext = '\n'.join(list(filter(lambda x: len(x.strip()), convertor_functext.split('\n'))))
convertor_functext = convertor_functext.replace(', )', ')')
namespace = {
'_tgt_type': schema,
'_lookup_table': lookup_table
}
exec(convertor_functext, namespace)
# пихаем сгенеренный метод в табличку,
# ожидаем что она обновится во всех вложенных методах,
# разрешая циклические зависимости
lookup_table[schema.__name__] = namespace['inflate']
return namespace['inflate']
from .inflator import SchemaInflatorGenerator

97
src/megasniff/inflator.py Normal file
View File

@@ -0,0 +1,97 @@
# Copyright (C) 2025 Shevchenko A
# SPDX-License-Identifier: LGPL-3.0-or-later
import importlib.resources
from collections.abc import Callable
from dataclasses import dataclass
from types import NoneType, UnionType
from typing import Optional, get_origin, get_args, Union, Annotated
import jinja2
from .utils import *
@dataclass
class RenderData:
argname: str
constrs: list[tuple[str, bool]] # typecall / use lookup table
typename: str
is_union: bool
is_optional: bool
default_option: Optional[str]
class SchemaInflatorGenerator:
templateLoader: jinja2.BaseLoader
templateEnv: jinja2.Environment
template: jinja2.Template
def __init__(self,
loader: Optional[jinja2.BaseLoader] = None,
convertor_template: str = 'inflator.jinja2'):
if loader is None:
template_path = importlib.resources.files('megasniff.templates')
loader = jinja2.FileSystemLoader(str(template_path))
self.templateLoader = loader
self.templateEnv = jinja2.Environment(loader=self.templateLoader)
self.template = self.templateEnv.get_template(convertor_template)
def schema_to_generator(self,
schema: type,
*,
_base_lookup_table: Optional[dict[str, Any]] = None) -> Callable[[dict[str, Any]], Any]:
# Я это написал, оно пока работает, и я не собираюсь это упрощать, сорян
type_hints = get_kwargs_type_hints(schema)
render_data = []
lookup_table = _base_lookup_table or {}
if schema.__name__ not in lookup_table.keys():
lookup_table[schema.__name__] = None
for argname, argtype in type_hints.items():
if argname in {'return', 'self'}:
continue
has_default, default_option = get_field_default(schema, argname)
argtypes = argtype,
type_origin = get_origin(argtype)
if any(map(lambda x: type_origin is x, [Union, UnionType, Optional, Annotated])):
argtypes = get_args(argtype)
if NoneType in argtypes or None in argtypes:
argtypes = tuple(filter(lambda x: x is not None and x is not NoneType, argtypes))
has_default = True
out_argtypes: list[tuple[str, bool]] = []
for argt in argtypes:
is_builtin = is_builtin_type(argt)
if not is_builtin and argt is not schema:
if argt.__name__ not in lookup_table.keys():
# если случилась циклическая зависимость, мы не хотим бексконечную рекурсию
lookup_table[argt.__name__] = self.schema_to_generator(argt, _base_lookup_table=lookup_table)
if argt is schema:
out_argtypes.append(('inflate', True))
else:
out_argtypes.append((argt.__name__, is_builtin))
render_data.append(
RenderData(argname, out_argtypes, repr(argtype), len(argtypes) > 1, has_default, default_option))
convertor_functext = self.template.render(conversions=render_data)
convertor_functext = '\n'.join(list(filter(lambda x: len(x.strip()), convertor_functext.split('\n'))))
convertor_functext = convertor_functext.replace(', )', ')')
namespace = {
'_tgt_type': schema,
'_lookup_table': lookup_table
}
exec(convertor_functext, namespace)
# пихаем сгенеренный метод в табличку,
# ожидаем что она обновится во всех вложенных методах,
# разрешая циклические зависимости
lookup_table[schema.__name__] = namespace['inflate']
return namespace['inflate']