Forward values from JSON file as class attributes I have a settings file, which is loaded in once and takes the values from the settings json file and uses them as the attribute values. I was thinking of using the @property instead of overriding __getattribute__ but I feel as if there is a better, more elegant solution. Note that the application it is used in is multi-threaded.

settings.py

class SettingsMeta(type, JsonFileWrapperBase):
    _file_path = os.path.join(os.getcwd(), "..", "data", "settings.json")

    # Declarations to avoid PyCharm highlighting
    manga_save_dir, database_path, total_scrapper_threads, log_file = None, None, None, None

    _default_data = {
        "manga_save_dir": r"D:\dir",
        "database_path": r"D:\db.sqlite3",
        "log_file": os.path.join(os.getcwd(), "..", "data", "log.log"),

        "total_scrapper_threads": 1
    }

    @classmethod
    def __getattribute__(mcs, item):
        data = mcs._get_data()

        if item in data:
            return data[item]

        elif hasattr(mcs, item):
            return getattr(mcs, item)

        raise AttributeError(f"Settings has no attribute {item}")


class Settings(metaclass=SettingsMeta):
    pass


json_file_wrapper_base.py

class JsonFileWrapperBase:
    _loaded_data = None

    _file_path = ""
    _default_data = ""

    @classmethod
    def _write_default(cls):
        cls._write_data(cls._default_data)

    @classmethod
    def _write_data(cls, d):
        os.makedirs(os.path.dirname(os.path.abspath(cls._file_path)), exist_ok=True)

        try:
            with open(cls._file_path, "w") as f:
                json.dump(d, f)

        except FileNotFoundError as e:
            print(e)

    @classmethod
    def _get_data(cls):
        """
        Reads and caches the JSON file, if the file has already been cached then return the cached file, I cache
        the file to avoid users editing the file during execution and causing errors
        """

        # File has not been cached yet
        if cls._loaded_data is None:
            cls._read_file()

        return cls._loaded_data

    @classmethod
    def _read_file(cls) -&gt; bool:
        assert cls._loaded_data is None, "_read_file should only be called once per object"

        # File doesn't exist or is not valid
        if not os.path.isfile(cls._file_path):
            cls._write_default()

        try:
            with open(cls._file_path, "r") as f:
                data = json.load(f)

                print(f"Log: Loaded {cls._file_path}")

                cls._loaded_data = data

        except FileNotFoundError as e:
            print(e)

            return False

        return True
Pathlib

Use it. It makes stuff like this:

os.path.join(os.getcwd(), "..", "data", "log.log"),


much nicer. It can also avoid this kind of thing:

"D:\dir"


namely, OS-specific directory separators. If you ever hope to have this be cross-platform, you'll want to remove your backslashes and use the libraries for path manipulation.