Replace configparser with tomllib

This commit is contained in:
2026-01-25 17:50:22 +09:00
parent cb385097dc
commit ecaec99212
15 changed files with 71 additions and 70 deletions
+6 -6
View File
@@ -20,9 +20,9 @@ Installer python, og lag og aktiver et venv. Installer så avhengighetene med `p
Deretter kan du kjøre programmet med Deretter kan du kjøre programmet med
```console ```console
python -m dibbler -c example-config.ini create-db python -m dibbler -c example-config.toml create-db
python -m dibbler -c example-config.ini seed-data python -m dibbler -c example-config.toml seed-data
python -m dibbler -c example-config.ini loop python -m dibbler -c example-config.toml loop
``` ```
## Nix ## Nix
@@ -46,7 +46,7 @@ Du kan også bygge pakken manuelt, eller kjøre den direkte:
```console ```console
nix build .#dibbler nix build .#dibbler
nix run .# -- --config example-config.ini create-db nix run .# -- --config example-config.toml create-db
nix run .# -- --config example-config.ini seed-data nix run .# -- --config example-config.toml seed-data
nix run .# -- --config example-config.ini loop nix run .# -- --config example-config.toml loop
``` ```
+18 -7
View File
@@ -1,13 +1,10 @@
# This module is supposed to act as a singleton and be filled from typing import Any
# with config variables by cli.py
from pathlib import Path from pathlib import Path
import tomllib
import os import os
import sys
import configparser DEFAULT_CONFIG_PATH = Path('/etc/dibbler/dibbler.toml')
config = configparser.ConfigParser()
DEFAULT_CONFIG_PATH = Path('/etc/dibbler/dibbler.conf')
def default_config_path_submissive_and_readable() -> bool: def default_config_path_submissive_and_readable() -> bool:
return DEFAULT_CONFIG_PATH.is_file() and any( return DEFAULT_CONFIG_PATH.is_file() and any(
@@ -23,3 +20,17 @@ def default_config_path_submissive_and_readable() -> bool:
(DEFAULT_CONFIG_PATH.stat().st_mode & 0o004), (DEFAULT_CONFIG_PATH.stat().st_mode & 0o004),
] ]
) )
config: dict[str, dict[str, Any]] = dict()
def load_config(config_path: Path | None = None):
global config
if config_path is not None:
with Path(config_path).open("rb") as file:
config = tomllib.load(file)
elif default_config_path_submissive_and_readable():
with DEFAULT_CONFIG_PATH.open("rb") as file:
config = tomllib.load(file)
else:
print("Could not read config file, it was neither provided nor readable in default location", file=sys.stderr)
sys.exit(1)
+4 -18
View File
@@ -1,19 +1,5 @@
from pathlib import Path from sqlalchemy.engine.base import Engine
from sqlalchemy.orm import Session
from sqlalchemy import create_engine engine: Engine = None
from sqlalchemy.orm import sessionmaker session: Session = None
from dibbler.conf import config
database_url: str | None = None
if (url := config.get("database", "url")) is not None:
database_url = url
elif (url_file := config.get("database", "url_file")) is not None:
with Path(url_file).open() as file:
database_url = file.read().strip()
assert database_url is not None, "Database URL must be specified in config"
engine = create_engine(database_url)
Session = sessionmaker(bind=engine)
+4 -4
View File
@@ -6,13 +6,13 @@ from collections import defaultdict
from .helpers import * from .helpers import *
from ..models import Transaction from ..models import Transaction
from ..db import Session from ..db import session as create_session
def getUser(): def getUser():
while 1: while 1:
string = input("user? ") string = input("user? ")
session = Session() session = create_session()
user = search_user(string, session) user = search_user(string, session)
session.close() session.close()
if not isinstance(user, list): if not isinstance(user, list):
@@ -40,7 +40,7 @@ def getUser():
def getProduct(): def getProduct():
while 1: while 1:
string = input("product? ") string = input("product? ")
session = Session() session = create_session()
product = search_product(string, session) product = search_product(string, session)
session.close() session.close()
if not isinstance(product, list): if not isinstance(product, list):
@@ -242,7 +242,7 @@ def buildDatabaseFromDb(inputType, inputProduct, inputUser):
sdate = input("enter start date (yyyy-mm-dd)? ") sdate = input("enter start date (yyyy-mm-dd)? ")
edate = input("enter end date (yyyy-mm-dd)? ") edate = input("enter end date (yyyy-mm-dd)? ")
print("building database...") print("building database...")
session = Session() session = create_session()
transaction_list = session.query(Transaction).all() transaction_list = session.query(Transaction).all()
inputLine = InputLine(inputUser, inputProduct, inputType) inputLine = InputLine(inputUser, inputProduct, inputType)
startDate = getDateDb(transaction_list[0].time, sdate) startDate = getDateDb(transaction_list[0].time, sdate)
+13 -9
View File
@@ -1,8 +1,11 @@
import argparse import argparse
import sys
from pathlib import Path from pathlib import Path
from dibbler.conf import DEFAULT_CONFIG_PATH, config, default_config_path_submissive_and_readable from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from dibbler.conf import load_config
from dibbler.db import engine, session
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@@ -12,7 +15,7 @@ parser.add_argument(
help="Path to the config file", help="Path to the config file",
type=Path, type=Path,
metavar="FILE", metavar="FILE",
default="config.ini", required=False,
) )
subparsers = parser.add_subparsers( subparsers = parser.add_subparsers(
@@ -29,12 +32,13 @@ subparsers.add_parser("seed-data", help="Fill with mock data")
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
if args.config is not None: load_config(args.config)
config.read(args.config)
elif default_config_path_submissive_and_readable(): from dibbler.conf import config
config.read(DEFAULT_CONFIG_PATH)
else: global engine, session
print("Could not read config file, it was neither provided nor readable in default location", file=sys.stderr) engine = create_engine(config['database']['url'])
session = sessionmaker(bind=engine)
if args.subcommand == "loop": if args.subcommand == "loop":
import dibbler.subcommands.loop as loop import dibbler.subcommands.loop as loop
+4 -4
View File
@@ -37,7 +37,7 @@ When finished, write an empty line to confirm the purchase.\n"""
""" """
assert isinstance(user, User) assert isinstance(user, User)
return user.credit > config.getint("limits", "low_credit_warning_limit") return user.credit > config["limits"]["low_credit_warning_limit"]
def low_credit_warning(self, user, timeout=False): def low_credit_warning(self, user, timeout=False):
assert isinstance(user, User) assert isinstance(user, User)
@@ -58,7 +58,7 @@ When finished, write an empty line to confirm the purchase.\n"""
print("***********************************************************************") print("***********************************************************************")
print("") print("")
print( print(
f"USER {user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d}." f"USER {user.name} HAS LOWER CREDIT THAN {config['limits']['low_credit_warning_limit']:d}."
) )
print("THIS PURCHASE WILL CHARGE YOUR CREDIT TWICE AS MUCH.") print("THIS PURCHASE WILL CHARGE YOUR CREDIT TWICE AS MUCH.")
print("CONSIDER PUTTING MONEY IN THE BOX TO AVOID THIS.") print("CONSIDER PUTTING MONEY IN THE BOX TO AVOID THIS.")
@@ -178,9 +178,9 @@ When finished, write an empty line to confirm the purchase.\n"""
for t in self.purchase.transactions: for t in self.purchase.transactions:
if not t.user.is_anonymous(): if not t.user.is_anonymous():
print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr") print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr")
if t.user.credit < config.getint("limits", "low_credit_warning_limit"): if t.user.credit < config["limits"]["low_credit_warning_limit"]:
print( print(
f"USER {t.user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d},", f"USER {t.user.name} HAS LOWER CREDIT THAN {config['limits']['low_credit_warning_limit']:d},",
"AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.", "AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.",
) )
+2 -2
View File
@@ -5,7 +5,7 @@ import re
import sys import sys
from select import select from select import select
from dibbler.db import Session from dibbler.db import session as create_session
from dibbler.models import User from dibbler.models import User
from dibbler.lib.helpers import ( from dibbler.lib.helpers import (
search_user, search_user,
@@ -485,7 +485,7 @@ class Menu(object):
self.set_context(None) self.set_context(None)
try: try:
if self.uses_db and not self.session: if self.uses_db and not self.session:
self.session = Session() self.session = create_session()
return self._execute(**kwargs) return self._execute(**kwargs)
except ExitMenu: except ExitMenu:
self.at_exit() self.at_exit()
+2 -2
View File
@@ -3,7 +3,7 @@ import os
import random import random
import sys import sys
from dibbler.db import Session from dibbler.db import session as create_session
from .buymenu import BuyMenu from .buymenu import BuyMenu
from .faq import FAQMenu from .faq import FAQMenu
@@ -28,7 +28,7 @@ class MainMenu(Menu):
else: else:
num = 1 num = 1
item_name = in_str item_name = in_str
buy_menu = BuyMenu(Session()) buy_menu = BuyMenu(create_session())
thing = buy_menu.search_for_thing(item_name, find_hidden_products=False) thing = buy_menu.search_for_thing(item_name, find_hidden_products=False)
if thing: if thing:
buy_menu.execute(initial_contents=[(thing, num)]) buy_menu.execute(initial_contents=[(thing, num)])
+2 -2
View File
@@ -56,7 +56,7 @@ class ShowUserMenu(Menu):
( (
"transactions", "transactions",
"Recent transactions (List of last " "Recent transactions (List of last "
+ str(config.getint("limits", "user_recent_transaction_limit")) + str(config["limits"]["user_recent_transaction_limit"])
+ ")", + ")",
), ),
("products", f"Which products {user.name} has bought, and how many"), ("products", f"Which products {user.name} has bought, and how many"),
@@ -65,7 +65,7 @@ class ShowUserMenu(Menu):
) )
what = selector.execute() what = selector.execute()
if what == "transactions": if what == "transactions":
self.print_transactions(user, config.getint("limits", "user_recent_transaction_limit")) self.print_transactions(user, config["limits"]["user_recent_transaction_limit"])
elif what == "products": elif what == "products":
self.print_purchased_products(user) self.print_purchased_products(user)
elif what == "transactions-all": elif what == "transactions-all":
+3 -3
View File
@@ -32,14 +32,14 @@ Put it up somewhere in the vicinity.
thing.bar_code, thing.bar_code,
thing.name, thing.name,
barcode_type=bar_type, barcode_type=bar_type,
rotate=config.getboolean("printer", "rotate"), rotate=config["printer"]["rotate"],
printer_type="QL-700", printer_type="QL-700",
label_type=config.get("printer", "label_type"), label_type=config.get("printer", "label_type"),
) )
elif isinstance(thing, User): elif isinstance(thing, User):
print_name_label( print_name_label(
text=thing.name, text=thing.name,
label_type=config.get("printer", "label_type"), label_type=config["printer"]["label_type"],
rotate=config.getboolean("printer", "rotate"), rotate=config["printer"]["rotate"],
printer_type="QL-700", printer_type="QL-700",
) )
+4 -4
View File
@@ -13,10 +13,10 @@ random.seed()
def main(): def main():
if not config.getboolean("general", "stop_allowed"): if not config["general"]["stop_allowed"]:
signal.signal(signal.SIGQUIT, signal.SIG_IGN) signal.signal(signal.SIGQUIT, signal.SIG_IGN)
if not config.getboolean("general", "stop_allowed"): if not config["general"]["stop_allowed"]:
signal.signal(signal.SIGTSTP, signal.SIG_IGN) signal.signal(signal.SIGTSTP, signal.SIG_IGN)
main = MainMenu( main = MainMenu(
@@ -56,7 +56,7 @@ def main():
exit_msg="happy happy joy joy", exit_msg="happy happy joy joy",
exit_confirm_msg="Really quit Dibbler?", exit_confirm_msg="Really quit Dibbler?",
) )
if not config.getboolean("general", "quit_allowed"): if not config["general"]["quit_allowed"]:
main.exit_disallowed_msg = "You can check out any time you like, but you can never leave." main.exit_disallowed_msg = "You can check out any time you like, but you can never leave."
while True: while True:
# noinspection PyBroadException # noinspection PyBroadException
@@ -68,7 +68,7 @@ def main():
except: except:
print("Something went wrong.") print("Something went wrong.")
print(f"{sys.exc_info()[0]}: {sys.exc_info()[1]}") print(f"{sys.exc_info()[0]}: {sys.exc_info()[1]}")
if config.getboolean("general", "show_tracebacks"): if config["general"]["show_tracebacks"]:
traceback.print_tb(sys.exc_info()[2]) traceback.print_tb(sys.exc_info()[2])
else: else:
break break
+2 -2
View File
@@ -1,5 +1,5 @@
import json import json
from dibbler.db import Session from dibbler.db import session as create_session
from pathlib import Path from pathlib import Path
@@ -17,7 +17,7 @@ def clear_db(session):
def main(): def main():
session = Session() session = create_session()
clear_db(session) clear_db(session)
product_items = [] product_items = []
user_items = [] user_items = []
+2 -2
View File
@@ -1,12 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
from dibbler.db import Session from dibbler.db import session as create_session
from dibbler.models import User from dibbler.models import User
def main(): def main():
# Start an SQL session # Start an SQL session
session = Session() session = create_session()
# Let's find all users with a negative credit # Let's find all users with a negative credit
slabbedasker = session.query(User).filter(User.credit < 0).all() slabbedasker = session.query(User).filter(User.credit < 0).all()
+5 -5
View File
@@ -1,7 +1,7 @@
{ config, pkgs, lib, ... }: let { config, pkgs, lib, ... }: let
cfg = config.services.dibbler; cfg = config.services.dibbler;
format = pkgs.formats.ini { }; format = pkgs.formats.toml { };
in { in {
options.services.dibbler = { options.services.dibbler = {
enable = lib.mkEnableOption "dibbler, the little kiosk computer"; enable = lib.mkEnableOption "dibbler, the little kiosk computer";
@@ -58,7 +58,7 @@ in {
config = lib.mkIf cfg.enable (lib.mkMerge [ config = lib.mkIf cfg.enable (lib.mkMerge [
{ {
services.dibbler.settings = lib.pipe ../example-config.ini [ services.dibbler.settings = lib.pipe ../example-config.toml [
builtins.readFile builtins.readFile
builtins.fromTOML builtins.fromTOML
(lib.mapAttrsRecursive (_: lib.mkDefault)) (lib.mapAttrsRecursive (_: lib.mkDefault))
@@ -67,7 +67,7 @@ in {
{ {
environment.systemPackages = [ cfg.package ]; environment.systemPackages = [ cfg.package ];
environment.etc."dibbler/dibbler.conf".source = format.generate "dibbler.conf" cfg.settings; environment.etc."dibbler/dibbler.toml".source = format.generate "dibbler.toml" cfg.settings;
users = { users = {
users.dibbler = { users.dibbler = {
@@ -97,7 +97,7 @@ in {
}; };
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.conf create-db"; ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done"; ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done";
StateDirectory = "dibbler"; StateDirectory = "dibbler";
@@ -163,7 +163,7 @@ in {
]; ];
dibblerArgs = lib.cli.toCommandLineShellGNU { } { dibblerArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/dibbler/dibbler.conf"; config = "/etc/dibbler/dibbler.toml";
}; };
in "${lib.getExe cfg.screenPackage} ${screenArgs} ${lib.getExe cfg.package} ${dibblerArgs} loop"; in "${lib.getExe cfg.screenPackage} ${screenArgs} ${lib.getExe cfg.package} ${dibblerArgs} loop";