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
1 Answer 1
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.
-
\$\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 whichmcs
is standard. \$\endgroup\$Joshua Nixon– Joshua Nixon2019年09月02日 16:32:46 +00:00Commented 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\$Joshua Nixon– Joshua Nixon2019年09月02日 16:40:01 +00:00Commented 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– Reinderien2019年09月02日 16:43:45 +00:00Commented Sep 2, 2019 at 16:43
-
\$\begingroup\$ Ok, thats fair enough. \$\endgroup\$Joshua Nixon– Joshua Nixon2019年09月02日 16:45:30 +00:00Commented Sep 2, 2019 at 16:45
JsonFileWrapperBase._get_data
. \$\endgroup\$