Python configuration dialog

satya - 2/25/2024, 2:04:27 PM

toml install


pip install tomlkit

satya - 2/25/2024, 2:04:52 PM

toml to json


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')

satya - 2/25/2024, 2:07:36 PM

Dictonary to toml


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)

satya - 2/25/2024, 2:13:41 PM

json string to a dict object


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)

satya - 2/25/2024, 2:20:07 PM

Example of TOML


# 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"

satya - 2/25/2024, 2:27:44 PM

JSON to TOML


#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)

satya - 2/25/2024, 2:56:04 PM

tomlkit docs, github

tomlkit docs, github

satya - 2/25/2024, 2:59:16 PM

JSON docs

JSON docs

satya - 2/25/2024, 3:00:48 PM

Round trip from TOML to an Object


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

satya - 2/25/2024, 3:01:15 PM

The object: AppConfig


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

satya - 2/25/2024, 3:01:34 PM

Toml file


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

satya - 2/25/2024, 3:02:33 PM

Imports


from pydantic import BaseModel
import json
import tomlkit

satya - 2/25/2024, 5:29:48 PM

Putting it all together


"""
*************************************************
* 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()

satya - 3/6/2024, 10:03:25 AM

Reasonable approach to get a project root


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

"""

satya - 3/6/2024, 10:25:26 AM

resoleve()

  1. Makes the path absolute
  2. Correctly represents the separators

satya - 3/6/2024, 3:10:39 PM

Toml config values are typed

  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

satya - 3/6/2024, 3:11:00 PM

Strings


# 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"""

satya - 3/6/2024, 3:13:51 PM

Examples


# 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"

satya - 3/17/2024, 10:25:23 AM

Date and time objects in toml


# 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

satya - 3/17/2024, 10:28:53 AM

The three datetime objects


from datetime import (
  date, 
  datetime, 
  time
)

satya - 3/17/2024, 10:30:19 AM

Rules

  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.

satya - 3/17/2024, 10:37:24 AM

Briefly

  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

satya - 3/17/2024, 11:20:27 AM

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

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

satya - 3/17/2024, 11:20:55 AM

TypeError: Object of type Date is not JSON serializable

TypeError: Object of type Date is not JSON serializable

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

satya - 3/17/2024, 12:16:07 PM

Dealing with datetime snafus


"""
*************************************************
* 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

satya - 3/17/2024, 12:16:38 PM

using those


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

satya - 3/17/2024, 12:20:29 PM

TOML Lists


# 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]
]

satya - 3/17/2024, 3:33:36 PM

strings spanning multiple lines


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.
"""

satya - 3/17/2024, 3:35:02 PM

Multiline literal strings


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.
'''

satya - 3/17/2024, 3:36:07 PM

Key elements

  1. They start with ''', the single quote characters
  2. Escape sequences are not escaped
  3. Leading space are preserved

satya - 3/17/2024, 3:37:40 PM

Keeping white spaces


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.
"""

satya - 3/17/2024, 3:44:55 PM

details

  1. use \ at the end of the line to preserve white space
  2. The first newline after """ or ''' is always ignored