From 91fb131abffcf0f8891821ccad09a6538e5818df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20K=C3=A1rolyi?= Date: Mon, 6 Dec 2021 17:58:28 +0100 Subject: [PATCH] Fixes #85, optional skipping of the autoupdate --- CHANGELOG.txt | 3 +++ README.md | 2 ++ setup.py | 2 +- tests/test_blacklist_check.py | 4 +++- tests/test_install.py | 6 +++--- validate_email/constants.py | 6 +++--- validate_email/domainlist_check.py | 19 ++++++++++--------- validate_email/updater.py | 20 +++++++++++++------- 8 files changed, 38 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fde4354..c2b2fee 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,6 @@ +1.0.4: +- Skipping the startup update with setting an environment variable called `PY3VE_IGNORE_UPDATER` + 1.0.3: - Moving project off github - Static type check fixes diff --git a/README.md b/README.md index eb23a67..cf68d7f 100644 --- a/README.md +++ b/README.md @@ -172,5 +172,7 @@ The update can be triggered manually: `callback`: An optional Callable (function/method) to be called when the update is done. +You can completely skip the auto-update on startup by setting the environment variable `PY3VE_IGNORE_UPDATER` to any value. + # Read the [FAQ](https://gitea.ksol.io/karolyi/py3-validate-email/src/branch/master/FAQ.md)! diff --git a/setup.py b/setup.py index e8ac02d..3a7e6f1 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ class BuildPyCommand(build_py): setup( name='py3-validate-email', - version='1.0.3', + version='1.0.4', packages=find_packages(exclude=['tests']), install_requires=['dnspython~=2.1', 'idna~=3.0', 'filelock~=3.0'], author='László Károlyi', diff --git a/tests/test_blacklist_check.py b/tests/test_blacklist_check.py index 987b713..365a6e0 100644 --- a/tests/test_blacklist_check.py +++ b/tests/test_blacklist_check.py @@ -11,11 +11,13 @@ from validate_email.validate_email import ( class BlacklistCheckTestCase(TestCase): 'Testing if the included blacklist filtering works.' - def setUpClass(): + def setUpClass(): # type: ignore update_builtin_blacklist(force=False, background=False) def test_blacklist_positive(self): 'Disallows blacklist item: mailinator.com.' + # The setting of the PY3VE_IGNORE_UPDATER variable doesn't + # matter here as the module has already download an initial list with self.assertRaises(DomainBlacklistedError): domainlist_check(EmailAddress('pm2@mailinator.com')) with self.assertRaises(DomainBlacklistedError): diff --git a/tests/test_install.py b/tests/test_install.py index fae0d9c..4814f88 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -16,11 +16,11 @@ class InstallTest(TestCase): 'Testing package installation.' def test_datadir_is_in_place(self): - 'Data directory should be in the virtualenv.' + 'Data directory should be in the virtualenv *after installation*.' output = check_output([ executable, '-c', ( - 'import sys;sys.path.remove("");import validate_email;' - 'print(validate_email.updater.BLACKLIST_FILEPATH_INSTALLED);' + 'import sys;sys.path.remove("");import validate_email;' + + 'print(validate_email.updater.BLACKLIST_FILEPATH_INSTALLED);' + 'print(validate_email.updater.ETAG_FILEPATH_INSTALLED, end="")' )]).decode('ascii') bl_path, etag_path = output.split('\n') diff --git a/validate_email/constants.py b/validate_email/constants.py index b4827f1..9725a5b 100644 --- a/validate_email/constants.py +++ b/validate_email/constants.py @@ -3,14 +3,14 @@ from re import compile as re_compile HOST_REGEX = re_compile( # max length for domain name labels is 63 characters per RFC 1034 - r'((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)' + r'((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+)' + r'(?:[A-Z0-9-]{2,63}(? str: + def _blacklist_path(self) -> Path: 'Return the path of the `blacklist.txt` that should be loaded.' try: # Zero size: file is touched to indicate the preinstalled @@ -43,9 +42,8 @@ class DomainListValidator(object): def reload_builtin_blacklist(self): '(Re)load our built-in blacklist.' - if not self._is_builtin_bl_used: - return - with FileLock(lock_file=LOCK_PATH): + TMP_PATH.mkdir(exist_ok=True) + with FileLock(lock_file=str(LOCK_PATH)): bl_path = self._blacklist_path LOGGER.debug(msg=f'(Re)loading blacklist from {bl_path}') try: @@ -57,7 +55,10 @@ class DomainListValidator(object): x.strip().lower() for x in lines if x.strip()) def __call__(self, email_address: EmailAddress) -> bool: - 'Do the checking here.' + """ + Check if the email domain is valid, raise + `DomainBlacklistedError` if not. + """ if email_address.domain in self.domain_whitelist: return True if email_address.domain in self.domain_blacklist: diff --git a/validate_email/updater.py b/validate_email/updater.py index ba9e3ba..d55cbf2 100644 --- a/validate_email/updater.py +++ b/validate_email/updater.py @@ -1,5 +1,6 @@ from http.client import HTTPResponse from logging import getLogger +from os import environ from pathlib import Path from tempfile import gettempdir, gettempprefix from threading import Thread @@ -19,9 +20,9 @@ except ImportError: LOGGER = getLogger(__name__) TMP_PATH = Path(gettempdir()).joinpath( f'{gettempprefix()}-py3-validate-email-{geteuid()}') -TMP_PATH.mkdir(exist_ok=True) +ENV_IGNORE_UPDATER = environ.get('PY3VE_IGNORE_UPDATER') BLACKLIST_URL = ( - 'https://raw.githubusercontent.com/disposable-email-domains/' + 'https://raw.githubusercontent.com/disposable-email-domains/' + 'disposable-email-domains/master/disposable_email_blocklist.conf') LIB_PATH_DEFAULT = Path(__file__).resolve().parent.joinpath('data') BLACKLIST_FILEPATH_INSTALLED = LIB_PATH_DEFAULT.joinpath('blacklist.txt') @@ -117,9 +118,11 @@ class BlacklistUpdater(object): self, force: bool = False, callback: Optional[Callable] = None): 'Start optionally updating the blacklist.txt file.' # Locking to avoid multi-process update on multi-process startup - # Import filelock locally because this module is als used by setup.py + # Import filelock locally because this module is als used by + # setup.py from filelock import FileLock - with FileLock(lock_file=LOCK_PATH): + TMP_PATH.mkdir(exist_ok=True) + with FileLock(lock_file=str(LOCK_PATH)): self._process(force=force) # Always execute callback because multiple processes can have # different versions of blacklists (one before, one after @@ -135,12 +138,15 @@ def update_builtin_blacklist( Update and reload the built-in blacklist. Return the `Thread` used to do the background update, so it can be `join()`-ed. """ + if ENV_IGNORE_UPDATER: + LOGGER.debug(msg='Skipping update of built-in blacklist.') + return LOGGER.debug(msg='Starting optional update of built-in blacklist.') blacklist_updater = BlacklistUpdater() kwargs = dict(force=force, callback=callback) if not background: blacklist_updater.process(**kwargs) return - bl_thread = Thread(target=blacklist_updater.process, kwargs=kwargs) - bl_thread.start() - return bl_thread + updater_thread = Thread(target=blacklist_updater.process, kwargs=kwargs) + updater_thread.start() + return updater_thread