How Configuration is Loaded from config.json in MoneyPrinterV2
MoneyPrinterV2 loads runtime settings by reading config.json from the repository root on every getter call, ensuring real-time configuration updates without application restarts.
The MoneyPrinterV2 repository centralizes all user-editable settings in a single JSON file. The loading logic resides entirely in src/config.py, which exposes type-safe getter functions that downstream modules import and use. This architecture guarantees that changes to config.json take effect immediately, as the file is parsed fresh each time a configuration value is requested.
Configuration Architecture Overview
The configuration system is built around a deterministic path resolution strategy and a stateless loading pattern.
Project Root Detection
At module initialization, src/config.py calculates an absolute project root that remains valid regardless of the current working directory. Line 8 establishes the ROOT_DIR constant:
ROOT_DIR = os.path.dirname(sys.path[0])
This path is then used to construct the absolute location of config.json, ensuring the file is found even when scripts are executed from subdirectories.
Stateless Loading Pattern
Unlike typical applications that load configuration once at startup, MoneyPrinterV2 implements a read-on-every-call strategy. Each getter function opens, parses, and extracts data from config.json independently. This eliminates the need for configuration reloading mechanisms and guarantees that manual edits to the JSON file are reflected in the next function invocation.
Step-by-Step Configuration Loading Process
When any module requests a configuration value, the following sequence executes:
- Path Construction: The getter builds the full file path using
os.path.join(ROOT_DIR, "config.json"). - File Opening: The JSON file is opened in read mode within a
withstatement to ensure proper resource handling. - JSON Parsing:
json.load(file)deserializes the contents into a Python dictionary namedconfig_json. - Value Extraction: The function retrieves the specific key (e.g.,
"verbose","headless","email"). Optional defaults are provided via.get(key, default)for backward compatibility. - Return: The extracted value is returned to the caller, and the file handle is automatically closed.
This sequence repeats for every configuration access, as seen in src/config.py at lines 39-40 for email credentials and lines 42-44 for the verbose flag.
Key Configuration Getters
The src/config.py module exposes numerous typed getter functions that isolate the loading logic from business logic.
Boolean Flags
get_verbose() and get_headless() return boolean values controlling debug output and browser visibility:
def get_verbose() -> bool:
with open(os.path.join(ROOT_DIR, "config.json"), "r") as file:
config_json = json.load(file)
return config_json.get("verbose", False)
The headless configuration, retrieved via get_headless() at lines 70-71, determines whether Selenium or Playwright browsers run without a GUI.
String and Numeric Values
get_email() returns the sender address for notification systems, while get_script_sentence_length() (lines 328-339) provides an optional integer limit for text generation:
def get_script_sentence_length() -> int | None:
with open(os.path.join(ROOT_DIR, "config.json"), "r") as file:
config_json = json.load(file)
return config_json.get("script_sentence_length")
First-Run Detection
get_first_time_running() (lines 18-23) checks for the existence of a hidden .mp directory to determine if the application is initializing for the first time, though the directory creation logic resides elsewhere.
Usage in Downstream Modules
Modules throughout the codebase import specific getters rather than accessing the configuration file directly.
In src/main.py, the verbose flag influences logging behavior at line 213:
from config import get_verbose, get_headless
if get_verbose():
print("Debug information enabled")
Provider classes under src/classes/—such as YouTube.py and Twitter.py—use these getters to retrieve API credentials and behavior modifiers without hardcoding values.
Adding Custom Configuration Options
To extend the configuration system, follow the established pattern in src/config.py:
-
Add the key to
config.json:{ "my_custom_setting": "value" } -
Implement a getter function:
def get_my_custom_setting() -> str: with open(os.path.join(ROOT_DIR, "config.json"), "r") as file: config_json = json.load(file) return config_json.get("my_custom_setting", "default_value") -
Import and use in application code:
from config import get_my_custom_setting setting = get_my_custom_setting()
This pattern ensures consistency with the existing codebase and maintains the real-time update capability.
Summary
- Centralized storage: All settings reside in
config.jsonat the repository root. - Dynamic loading:
src/config.pyparses the JSON file on every getter invocation, ensuring changes take effect immediately. - Path resilience:
ROOT_DIRcalculation at line 8 guarantees consistent file access regardless of execution context. - Typed accessors: Individual getter functions like
get_verbose(),get_headless(), andget_script_sentence_length()provide clean APIs for the rest of the application. - Validation helper:
scripts/preflight_local.pyverifies thatconfig.jsonexists before the application starts.
Frequently Asked Questions
Where is the configuration file located in MoneyPrinterV2?
The configuration file is named config.json and must be placed in the repository root directory. The src/config.py module calculates the absolute path dynamically using ROOT_DIR = os.path.dirname(sys.path[0]), ensuring the file is found even when running scripts from subdirectories.
Does MoneyPrinterV2 reload configuration without restarting?
Yes. MoneyPrinterV2 does not cache configuration values in memory. Instead, every getter function in src/config.py opens and parses config.json fresh each time it is called. This design allows users to edit the JSON file while the application is running and see the changes reflected immediately in subsequent operations.
How does MoneyPrinterV2 handle missing configuration keys?
The getter functions use the dictionary .get() method with sensible defaults. For example, get_verbose() returns config_json.get("verbose", False), ensuring the application defaults to non-verbose behavior if the key is absent. Optional settings like script_sentence_length return None when missing, allowing the application to fall back to internal defaults.
What is the purpose of config.example.json in the repository?
config.example.json serves as a template for users to create their own config.json. It contains all available configuration keys with example values. The preflight script scripts/preflight_local.py checks for the existence of config.json at startup and alerts the user if it is missing, directing them to copy the example file.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →