From 1492c02bfa47e1a33aac0c590bcbcebc30d9ccbe Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Thu, 3 Oct 2024 17:39:39 -0700 Subject: [PATCH] lazy-load loadfire and ldap lib for faster startup time --- archivebox/config/legacy.py | 14 ++-- archivebox/core/settings.py | 6 +- archivebox/plugins_auth/ldap/apps.py | 12 ++-- archivebox/plugins_auth/ldap/settings.py | 91 ++++++++++++++---------- 4 files changed, 71 insertions(+), 52 deletions(-) diff --git a/archivebox/config/legacy.py b/archivebox/config/legacy.py index af9a9c8b..0163de8e 100644 --- a/archivebox/config/legacy.py +++ b/archivebox/config/legacy.py @@ -823,15 +823,15 @@ def setup_django(out_dir: Path | None=None, check_db=False, config: benedict=CON bump_startup_progress_bar() # https://docs.pydantic.dev/logfire/integrations/django/ Logfire Debugging - if settings.DEBUG_LOGFIRE: - from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor - SQLite3Instrumentor().instrument() + # if settings.DEBUG_LOGFIRE: + # from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor + # SQLite3Instrumentor().instrument() - import logfire + # import logfire - logfire.configure() - logfire.instrument_django(is_sql_commentor_enabled=True) - logfire.info(f'Started ArchiveBox v{CONSTANTS.VERSION}', argv=sys.argv) + # logfire.configure() + # logfire.instrument_django(is_sql_commentor_enabled=True) + # logfire.info(f'Started ArchiveBox v{CONSTANTS.VERSION}', argv=sys.argv) except KeyboardInterrupt: raise SystemExit(2) diff --git a/archivebox/core/settings.py b/archivebox/core/settings.py index ecf6b724..7be4f3e6 100644 --- a/archivebox/core/settings.py +++ b/archivebox/core/settings.py @@ -600,9 +600,9 @@ if DEBUG_REQUESTS_TRACKER: ), } -# https://docs.pydantic.dev/logfire/integrations/django/ (similar to DataDog / NewRelic / etc.) -DEBUG_LOGFIRE = False -DEBUG_LOGFIRE = DEBUG_LOGFIRE and (DATA_DIR / '.logfire').is_dir() +# # https://docs.pydantic.dev/logfire/integrations/django/ (similar to DataDog / NewRelic / etc.) +# DEBUG_LOGFIRE = False +# DEBUG_LOGFIRE = DEBUG_LOGFIRE and (DATA_DIR / '.logfire').is_dir() # For usage with https://www.jetadmin.io/integrations/django diff --git a/archivebox/plugins_auth/ldap/apps.py b/archivebox/plugins_auth/ldap/apps.py index 3d42d41e..7117de0e 100644 --- a/archivebox/plugins_auth/ldap/apps.py +++ b/archivebox/plugins_auth/ldap/apps.py @@ -13,11 +13,13 @@ from abx.archivebox.base_hook import BaseHook from abx.archivebox.base_binary import BaseBinary, BaseBinProvider from plugins_pkg.pip.apps import SYS_PIP_BINPROVIDER, VENV_PIP_BINPROVIDER -from .settings import LDAP_CONFIG, LDAP_LIB +from .settings import LDAP_CONFIG, get_ldap_lib ###################### Config ########################## +LDAP_LIB = lambda: get_ldap_lib()[0] + class LdapBinary(BaseBinary): name: str = 'ldap' @@ -26,12 +28,12 @@ class LdapBinary(BaseBinary): provider_overrides: Dict[BinProviderName, ProviderLookupDict] = { VENV_PIP_BINPROVIDER.name: { - "abspath": lambda: LDAP_LIB and Path(inspect.getfile(LDAP_LIB)), - "version": lambda: LDAP_LIB and SemVer(LDAP_LIB.__version__), + "abspath": lambda: LDAP_LIB() and Path(inspect.getfile(LDAP_LIB())), + "version": lambda: LDAP_LIB() and SemVer(LDAP_LIB().__version__), }, SYS_PIP_BINPROVIDER.name: { - "abspath": lambda: LDAP_LIB and Path(inspect.getfile(LDAP_LIB)), - "version": lambda: LDAP_LIB and SemVer(LDAP_LIB.__version__), + "abspath": lambda: LDAP_LIB() and Path(inspect.getfile(LDAP_LIB())), + "version": lambda: LDAP_LIB() and SemVer(LDAP_LIB().__version__), }, } diff --git a/archivebox/plugins_auth/ldap/settings.py b/archivebox/plugins_auth/ldap/settings.py index 440e592c..36e5b1d9 100644 --- a/archivebox/plugins_auth/ldap/settings.py +++ b/archivebox/plugins_auth/ldap/settings.py @@ -8,12 +8,19 @@ from pydantic import Field, model_validator, computed_field from abx.archivebox.base_configset import BaseConfigSet LDAP_LIB = None -try: - import ldap - from django_auth_ldap.config import LDAPSearch - LDAP_LIB = ldap -except ImportError: - pass +LDAP_SEARCH = None + +def get_ldap_lib(): + global LDAP_LIB, LDAP_SEARCH + if LDAP_LIB and LDAP_SEARCH: + return LDAP_LIB, LDAP_SEARCH + try: + import ldap + from django_auth_ldap.config import LDAPSearch + LDAP_LIB, LDAP_SEARCH = ldap, LDAPSearch + except ImportError: + pass + return LDAP_LIB, LDAP_SEARCH ###################### Config ########################## @@ -41,35 +48,40 @@ class LdapConfig(BaseConfigSet): @model_validator(mode='after') def validate_ldap_config(self): - # Check that LDAP libraries are installed - if self.LDAP_ENABLED and LDAP_LIB is None: - sys.stderr.write('[X] Error: LDAP Authentication is enabled but LDAP libraries are not installed. You may need to run: pip install archivebox[ldap]\n') - # dont hard exit here. in case the user is just running "archivebox version" or "archivebox help", we still want those to work despite broken ldap - # sys.exit(1) - self.update(LDAP_ENABLED=False) + if self.LDAP_ENABLED: + LDAP_LIB, _LDAPSearch = get_ldap_lib() + # Check that LDAP libraries are installed + if LDAP_LIB is None: + sys.stderr.write('[X] Error: LDAP Authentication is enabled but LDAP libraries are not installed. You may need to run: pip install archivebox[ldap]\n') + # dont hard exit here. in case the user is just running "archivebox version" or "archivebox help", we still want those to work despite broken ldap + # sys.exit(1) + self.update_in_place(LDAP_ENABLED=False) - # Check that all required LDAP config options are set - if self.LDAP_ENABLED and not self.LDAP_CONFIG_IS_SET: - missing_config_options = [ - key for key, value in self.model_dump().items() - if value is None and key != 'LDAP_ENABLED' - ] - sys.stderr.write('[X] Error: LDAP_* config options must all be set if LDAP_ENABLED=True\n') - sys.stderr.write(f' Missing: {", ".join(missing_config_options)}\n') - self.update(LDAP_ENABLED=False) + # Check that all required LDAP config options are set + if self.self.LDAP_CONFIG_IS_SET: + missing_config_options = [ + key for key, value in self.model_dump().items() + if value is None and key != 'LDAP_ENABLED' + ] + sys.stderr.write('[X] Error: LDAP_* config options must all be set if LDAP_ENABLED=True\n') + sys.stderr.write(f' Missing: {", ".join(missing_config_options)}\n') + self.update_in_place(LDAP_ENABLED=False) return self @computed_field @property def LDAP_CONFIG_IS_SET(self) -> bool: """Check that all required LDAP config options are set""" - return bool(LDAP_LIB) and self.LDAP_ENABLED and bool( - self.LDAP_SERVER_URI - and self.LDAP_BIND_DN - and self.LDAP_BIND_PASSWORD - and self.LDAP_USER_BASE - and self.LDAP_USER_FILTER - ) + if self.LDAP_ENABLED: + LDAP_LIB, _LDAPSearch = get_ldap_lib() + return bool(LDAP_LIB) and self.LDAP_ENABLED and bool( + self.LDAP_SERVER_URI + and self.LDAP_BIND_DN + and self.LDAP_BIND_PASSWORD + and self.LDAP_USER_BASE + and self.LDAP_USER_FILTER + ) + return False @computed_field @property @@ -84,19 +96,24 @@ class LdapConfig(BaseConfigSet): @computed_field @property def AUTHENTICATION_BACKENDS(self) -> List[str]: - return [ - 'django.contrib.auth.backends.ModelBackend', - 'django_auth_ldap.backend.LDAPBackend', - ] + if self.LDAP_ENABLED: + return [ + 'django.contrib.auth.backends.ModelBackend', + 'django_auth_ldap.backend.LDAPBackend', + ] + return [] @computed_field @property def AUTH_LDAP_USER_SEARCH(self) -> Optional[object]: - return self.LDAP_USER_FILTER and LDAPSearch( - self.LDAP_USER_BASE, - LDAP_LIB.SCOPE_SUBTREE, # type: ignore - '(&(' + self.LDAP_USERNAME_ATTR + '=%(user)s)' + self.LDAP_USER_FILTER + ')', - ) + if self.LDAP_ENABLED: + LDAP_LIB, LDAPSearch = get_ldap_lib() + return self.LDAP_USER_FILTER and LDAPSearch( + self.LDAP_USER_BASE, + LDAP_LIB.SCOPE_SUBTREE, # type: ignore + '(&(' + self.LDAP_USERNAME_ATTR + '=%(user)s)' + self.LDAP_USER_FILTER + ')', + ) + return None LDAP_CONFIG = LdapConfig()