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
asked Sep 2, 2019 at 15:38
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Please add your imports. This is especially important since you use JsonFileWrapperBase._get_data. \$\endgroup\$ 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.

answered Sep 2, 2019 at 16:26
\$\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\$ Commented Sep 2, 2019 at 16:43
  • \$\begingroup\$ Ok, thats fair enough. \$\endgroup\$ Commented Sep 2, 2019 at 16:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.