| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- from typing import Any, List, Callable
- import json
- import ast
- import inspect
- import toml
- import re
- import configparser
- from pathlib import Path, PosixPath
- from pydantic.json_schema import GenerateJsonSchema
- from pydantic_core import to_jsonable_python
- JSONValue = str | bool | int | None | List['JSONValue']
- TOML_HEADER = "# Converted from INI to TOML format: https://toml.io/en/\n\n"
- def load_ini_value(val: str) -> JSONValue:
- """Convert lax INI values into strict TOML-compliant (JSON) values"""
- if val.lower() in ('true', 'yes', '1'):
- return True
- if val.lower() in ('false', 'no', '0'):
- return False
- if val.isdigit():
- return int(val)
- try:
- return ast.literal_eval(val)
- except Exception:
- pass
- try:
- return json.loads(val)
- except Exception:
- pass
-
- return val
- def convert(ini_str: str) -> str:
- """Convert a string of INI config into its TOML equivalent (warning: strips comments)"""
- config = configparser.ConfigParser()
- config.optionxform = str # capitalize key names
- config.read_string(ini_str)
- # Initialize an empty dictionary to store the TOML representation
- toml_dict = {}
- # Iterate over each section in the INI configuration
- for section in config.sections():
- toml_dict[section] = {}
- # Iterate over each key-value pair in the section
- for key, value in config.items(section):
- parsed_value = load_ini_value(value)
- # Convert the parsed value to its TOML-compatible JSON representation
- toml_dict[section.upper()][key.upper()] = json.dumps(parsed_value)
- # Build the TOML string
- toml_str = TOML_HEADER
- for section, items in toml_dict.items():
- toml_str += f"[{section}]\n"
- for key, value in items.items():
- toml_str += f"{key} = {value}\n"
- toml_str += "\n"
- return toml_str.strip()
- class JSONSchemaWithLambdas(GenerateJsonSchema):
- """
- Encode lambda functions in default values properly.
- Usage:
- >>> json.dumps(value, encoder=JSONSchemaWithLambdas())
- """
- def encode_default(self, default: Any) -> Any:
- config = self._config
- if isinstance(default, Callable):
- return '{{lambda ' + inspect.getsource(default).split('=lambda ')[-1].strip()[:-1] + '}}'
- return to_jsonable_python(
- default,
- timedelta_mode=config.ser_json_timedelta,
- bytes_mode=config.ser_json_bytes,
- serialize_unknown=True
- )
- # for computed_field properties render them like this instead:
- # inspect.getsource(field.wrapped_property.fget).split('def ', 1)[-1].split('\n', 1)[-1].strip().strip('return '),
- def better_toml_dump_str(val: Any) -> str:
- try:
- return toml.encoder._dump_str(val) # type: ignore
- except Exception:
- # if we hit any of toml's numerous encoding bugs,
- # fall back to using json representation of string
- return json.dumps(str(val))
- class CustomTOMLEncoder(toml.encoder.TomlEncoder):
- """
- Custom TomlEncoder to work around https://github.com/uiri/toml's many encoding bugs.
- More info: https://github.com/fabiocaccamo/python-benedict/issues/439
- >>> toml.dumps(value, encoder=CustomTOMLEncoder())
- """
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- self.dump_funcs[Path] = lambda x: json.dumps(str(x))
- self.dump_funcs[PosixPath] = lambda x: json.dumps(str(x))
- self.dump_funcs[str] = better_toml_dump_str
- self.dump_funcs[re.RegexFlag] = better_toml_dump_str
|