dirty
This commit is contained in:
parent
19813d4d45
commit
5c2fb8c32b
20 changed files with 371 additions and 22 deletions
2
Pipfile
2
Pipfile
|
|
@ -4,7 +4,7 @@ verify_ssl = true
|
||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
byteb4rb1e_sphinxcontrib = { editable = true, path = '.'}
|
byteb4rb1e.sphinxcontrib = { editable = true, path = '.'}
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
sphinx = "*"
|
sphinx = "*"
|
||||||
|
|
|
||||||
8
Pipfile.lock
generated
8
Pipfile.lock
generated
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "48c822d9e7bedd5ee25106b31093ce10a0071d7b12f7861e4e4cc46c5549c9ed"
|
"sha256": "4eac08fb6ab8c543a1fa6030c3c69a57aa6ae6de69e4f47ec483128bb61b2867"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
@ -32,9 +32,9 @@
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==2.17.0"
|
"version": "==2.17.0"
|
||||||
},
|
},
|
||||||
"byteb4rb1e-sphinxcontrib": {
|
"byteb4rb1e.utils": {
|
||||||
"editable": true,
|
"git": "https://bitbucket.org/byteb4rb1e/py-utils.git",
|
||||||
"path": "."
|
"ref": "d0dfa1cb12702e6d25f3a9eeab02968eda8d06ba"
|
||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ requires = [
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "byteb4rb1e.sphinxcontrib"
|
name = "byteb4rb1e.sphinxcontrib.ext"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "Tiara Rodney", email = "tiara.rodney@byteb4rb1e.me" }
|
{ name = "Tiara Rodney", email = "tiara.rodney@byteb4rb1e.me" }
|
||||||
|
|
@ -30,6 +30,7 @@ classifiers = [
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sphinx>=5.1",
|
"sphinx>=5.1",
|
||||||
|
"byteb4rb1e.utils @ git+https://bitbucket.org/byteb4rb1e/py-utils.git@32ae99c5fa0174761f4053fce1130f3cd7a2a68b"
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
|
|
@ -39,6 +40,7 @@ Bitbucket = "https://bitbucket.org/byteb4rb1e/sphinxcontrib"
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
namespaces = true
|
||||||
|
|
||||||
[tool.autopep8]
|
[tool.autopep8]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import importlib.resources
|
||||||
|
import mimetypes
|
||||||
|
from pathlib import Path
|
||||||
|
import urllib.request
|
||||||
|
import tarfile
|
||||||
|
from typing import Tuple, Optional, Dict
|
||||||
|
from io import IOBase
|
||||||
|
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.environment import BuildEnvironment
|
||||||
|
from sphinx.util.logging import getLogger
|
||||||
|
|
||||||
|
from byteb4rb1e.utils.urllib.request import PkgHandler
|
||||||
|
|
||||||
|
|
||||||
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
_static_archives: Dict[str, IOBase] = {}
|
||||||
|
_opener: urllib.request.OpenerDirector = urllib.request.build_opener(
|
||||||
|
PkgHandler()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def on_config_inited(
|
||||||
|
app: Sphinx,
|
||||||
|
env: BuildEnvironment
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
global _static_archives
|
||||||
|
|
||||||
|
static_archives = app.config['html_static_archive']
|
||||||
|
|
||||||
|
if not static_archives:
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(static_archives, str):
|
||||||
|
static_archives = [static_archives]
|
||||||
|
|
||||||
|
for url in static_archives:
|
||||||
|
_static_archives[url] = _opener.open(url)
|
||||||
|
|
||||||
|
|
||||||
|
def on_env_updated(
|
||||||
|
app: Sphinx,
|
||||||
|
env: BuildEnvironment
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
for url, fh in _static_archives.items():
|
||||||
|
modes = {
|
||||||
|
'application/x-tar': 'r',
|
||||||
|
'application/x-tar+gzip': 'r:gz',
|
||||||
|
'application/x-tar+bzip2': 'r:bz2',
|
||||||
|
'application/x-tar+xz': 'r:xz',
|
||||||
|
}
|
||||||
|
|
||||||
|
mime_type = fh.headers.get('Content-Type')
|
||||||
|
|
||||||
|
if mime_type not in modes.keys():
|
||||||
|
raise Exception('')
|
||||||
|
|
||||||
|
archive = tarfile.open(fileobj=fh, mode=modes[mime_type])
|
||||||
|
|
||||||
|
for file in archive.getmembers():
|
||||||
|
if file.isdir():
|
||||||
|
continue
|
||||||
|
|
||||||
|
shadow_file = Path(app.outdir) / '_static' / file.name
|
||||||
|
|
||||||
|
if not shadow_file.exists():
|
||||||
|
logger.info(f'html_static_archive (extract): {url}::{file.name}')
|
||||||
|
shadow_file.parent.mkdir(parents=True)
|
||||||
|
archive.extract(file, path=shadow_file)
|
||||||
|
|
||||||
|
archive.close()
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app: Sphinx):
|
||||||
|
app.add_config_value('html_static_archive', [], True)
|
||||||
|
app.connect('env-check-consistency', on_config_inited)
|
||||||
|
app.connect('env-check-consistency', on_env_updated)
|
||||||
|
|
||||||
92
src/byteb4rb1e/sphinxcontrib/testing/pytest/fixtures.py
Normal file
92
src/byteb4rb1e/sphinxcontrib/testing/pytest/fixtures.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
from collections import namedtuple
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
from typing import Any, Tuple, Callable, Iterator, Dict
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from sphinx.testing.util import SphinxTestApp
|
||||||
|
from sphinx.testing.fixtures import make_app, test_params
|
||||||
|
|
||||||
|
from byteb4rb1e.testing.pytest.fixtures import current_test
|
||||||
|
|
||||||
|
_SPHINX_TESTAPP_COUNTER: Dict[str, int] = {}
|
||||||
|
_SPHINX_TESTSRC_COUNTER: Dict[str, int] = {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_sphinx_testsrc_dir(
|
||||||
|
tmp_path: Path,
|
||||||
|
):
|
||||||
|
global _SPHINX_TESTSRC_COUNTER
|
||||||
|
|
||||||
|
_SPHINX_TESTSRC_COUNTER.setdefault(tmp_path, 0)
|
||||||
|
|
||||||
|
testsrc_id = _SPHINX_TESTSRC_COUNTER[tmp_path]
|
||||||
|
_SPHINX_TESTSRC_COUNTER[tmp_path] += 1
|
||||||
|
|
||||||
|
srcdir = tmp_path / f'sphinx-testsrc-{testsrc_id}'
|
||||||
|
|
||||||
|
def wrap():
|
||||||
|
srcdir.mkdir(parents=True)
|
||||||
|
|
||||||
|
(srcdir / 'conf.py').write_text("""
|
||||||
|
project = 'foobar'
|
||||||
|
master_doc = 'index'
|
||||||
|
""")
|
||||||
|
|
||||||
|
(srcdir / 'index.rst').write_text("""
|
||||||
|
######
|
||||||
|
Foobar
|
||||||
|
######
|
||||||
|
|
||||||
|
Hello world!
|
||||||
|
""")
|
||||||
|
|
||||||
|
return srcdir
|
||||||
|
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_sphinx_testapp(
|
||||||
|
make_app: Callable[[], SphinxTestApp],
|
||||||
|
tmp_path: Path,
|
||||||
|
caplog
|
||||||
|
):
|
||||||
|
global _SPHINX_TESTAPP_COUNTER
|
||||||
|
|
||||||
|
_SPHINX_TESTAPP_COUNTER.setdefault(tmp_path, 0)
|
||||||
|
|
||||||
|
testapp_id = _SPHINX_TESTAPP_COUNTER[tmp_path]
|
||||||
|
_SPHINX_TESTAPP_COUNTER[tmp_path] += 1
|
||||||
|
|
||||||
|
"""Provides the 'sphinx.application.Sphinx' object"""
|
||||||
|
def wrap(*args, **kwargs) -> Iterator[SphinxTestApp]:
|
||||||
|
assert kwargs.get('srcdir'), 'srcdir keyword argument missing'
|
||||||
|
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
basedir = tmp_path / f'sphinx-testapp-{testapp_id}'
|
||||||
|
srcdir = basedir / 'src'
|
||||||
|
builddir = basedir / 'build'
|
||||||
|
|
||||||
|
shutil.copytree(kwargs['srcdir'], srcdir, dirs_exist_ok=True)
|
||||||
|
kwargs['srcdir'] = srcdir
|
||||||
|
|
||||||
|
kwargs.setdefault('builddir', builddir)
|
||||||
|
|
||||||
|
app_ = make_app(*args, **kwargs)
|
||||||
|
|
||||||
|
logger.debug(json.dumps({
|
||||||
|
'builder': app_.builder.name,
|
||||||
|
'srcdir': str(app_.srcdir),
|
||||||
|
'outdir': str(app_.outdir)
|
||||||
|
}, indent = 4))
|
||||||
|
|
||||||
|
return app_
|
||||||
|
|
||||||
|
return wrap
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
from sphinx.application import Sphinx
|
|
||||||
from sphinx.util.typing import ExtensionMetadata
|
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> ExtensionMetadata:
|
|
||||||
"""add this extension and its children to a Sphinx application"""
|
|
||||||
return {}
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,117 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import tarfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from byteb4rb1e.sphinxcontrib.testing.pytest.fixtures import (
|
||||||
|
mock_sphinx_testapp,
|
||||||
|
mock_sphinx_testsrc_dir
|
||||||
|
)
|
||||||
|
from byteb4rb1e.testing.pytest.decorators import run_in_subprocess_once
|
||||||
|
from byteb4rb1e.testing.pytest.fixtures import mock_system_site_package_dir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@run_in_subprocess_once()
|
||||||
|
def test_default(
|
||||||
|
mock_sphinx_testapp,
|
||||||
|
mock_sphinx_testsrc_dir,
|
||||||
|
mock_system_site_package_dir,
|
||||||
|
tmp_path,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
pkg_dir = mock_system_site_package_dir('dummypkg')
|
||||||
|
|
||||||
|
mock_static_archive = tarfile.open(pkg_dir / 'data.tar', 'w')
|
||||||
|
|
||||||
|
(tmp_path / 'foobar.txt').write_text("Hello world!")
|
||||||
|
|
||||||
|
mock_static_archive.addfile(
|
||||||
|
mock_static_archive.gettarinfo(
|
||||||
|
arcname='foobar.txt',
|
||||||
|
fileobj=(tmp_path / 'foobar.txt').open('r')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_static_archive.close()
|
||||||
|
|
||||||
|
app = mock_sphinx_testapp(srcdir=mock_sphinx_testsrc_dir())
|
||||||
|
|
||||||
|
app.setup_extension('byteb4rb1e.sphinxcontrib.ext.html_static_archive')
|
||||||
|
|
||||||
|
app.config['html_static_archive'] = f'pkg://dummypkg/data.tar'
|
||||||
|
|
||||||
|
app.build(force_all=True)
|
||||||
|
|
||||||
|
assert (Path(app.outdir) / '_static' / 'foobar.txt').exists()
|
||||||
|
|
||||||
|
|
||||||
|
@run_in_subprocess_once()
|
||||||
|
def test_compression(
|
||||||
|
mock_sphinx_testapp,
|
||||||
|
mock_sphinx_testsrc_dir,
|
||||||
|
mock_system_site_package_dir,
|
||||||
|
tmp_path,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
pkg_dir = mock_system_site_package_dir('dummypkg')
|
||||||
|
|
||||||
|
mock_static_archive = tarfile.open(pkg_dir / 'data.tar.gz', 'w:gz')
|
||||||
|
|
||||||
|
(tmp_path / 'foobar.txt').write_text("Hello world!")
|
||||||
|
|
||||||
|
mock_static_archive.addfile(
|
||||||
|
mock_static_archive.gettarinfo(
|
||||||
|
arcname='foobar.txt',
|
||||||
|
fileobj=(tmp_path / 'foobar.txt').open('r')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_static_archive.close()
|
||||||
|
|
||||||
|
app = mock_sphinx_testapp(srcdir=mock_sphinx_testsrc_dir())
|
||||||
|
|
||||||
|
app.setup_extension('byteb4rb1e.sphinxcontrib.ext.html_static_archive')
|
||||||
|
|
||||||
|
app.config['html_static_archive'] = f'pkg://dummypkg/data.tar.gz'
|
||||||
|
|
||||||
|
app.build(force_all=True)
|
||||||
|
|
||||||
|
assert (Path(app.outdir) / '_static' / 'foobar.txt').exists()
|
||||||
|
|
||||||
|
|
||||||
|
@run_in_subprocess_once()
|
||||||
|
def test_subdirs(
|
||||||
|
mock_sphinx_testapp,
|
||||||
|
mock_sphinx_testsrc_dir,
|
||||||
|
mock_system_site_package_dir,
|
||||||
|
tmp_path,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
pkg_dir = mock_system_site_package_dir('dummypkg')
|
||||||
|
|
||||||
|
mock_static_archive = tarfile.open(pkg_dir / 'data.tar.gz', 'w:gz')
|
||||||
|
|
||||||
|
(tmp_path / 'foobar.txt').write_text("Hello world!")
|
||||||
|
|
||||||
|
mock_static_archive.addfile(
|
||||||
|
mock_static_archive.gettarinfo(
|
||||||
|
arcname='foo/bar/foobar.txt',
|
||||||
|
fileobj=(tmp_path / 'foobar.txt').open('r')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_static_archive.close()
|
||||||
|
|
||||||
|
app = mock_sphinx_testapp(srcdir=mock_sphinx_testsrc_dir())
|
||||||
|
|
||||||
|
app.setup_extension('byteb4rb1e.sphinxcontrib.ext.html_static_archive')
|
||||||
|
|
||||||
|
app.config['html_static_archive'] = f'pkg://dummypkg/data.tar.gz'
|
||||||
|
|
||||||
|
app.build(force_all=True)
|
||||||
|
|
||||||
|
assert (Path(app.outdir) / '_static' / 'foo' / 'bar' / 'foobar.txt').exists()
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.pytest
|
||||||
|
|
||||||
|
from byteb4rb1e.sphinxcontrib.testing.pytest.fixtures import (
|
||||||
|
mock_sphinx_testapp,
|
||||||
|
mock_sphinx_testsrc_dir
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_mock_sphinx_testsrc_dir(mock_sphinx_testsrc_dir):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
srcdir = mock_sphinx_testsrc_dir()
|
||||||
|
|
||||||
|
assert (srcdir / 'conf.py').exists()
|
||||||
|
assert (srcdir / 'index.rst').exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_mock_sphinx_testapp(mock_sphinx_testapp, mock_sphinx_testsrc_dir):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
srcdir = mock_sphinx_testsrc_dir()
|
||||||
|
|
||||||
|
app = mock_sphinx_testapp(srcdir=srcdir)
|
||||||
|
|
||||||
|
assert app.builder is not None
|
||||||
|
assert app.srcdir.samefile(srcdir) is False # Should be copied to internal src
|
||||||
|
|
||||||
|
app.build(force_all=True)
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
def test_default() -> None:
|
|
||||||
assert 1 == 1
|
|
||||||
17
tests/integration/conftest.py
Normal file
17
tests/integration/conftest.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
pytest_plugins = ['byteb4rb1e.sphinxcontrib.testing.pytest.fixtures']
|
||||||
|
|
||||||
|
_TESTS_ROOT = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
# register an additional marker
|
||||||
|
config.addinivalue_line(
|
||||||
|
"markers", "pytest: test pytest integration"
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def rootdir() -> Path:
|
||||||
|
return _TESTS_ROOT
|
||||||
28
tox.ini
28
tox.ini
|
|
@ -2,8 +2,9 @@
|
||||||
requires =
|
requires =
|
||||||
tox>=4.19
|
tox>=4.19
|
||||||
env_list =
|
env_list =
|
||||||
py3{8-12}-{unit}
|
unit-py3{9-13}
|
||||||
py3{8-12}-sphinx{5-8}-{integration}
|
integration-py3{9-13}-sphinx{5-8}
|
||||||
|
integration-py3{9-13}-sphinx{5-8}-pytest8
|
||||||
lint
|
lint
|
||||||
format
|
format
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ deps =
|
||||||
commands =
|
commands =
|
||||||
black --check src tests
|
black --check src tests
|
||||||
|
|
||||||
[testenv:py3{9-13}-unit]
|
[testenv:unit-py3{9-13}]
|
||||||
description = run type check on code base
|
description = run type check on code base
|
||||||
labels = unit
|
labels = unit
|
||||||
deps =
|
deps =
|
||||||
|
|
@ -44,8 +45,8 @@ deps =
|
||||||
commands =
|
commands =
|
||||||
pytest tests/unit --junitxml=test-reports/{env_name}.xml
|
pytest tests/unit --junitxml=test-reports/{env_name}.xml
|
||||||
|
|
||||||
[testenv:py3{9-13}-sphinx{5-7}-integration]
|
[testenv:integration-py3{9-13}-sphinx{5-7}]
|
||||||
description = run type check on code base
|
description = run sphinx 5-7 integration tests
|
||||||
labels = integration
|
labels = integration
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
|
|
@ -56,8 +57,8 @@ deps =
|
||||||
commands =
|
commands =
|
||||||
pytest tests/integration --junitxml=test-reports/{env_name}.xml
|
pytest tests/integration --junitxml=test-reports/{env_name}.xml
|
||||||
|
|
||||||
[testenv:py3{10-13}-sphinx8-integration]
|
[testenv:integration-py3{10-13}-sphinx8]
|
||||||
description = run type check on code base
|
description = run sphinx 8 integration tests
|
||||||
labels = integration
|
labels = integration
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
|
|
@ -65,3 +66,16 @@ deps =
|
||||||
pytest
|
pytest
|
||||||
commands =
|
commands =
|
||||||
pytest tests/integration --junitxml=test-reports/{env_name}.xml
|
pytest tests/integration --junitxml=test-reports/{env_name}.xml
|
||||||
|
|
||||||
|
[testenv:integration-py3{10-13}-sphinx{5-8}-pytest8]
|
||||||
|
description = run pytest 8 testing integration tests (excluding Python 3.9)
|
||||||
|
labels = integration
|
||||||
|
deps =
|
||||||
|
{[testenv]deps}
|
||||||
|
sphinx5: sphinx>=5.0,<=6.0
|
||||||
|
sphinx6: sphinx>=6.0,<=7.0
|
||||||
|
sphinx7: sphinx>=7.0,<=8.0
|
||||||
|
sphinx8: sphinx>=8.0,<=9.0
|
||||||
|
pytest8: pytest>=8.0,<=9.0
|
||||||
|
commands =
|
||||||
|
pytest tests/integration -m pytest --junitxml=test-reports/{env_name}.xml
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue