-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Currently the replit library has a very gross quirk: it has a global in `replit.database.default_db.db`, and the mere action of importing this library causes side effects to run! (connects to the database, starts a thread to refresh the URL, and prints a warning to stdout, adding insult to injury). So we're trading that very gross quirk with a gross workaround to preserve backwards compatibility: the modules that somehow end up importing that module now have a `__getattr__` that _lazily_ calls the code that used to be invoked as a side-effect of importing the library. Maybe in the future we'll deploy a breaking version of the library where we're not beholden to this backwards-compatibility quirck.
- Loading branch information
Showing
6 changed files
with
110 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,64 @@ | ||
"""A module containing the default database.""" | ||
from os import environ, path | ||
import logging | ||
import os | ||
import os.path | ||
import threading | ||
from typing import Optional | ||
from typing import Any, Optional | ||
|
||
|
||
from .database import Database | ||
|
||
|
||
def get_db_url() -> str: | ||
def get_db_url() -> Optional[str]: | ||
"""Fetches the most up-to-date db url from the Repl environment.""" | ||
# todo look into the security warning ignored below | ||
tmpdir = "/tmp/replitdb" # noqa: S108 | ||
if path.exists(tmpdir): | ||
if os.path.exists(tmpdir): | ||
with open(tmpdir, "r") as file: | ||
db_url = file.read() | ||
else: | ||
db_url = environ.get("REPLIT_DB_URL") | ||
return file.read() | ||
|
||
return db_url | ||
return os.environ.get("REPLIT_DB_URL") | ||
|
||
|
||
def refresh_db() -> None: | ||
"""Refresh the DB URL every hour.""" | ||
global db | ||
db_url = get_db_url() | ||
db.update_db_url(db_url) | ||
threading.Timer(3600, refresh_db).start() | ||
class LazyDB: | ||
"""A way to lazily create a database connection.""" | ||
|
||
_instance: Optional["LazyDB"] = None | ||
|
||
db: Optional[Database] | ||
db_url = get_db_url() | ||
if db_url: | ||
db = Database(db_url) | ||
else: | ||
# The user will see errors if they try to use the database. | ||
print("Warning: error initializing database. Replit DB is not configured.") | ||
db = None | ||
def __init__(self) -> None: | ||
self.db: Optional[Database] = None | ||
self.db_url = get_db_url() | ||
if self.db_url: | ||
self.db = Database(self.db_url) | ||
self.refresh_db() | ||
else: | ||
logging.warning( | ||
"Warning: error initializing database. Replit DB is not configured." | ||
) | ||
|
||
if db: | ||
refresh_db() | ||
def refresh_db(self) -> None: | ||
"""Refresh the DB URL every hour.""" | ||
if not self.db: | ||
return | ||
self.db_url = get_db_url() | ||
if self.db_url: | ||
self.db.update_db_url(self.db_url) | ||
threading.Timer(3600, self.refresh_db).start() | ||
|
||
@classmethod | ||
def get_instance(cls) -> "LazyDB": | ||
if cls._instance is None: | ||
cls._instance = LazyDB() | ||
return cls._instance | ||
|
||
|
||
# Previous versions of this library would just have side-effects and always set | ||
# up a database unconditionally. That is very undesirable, so instead of doing | ||
# that, we are using this egregious hack to get the database / database URL | ||
# lazily. | ||
def __getattr__(name: str) -> Any: | ||
if name == "db": | ||
return LazyDB.get_instance().db | ||
if name == "db_url": | ||
return LazyDB.get_instance().db_url | ||
raise AttributeError(name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters