Hello community,

here is the log from the commit of package python-pytest-check-links for 
openSUSE:Factory checked in at 2020-05-03 22:47:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-check-links (Old)
 and      /work/SRC/openSUSE:Factory/.python-pytest-check-links.new.2738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pytest-check-links"

Sun May  3 22:47:10 2020 rev:5 rq:799795 version:0.4.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-pytest-check-links/python-pytest-check-links.changes
      2020-05-01 11:09:28.415341218 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pytest-check-links.new.2738/python-pytest-check-links.changes
    2020-05-03 22:47:11.911150292 +0200
@@ -1,0 +2,8 @@
+Sun May  3 07:27:33 UTC 2020 - Tomáš Chvátal <[email protected]>
+
+- Update to 0.4.0:
+  * Drop py27 support and add badges #15
+  * Use requests(-cache) #12
+  * Add checking of self- and local html anchors #10
+
+-------------------------------------------------------------------

Old:
----
  pytest_check_links-0.3.4.tar.gz

New:
----
  pytest_check_links-0.4.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pytest-check-links.spec ++++++
--- /var/tmp/diff_new_pack.MxoRdA/_old  2020-05-03 22:47:12.547151587 +0200
+++ /var/tmp/diff_new_pack.MxoRdA/_new  2020-05-03 22:47:12.551151595 +0200
@@ -17,10 +17,11 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%define skip_python2 1
 # Tests natually require internet
 %bcond_with test
 Name:           python-pytest-check-links
-Version:        0.3.4
+Version:        0.4.0
 Release:        0
 Summary:        Pytest plugin for checking links in files
 License:        BSD-3-Clause
@@ -34,7 +35,7 @@
 Requires:       python-docutils
 Requires:       python-html5lib
 Requires:       python-pytest >= 2.8
-Requires:       python-six
+Requires:       python-requests
 Requires(post): update-alternatives
 Requires(postun): update-alternatives
 Recommends:     python-jupyter_nbconvert
@@ -45,7 +46,7 @@
 BuildRequires:  %{python_module jupyter_nbconvert}
 BuildRequires:  %{python_module jupyter_nbformat}
 BuildRequires:  %{python_module pytest >= 2.8}
-BuildRequires:  %{python_module six}
+BuildRequires:  %{python_module requests}
 %endif
 %python_subpackages
 

++++++ pytest_check_links-0.3.4.tar.gz -> pytest_check_links-0.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/.travis.yml 
new/pytest_check_links-0.4.0/.travis.yml
--- old/pytest_check_links-0.3.4/.travis.yml    2020-03-30 11:40:43.000000000 
+0200
+++ new/pytest_check_links-0.4.0/.travis.yml    2020-05-02 11:02:00.000000000 
+0200
@@ -1,17 +1,17 @@
 language: python
 dist: xenial
 python:
+  - 3.8
   - 3.7
   - 3.6
   - 3.5
-  - 3.4
-  - 2.7
 before_install:
   - pip install --upgrade setuptools pip
 install:
-  - pip install --upgrade . pytest-cov
+  - pip install --upgrade .[cache] pytest-cov
 script:
