diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b86ab67b..a451ae95 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,9 +8,6 @@ jobs: fail-fast: false matrix: include: - - python-version: "3.7" - env: - TOXENV: py - python-version: "3.8" env: TOXENV: py diff --git a/parsel/csstranslator.py b/parsel/csstranslator.py index c4f95e66..fa32d2b3 100644 --- a/parsel/csstranslator.py +++ b/parsel/csstranslator.py @@ -1,5 +1,5 @@ from functools import lru_cache -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Protocol from cssselect import GenericTranslator as OriginalGenericTranslator from cssselect import HTMLTranslator as OriginalHTMLTranslator @@ -67,17 +67,13 @@ def join( return self -if TYPE_CHECKING: - # requires Python 3.8 - from typing import Protocol - - # e.g. cssselect.GenericTranslator, cssselect.HTMLTranslator - class TranslatorProtocol(Protocol): - def xpath_element(self, selector: Element) -> OriginalXPathExpr: - pass +# e.g. cssselect.GenericTranslator, cssselect.HTMLTranslator +class TranslatorProtocol(Protocol): + def xpath_element(self, selector: Element) -> OriginalXPathExpr: + pass - def css_to_xpath(self, css: str, prefix: str = ...) -> str: - pass + def css_to_xpath(self, css: str, prefix: str = ...) -> str: + pass class TranslatorMixin: @@ -87,7 +83,7 @@ class TranslatorMixin: """ def xpath_element( - self: "TranslatorProtocol", selector: Element + self: TranslatorProtocol, selector: Element ) -> XPathExpr: # https://github.com/python/mypy/issues/12344 xpath = super().xpath_element(selector) # type: ignore[safe-super] diff --git a/parsel/selector.py b/parsel/selector.py index 8717b40c..11d79798 100644 --- a/parsel/selector.py +++ b/parsel/selector.py @@ -9,21 +9,19 @@ Any, Dict, List, + Literal, Mapping, Optional, Pattern, + SupportsIndex, Tuple, Type, + TypedDict, TypeVar, Union, ) from warnings import warn -try: - from typing import TypedDict # pylint: disable=ungrouped-imports -except ImportError: # Python 3.7 - from typing_extensions import TypedDict - import jmespath from lxml import etree, html from packaging.version import Version @@ -31,19 +29,14 @@ from .csstranslator import GenericTranslator, HTMLTranslator from .utils import extract_regex, flatten, iflatten, shorten -if typing.TYPE_CHECKING: - # both require Python 3.8 - from typing import Literal, SupportsIndex - - # simplified _OutputMethodArg from types-lxml - _TostringMethodType = Literal[ - "html", - "xml", - ] - _SelectorType = TypeVar("_SelectorType", bound="Selector") _ParserType = Union[etree.XMLParser, etree.HTMLParser] +# simplified _OutputMethodArg from types-lxml +_TostringMethodType = Literal[ + "html", + "xml", +] lxml_version = Version(etree.__version__) lxml_huge_tree_version = Version("4.2") diff --git a/setup.py b/setup.py index 7742141c..570d0c12 100644 --- a/setup.py +++ b/setup.py @@ -29,10 +29,9 @@ "jmespath", "lxml", "packaging", - "typing_extensions; python_version < '3.8'", "w3lib>=1.19.0", ], - python_requires=">=3.7", + python_requires=">=3.8", license="BSD", zip_safe=False, keywords="parsel", @@ -45,7 +44,6 @@ "Topic :: Text Processing :: Markup :: HTML", "Topic :: Text Processing :: Markup :: XML", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", diff --git a/tests/test_selector_csstranslator.py b/tests/test_selector_csstranslator.py index bd52f166..b3cdfa9a 100644 --- a/tests/test_selector_csstranslator.py +++ b/tests/test_selector_csstranslator.py @@ -2,13 +2,17 @@ Selector tests for cssselect backend """ import unittest -from typing import TYPE_CHECKING, Any, Callable, List, Type +from typing import Any, Callable, List, Type, Protocol, Tuple, Union import cssselect import pytest from packaging.version import Version -from parsel.csstranslator import GenericTranslator, HTMLTranslator +from parsel.csstranslator import ( + GenericTranslator, + HTMLTranslator, + TranslatorProtocol, +) from parsel import Selector from cssselect.parser import SelectorSyntaxError from cssselect.xpath import ExpressionError @@ -50,39 +54,34 @@ """ -if TYPE_CHECKING: - # requires Python 3.8 - from typing import Protocol +class TranslatorTestProtocol(Protocol): + tr_cls: Type[TranslatorProtocol] + tr: TranslatorProtocol - from parsel.csstranslator import TranslatorProtocol + def c2x(self, css: str, prefix: str = ...) -> str: + pass - class TranslatorTestProtocol(Protocol): - tr_cls: Type[TranslatorProtocol] - tr: TranslatorProtocol + def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None: + pass - def c2x(self, css: str, prefix: str = ...) -> str: - pass - - def assertEqual(self, first: Any, second: Any, msg: Any = ...) -> None: - pass - - def assertRaises( - self, - expected_exception: type[BaseException] - | tuple[type[BaseException], ...], - callable: Callable[..., object], - *args: Any, - **kwargs: Any, - ) -> None: - pass + def assertRaises( + self, + expected_exception: Union[ + Type[BaseException], Tuple[Type[BaseException], ...] + ], + callable: Callable[..., object], + *args: Any, + **kwargs: Any, + ) -> None: + pass class TranslatorTestMixin: - def setUp(self: "TranslatorTestProtocol") -> None: + def setUp(self: TranslatorTestProtocol) -> None: self.tr = self.tr_cls() self.c2x = self.tr.css_to_xpath - def test_attr_function(self: "TranslatorTestProtocol") -> None: + def test_attr_function(self: TranslatorTestProtocol) -> None: cases = [ ("::attr(name)", "descendant-or-self::*/@name"), ("a::attr(href)", "descendant-or-self::a/@href"), @@ -95,7 +94,7 @@ def test_attr_function(self: "TranslatorTestProtocol") -> None: for css, xpath in cases: self.assertEqual(self.c2x(css), xpath, css) - def test_attr_function_exception(self: "TranslatorTestProtocol") -> None: + def test_attr_function_exception(self: TranslatorTestProtocol) -> None: cases = [ ("::attr(12)", ExpressionError), ("::attr(34test)", ExpressionError), @@ -104,7 +103,7 @@ def test_attr_function_exception(self: "TranslatorTestProtocol") -> None: for css, exc in cases: self.assertRaises(exc, self.c2x, css) - def test_text_pseudo_element(self: "TranslatorTestProtocol") -> None: + def test_text_pseudo_element(self: TranslatorTestProtocol) -> None: cases = [ ("::text", "descendant-or-self::text()"), ("p::text", "descendant-or-self::p/text()"), @@ -133,7 +132,7 @@ def test_text_pseudo_element(self: "TranslatorTestProtocol") -> None: for css, xpath in cases: self.assertEqual(self.c2x(css), xpath, css) - def test_pseudo_function_exception(self: "TranslatorTestProtocol") -> None: + def test_pseudo_function_exception(self: TranslatorTestProtocol) -> None: cases = [ ("::attribute(12)", ExpressionError), ("::text()", ExpressionError), @@ -142,14 +141,14 @@ def test_pseudo_function_exception(self: "TranslatorTestProtocol") -> None: for css, exc in cases: self.assertRaises(exc, self.c2x, css) - def test_unknown_pseudo_element(self: "TranslatorTestProtocol") -> None: + def test_unknown_pseudo_element(self: TranslatorTestProtocol) -> None: cases = [ ("::text-node", ExpressionError), ] for css, exc in cases: self.assertRaises(exc, self.c2x, css) - def test_unknown_pseudo_class(self: "TranslatorTestProtocol") -> None: + def test_unknown_pseudo_class(self: TranslatorTestProtocol) -> None: cases = [ (":text", ExpressionError), (":attribute(name)", ExpressionError), diff --git a/tests/typing/selector.py b/tests/typing/selector.py index 0db34541..b6f14345 100644 --- a/tests/typing/selector.py +++ b/tests/typing/selector.py @@ -1,5 +1,7 @@ # Basic usage of the Selector, strongly typed to test the typing of parsel's API. import re +from typing import List + from parsel import Selector @@ -8,9 +10,9 @@ def correct() -> None: text="" ) - li_values: list[str] = selector.css("li").getall() + li_values: List[str] = selector.css("li").getall() selector.re_first(re.compile(r"[32]"), "").strip() - xpath_values: list[str] = selector.xpath( + xpath_values: List[str] = selector.xpath( "//somens:a/text()", namespaces={"somens": "http://scrapy.org"} ).extract() diff --git a/tox.ini b/tox.ini index c9839477..13b654c6 100644 --- a/tox.ini +++ b/tox.ini @@ -29,7 +29,6 @@ deps = types-setuptools==67.2.0.1 py==1.11.0 mypy==1.0.0 - typing-extensions==4.4.0 commands = mypy {posargs:parsel tests} --strict