Add types to components.adblock

This commit is contained in:
Florian Bruhin 2018-12-10 15:52:00 +01:00
parent 3b53270ee3
commit a96c6efc34

View File

@ -25,21 +25,22 @@ import functools
import posixpath import posixpath
import zipfile import zipfile
import logging import logging
import typing
import pathlib
import argparse
from PyQt5.QtCore import QUrl
from qutebrowser.api import (cmdutils, hook, config, message, downloads, from qutebrowser.api import (cmdutils, hook, config, message, downloads,
requests) requests, apitypes)
logger = logging.getLogger('misc') logger = logging.getLogger('misc')
_host_blocker = None _host_blocker = typing.cast('HostBlocker', None)
def _guess_zip_filename(zf): def _guess_zip_filename(zf: zipfile.ZipFile) -> str:
"""Guess which file to use inside a zip file. """Guess which file to use inside a zip file."""
Args:
zf: A ZipFile instance.
"""
files = zf.namelist() files = zf.namelist()
if len(files) == 1: if len(files) == 1:
return files[0] return files[0]
@ -50,7 +51,7 @@ def _guess_zip_filename(zf):
raise FileNotFoundError("No hosts file found in zip") raise FileNotFoundError("No hosts file found in zip")
def get_fileobj(byte_io): def get_fileobj(byte_io: typing.IO[bytes]) -> typing.IO[bytes]:
"""Get a usable file object to read the hosts file from.""" """Get a usable file object to read the hosts file from."""
byte_io.seek(0) # rewind downloaded file byte_io.seek(0) # rewind downloaded file
if zipfile.is_zipfile(byte_io): if zipfile.is_zipfile(byte_io):
@ -63,24 +64,19 @@ def get_fileobj(byte_io):
return byte_io return byte_io
def _is_whitelisted_url(url): def _is_whitelisted_url(url: QUrl) -> bool:
"""Check if the given URL is on the adblock whitelist. """Check if the given URL is on the adblock whitelist."""
Args:
url: The URL to check as QUrl.
"""
for pattern in config.val.content.host_blocking.whitelist: for pattern in config.val.content.host_blocking.whitelist:
if pattern.matches(url): if pattern.matches(url):
return True return True
return False return False
class _FakeDownload: class _FakeDownload(downloads.TempDownload):
"""A download stub to use on_download_finished with local files.""" """A download stub to use on_download_finished with local files."""
def __init__(self, fileobj): def __init__(self, fileobj: typing.IO[bytes]) -> None:
self.basename = os.path.basename(fileobj.name)
self.fileobj = fileobj self.fileobj = fileobj
self.successful = True self.successful = True
@ -98,11 +94,12 @@ class HostBlocker:
_config_hosts_file: The path to a blocked-hosts in ~/.config _config_hosts_file: The path to a blocked-hosts in ~/.config
""" """
def __init__(self, *, data_dir, config_dir, args): def __init__(self, *, data_dir: pathlib.Path, config_dir: pathlib.Path,
args: argparse.Namespace) -> None:
self._args = args self._args = args
self._blocked_hosts = set() self._blocked_hosts = set() # type: typing.Set[str]
self._config_blocked_hosts = set() self._config_blocked_hosts = set() # type: typing.Set[str]
self._in_progress = [] self._in_progress = [] # type: typing.List[downloads.TempDownload]
self._done_count = 0 self._done_count = 0
self._local_hosts_file = str(data_dir / 'blocked-hosts') self._local_hosts_file = str(data_dir / 'blocked-hosts')
@ -121,7 +118,7 @@ class HostBlocker:
if not config.get('content.host_blocking.enabled', if not config.get('content.host_blocking.enabled',
url=first_party_url): url=first_party_url):
return False return
host = info.request_url.host() host = info.request_url.host()
blocked = ((host in self._blocked_hosts or blocked = ((host in self._blocked_hosts or
@ -133,7 +130,7 @@ class HostBlocker:
.format(info.request_url.host())) .format(info.request_url.host()))
info.block() info.block()
def _read_hosts_file(self, filename, target): def _read_hosts_file(self, filename: str, target: typing.Set[str]) -> bool:
"""Read hosts from the given filename. """Read hosts from the given filename.
Args: Args:
@ -155,7 +152,7 @@ class HostBlocker:
return True return True
def read_hosts(self): def read_hosts(self) -> None:
"""Read hosts from the existing blocked-hosts file.""" """Read hosts from the existing blocked-hosts file."""
self._blocked_hosts = set() self._blocked_hosts = set()
@ -171,7 +168,7 @@ class HostBlocker:
config.val.content.host_blocking.enabled): config.val.content.host_blocking.enabled):
message.info("Run :adblock-update to get adblock lists.") message.info("Run :adblock-update to get adblock lists.")
def adblock_update(self): def adblock_update(self) -> None:
"""Update the adblock block lists.""" """Update the adblock block lists."""
self._read_hosts_file(self._config_hosts_file, self._read_hosts_file(self._config_hosts_file,
self._config_blocked_hosts) self._config_blocked_hosts)
@ -192,7 +189,7 @@ class HostBlocker:
download.finished.connect( download.finished.connect(
functools.partial(self._on_download_finished, download)) functools.partial(self._on_download_finished, download))
def _import_local(self, filename): def _import_local(self, filename: str) -> None:
"""Adds the contents of a file to the blocklist. """Adds the contents of a file to the blocklist.
Args: Args:
@ -208,24 +205,24 @@ class HostBlocker:
self._in_progress.append(download) self._in_progress.append(download)
self._on_download_finished(download) self._on_download_finished(download)
def _parse_line(self, line): def _parse_line(self, raw_line: bytes) -> bool:
"""Parse a line from a host file. """Parse a line from a host file.
Args: Args:
line: The bytes object to parse. raw_line: The bytes object to parse.
Returns: Returns:
True if parsing succeeded, False otherwise. True if parsing succeeded, False otherwise.
""" """
if line.startswith(b'#'): if raw_line.startswith(b'#'):
# Ignoring comments early so we don't have to care about # Ignoring comments early so we don't have to care about
# encoding errors in them. # encoding errors in them.
return True return True
try: try:
line = line.decode('utf-8') line = raw_line.decode('utf-8')
except UnicodeDecodeError: except UnicodeDecodeError:
logger.error("Failed to decode: {!r}".format(line)) logger.error("Failed to decode: {!r}".format(raw_line))
return False return False
# Remove comments # Remove comments
@ -256,14 +253,11 @@ class HostBlocker:
return True return True
def _merge_file(self, byte_io): def _merge_file(self, byte_io: io.BytesIO) -> None:
"""Read and merge host files. """Read and merge host files.
Args: Args:
byte_io: The BytesIO object of the completed download. byte_io: The BytesIO object of the completed download.
Return:
A set of the merged hosts.
""" """
error_count = 0 error_count = 0
line_count = 0 line_count = 0
@ -286,7 +280,7 @@ class HostBlocker:
message.error("adblock: {} read errors for {}".format( message.error("adblock: {} read errors for {}".format(
error_count, byte_io.name)) error_count, byte_io.name))
def _on_lists_downloaded(self): def _on_lists_downloaded(self) -> None:
"""Install block lists after files have been downloaded.""" """Install block lists after files have been downloaded."""
with open(self._local_hosts_file, 'w', encoding='utf-8') as f: with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
for host in sorted(self._blocked_hosts): for host in sorted(self._blocked_hosts):
@ -294,7 +288,7 @@ class HostBlocker:
message.info("adblock: Read {} hosts from {} sources.".format( message.info("adblock: Read {} hosts from {} sources.".format(
len(self._blocked_hosts), self._done_count)) len(self._blocked_hosts), self._done_count))
def update_files(self): def update_files(self) -> None:
"""Update files when the config changed.""" """Update files when the config changed."""
if not config.val.content.host_blocking.lists: if not config.val.content.host_blocking.lists:
try: try:
@ -304,11 +298,11 @@ class HostBlocker:
except OSError as e: except OSError as e:
logger.exception("Failed to delete hosts file: {}".format(e)) logger.exception("Failed to delete hosts file: {}".format(e))
def _on_download_finished(self, download): def _on_download_finished(self, download: downloads.TempDownload) -> None:
"""Check if all downloads are finished and if so, trigger reading. """Check if all downloads are finished and if so, trigger reading.
Arguments: Arguments:
download: The finished DownloadItem. download: The finished download.
""" """
self._in_progress.remove(download) self._in_progress.remove(download)
if download.successful: if download.successful:
@ -325,7 +319,7 @@ class HostBlocker:
@cmdutils.register() @cmdutils.register()
def adblock_update(): def adblock_update() -> None:
"""Update the adblock block lists. """Update the adblock block lists.
This updates `~/.local/share/qutebrowser/blocked-hosts` with downloaded This updates `~/.local/share/qutebrowser/blocked-hosts` with downloaded
@ -337,12 +331,12 @@ def adblock_update():
@hook.config_changed('content.host_blocking.lists') @hook.config_changed('content.host_blocking.lists')
def on_config_changed(): def on_config_changed() -> None:
_host_blocker.update_files() _host_blocker.update_files()
@hook.init() @hook.init()
def init(context): def init(context: apitypes.InitContext) -> None:
global _host_blocker global _host_blocker
_host_blocker = HostBlocker(data_dir=context.data_dir, _host_blocker = HostBlocker(data_dir=context.data_dir,
config_dir=context.config_dir, config_dir=context.config_dir,