-  - pytest --cov pytest_check_links
+  # disable automatic inclusion for coverage
+  - pytest -p no:check-links --cov pytest_check_links --cov-report term-missing
 after_success:
   - pip install codecov
   - codecov
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/CHANGELOG.md 
new/pytest_check_links-0.4.0/CHANGELOG.md
--- old/pytest_check_links-0.3.4/CHANGELOG.md   1970-01-01 01:00:00.000000000 
+0100
+++ new/pytest_check_links-0.4.0/CHANGELOG.md   2020-05-02 11:02:00.000000000 
+0200
@@ -0,0 +1,13 @@
+# Change Log
+
+## 0.4.0
+* Drop py27 support and add badges 
[#15](https://github.com/jupyterlab/pytest-check-links/pull/15)
+* Use requests(-cache) 
[#12](https://github.com/jupyterlab/pytest-check-links/pull/12)
+* Add checking of self- and local html anchors  
[#10](https://github.com/jupyterlab/pytest-check-links/pull/10)
+
+## 0.3.0
+* Honor Retry-After Header 
[#9](https://github.com/jupyterlab/pytest-check-links/pull/9)
+* Update metadata for JupyterLab Org 
[#7](https://github.com/jupyterlab/pytest-check-links/pull/7)
+
+## 0.2.0
+* Add rst handling 
[#4](https://github.com/jupyterlab/pytest-check-links/pull/4)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/ChangeLog 
new/pytest_check_links-0.4.0/ChangeLog
--- old/pytest_check_links-0.3.4/ChangeLog      2020-04-21 17:09:34.000000000 
+0200
+++ new/pytest_check_links-0.4.0/ChangeLog      2020-05-02 11:02:45.000000000 
+0200
@@ -1,6 +1,31 @@
 CHANGES
 =======
 
+v0.4.0
+------
+
+* Add changelog
+* Add pypi badge
+* add badges
+* drop py27 support
+* remove allowable\_codes, attempt parsing backend-opts as json first
+* py27-compatible syntax
+* go ahead and handle/test remote #anchors
+* travis coverage tweaks
+* add handling (and test) of retries with cache
+* more py35/27 stringifying
+* more py27 syntax
+* whitespace
+* add test for custom cache location, stringify paths for <=3.5
+* reorganize cache tests, add memory backend test
+* hoist session to a singleton, ignore anchors for cache
+* some work on docs, etc
+* less-verbose protocol error handling
+* add requests-cache
+* use requests
+* Format release document
+* Add release notes
+
 0.3.4
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/PKG-INFO 
new/pytest_check_links-0.4.0/PKG-INFO
--- old/pytest_check_links-0.3.4/PKG-INFO       2020-04-21 17:09:34.000000000 
+0200
+++ new/pytest_check_links-0.4.0/PKG-INFO       2020-05-02 11:02:46.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pytest_check_links
-Version: 0.3.4
+Version: 0.4.0
 Summary: Check links in files
 Home-page: https://github.com/jupyterlab/pytest-check-links
 Author: Jupyter Development Team
@@ -10,29 +10,97 @@
         
         pytest plugin that checks URLs for HTML-containing files.
         
-        Supported files:
+        
[![codecov](https://codecov.io/gh/jupyterlab/pytest-check-links/branch/master/graph/badge.svg)](https://codecov.io/gh/jupyterlab/pytest-check-links)
+        [![Build 
Status](https://travis-ci.com/jupyterlab/pytest-check-links.svg?branch=master)](https://travis-ci.com/jupyterlab/pytest-check-links)
+        [![PyPI 
version](https://badge.fury.io/py/pytest-check-links.svg)](https://badge.fury.io/py/pytest-check-links)
+        
+        ## Supported files
+        
+        - `.html`
+        - `.rst`
+        - `.md` (TODO: select renderer)
+        - `.ipynb` (requires `nbconvert`)
         
-        - .html
-        - .rst
-        - .md (TODO: select renderer)
-        - .ipynb (requires nbconvert)
-        
-        Install:
+        ## Install
         
             pip install pytest-check-links
         
-        Use:
+        ## Use
         
             pytest --check-links mynotebook.ipynb
         
+        ## Configure
+        
+        #### --links-ext
+        
+        > default: `md,rst,html,ipynb`
+        
+        A comma-separated list of extensions to check
+        
+        #### --check-anchors
+        
+        Also check whether links with `#anchors` HTML files (either local, or 
with
+        served with `html` in the `Content-Type`) actually exist, and point to 
_exactly one_
+        named anchor.
+        
+        ### Cache
+        
+        Caching requires the installation of `requests-cache`.
+        
+            pip install requests-cache
+        
+        If enabled, each occurance of a link will be checked, no matter how 
many times
+        it appears in a collection of files to check.
+        
+        #### --check-links-cache
+        
+        Cache requests when checking links. Caching is disabled by default, 
and this option
+        must be provided, even if other cache configuration options are 
provided.
+        
+        #### --check-links-cache-name
+        
+        > default: `.pytest-check-links-cache`
+        
+        Name of link cache, either the base name of a file or similar, 
depending on backend.
+        
+        #### --check-links-cache-backend
+        
+        > default: `sqlite3`
+        
+        Cache persistence backend. The other known backends are:
+        - `memory`
+        - `redis`
+        - `mongodb`
+        
+        See the [requests-cache 
documentation](https://requests-cache.readthedocs.io)
+        for more information.
+        
+        #### --check-links-cache-expire-after
+        
+        > default: `None` (unlimited)
+        
+        Time to cache link responses (seconds).
+        
+        #### --check-links-cache-backend-opt
+        
+        Backend-specific options for link cache, provided as `key:value`. 
These are passed
+        directly to the `requests_cache.CachedSession` constructor, as they 
vary depending
+        on the backend.
+        
+        Values will be parsed as JSON first, so to overload the default of 
caching all
+        HTTP response codes (which requires a list of `int`s):
+        
+        ```bash
+        --check-links-backend-opt allowable_codes:[200]
+        ```
+        
         
-        TODO:
+        ## TODO
         
         - pick a markdown renderer (probably commonmark) or make the markdown 
renderer pluggable
         - options for validating links (allow absolute links, only remote or 
local, etc.)
         - check internal links (`#anchors`)
         - find URLs in Python docstrings
-        - test myself, obvs!
         
         
 Keywords: setup,distutils
@@ -43,3 +111,4 @@
 Classifier: Programming Language :: Python
 Classifier: Topic :: Software Development :: Testing
 Description-Content-Type: text/markdown
+Provides-Extra: cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/README.md 
new/pytest_check_links-0.4.0/README.md
--- old/pytest_check_links-0.3.4/README.md      2020-04-11 23:53:54.000000000 
+0200
+++ new/pytest_check_links-0.4.0/README.md      2020-05-02 11:02:00.000000000 
+0200
@@ -2,26 +2,94 @@
 
 pytest plugin that checks URLs for HTML-containing files.
 
-Supported files:
+[![codecov](https://codecov.io/gh/jupyterlab/pytest-check-links/branch/master/graph/badge.svg)](https://codecov.io/gh/jupyterlab/pytest-check-links)
+[![Build 
Status](https://travis-ci.com/jupyterlab/pytest-check-links.svg?branch=master)](https://travis-ci.com/jupyterlab/pytest-check-links)
+[![PyPI 
version](https://badge.fury.io/py/pytest-check-links.svg)](https://badge.fury.io/py/pytest-check-links)
+
+## Supported files
+
+- `.html`
+- `.rst`
+- `.md` (TODO: select renderer)
+- `.ipynb` (requires `nbconvert`)
 
-- .html
-- .rst
-- .md (TODO: select renderer)
-- .ipynb (requires nbconvert)
-
-Install:
+## Install
 
     pip install pytest-check-links
 
-Use:
+## Use
 
     pytest --check-links mynotebook.ipynb
 
+## Configure
+
+#### --links-ext
+
+> default: `md,rst,html,ipynb`
+
+A comma-separated list of extensions to check
+
+#### --check-anchors
+
+Also check whether links with `#anchors` HTML files (either local, or with
+served with `html` in the `Content-Type`) actually exist, and point to 
_exactly one_
+named anchor.
+
+### Cache
+
+Caching requires the installation of `requests-cache`.
+
+    pip install requests-cache
+
+If enabled, each occurance of a link will be checked, no matter how many times
+it appears in a collection of files to check.
+
+#### --check-links-cache
+
+Cache requests when checking links. Caching is disabled by default, and this 
option
+must be provided, even if other cache configuration options are provided.
+
+#### --check-links-cache-name
+
+> default: `.pytest-check-links-cache`
+
+Name of link cache, either the base name of a file or similar, depending on 
backend.
+
+#### --check-links-cache-backend
+
+> default: `sqlite3`
+
+Cache persistence backend. The other known backends are:
+- `memory`
+- `redis`
+- `mongodb`
+
+See the [requests-cache documentation](https://requests-cache.readthedocs.io)
+for more information.
+
+#### --check-links-cache-expire-after
+
+> default: `None` (unlimited)
+
+Time to cache link responses (seconds).
+
+#### --check-links-cache-backend-opt
+
+Backend-specific options for link cache, provided as `key:value`. These are 
passed
+directly to the `requests_cache.CachedSession` constructor, as they vary 
depending
+on the backend.
+
+Values will be parsed as JSON first, so to overload the default of caching all
+HTTP response codes (which requires a list of `int`s):
+
+```bash
+--check-links-backend-opt allowable_codes:[200]
+```
+
 
-TODO:
+## TODO
 
 - pick a markdown renderer (probably commonmark) or make the markdown renderer 
pluggable
 - options for validating links (allow absolute links, only remote or local, 
etc.)
 - check internal links (`#anchors`)
 - find URLs in Python docstrings
-- test myself, obvs!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/RELEASE.md 
new/pytest_check_links-0.4.0/RELEASE.md
--- old/pytest_check_links-0.3.4/RELEASE.md     1970-01-01 01:00:00.000000000 
+0100
+++ new/pytest_check_links-0.4.0/RELEASE.md     2020-04-21 17:12:53.000000000 
+0200
@@ -0,0 +1,10 @@
+
+
+```
+git pull origin master
+git tag ${NEW_VERSION}
+rm -rf dist build
+python setup.py sdist
+twine check dist/* && twine upload dist/*
+git push origin master --tags
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/examples/anchors_remote.html 
new/pytest_check_links-0.4.0/examples/anchors_remote.html
--- old/pytest_check_links-0.3.4/examples/anchors_remote.html   1970-01-01 
01:00:00.000000000 +0100
+++ new/pytest_check_links-0.4.0/examples/anchors_remote.html   2020-05-02 
11:02:00.000000000 +0200
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+  <head></head>
+  <body>
+    <a 
href="https://www.w3.org/TR/REC-html40/struct/links.html#anchors";>anchors 
exists</a>
+    <a 
href="https://www.w3.org/TR/REC-html40/struct/links.html#no-such-anchor";>doesn't
 exist</a>
+  </body>
+</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/examples/httpbin.md 
new/pytest_check_links-0.4.0/examples/httpbin.md
--- old/pytest_check_links-0.3.4/examples/httpbin.md    1970-01-01 
01:00:00.000000000 +0100
+++ new/pytest_check_links-0.4.0/examples/httpbin.md    2020-05-02 
11:02:00.000000000 +0200
@@ -0,0 +1,6 @@
+- [one](https://httpbin.org/delay/0.25#1)
+- [two](https://httpbin.org/delay/0.25#2)
+- [three](https://httpbin.org/delay/0.25#3)
+- [four](https://httpbin.org/delay/0.25#4)
+- [five](https://httpbin.org/delay/0.25#5)
+- [six](https://httpbin.org/delay/0.25#6)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/pytest_check_links/args.py 
new/pytest_check_links-0.4.0/pytest_check_links/args.py
--- old/pytest_check_links-0.3.4/pytest_check_links/args.py     2020-03-30 
11:40:43.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links/args.py     2020-05-02 
11:02:00.000000000 +0200
@@ -1,5 +1,5 @@
 import argparse
-
+import json
 
 class StoreExtensionsAction(argparse.Action):
 
@@ -14,3 +14,26 @@
 
     def parse_extensions(self, csv):
         return {'.%s' % ext.lstrip('.') for ext in csv.split(',')}
+
+
+class StoreCacheAction(argparse.Action):
+    """Build the cache session kwargs
+    """
+    def __call__(self, parser, namespace, values, option_string=None):
+        ns_name = "check_links_cache_kwargs"
+        if not hasattr(namespace, ns_name):
+            setattr(namespace, ns_name, {})
+        dest = self.dest.replace("check_links_cache_", "")
+        kwargs = namespace.check_links_cache_kwargs
+        if dest == "name":
+            kwargs["cache_name"] = values
+        elif dest == "expire_after":
+            kwargs["expire_after"] = float(values)
+        elif dest == "backend_opt":
+            key, value = str(values).split(":", 1)
+            try:
+                kwargs[key] = json.loads(value)
+            except:
+                kwargs[key] = value
+        else:
+            kwargs[dest] = values
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/pytest_check_links/plugin.py 
new/pytest_check_links-0.4.0/pytest_check_links/plugin.py
--- old/pytest_check_links-0.3.4/pytest_check_links/plugin.py   2020-04-21 
17:04:48.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links/plugin.py   2020-05-02 
11:02:00.000000000 +0200
@@ -3,19 +3,26 @@
 import os
 import time
 import warnings
-from six.moves.urllib.request import urlopen, Request
-from six.moves.urllib.parse import unquote
 
 import html5lib
 import pytest
+from requests import Session, Request
+from requests.compat import unquote
 
-from .args import StoreExtensionsAction
+from .args import StoreExtensionsAction, StoreCacheAction
 
 _ENC = 'utf8'
 
 default_extensions = {'.md', '.rst', '.html', '.ipynb'}
 supported_extensions = {'.md', '.rst', '.html', '.ipynb'}
 
+default_cache = dict(
+    cache_name='.pytest-check-links-cache',
+    backend=None,
+    expire_after=None,
+    allowable_codes=list(range(200, 512)),
+)
+
 
 def pytest_addoption(parser):
     group = parser.getgroup("general")
@@ -30,6 +37,19 @@
              "extensions are: %s." %
                 extensions_str(supported_extensions))
 
+    group.addoption('--check-links-cache', action='store_true',
+        help="Cache requests when checking links")
+    group.addoption('--check-links-cache-name', action=StoreCacheAction,
+        help="Name of link cache")
+    group.addoption('--check-links-cache-backend', action=StoreCacheAction,
+        help="Cache persistence backend")
+    group.addoption('--check-links-cache-expire-after', 
action=StoreCacheAction,
+        help="Time to cache link responses (seconds)")
+    group.addoption('--check-links-cache-allowable-codes', 
action=StoreCacheAction,
+        help="HTTP response codes to cache")
+    group.addoption('--check-links-cache-backend-opt', action=StoreCacheAction,
+        help="Backend-specific options for link cache, specfied as 
`opt:value`")
+
 
 def pytest_configure(config):
     if config.option.links_ext:
@@ -39,15 +59,41 @@
 def pytest_collect_file(path, parent):
     config = parent.config
     if config.option.check_links:
+        requests_session = ensure_requests_session(config)
         if path.ext.lower() in config.option.links_ext:
-            return CheckLinks(path, parent, config.option.check_anchors)
+            return CheckLinks(path, parent, requests_session, 
config.option.check_anchors)
+
+
+def ensure_requests_session(config):
+    """Build the singleton requests.Session (or subclass)
+    """
+    session_attr = "check_links_requests_session"
+
+    if not hasattr(config.option, session_attr):
+        if config.option.check_links_cache:
+            from requests_cache import CachedSession
+            conf_kwargs = getattr(config.option, "check_links_cache_kwargs", 
{})
+            kwargs = dict(default_cache)
+            kwargs.update(conf_kwargs)
+            requests_session = CachedSession(**kwargs)
+            if kwargs.get("expire_after"):
+                requests_session.remove_expired_responses()
+        else:
+            requests_session = Session()
+
+        requests_session.headers['User-Agent'] = 'pytest-check-links'
+
+        setattr(config.option, session_attr, requests_session)
+
+    return getattr(config.option, session_attr)
 
 
 class CheckLinks(pytest.File):
     """Check the links in a file"""
-    def __init__(self, path, parent, check_anchors):
+    def __init__(self, path, parent, requests_session, check_anchors=False):
         super(CheckLinks, self).__init__(path, parent)
         self.check_anchors = check_anchors
+        self.requests_session = requests_session
 
     def _html_from_html(self):
         """Return HTML from an HTML file"""
@@ -159,7 +205,6 @@
     def __init__(self, name, parent, target, parsed, description=''):
         super(LinkItem, self).__init__(name, parent)
         self.target = target
-        self.retry_attempts = 0
         self.parsed = parsed
         self.description = description or '{}: {}'.format(self.fspath, target)
 
@@ -173,26 +218,31 @@
     def reportinfo(self):
         return self.fspath, 0, self.description
 
-    def handle_retry(self, obj):
+    def sleep(self, response):
         """Handle responses with a Retry-After header.
 
         https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
         """
-        if self.retry_attempts < 3:
+        header = response.headers.get('Retry-After')
+
+        if header is None:
+            return False
+
+        if header == '1m0s':
+            sleep_time = 60
+        else:
             try:
-                sleep_time = int(obj.headers['Retry-After'])
+                sleep_time = int(header)
             except ValueError:
                 sleep_time = 10
-            # Github uses this non-conforming Retry-After
-            if obj.headers['Retry-After'] == '1m0s':
-                sleep_time = 60
-            self.retry_attempts += 1
-            time.sleep(sleep_time)
-            return self.runtest()
 
-        raise BrokenLinkError(self.target, "%s %s" % (obj.code, obj.reason))
+        time.sleep(sleep_time)
+
+        return True
 
     def handle_anchor(self, parsed, anchor):
+        """Verify an anchor exists in the parsed HTML
+        """
         anchors = parsed.findall('*//a[@name="{}"]'.format(anchor))
 
         if not anchors:
@@ -206,29 +256,55 @@
                 )
             )
 
+    def fetch_with_retries(self, url, retries=3):
+        """Fetch a URL, optionally retrying after a delay (by header)
+        """
+
+        url_no_anchor = url.split("#")[0]
+        session = self.parent.requests_session
+
+        try:
+            response = session.get(url_no_anchor)
+        except Exception as err:
+            raise BrokenLinkError(url, "%s" % err)
+
+        if response.status_code >= 400:
+            if retries and self.sleep(response):
+                self.uncache_url(url_no_anchor)
+                return self.fetch_with_retries(url, retries=retries - 1)
+
+            raise BrokenLinkError(url, "%d: %s" % (
+                response.status_code,
+                response.reason
+            ))
+
+        return response
+
+    def uncache_url(self, url):
+        uncached = False
+        session = self.parent.requests_session
+        if hasattr(session, "cache"):
+            request = Request('GET', url, headers=session.headers).prepare()
+            key = session.cache.create_key(request)
+            if session.cache.has_key(key):
+                session.cache.delete(key)
+                uncached = True
+        return uncached
+
     def runtest(self):
-        if ':' in self.target:
-            # external reference, download
-            req = Request(self.target)
-            req.add_header('User-Agent', 'pytest-check-links')
-            try:
-                f = urlopen(req)
-            except Exception as e:
-                if hasattr(e, 'headers') and 'Retry-After' in e.headers:
-                    return self.handle_retry(e)
-                raise BrokenLinkError(self.target, str(e))
-            else:
-                code = f.getcode()
-                f.close()
-                if code >= 400:
-                    if 'Retry-After' in f.headers:
-                        return self.handle_retry(e)
-                    raise BrokenLinkError(self.target, str(code))
+        url = self.target
+
+        if ':' in url:
+            response = self.fetch_with_retries(url)
+            if self.parent.check_anchors and '#' in url:
+                anchor = url.split('#')[1]
+                if anchor and "html" in response.headers.get("Content-Type"):
+                    parsed = html5lib.parse(response.content, 
namespaceHTMLElements=False)
+                    return self.handle_anchor(parsed, anchor)
         else:
-            if self.target.startswith('/'):
-                raise BrokenLinkError(self.target, "absolute path link")
+            if url.startswith('/'):
+                raise BrokenLinkError(url, "absolute path link")
             # relative URL
-            url = self.target
             anchor = None
             if '?' in url:
                 url = url.split('?')[0]
@@ -256,7 +332,7 @@
                     break
             if not exists:
                 target_path = dirpath.join(url_path)
-                raise BrokenLinkError(self.target, "No such file: %s" % 
target_path)
+                raise BrokenLinkError(url, "No such file: %s" % target_path)
 
 
 def extensions_str(extensions):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/pytest_check_links.egg-info/PKG-INFO 
new/pytest_check_links-0.4.0/pytest_check_links.egg-info/PKG-INFO
--- old/pytest_check_links-0.3.4/pytest_check_links.egg-info/PKG-INFO   
2020-04-21 17:09:34.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links.egg-info/PKG-INFO   
2020-05-02 11:02:46.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: pytest-check-links
-Version: 0.3.4
+Version: 0.4.0
 Summary: Check links in files
 Home-page: https://github.com/jupyterlab/pytest-check-links
 Author: Jupyter Development Team
@@ -10,29 +10,97 @@
         
         pytest plugin that checks URLs for HTML-containing files.
         
-        Supported files:
+        
[![codecov](https://codecov.io/gh/jupyterlab/pytest-check-links/branch/master/graph/badge.svg)](https://codecov.io/gh/jupyterlab/pytest-check-links)
+        [![Build 
Status](https://travis-ci.com/jupyterlab/pytest-check-links.svg?branch=master)](https://travis-ci.com/jupyterlab/pytest-check-links)
+        [![PyPI 
version](https://badge.fury.io/py/pytest-check-links.svg)](https://badge.fury.io/py/pytest-check-links)
+        
+        ## Supported files
+        
+        - `.html`
+        - `.rst`
+        - `.md` (TODO: select renderer)
+        - `.ipynb` (requires `nbconvert`)
         
-        - .html
-        - .rst
-        - .md (TODO: select renderer)
-        - .ipynb (requires nbconvert)
-        
-        Install:
+        ## Install
         
             pip install pytest-check-links
         
-        Use:
+        ## Use
         
             pytest --check-links mynotebook.ipynb
         
+        ## Configure
+        
+        #### --links-ext
+        
+        > default: `md,rst,html,ipynb`
+        
+        A comma-separated list of extensions to check
+        
+        #### --check-anchors
+        
+        Also check whether links with `#anchors` HTML files (either local, or 
with
+        served with `html` in the `Content-Type`) actually exist, and point to 
_exactly one_
+        named anchor.
+        
+        ### Cache
+        
+        Caching requires the installation of `requests-cache`.
+        
+            pip install requests-cache
+        
+        If enabled, each occurance of a link will be checked, no matter how 
many times
+        it appears in a collection of files to check.
+        
+        #### --check-links-cache
+        
+        Cache requests when checking links. Caching is disabled by default, 
and this option
+        must be provided, even if other cache configuration options are 
provided.
+        
+        #### --check-links-cache-name
+        
+        > default: `.pytest-check-links-cache`
+        
+        Name of link cache, either the base name of a file or similar, 
depending on backend.
+        
+        #### --check-links-cache-backend
+        
+        > default: `sqlite3`
+        
+        Cache persistence backend. The other known backends are:
+        - `memory`
+        - `redis`
+        - `mongodb`
+        
+        See the [requests-cache 
documentation](https://requests-cache.readthedocs.io)
+        for more information.
+        
+        #### --check-links-cache-expire-after
+        
+        > default: `None` (unlimited)
+        
+        Time to cache link responses (seconds).
+        
+        #### --check-links-cache-backend-opt
+        
+        Backend-specific options for link cache, provided as `key:value`. 
These are passed
+        directly to the `requests_cache.CachedSession` constructor, as they 
vary depending
+        on the backend.
+        
+        Values will be parsed as JSON first, so to overload the default of 
caching all
+        HTTP response codes (which requires a list of `int`s):
+        
+        ```bash
+        --check-links-backend-opt allowable_codes:[200]
+        ```
+        
         
-        TODO:
+        ## TODO
         
         - pick a markdown renderer (probably commonmark) or make the markdown 
renderer pluggable
         - options for validating links (allow absolute links, only remote or 
local, etc.)
         - check internal links (`#anchors`)
         - find URLs in Python docstrings
-        - test myself, obvs!
         
         
 Keywords: setup,distutils
@@ -43,3 +111,4 @@
 Classifier: Programming Language :: Python
 Classifier: Topic :: Software Development :: Testing
 Description-Content-Type: text/markdown
+Provides-Extra: cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/pytest_check_links.egg-info/SOURCES.txt 
new/pytest_check_links-0.4.0/pytest_check_links.egg-info/SOURCES.txt
--- old/pytest_check_links-0.3.4/pytest_check_links.egg-info/SOURCES.txt        
2020-04-21 17:09:34.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links.egg-info/SOURCES.txt        
2020-05-02 11:02:46.000000000 +0200
@@ -1,14 +1,18 @@
 .travis.yml
 AUTHORS
+CHANGELOG.md
 ChangeLog
 LICENSE
 README.md
+RELEASE.md
 pytest.ini
 requirements.txt
 setup.cfg
 setup.py
 examples/anchors_other.html
+examples/anchors_remote.html
 examples/anchors_self.html
+examples/httpbin.md
 examples/linkcheck.ipynb
 examples/markdown.md
 examples/rst.rst
@@ -24,5 +28,8 @@
 pytest_check_links.egg-info/pbr.json
 pytest_check_links.egg-info/requires.txt
 pytest_check_links.egg-info/top_level.txt
+test/__init__.py
 test/conftest.py
+test/test_anchors.py
+test/test_cache.py
 test/test_check_links.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/pytest_check_links.egg-info/pbr.json 
new/pytest_check_links-0.4.0/pytest_check_links.egg-info/pbr.json
--- old/pytest_check_links-0.3.4/pytest_check_links.egg-info/pbr.json   
2020-04-21 17:09:34.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links.egg-info/pbr.json   
2020-05-02 11:02:46.000000000 +0200
@@ -1 +1 @@
-{"git_version": "5d62197", "is_release": false}
\ No newline at end of file
+{"git_version": "3b1564b", "is_release": false}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/pytest_check_links-0.3.4/pytest_check_links.egg-info/requires.txt 
new/pytest_check_links-0.4.0/pytest_check_links.egg-info/requires.txt
--- old/pytest_check_links-0.3.4/pytest_check_links.egg-info/requires.txt       
2020-04-21 17:09:34.000000000 +0200
+++ new/pytest_check_links-0.4.0/pytest_check_links.egg-info/requires.txt       
2020-05-02 11:02:46.000000000 +0200
@@ -1,6 +1,9 @@
-pytest>=2.8
+docutils
 html5lib
-six
-nbformat
 nbconvert
-docutils
+nbformat
+pytest>=2.8
+requests
+
+[cache]
+requests-cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/requirements.txt 
new/pytest_check_links-0.4.0/requirements.txt
--- old/pytest_check_links-0.3.4/requirements.txt       2020-03-30 
11:40:43.000000000 +0200
+++ new/pytest_check_links-0.4.0/requirements.txt       2020-05-02 
11:02:00.000000000 +0200
@@ -1,6 +1,6 @@
-pytest >= 2.8
+docutils
 html5lib
-six
-nbformat
 nbconvert
-docutils
+nbformat
+pytest >= 2.8
+requests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/setup.cfg 
new/pytest_check_links-0.4.0/setup.cfg
--- old/pytest_check_links-0.3.4/setup.cfg      2020-04-21 17:09:34.000000000 
+0200
+++ new/pytest_check_links-0.4.0/setup.cfg      2020-05-02 11:02:46.000000000 
+0200
@@ -30,6 +30,10 @@
 console_scripts = 
        pytest-check-links = pytest_check_links.__main__:main
 
+[options.extras_require]
+cache = 
+       requests-cache
+
 [pep8]
 ignore = E128
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/test/__init__.py 
new/pytest_check_links-0.4.0/test/__init__.py
--- old/pytest_check_links-0.3.4/test/__init__.py       1970-01-01 
01:00:00.000000000 +0100
+++ new/pytest_check_links-0.4.0/test/__init__.py       2020-05-02 
11:02:00.000000000 +0200
@@ -0,0 +1,4 @@
+import os
+
+here = os.path.dirname(os.path.abspath(__file__))
+examples = os.path.join(os.path.dirname(here), 'examples')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/test/test_anchors.py 
new/pytest_check_links-0.4.0/test/test_anchors.py
--- old/pytest_check_links-0.3.4/test/test_anchors.py   1970-01-01 
01:00:00.000000000 +0100
+++ new/pytest_check_links-0.4.0/test/test_anchors.py   2020-05-02 
11:02:00.000000000 +0200
@@ -0,0 +1,28 @@
+from . import examples
+
+import pytest
+
+
[email protected]
+def anchor_args():
+    return ["-v", "--check-links", "--check-anchors"]
+
+
+def test_anchors_local_self(testdir, anchor_args):
+    testdir.copy_example('anchors_self.html')
+    result = testdir.runpytest(*anchor_args)
+    result.assert_outcomes(passed=1, failed=2)
+
+
+def test_anchors_local_other(testdir, anchor_args):
+    testdir.copy_example('anchors_self.html')
+    testdir.copy_example('anchors_other.html')
+    args = anchor_args + ["anchors_other.html"]
+    result = testdir.runpytest(*args)
+    result.assert_outcomes(passed=1, failed=2)
+
+
+def test_anchors_external(testdir, anchor_args):
+    testdir.copy_example('anchors_remote.html')
+    result = testdir.runpytest(*anchor_args)
+    result.assert_outcomes(passed=1, failed=1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/test/test_cache.py 
new/pytest_check_links-0.4.0/test/test_cache.py
--- old/pytest_check_links-0.3.4/test/test_cache.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/pytest_check_links-0.4.0/test/test_cache.py     2020-05-02 
11:02:00.000000000 +0200
@@ -0,0 +1,141 @@
+import os
+import time
+import shutil
+
+from glob import glob
+
+import pytest
+
+import requests_cache
+
+from . import examples
+
+
[email protected]
+def base_args():
+    return ["-v", "--check-links", "--check-links-cache"]
+
+
[email protected]
+def memory_args(base_args):
+    return base_args + ["--check-links-cache-backend", "memory"]
+
+
+def assert_sqlite(testdir, name=None, tmpdir=None, exists=True):
+    name = name or ".pytest-check-links-cache.sqlite"
+    tmpdir = str(tmpdir or testdir.tmpdir)
+    caches = list(glob(os.path.join(tmpdir, name)))
+    if exists:
+        assert caches
+    else:
+        assert not caches
+
+
[email protected]("cache_name", [
+    None,
+    "custom-cache"
+])
+def test_cache_expiry(testdir, base_args, cache_name, tmpdir):
+    """will the default sqlite3 backend persist and then expire?
+    """
+    testdir.copy_example('linkcheck.ipynb')
+
+    args = base_args + ["--check-links-cache-expire-after", "2"]
+    if cache_name:
+        args += ["--check-links-cache-name", os.path.join(str(tmpdir), 
cache_name)]
+    expected = dict(passed=3, failed=3)
+    t0 = time.time()
+    result = testdir.runpytest(*args)
+    t1 = time.time()
+    result.assert_outcomes(**expected)
+
+    if cache_name:
+        assert_sqlite(testdir, name="{}.sqlite".format(cache_name), 
tmpdir=tmpdir)
+    else:
+        assert_sqlite(testdir)
+
+    t2 = time.time()
+    result = testdir.runpytest(*args)
+    t3 = time.time()
+    result.assert_outcomes(**expected)
+
+    assert t1 - t0 > t3 - t2, "cache did not make second run faster"
+
+    time.sleep(2)
+
+    t4 = time.time()
+    result = testdir.runpytest(*args)
+    t5 = time.time()
+    result.assert_outcomes(**expected)
+
+    assert t5 - t4 > t3 - t2, "cache did not expire"
+
+
+def test_cache_memory(testdir, memory_args):
+    """will the memory backend cache links inside a run?
+    """
+    expected = dict(passed=3, failed=0)
+
+    testdir.copy_example('httpbin.md')
+
+    def run(passed):
+        t0 = time.time()
+        result = testdir.runpytest(*memory_args)
+        t1 = time.time()
+        result.assert_outcomes(passed=passed, failed=0)
+        assert_sqlite(testdir, exists=False)
+        return t1 - t0
+
+    d0 = run(6)
+
+    for i in range(5):
+        shutil.copy(
+            os.path.join(str(testdir.tmpdir), "httpbin.md"),
+            os.path.join(str(testdir.tmpdir), "httpbin{}.md".format(i))
+        )
+
+    d1 = run(36)
+    # allow a healthy savings margin for network flake
+    assert d1 < d0 * 4
+
+
+def test_cache_retry(testdir, memory_args):
+    """will a Retry-After header work with cache?
+    """
+
+    testdir.copy_example('httpbin.md')
+
+    attempts = []
+
+    _get = requests_cache.CachedSession.get
+
+    def mock_get(*args, **kwargs):
+        response = _get(*args, **kwargs)
+        if len(attempts) < 5:
+            response.status_code = 502
+            response.headers['Retry-After'] = '0'
+        attempts.append([args, kwargs])
+        return response
+
+    requests_cache.CachedSession.get = mock_get
+
+    result = testdir.runpytest(*memory_args)
+
+    try:
+        result.assert_outcomes(passed=5, failed=1)
+        assert len(attempts) == 10
+    finally:
+        requests_cache.CachedSession.get = _get
+
+
+def test_cache_backend_opts(testdir, base_args):
+    testdir.copy_example('httpbin.md')
+    args = base_args + [
+        "--check-links-cache-backend-opt", "fast_save:true",
+        "--check-links-cache-name", "foo",
+        "--check-links-cache-backend-opt", "extension:.db",
+        "--check-links-cache-backend-opt", "allowable_codes:[200]"
+    ]
+    result = testdir.runpytest(*args)
+    result.assert_outcomes(passed=6, failed=0)
+    assert_sqlite(testdir, name="foo.db")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest_check_links-0.3.4/test/test_check_links.py 
new/pytest_check_links-0.4.0/test/test_check_links.py
--- old/pytest_check_links-0.3.4/test/test_check_links.py       2020-04-21 
17:04:48.000000000 +0200
+++ new/pytest_check_links-0.4.0/test/test_check_links.py       2020-05-02 
11:02:00.000000000 +0200
@@ -1,7 +1,4 @@
-import os
-
-here = os.path.dirname(os.path.abspath(__file__))
-examples = os.path.join(os.path.dirname(here), 'examples')
+from . import examples
 
 
 def test_ipynb(testdir):
@@ -25,14 +22,3 @@
     testdir.copy_example('markdown.md')
     result = testdir.runpytest("-v", "--check-links", "--links-ext=.md,rst")
     result.assert_outcomes(passed=15, failed=6)
-
-def test_anchors_self(testdir):
-    testdir.copy_example('anchors_self.html')
-    result = testdir.runpytest("-v", "--check-links", "--check-anchors")
-    result.assert_outcomes(passed=1, failed=2)
-
-def test_anchors_other(testdir):
-    testdir.copy_example('anchors_self.html')
-    testdir.copy_example('anchors_other.html')
-    result = testdir.runpytest("-v", "--check-links", "--check-anchors", 
"anchors_other.html")
-    result.assert_outcomes(passed=1, failed=2)


Reply via email to