diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a911d4cfe..f76a04ca1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,3 +22,9 @@ repos:
- id: blacken-docs
additional_dependencies:
- black==24.2.0
+- repo: https://github.com/asottile/pyupgrade
+ rev: v3.15.2
+ hooks:
+ - id: pyupgrade
+ args: [--py38-plus, --keep-runtime-typing]
+ exclude: scrapy/__init__.py
diff --git a/scrapy/core/downloader/contextfactory.py b/scrapy/core/downloader/contextfactory.py
index 0e77cd2fe..9f6edb630 100644
--- a/scrapy/core/downloader/contextfactory.py
+++ b/scrapy/core/downloader/contextfactory.py
@@ -107,7 +107,7 @@ class ScrapyClientContextFactory(BrowserLikePolicyForHTTPS):
ctx.set_options(0x4) # OP_LEGACY_SERVER_CONNECT
return ctx
- def creatorForNetloc(self, hostname: bytes, port: int) -> "ClientTLSOptions":
+ def creatorForNetloc(self, hostname: bytes, port: int) -> ClientTLSOptions:
return ScrapyClientTLSOptions(
hostname.decode("ascii"),
self.getContext(),
@@ -134,7 +134,7 @@ class BrowserLikeContextFactory(ScrapyClientContextFactory):
``SSLv23_METHOD``) which allows TLS protocol negotiation.
"""
- def creatorForNetloc(self, hostname: bytes, port: int) -> "ClientTLSOptions":
+ def creatorForNetloc(self, hostname: bytes, port: int) -> ClientTLSOptions:
# trustRoot set to platformTrust() will use the platform's root CAs.
#
# This means that a website like https://www.cacert.org will be rejected
@@ -158,8 +158,8 @@ class AcceptableProtocolsContextFactory:
self._wrapped_context_factory: Any = context_factory
self._acceptable_protocols: List[bytes] = acceptable_protocols
- def creatorForNetloc(self, hostname: bytes, port: int) -> "ClientTLSOptions":
- options: "ClientTLSOptions" = self._wrapped_context_factory.creatorForNetloc(
+ def creatorForNetloc(self, hostname: bytes, port: int) -> ClientTLSOptions:
+ options: ClientTLSOptions = self._wrapped_context_factory.creatorForNetloc(
hostname, port
)
_setAcceptableProtocols(options._ctx, self._acceptable_protocols)
diff --git a/scrapy/downloadermiddlewares/retry.py b/scrapy/downloadermiddlewares/retry.py
index 46587a898..0637f09d4 100644
--- a/scrapy/downloadermiddlewares/retry.py
+++ b/scrapy/downloadermiddlewares/retry.py
@@ -147,9 +147,7 @@ class RetryMiddleware(metaclass=BackwardsCompatibilityMetaclass):
if not settings.getbool("RETRY_ENABLED"):
raise NotConfigured
self.max_retry_times = settings.getint("RETRY_TIMES")
- self.retry_http_codes = set(
- int(x) for x in settings.getlist("RETRY_HTTP_CODES")
- )
+ self.retry_http_codes = {int(x) for x in settings.getlist("RETRY_HTTP_CODES")}
self.priority_adjust = settings.getint("RETRY_PRIORITY_ADJUST")
try:
diff --git a/scrapy/extensions/debug.py b/scrapy/extensions/debug.py
index 26726b662..a0fc7b99f 100644
--- a/scrapy/extensions/debug.py
+++ b/scrapy/extensions/debug.py
@@ -55,7 +55,7 @@ class StackTraceDump:
)
def _thread_stacks(self) -> str:
- id2name = dict((th.ident, th.name) for th in threading.enumerate())
+ id2name = {th.ident: th.name for th in threading.enumerate()}
dumps = ""
for id_, frame in sys._current_frames().items():
name = id2name.get(id_, "")
diff --git a/scrapy/http/request/__init__.py b/scrapy/http/request/__init__.py
index 77149333c..3da2e111d 100644
--- a/scrapy/http/request/__init__.py
+++ b/scrapy/http/request/__init__.py
@@ -189,10 +189,10 @@ class Request(object_ref):
def __repr__(self) -> str:
return f"<{self.method} {self.url}>"
- def copy(self) -> "Request":
+ def copy(self) -> Request:
return self.replace()
- def replace(self, *args: Any, **kwargs: Any) -> "Request":
+ def replace(self, *args: Any, **kwargs: Any) -> Request:
"""Create a new Request with the same attributes except for those given new values"""
for x in self.attributes:
kwargs.setdefault(x, getattr(self, x))
@@ -237,7 +237,7 @@ class Request(object_ref):
request_kwargs.update(kwargs)
return cls(**request_kwargs)
- def to_dict(self, *, spider: Optional["scrapy.Spider"] = None) -> Dict[str, Any]:
+ def to_dict(self, *, spider: Optional[scrapy.Spider] = None) -> Dict[str, Any]:
"""Return a dictionary containing the Request's data.
Use :func:`~scrapy.utils.request.request_from_dict` to convert back into a :class:`~scrapy.Request` object.
diff --git a/scrapy/http/request/rpc.py b/scrapy/http/request/rpc.py
index e20e7c438..096ecd370 100644
--- a/scrapy/http/request/rpc.py
+++ b/scrapy/http/request/rpc.py
@@ -21,7 +21,7 @@ DUMPS_ARGS = get_func_args(xmlrpclib.dumps)
class XmlRpcRequest(Request):
def __init__(self, *args: Any, encoding: Optional[str] = None, **kwargs: Any):
if "body" not in kwargs and "params" in kwargs:
- kw = dict((k, kwargs.pop(k)) for k in DUMPS_ARGS if k in kwargs)
+ kw = {k: kwargs.pop(k) for k in DUMPS_ARGS if k in kwargs}
kwargs["body"] = xmlrpclib.dumps(**kw)
# spec defines that requests must use POST method
diff --git a/scrapy/settings/__init__.py b/scrapy/settings/__init__.py
index 4448b6f4b..ea1db03f1 100644
--- a/scrapy/settings/__init__.py
+++ b/scrapy/settings/__init__.py
@@ -275,7 +275,7 @@ class BaseSettings(MutableMapping[_SettingsKeyT, Any]):
assert isinstance(value, (dict, list))
return copy.deepcopy(value)
- def getwithbase(self, name: _SettingsKeyT) -> "BaseSettings":
+ def getwithbase(self, name: _SettingsKeyT) -> BaseSettings:
"""Get a composition of a dictionary-like setting and its `_BASE`
counterpart.
@@ -438,7 +438,7 @@ class BaseSettings(MutableMapping[_SettingsKeyT, Any]):
if self.frozen:
raise TypeError("Trying to modify an immutable Settings object")
- def copy(self) -> "Self":
+ def copy(self) -> Self:
"""
Make a deep copy of current settings.
@@ -460,7 +460,7 @@ class BaseSettings(MutableMapping[_SettingsKeyT, Any]):
"""
self.frozen = True
- def frozencopy(self) -> "Self":
+ def frozencopy(self) -> Self:
"""
Return an immutable copy of the current settings.
diff --git a/scrapy/utils/benchserver.py b/scrapy/utils/benchserver.py
index e9ea51aa1..550516141 100644
--- a/scrapy/utils/benchserver.py
+++ b/scrapy/utils/benchserver.py
@@ -22,9 +22,7 @@ class Root(Resource):
for nl in nlist:
args["n"] = nl
argstr = urlencode(args, doseq=True)
- request.write(
- f"follow {nl}
".encode("utf8")
- )
+ request.write(f"follow {nl}
".encode())
request.write(b"