From 0a471729e7d113dd0a5245855b319d60306deb39 Mon Sep 17 00:00:00 2001 From: nikto_b Date: Sat, 12 Jul 2025 00:51:05 +0300 Subject: [PATCH] Filter `import * from megasniff` fields --- src/megasniff/__init__.py | 98 +-------------------------------------- src/megasniff/inflator.py | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 97 deletions(-) create mode 100644 src/megasniff/inflator.py diff --git a/src/megasniff/__init__.py b/src/megasniff/__init__.py index b181f2e..21ed1a6 100644 --- a/src/megasniff/__init__.py +++ b/src/megasniff/__init__.py @@ -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 diff --git a/src/megasniff/inflator.py b/src/megasniff/inflator.py new file mode 100644 index 0000000..b181f2e --- /dev/null +++ b/src/megasniff/inflator.py @@ -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']