latexify_py
latexify_py copied to clipboard
Config class
Follows #65
It may be good if we provide a config class integrating every setting, which are currently passed directly to with_latex
:
import latexify
config = latexify.Config.defaults()
config.use_math_symbols()
config.expand_function("expit")
@latexify.with_latex(config)
def f(x):
return expit(x)
# Will generate: \mathrm{f}(x) \triangleq \frac{1}{1+\exp{(-x)}}
possible logic:
inner_config = copy(config if config is not None else Config.defaults())
for each kwarg:
inner_config.update(kwarg)
inner_process(fn, inner_config)
working on this
@chunibyo-wly Thanks for working on this. Since this feature should be carefully designed, could you provide your idea about the implementation before throwing PRs?
hello, thanks for your reply, could you please give me some time to do some tests? I will give you answer later
@chunibyo-wly Sure. I targeted this feature on the release after the next (0.3) so we don't need to rush at this point.
- I think we could use bitwise and dynamic method creation to do this
from __future__ import annotations
from latexify.constants import CONFIGURATION
from typing import Callable
class Config:
def __init__(self,
identifiers: dict[str, str] | None = None,
reduce_assignments: bool = False,
use_math_symbols: bool = False,
use_raw_function_name: bool = False,
use_signature: bool = True,) -> None:
self.flag: int = reduce_assignments * CONFIGURATION['reduce_assignments'] \
+ use_math_symbols * CONFIGURATION["use_math_symbols"] \
+ use_raw_function_name * CONFIGURATION["use_raw_function_name"] \
+ use_signature * CONFIGURATION["use_signature"]
self.identifiers: dict[str, str] = identifiers
self.expanded_function: set[str] = set()
for _config in CONFIGURATION:
setattr(self, _config, self.__make_method_change_flag(_config))
setattr(self, f"is_{_config}", self.__make_method_use_flag(_config))
@staticmethod
def defaults() -> Config:
return Config()
def expand_function(self, function: str | list[str]) -> None:
if type(function) == str:
self.expanded_function.add(function)
elif type(function) == list:
self.expand_function.update(function)
def __make_method_change_flag(self, config_name: str) -> Callable:
def _method(enabled: bool | None = True) -> None:
if enabled:
self.flag += CONFIGURATION[config_name]
else:
self.flag -= CONFIGURATION[config_name]
return _method
def __make_method_use_flag(self, config_name: str) -> Callable:
@property
def _method() -> bool:
return (self.flag & CONFIGURATION[config_name]) != 0
return _method
and constants is look like this:
CONFIGURATION = {
"reduce_assignments": 0,
"use_math_symbols": 2,
"use_raw_function_name": 4,
"use_signature": 8
}
- change frontend
get_latex
and reserved original parameter for user convenience
def get_latex(
fn: Callable[..., Any],
*,
identifiers: dict[str, str] | None = None,
reduce_assignments: bool = False,
use_math_symbols: bool = False,
use_raw_function_name: bool = False,
use_signature: bool = True,
configuration: Config | None = None,
) -> str:
@chunibyo-wly Thanks! I think we don't need bitset here and it is enough to store individual members for all boolean configs. We basically don't need to care about the efficiency of this class because parsing and codegen are much (maybe a hundred of times) slower.
I think we can simply define the config class as a dataclass with only nullable members, with the merge
method that generates a new Config while maintaining the original config unchanged:
@dataclasses.dataclass
class Config(frozen=True):
some_flag: SomeType | None
...
def merge(self, *, config: Config | None = None, **kwargs) -> Config:
def merge_field(name: str) -> Any:
# Precedence: kwargs -> config -> self
arg = kwargs.get(name)
if arg is None and config is not None:
arg = getattr(config, name)
if arg is None:
arg = getattr(self, name)
return arg
return Config(
**{f.name: merge_field(f.name) for f in dataclasses.fields(self)}
)
then define the defaults with non-null values:
_DEFAULT_CONFIG = Config(
some_flag=some_value,
...
)
In get_latex, we can construct the final config as follows:
def get_latex(fn: Callable[..., Any], *, config: Config | None, **kwargs):
merged_config = _DEFAULT_CONFIG.merge(config=config, **kwargs)
thanks for your advice, I will write code based on your suggestions later