Python configuration dialog


pip install tomlkit

import tomlkit
import json

# Function to convert TOML file to JSON file
def toml_to_json(toml_file_path, json_file_path):
    # Read TOML file
    with open(toml_file_path, 'r') as toml_file:
        toml_data = toml_file.read()

    # Parse TOML data
    parsed_toml = tomlkit.parse(toml_data)

    # Convert parsed TOML to JSON string
    json_data = json.dumps(parsed_toml, indent=4)

    # Write JSON data to file
    with open(json_file_path, 'w') as json_file:
        json_file.write(json_data)

# Example usage
toml_to_json('example.toml', 'example.json')

import toml

# Example Python object
data = {
    "database": {
        "server": "192.168.1.1",
        "ports": [8001, 8002, 8003],
        "connection_max": 5000,
        "enabled": True
    },
    "servers": {
        "alpha": {"ip": "10.0.0.1", "dc": "eqdc10"},
        "beta": {"ip": "10.0.0.2", "dc": "eqdc10"}
    }
}

# Convert Python object to TOML string
toml_string = toml.dumps(data)

# Write TOML string to file
with open('example.toml', 'w') as toml_file:
    toml_file.write(toml_string)

json_string = '{"name": "John Doe", "age": 30, "is_student": false}'

# Convert JSON string to Python dictionary
data_dict = json.loads(json_string)

print(data_dict)

# Example TOML configuration

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00Z # First-class support for dates and times

