# Path: xpaw/errors.py
# class ClientError(Exception):
#     """
#     Downloader client error.
#     """
# 
# class NotEnabled(Exception):
#     """
#     Not enabled.
#     """
# 
# class HttpError(Exception):
#     """
#     HTTP status is not 2xx.
#     """
# 
#     def __init__(self, *args, response=None, **kwargs):
#         self.response = response
#         super().__init__(*args, **kwargs)
# 
# Path: xpaw/utils.py
# def with_not_none_params(**kwargs):
#     params = {}
#     for k, v in kwargs.items():
#         if v is not None:
#             params[k] = v
#     return params


import logging
from xpaw.errors import ClientError, NotEnabled, HttpError
from xpaw.utils import with_not_none_params
            .format(cls_name, repr(self._max_retry_times), repr(self._retry_http_status))

    @classmethod
    def from_crawler(cls, crawler):
        config = crawler.config
        if not config.getbool('retry_enabled'):
            raise NotEnabled
        return cls(**with_not_none_params(max_retry_times=config.getint('max_retry_times'),
                                          retry_http_status=config.getlist('retry_http_status')))

    def handle_response(self, request, response):
        for p in self._retry_http_status:
            if self.match_status(p, response.status):
                return self.retry(request, "HTTP status={}".format(response.status))

    @staticmethod
    def match_status(pattern, status):
        if isinstance(pattern, int):
            return pattern == status
        verse = False
        if pattern.startswith("!") or pattern.startswith("~"):
            verse = True
            pattern = pattern[1:]
        s = str(status)
        n = len(s)
        match = True
        if len(pattern) != n:
            match = False
        else:
            for i in range(n):

# Path: xpaw/errors.py
# class ClientError(Exception):
#     """
#     Downloader client error.
#     """
# 
# class NotEnabled(Exception):
#     """
#     Not enabled.
#     """
# 
# class HttpError(Exception):
#     """
#     HTTP status is not 2xx.
#     """
# 
#     def __init__(self, *args, response=None, **kwargs):
#         self.response = response
#         super().__init__(*args, **kwargs)
# 
# Path: xpaw/utils.py
# def with_not_none_params(**kwargs):
#     params = {}
#     for k, v in kwargs.items():
#         if v is not None:
#             params[k] = v
#     return params


# Path: xpaw/extensions/retry.py
import logging
from xpaw.errors import ClientError, NotEnabled, HttpError
from xpaw.utils import with_not_none_params

            .format(cls_name, repr(self._max_retry_times), repr(self._retry_http_status))

    @classmethod
    def from_crawler(cls, crawler):
        config = crawler.config
        if not config.getbool('retry_enabled'):
            raise NotEnabled
        return cls(**with_not_none_params(max_retry_times=config.getint('max_retry_times'),
                                          retry_http_status=config.getlist('retry_http_status')))

    def handle_response(self, request, response):
        for p in self._retry_http_status:
            if self.match_status(p, response.status):
                return self.retry(request, "HTTP status={}".format(response.status))

    @staticmethod
    def match_status(pattern, status):
        if isinstance(pattern, int):
            return pattern == status
        verse = False
        if pattern.startswith("!") or pattern.startswith("~"):
            verse = True
            pattern = pattern[1:]
        s = str(status)
        n = len(s)
        match = True
        if len(pattern) != n:
            match = False
        else:
            for i in range(n):

                if pattern[i] != "x" and pattern[i] != "X" and pattern[i] != s[i]: