How to Build a Thread‑Safe Singleton Config Service in Python

Learn how to create a lightweight, thread‑safe Python configuration manager that reads from and writes to a JSON file, ensures a single global Config instance across multiple threads, and persists changes on program exit using the atexit hook.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
How to Build a Thread‑Safe Singleton Config Service in Python

Background

Develop a multithreaded backend service with Python 3.12.0a6 that shares a single configuration across all threads, allows dynamic updates via an HTTP interface, and persists the configuration on shutdown.

Requirement 1: Simple Configuration Items

Support basic configuration fields such as name and port. The core implementation fits in fewer than 50 lines of Python.

"""实例配置管理"""
from dataclasses import dataclass

@dataclass
class Config(object):
    name: str = "mysql"
    port: int = 3306

Requirement 2: Global Singleton Config Object

Ensure that only one Config instance exists so that all threads operate on the same configuration.

"""实例配置管理"""
from dataclasses import dataclass

@dataclass
class Config(object):
    name: str = "mysql"
    port: int = 3306
    _instance = None

    # Singleton pattern
    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

Interactive demonstration shows that id(a) and id(b) are identical, confirming the singleton behavior.

Requirement 3: Initialise Config from a JSON File

Read /tmp/config.json at creation time and override default values. If the file is missing or an error occurs, keep defaults.

import json
import logging
from pathlib import Path
from dataclasses import dataclass

@dataclass
class Config(object):
    name: str = "mysql"
    port: int = 3306
    _instance = None

    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

    def __post_init__(self):
        """If the config file exists, load its values and overwrite defaults.
        On any exception keep the defaults.
        """
        if (config_file := Path("/tmp/config.json")) and config_file.exists():
            try:
                with open(config_file) as f:
                    json_data = json.loads(f.read())
                    self.__dict__.update(json_data)
            except Exception as err:
                pass
        else:
            logging.warn("config file '{}' not exists. well using defautl values .".format(config_file))

Example config.json:

{
  "name": "trump",
  "port": 8848
}

Running the class now yields Config(name='trump', port=8848), confirming the values were loaded.

Requirement 4: Persist Config on Program Exit

Register an atexit hook that writes the current configuration back to /tmp/config.json when the interpreter terminates.

import json
import atexit
import logging
from pathlib import Path
from dataclasses import dataclass, asdict

@dataclass
class Config(object):
    name: str = "mysql"
    port: int = 3306
    _instance = None

    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

    def __post_init__(self):
        """Load values from the config file if it exists; otherwise keep defaults.
        Register a shutdown hook to save the current config.
        """
        if (config_file := Path("/tmp/config.json")) and config_file.exists():
            try:
                with open(config_file) as f:
                    json_data = json.loads(f.read())
                    self.__dict__.update(json_data)
            except Exception as err:
                pass
        else:
            logging.warn("config file '{}' not exists. well using defautl values .".format(config_file))

        # Save config on exit
        def sync_to_disk():
            json_str = json.dumps(asdict(self), indent=4)
            with open(config_file, 'w') as f:
                logging.warning("save configs to '{}' ".format(config_file))
                f.write(json_str)

        atexit.register(sync_to_disk)

Interactive test shows that after modifying a.name = "hello-world" and exiting, the JSON file contains the updated values:

{
    "name": "hello-world",
    "port": 8848
}

This demonstrates a complete, thread‑safe, singleton configuration manager that reads from and writes to disk automatically.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonConfigurationJSONmultithreadingSingletonatexit
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.