7
\$\begingroup\$

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) -> 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
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Please add your imports. This is especially important since you use JsonFileWrapperBase._get_data. \$\endgroup\$
    – Graipher
    Commented Sep 2, 2019 at 15:46
  • 1
    \$\begingroup\$ I don't think the base class is relevant to my question about best practises forwarding values from JSON but I added it to my question neverthless. \$\endgroup\$ Commented Sep 2, 2019 at 15:49

1 Answer 1

1
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ OK. First thing is that the base class is in a different file. i only added due to a comment (I'll make it clearer in my question). Second, the assert is a very last resort I used for debugging, It will never be called. Thirdly, as you can see I use a metaclass as solely overriding __getattribute__ doesn't work for which mcs is standard. \$\endgroup\$ Commented Sep 2, 2019 at 16:32
  • \$\begingroup\$ It was an formatting error regarding your assert comment. You haven't actually answered my question regarding forwarding values from JSON. \$\endgroup\$ Commented Sep 2, 2019 at 16:40
  • \$\begingroup\$ That's true! And it doesn't matter - refer to codereview.meta.stackexchange.com/questions/5773/… \$\endgroup\$
    – Reinderien
    Commented Sep 2, 2019 at 16:43
  • \$\begingroup\$ Ok, thats fair enough. \$\endgroup\$ Commented Sep 2, 2019 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.