# 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) -> 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

• Please add your imports. This is especially important since you use JsonFileWrapperBase._get_data. – Graipher Sep 2 '19 at 15:46
• 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. – Joshua Nixon Sep 2 '19 at 15:49

## 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.

• 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. – Joshua Nixon Sep 2 '19 at 16:32
• It was an formatting error regarding your assert comment. You haven't actually answered my question regarding forwarding values from JSON. – Joshua Nixon Sep 2 '19 at 16:40
• That's true! And it doesn't matter - refer to codereview.meta.stackexchange.com/questions/5773/… – Reinderien Sep 2 '19 at 16:43
• Ok, thats fair enough. – Joshua Nixon Sep 2 '19 at 16:45