[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true

[servers]

  # Indentation is optional but can improve readability
  [servers.alpha]
  ip = "10.0.0.1"
  dc = "eqdc10"

  [servers.beta]
  ip = "10.0.0.2"
  dc = "eqdc10"

[[products]] # Array of tables
name = "Hammer"
sku = 738594937

[[products]]
name = "Nail"
sku = 284758393
color = "gray"

#Get a dict object first

json_str: str
d: dict = json.loads(json_str)

#Now convert the dict to TOML
toml_str: str = tomlkit.dumps(dict)

tomlkit docs, github

JSON docs


def _readTOMLConfigFile() -> AppConfig:
    configfile = _getAppTOMLConfigFilename()
    toml_str = fileutils.read_text_file(configfile)
    parsed_toml: tomlkit.TOMLDocument = tomlkit.parse(toml_str)
    json_str: str = json.dumps(parsed_toml,indent=4)
    dict = json.loads(json_str)
    obj = AppConfig(**dict)
    return obj

class AppConfig (BaseModel):
    api_token_name: str = ""
    embedding_api: str = ""
    llm_api: str =""

#
#*************************************************
# Application configuration file
#*************************************************
#
api_token_name = "api-token-name"
embedding_api = "embedding api"
llm_api = "llm api"

from pydantic import BaseModel
import json
import tomlkit

"""
*************************************************
* baselib related imports
*************************************************
"""
from baselib import baselog as log

from config.appconfig import AppConfig
from config import appconfig as appconfig

def _getAppConfigObject():
    log.info("Reading Application configuration file")
    return appconfig.readTOMLConfigFile()

def initAppServices(cls):
    log.ph1("Initializing App Services")
    cls.class_app_config = _getAppConfigObject()
    return cls

@initAppServices
class AppServices:
    class_app_config: AppConfig
    @staticmethod
    def config():
        return AppServices.class_app_config
    
"""
*************************************************
* Testing
*************************************************
"""
def _testConfig1():
    token = AppServices.config().api_token_name
    log.ph("Token from config", token)

def _testConfig2():
    a = AppServices.config().embedding_api
    log.ph("embedding api", a)

def test():
    _testConfig1()
    _testConfig2()

def localTest():
    log.ph1("Starting local test")
    test()
    log.ph1("End local test")

if __name__ == '__main__':
    localTest()

def _getProjectRoot():
    baselibPath = Path(baselib.__file__)
    parent = baselibPath.parent.parent.resolve()

    log.ph("baselib path", baselibPath)
    log.ph("root", parent)
    return parent

"""
Prints the following: 

baselib path
***********************
C:\satya\data\code\LearnPythonRepo\baselib\__init__.py

root
***********************
C:\satya\data\code\LearnPythonRepo

"""
  1. Makes the path absolute
  2. Correctly represents the separators
  1. 1. strings have to be quoted
  2. 2. Numbers no
  3. 3. Dates no
  4. 4. Lists have their own syntax
  5. 5. Booleans no

# Examples of string values
single_quoted = 'This is a single-quoted string'
double_quoted = "This is a double-quoted string"
multi_line = """This is a
multi-line string"""

# Examples of boolean values
is_active = true
has_access = false

# Examples of numeric values
age = 30
pi = 3.1415

# Example of datetime value
created_at = 2021-12-14T12:00:00Z

# Example of an array
items = ["item1", "item2", "item3"]
scores = [97, 85, 92]


# Example of keys with special characters
"content-type" = "application/json"
"multi word key" = "value"

# Date-only format
date = 2022-01-01

# Full date-time with UTC offset
datetime_with_offset = 2022-01-01T12:00:00+01:00

# Full date-time in UTC (Zulu time)
datetime_utc = 2022-01-01T12:00:00Z

# Local date-time (without timezone information)
local_datetime = 2022-01-01T12:00:00

# Time-only format
time = 12:00:00

from datetime import (
  date, 
  datetime, 
  time
)
  1. Date-only strings are converted into Python's datetime.date objects, which contain year, month, and day information.
  2. Full date-time strings, including those with UTC offset or Zulu time (Z), are converted into datetime.datetime objects. These objects include year, month, day, hour, minute, second, microsecond, and timezone information (if specified).
  3. Local date-time strings (without timezone information) are also converted into datetime.datetime objects, but without timezone information attached.
  4. Time-only strings are converted into datetime.time objects, containing hour, minute, second, and microsecond.
  1. Date-only strings: datetime.date (year, month, and day): 2022-01-01
  2. Full date-time string: datetime.datetime: (utc)2022-01-01T12:00:00Z, 2022-01-01T12:00:00+01:00,
  3. Local date-time strings: datetime.datetime: 2022-01-01T12:00:00
  4. Time-only strings: datetime.time: hh:mm:ss:microsecond: 12:34:56.789123

SOF: You may need to deal with this: datetime json issues

TypeError: Object of type Date is not JSON serializable

Search for: TypeError: Object of type Date is not JSON serializable


"""
*************************************************
* Datetime json support
*************************************************
"""
from datetime import datetime, date, time
from typing import Any, Dict

class TypedDateTimeEncoder(json.JSONEncoder):
    def default(self, o: Any) -> Dict[str, Any]:
        if isinstance(o, datetime):
            return {"type": "datetime", "value": o.isoformat()}
        elif isinstance(o, date):
            return {"type": "date", "value": o.isoformat()}
        elif isinstance(o, time):
            return {"type": "time", "value": o.isoformat()}
        return super().default(o)
    

def typed_datetime_decoder(dct: Dict[str, Any]) -> Any:
    if "type" in dct and "value" in dct:
        type_info = dct["type"]
        value = dct["value"]
        if type_info == "datetime":
            return datetime.fromisoformat(value)
        elif type_info == "date":
            return date.fromisoformat(value)
        elif type_info == "time":
            return time.fromisoformat(value)
    return dct  # Return the original dictionary if no type info is found

json_str: str = json.dumps(parsed_toml,indent=4, cls=TypedDateTimeEncoder)
dict = json.loads(json_str, object_hook=typed_datetime_decoder)

# An array of integers
numbers = [
    1,
    2,
    3,
    4,
    5
]

# An array of strings
fruits = [
    "apple",
    "banana",
    "cherry"
]

# An array of floats
floats = [
    1.1,
    2.2,
    3.3
]

# An array of dates
dates = [
    2022-01-01,
    2022-01-02,
    2022-01-03
]

# A nested array
nested_arrays = [
    [1, 2],
    [3, 4, 5],
    [6]
]

multi_line_string = """
This is a multi-line string.
Special characters like \n are escaped.
Leading whitespace on the second and subsequent lines is ignored.
"""

multi_line_literal_string = '''
This is a multi-line literal string.
Escape sequences like \n are not escaped.
Leading whitespace on the second and subsequent lines is preserved.
'''
  1. They start with ''', the single quote characters
  2. Escape sequences are not escaped
  3. Leading space are preserved

multi_line_string_with_spaces = """
This is a multi-line string with leading whitespace.\ 
    This line has leading spaces that are preserved because of the backslash.\ 
    This line, too.
"""
  1. use \ at the end of the line to preserve white space
  2. The first newline after """ or ''' is always ignored