forked from karolyi/py3-validate-email
Fixes #85, optional skipping of the autoupdate
This commit is contained in:
parent
07512c45db
commit
91fb131abf
|
@ -1,3 +1,6 @@
|
||||||
|
1.0.4:
|
||||||
|
- Skipping the startup update with setting an environment variable called `PY3VE_IGNORE_UPDATER`
|
||||||
|
|
||||||
1.0.3:
|
1.0.3:
|
||||||
- Moving project off github
|
- Moving project off github
|
||||||
- Static type check fixes
|
- Static type check fixes
|
||||||
|
|
|
@ -172,5 +172,7 @@ The update can be triggered manually:
|
||||||
|
|
||||||
`callback`: An optional Callable (function/method) to be called when the update is done.
|
`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)!
|
# Read the [FAQ](https://gitea.ksol.io/karolyi/py3-validate-email/src/branch/master/FAQ.md)!
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -57,7 +57,7 @@ class BuildPyCommand(build_py):
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='py3-validate-email',
|
name='py3-validate-email',
|
||||||
version='1.0.3',
|
version='1.0.4',
|
||||||
packages=find_packages(exclude=['tests']),
|
packages=find_packages(exclude=['tests']),
|
||||||
install_requires=['dnspython~=2.1', 'idna~=3.0', 'filelock~=3.0'],
|
install_requires=['dnspython~=2.1', 'idna~=3.0', 'filelock~=3.0'],
|
||||||
author='László Károlyi',
|
author='László Károlyi',
|
||||||
|
|
|
@ -11,11 +11,13 @@ from validate_email.validate_email import (
|
||||||
class BlacklistCheckTestCase(TestCase):
|
class BlacklistCheckTestCase(TestCase):
|
||||||
'Testing if the included blacklist filtering works.'
|
'Testing if the included blacklist filtering works.'
|
||||||
|
|
||||||
def setUpClass():
|
def setUpClass(): # type: ignore
|
||||||
update_builtin_blacklist(force=False, background=False)
|
update_builtin_blacklist(force=False, background=False)
|
||||||
|
|
||||||
def test_blacklist_positive(self):
|
def test_blacklist_positive(self):
|
||||||
'Disallows blacklist item: mailinator.com.'
|
'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):
|
with self.assertRaises(DomainBlacklistedError):
|
||||||
domainlist_check(EmailAddress('pm2@mailinator.com'))
|
domainlist_check(EmailAddress('pm2@mailinator.com'))
|
||||||
with self.assertRaises(DomainBlacklistedError):
|
with self.assertRaises(DomainBlacklistedError):
|
||||||
|
|
|
@ -16,11 +16,11 @@ class InstallTest(TestCase):
|
||||||
'Testing package installation.'
|
'Testing package installation.'
|
||||||
|
|
||||||
def test_datadir_is_in_place(self):
|
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([
|
output = check_output([
|
||||||
executable, '-c', (
|
executable, '-c', (
|
||||||
'import sys;sys.path.remove("");import validate_email;'
|
'import sys;sys.path.remove("");import validate_email;' +
|
||||||
'print(validate_email.updater.BLACKLIST_FILEPATH_INSTALLED);'
|
'print(validate_email.updater.BLACKLIST_FILEPATH_INSTALLED);' +
|
||||||
'print(validate_email.updater.ETAG_FILEPATH_INSTALLED, end="")'
|
'print(validate_email.updater.ETAG_FILEPATH_INSTALLED, end="")'
|
||||||
)]).decode('ascii')
|
)]).decode('ascii')
|
||||||
bl_path, etag_path = output.split('\n')
|
bl_path, etag_path = output.split('\n')
|
||||||
|
|
|
@ -3,14 +3,14 @@ from re import compile as re_compile
|
||||||
|
|
||||||
HOST_REGEX = re_compile(
|
HOST_REGEX = re_compile(
|
||||||
# max length for domain name labels is 63 characters per RFC 1034
|
# 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}(?<!-))\Z', IGNORECASE)
|
r'(?:[A-Z0-9-]{2,63}(?<!-))\Z', IGNORECASE)
|
||||||
LITERAL_REGEX = re_compile(
|
LITERAL_REGEX = re_compile(
|
||||||
# literal form, ipv4 or ipv6 address (SMTP 4.1.3)
|
# literal form, ipv4 or ipv6 address (SMTP 4.1.3)
|
||||||
r'\[([A-f0-9:\.]+)\]\Z', IGNORECASE)
|
r'\[([A-f0-9:\.]+)\]\Z', IGNORECASE)
|
||||||
USER_REGEX = re_compile(
|
USER_REGEX = re_compile(
|
||||||
# dot-atom
|
# dot-atom
|
||||||
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z"
|
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*\Z" +
|
||||||
# quoted-string
|
# quoted-string
|
||||||
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013'
|
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013' +
|
||||||
r'\014\016-\177])*"\Z)', IGNORECASE)
|
r'\014\016-\177])*"\Z)', IGNORECASE)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
|
@ -6,8 +7,8 @@ from filelock import FileLock
|
||||||
from .email_address import EmailAddress
|
from .email_address import EmailAddress
|
||||||
from .exceptions import DomainBlacklistedError
|
from .exceptions import DomainBlacklistedError
|
||||||
from .updater import (
|
from .updater import (
|
||||||
BLACKLIST_FILEPATH_INSTALLED, BLACKLIST_FILEPATH_TMP, LOCK_PATH,
|
BLACKLIST_FILEPATH_INSTALLED, BLACKLIST_FILEPATH_TMP, ENV_IGNORE_UPDATER,
|
||||||
update_builtin_blacklist)
|
LOCK_PATH, TMP_PATH, update_builtin_blacklist)
|
||||||
|
|
||||||
SetOrNone = Optional[set]
|
SetOrNone = Optional[set]
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
|
@ -17,7 +18,6 @@ class DomainListValidator(object):
|
||||||
'Check the provided email against domain lists.'
|
'Check the provided email against domain lists.'
|
||||||
domain_whitelist = set()
|
domain_whitelist = set()
|
||||||
domain_blacklist = set('localhost')
|
domain_blacklist = set('localhost')
|
||||||
_is_builtin_bl_used: bool = False
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, whitelist: SetOrNone = None, blacklist: SetOrNone = None):
|
self, whitelist: SetOrNone = None, blacklist: SetOrNone = None):
|
||||||
|
@ -26,11 +26,10 @@ class DomainListValidator(object):
|
||||||
if blacklist:
|
if blacklist:
|
||||||
self.domain_blacklist = set(x.lower() for x in blacklist)
|
self.domain_blacklist = set(x.lower() for x in blacklist)
|
||||||
else:
|
else:
|
||||||
self._is_builtin_bl_used = True
|
|
||||||
self.reload_builtin_blacklist()
|
self.reload_builtin_blacklist()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _blacklist_path(self) -> str:
|
def _blacklist_path(self) -> Path:
|
||||||
'Return the path of the `blacklist.txt` that should be loaded.'
|
'Return the path of the `blacklist.txt` that should be loaded.'
|
||||||
try:
|
try:
|
||||||
# Zero size: file is touched to indicate the preinstalled
|
# Zero size: file is touched to indicate the preinstalled
|
||||||
|
@ -43,9 +42,8 @@ class DomainListValidator(object):
|
||||||
|
|
||||||
def reload_builtin_blacklist(self):
|
def reload_builtin_blacklist(self):
|
||||||
'(Re)load our built-in blacklist.'
|
'(Re)load our built-in blacklist.'
|
||||||
if not self._is_builtin_bl_used:
|
TMP_PATH.mkdir(exist_ok=True)
|
||||||
return
|
with FileLock(lock_file=str(LOCK_PATH)):
|
||||||
with FileLock(lock_file=LOCK_PATH):
|
|
||||||
bl_path = self._blacklist_path
|
bl_path = self._blacklist_path
|
||||||
LOGGER.debug(msg=f'(Re)loading blacklist from {bl_path}')
|
LOGGER.debug(msg=f'(Re)loading blacklist from {bl_path}')
|
||||||
try:
|
try:
|
||||||
|
@ -57,7 +55,10 @@ class DomainListValidator(object):
|
||||||
x.strip().lower() for x in lines if x.strip())
|
x.strip().lower() for x in lines if x.strip())
|
||||||
|
|
||||||
def __call__(self, email_address: EmailAddress) -> bool:
|
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:
|
if email_address.domain in self.domain_whitelist:
|
||||||
return True
|
return True
|
||||||
if email_address.domain in self.domain_blacklist:
|
if email_address.domain in self.domain_blacklist:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from http.client import HTTPResponse
|
from http.client import HTTPResponse
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import gettempdir, gettempprefix
|
from tempfile import gettempdir, gettempprefix
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
@ -19,9 +20,9 @@ except ImportError:
|
||||||
LOGGER = getLogger(__name__)
|
LOGGER = getLogger(__name__)
|
||||||
TMP_PATH = Path(gettempdir()).joinpath(
|
TMP_PATH = Path(gettempdir()).joinpath(
|
||||||
f'{gettempprefix()}-py3-validate-email-{geteuid()}')
|
f'{gettempprefix()}-py3-validate-email-{geteuid()}')
|
||||||
TMP_PATH.mkdir(exist_ok=True)
|
ENV_IGNORE_UPDATER = environ.get('PY3VE_IGNORE_UPDATER')
|
||||||
BLACKLIST_URL = (
|
BLACKLIST_URL = (
|
||||||
'https://raw.githubusercontent.com/disposable-email-domains/'
|
'https://raw.githubusercontent.com/disposable-email-domains/' +
|
||||||
'disposable-email-domains/master/disposable_email_blocklist.conf')
|
'disposable-email-domains/master/disposable_email_blocklist.conf')
|
||||||
LIB_PATH_DEFAULT = Path(__file__).resolve().parent.joinpath('data')
|
LIB_PATH_DEFAULT = Path(__file__).resolve().parent.joinpath('data')
|
||||||
BLACKLIST_FILEPATH_INSTALLED = LIB_PATH_DEFAULT.joinpath('blacklist.txt')
|
BLACKLIST_FILEPATH_INSTALLED = LIB_PATH_DEFAULT.joinpath('blacklist.txt')
|
||||||
|
@ -117,9 +118,11 @@ class BlacklistUpdater(object):
|
||||||
self, force: bool = False, callback: Optional[Callable] = None):
|
self, force: bool = False, callback: Optional[Callable] = None):
|
||||||
'Start optionally updating the blacklist.txt file.'
|
'Start optionally updating the blacklist.txt file.'
|
||||||
# Locking to avoid multi-process update on multi-process startup
|
# 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
|
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)
|
self._process(force=force)
|
||||||
# Always execute callback because multiple processes can have
|
# Always execute callback because multiple processes can have
|
||||||
# different versions of blacklists (one before, one after
|
# 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
|
Update and reload the built-in blacklist. Return the `Thread` used
|
||||||
to do the background update, so it can be `join()`-ed.
|
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.')
|
LOGGER.debug(msg='Starting optional update of built-in blacklist.')
|
||||||
blacklist_updater = BlacklistUpdater()
|
blacklist_updater = BlacklistUpdater()
|
||||||
kwargs = dict(force=force, callback=callback)
|
kwargs = dict(force=force, callback=callback)
|
||||||
if not background:
|
if not background:
|
||||||
blacklist_updater.process(**kwargs)
|
blacklist_updater.process(**kwargs)
|
||||||
return
|
return
|
||||||
bl_thread = Thread(target=blacklist_updater.process, kwargs=kwargs)
|
updater_thread = Thread(target=blacklist_updater.process, kwargs=kwargs)
|
||||||
bl_thread.start()
|
updater_thread.start()
|
||||||
return bl_thread
|
return updater_thread
|
||||||
|
|
Loading…
Reference in New Issue