mypy: Add types-beautifulsoup4.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-01-21 22:31:33 -08:00 committed by Anders Kaseorg
parent 8d9fe9cfb0
commit 4922632601
8 changed files with 39 additions and 11 deletions

View File

@ -51,7 +51,6 @@ module = [
"aioapns.*", "aioapns.*",
"bitfield.*", "bitfield.*",
"bmemcached.*", "bmemcached.*",
"bs4.*",
"bson.*", "bson.*",
"cairosvg.*", "cairosvg.*",
"circuitbreaker.*", "circuitbreaker.*",

View File

@ -1926,6 +1926,10 @@ typeguard==2.13.3 \
--hash=sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4 \ --hash=sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4 \
--hash=sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1 --hash=sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1
# via testslide # via testslide
types-beautifulsoup4==4.10.11 \
--hash=sha256:29d1c71b378e75c722bc63845dde8d69832f5fa398c8c1322a5fd144ff6df714 \
--hash=sha256:2fad472ea689a457c97098069ad133ddffe45c1f758bc81273b43b5c3a2258da
# via -r requirements/mypy.in
types-boto==2.49.6 \ types-boto==2.49.6 \
--hash=sha256:c23e97e5bed847fce1578c7f07e14d278b6f18bb2085b24467a9a6520848acf7 \ --hash=sha256:c23e97e5bed847fce1578c7f07e14d278b6f18bb2085b24467a9a6520848acf7 \
--hash=sha256:d3ac75943063d43108b46140143c7fa9ae9d3f7007633d7119b63f722e3e02b0 --hash=sha256:d3ac75943063d43108b46140143c7fa9ae9d3f7007633d7119b63f722e3e02b0

View File

@ -8,6 +8,7 @@ backoff-stubs
boto3-stubs[s3] boto3-stubs[s3]
lxml-stubs lxml-stubs
sqlalchemy[mypy] sqlalchemy[mypy]
types-beautifulsoup4
types-boto types-boto
types-certifi types-certifi
types-chardet types-chardet

View File

@ -184,6 +184,10 @@ typed-ast==1.5.1 \
--hash=sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb \ --hash=sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb \
--hash=sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e --hash=sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e
# via mypy # via mypy
types-beautifulsoup4==4.10.11 \
--hash=sha256:29d1c71b378e75c722bc63845dde8d69832f5fa398c8c1322a5fd144ff6df714 \
--hash=sha256:2fad472ea689a457c97098069ad133ddffe45c1f758bc81273b43b5c3a2258da
# via -r requirements/mypy.in
types-boto==2.49.6 \ types-boto==2.49.6 \
--hash=sha256:c23e97e5bed847fce1578c7f07e14d278b6f18bb2085b24467a9a6520848acf7 \ --hash=sha256:c23e97e5bed847fce1578c7f07e14d278b6f18bb2085b24467a9a6520848acf7 \
--hash=sha256:d3ac75943063d43108b46140143c7fa9ae9d3f7007633d7119b63f722e3e02b0 --hash=sha256:d3ac75943063d43108b46140143c7fa9ae9d3f7007633d7119b63f722e3e02b0

View File

@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 113
# historical commits sharing the same major version, in which case a # historical commits sharing the same major version, in which case a
# minor version bump suffices. # minor version bump suffices.
PROVISION_VERSION = "171.0" PROVISION_VERSION = "172.0"

View File

@ -1,5 +1,7 @@
from typing import Dict, Optional from typing import Dict, Optional
from bs4.element import Tag
from zerver.lib.url_preview.parsers.base import BaseParser from zerver.lib.url_preview.parsers.base import BaseParser
@ -22,7 +24,8 @@ class GenericParser(BaseParser):
def _get_description(self) -> Optional[str]: def _get_description(self) -> Optional[str]:
soup = self._soup soup = self._soup
meta_description = soup.find("meta", attrs={"name": "description"}) meta_description = soup.find("meta", attrs={"name": "description"})
if meta_description and meta_description.get("content", "") != "": if isinstance(meta_description, Tag) and meta_description.get("content", "") != "":
assert isinstance(meta_description["content"], str)
return meta_description["content"] return meta_description["content"]
first_h1 = soup.find("h1") first_h1 = soup.find("h1")
if first_h1: if first_h1:
@ -43,6 +46,7 @@ class GenericParser(BaseParser):
first_h1 = soup.find("h1") first_h1 = soup.find("h1")
if first_h1: if first_h1:
first_image = first_h1.find_next_sibling("img", src=True) first_image = first_h1.find_next_sibling("img", src=True)
if first_image and first_image["src"] != "": if isinstance(first_image, Tag) and first_image["src"] != "":
assert isinstance(first_image["src"], str)
return first_image["src"] return first_image["src"]
return None return None

View File

@ -18,6 +18,7 @@ import orjson
import requests import requests
import responses import responses
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from bs4.element import Tag
from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from django.conf import settings from django.conf import settings
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
@ -796,8 +797,13 @@ class DesktopFlowTestingLib(ZulipTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
soup = BeautifulSoup(response.content, "html.parser") soup = BeautifulSoup(response.content, "html.parser")
desktop_data = soup.find("input", value=True)["value"] input = soup.find("input", value=True)
browser_url = soup.find("a", href=True)["href"] assert isinstance(input, Tag)
desktop_data = input["value"]
assert isinstance(desktop_data, str)
a = soup.find("a", href=True)
assert isinstance(a, Tag)
browser_url = a["href"]
self.assertEqual(browser_url, "/login/") self.assertEqual(browser_url, "/login/")
decrypted_key = self.verify_desktop_data_and_return_key(desktop_data, desktop_flow_otp) decrypted_key = self.verify_desktop_data_and_return_key(desktop_data, desktop_flow_otp)

View File

@ -6,6 +6,7 @@ from bs4 import BeautifulSoup
from zerver.lib.realm_icon import get_realm_icon_url from zerver.lib.realm_icon import get_realm_icon_url
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.utils import assert_is_not_none
from zerver.middleware import is_slow_query, write_log_line from zerver.middleware import is_slow_query, write_log_line
from zerver.models import get_realm from zerver.models import get_realm
@ -70,10 +71,14 @@ class OpenGraphTest(ZulipTestCase):
response = self.client_get(path) response = self.client_get(path)
self.assertEqual(response.status_code, status_code) self.assertEqual(response.status_code, status_code)
bs = BeautifulSoup(response.content, features="lxml") bs = BeautifulSoup(response.content, features="lxml")
open_graph_title = bs.select_one('meta[property="og:title"]').get("content") open_graph_title = assert_is_not_none(bs.select_one('meta[property="og:title"]')).get(
"content"
)
self.assertEqual(open_graph_title, title) self.assertEqual(open_graph_title, title)
open_graph_description = bs.select_one('meta[property="og:description"]').get("content") open_graph_description = assert_is_not_none(
bs.select_one('meta[property="og:description"]')
).get("content")
for substring in in_description: for substring in in_description:
self.assertIn(substring, open_graph_description) self.assertIn(substring, open_graph_description)
for substring in not_in_description: for substring in not_in_description:
@ -195,7 +200,9 @@ class OpenGraphTest(ZulipTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
bs = BeautifulSoup(response.content, features="lxml") bs = BeautifulSoup(response.content, features="lxml")
open_graph_image = bs.select_one('meta[property="og:image"]').get("content") open_graph_image = assert_is_not_none(bs.select_one('meta[property="og:image"]')).get(
"content"
)
self.assertEqual(open_graph_image, f"{realm.uri}{realm_icon}") self.assertEqual(open_graph_image, f"{realm.uri}{realm_icon}")
def test_login_page_realm_icon_absolute_url(self) -> None: def test_login_page_realm_icon_absolute_url(self) -> None:
@ -210,7 +217,9 @@ class OpenGraphTest(ZulipTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
bs = BeautifulSoup(response.content, features="lxml") bs = BeautifulSoup(response.content, features="lxml")
open_graph_image = bs.select_one('meta[property="og:image"]').get("content") open_graph_image = assert_is_not_none(bs.select_one('meta[property="og:image"]')).get(
"content"
)
self.assertEqual(open_graph_image, icon_url) self.assertEqual(open_graph_image, icon_url)
def test_no_realm_api_page_og_url(self) -> None: def test_no_realm_api_page_og_url(self) -> None:
@ -218,6 +227,7 @@ class OpenGraphTest(ZulipTestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
bs = BeautifulSoup(response.content, features="lxml") bs = BeautifulSoup(response.content, features="lxml")
open_graph_url = bs.select_one('meta[property="og:url"]').get("content") open_graph_url = assert_is_not_none(bs.select_one('meta[property="og:url"]')).get("content")
assert isinstance(open_graph_url, str)
self.assertTrue(open_graph_url.endswith("/api/")) self.assertTrue(open_graph_url.endswith("/api/"))