Compare commits
36 commits
Author | SHA1 | Date | |
---|---|---|---|
Jan Dittberner | 04985650e7 | ||
Jan Dittberner | 9d94b308ae | ||
Jan Dittberner | 1afe18b429 | ||
Jan Dittberner | 7c675a6fdb | ||
Jan Dittberner | c721d1bf9c | ||
Jan Dittberner | 6d425acaac | ||
Jan Dittberner | 7acff11695 | ||
Jan Dittberner | d1a151f8bd | ||
Jan Dittberner | 1451a5a1c0 | ||
Jan Dittberner | e53838acfb | ||
Jan Dittberner | 49a3d89488 | ||
Jan Dittberner | 8bc07c611f | ||
Jan Dittberner | 8044e1a071 | ||
Jan Dittberner | 80fad8cd49 | ||
Jan Dittberner | 43f01a1235 | ||
Jan Dittberner | fdb126996a | ||
Jan Dittberner | 0af66c6964 | ||
Jan Dittberner | 3daf568fb6 | ||
Jan Dittberner | 7df1ae3b12 | ||
Jan Dittberner | ba3baef5a1 | ||
Jan Dittberner | 94ad227587 | ||
Jan Dittberner | 281f740589 | ||
Jan Dittberner | b243fc6249 | ||
Jan Dittberner | 585085250e | ||
Jan Dittberner | fc809c1041 | ||
Jan Dittberner | 12a6b34550 | ||
Jan Dittberner | 713dc39b9e | ||
Jan Dittberner | 14238838ef | ||
Jan Dittberner | e66e863878 | ||
Jan Dittberner | 24c451c2fb | ||
Jan Dittberner | 460ea45061 | ||
Jan Dittberner | 70e447986d | ||
Jan Dittberner | 74e9a3a7a6 | ||
Jan Dittberner | f641b1b1b7 | ||
Jan Dittberner | 54c11b4357 | ||
Jan Dittberner | f81389ef6b |
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -2,10 +2,14 @@
|
|||
*.pyc
|
||||
*.pyo
|
||||
.*.swp
|
||||
.ropeproject/
|
||||
__pycache__/
|
||||
dist/
|
||||
.coverage
|
||||
htmlcov/
|
||||
build/
|
||||
.idea/
|
||||
.ropeproject/
|
||||
/.*_cache/
|
||||
/.python-version
|
||||
/.tox/
|
||||
__pycache__/
|
||||
_build/
|
||||
build/
|
||||
dist/
|
||||
htmlcov/
|
||||
|
|
47
CHANGES.rst
47
CHANGES.rst
|
@ -1,6 +1,53 @@
|
|||
Changes
|
||||
=======
|
||||
|
||||
0.6.1 - 2023-01-29
|
||||
------------------
|
||||
|
||||
* reupload to include namespace package
|
||||
|
||||
0.6.0 - 2023-01-29
|
||||
------------------
|
||||
|
||||
* add development documentation in development.rst
|
||||
* use tox as test runner
|
||||
* add type annotations
|
||||
* fix Sphinx 6 deprecation warnings
|
||||
|
||||
0.5.1 - 2021-09-04
|
||||
------------------
|
||||
|
||||
* fix anchors in links
|
||||
|
||||
0.5.0 - 2021-09-04
|
||||
------------------
|
||||
|
||||
* fix Docutils error
|
||||
* adapt to Sphinx 4
|
||||
|
||||
0.4.0 - 2021-01-02
|
||||
------------------
|
||||
|
||||
* fix Docutils deprecation warning
|
||||
* fix test deprecation warning from Sphinx
|
||||
* use pytest for testing
|
||||
* switch to PEP-517 build API and metadata
|
||||
|
||||
0.3.0 - 2019-07-13
|
||||
------------------
|
||||
|
||||
* update to Sphinx 2.1 API
|
||||
|
||||
0.2.4 - 2016-05-07
|
||||
------------------
|
||||
|
||||
* fix index generation issue
|
||||
|
||||
0.2.3 - 2016-05-05
|
||||
------------------
|
||||
|
||||
* fix regression in HTML builder that has been introduced in 0.2.2
|
||||
|
||||
0.2.2 - 2016-05-05
|
||||
------------------
|
||||
|
||||
|
|
47
DEVELOPMENT.rst
Normal file
47
DEVELOPMENT.rst
Normal file
|
@ -0,0 +1,47 @@
|
|||
Development
|
||||
===========
|
||||
|
||||
The extension is developed in a git repository that can be cloned by running::
|
||||
|
||||
git clone https://git.dittberner.info/jan/sphinxext-ip.git
|
||||
|
||||
Running test
|
||||
------------
|
||||
|
||||
To install all dependencies and run the tests use::
|
||||
|
||||
pipenv install --dev
|
||||
pipenv run tox
|
||||
|
||||
Release a new version
|
||||
---------------------
|
||||
|
||||
Start by deciding the new release number and perform the following steps:
|
||||
|
||||
* update CHANGES.rst
|
||||
* change ``version`` in setup.cfg
|
||||
* change ``__version__`` in jandd/sphinxext/ip/__init__.rst
|
||||
* change ``version`` in tests/root/conf.py
|
||||
* commit and push your changes ::
|
||||
|
||||
git commit -m "Release <version>"
|
||||
git push
|
||||
|
||||
* create an annotated and signed tag with the new version number (``git
|
||||
shortlog <previous_tag>..HEAD`` could help to create a good release tag
|
||||
message) ::
|
||||
|
||||
git tag -s -a <version>
|
||||
|
||||
* build the release artifacts ::
|
||||
|
||||
rm -rf dist jandd.sphinxext.ip.egg-info
|
||||
pipenv run python3 setup.py egg_info -b <version< bdist_wheel sdist
|
||||
|
||||
* upload to PyPI using twine ::
|
||||
|
||||
pipenv run twine upload -s dist/*
|
||||
|
||||
* push the tag to git ::
|
||||
|
||||
git push --tags
|
|
@ -1,2 +1,2 @@
|
|||
include README.rst
|
||||
include README.rst CHANGES.rst
|
||||
recursive-include tests/root *.rst Makefile conf.py
|
||||
|
|
16
Pipfile
Normal file
16
Pipfile
Normal file
|
@ -0,0 +1,16 @@
|
|||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
coverage = "*"
|
||||
twine = "*"
|
||||
pytest = "*"
|
||||
tox = "*"
|
||||
black = "*"
|
||||
|
||||
[packages]
|
||||
jandd-sphinxext-ip = { path = ".", editable = true }
|
||||
Sphinx = ">=5"
|
||||
docutils = "*"
|
948
Pipfile.lock
generated
Normal file
948
Pipfile.lock
generated
Normal file
|
@ -0,0 +1,948 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "845c2f660368ac0a26cd73d6ad9e2e5164014c5f1458797ad9258f23a3e71d1f"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"alabaster": {
|
||||
"hashes": [
|
||||
"sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3",
|
||||
"sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.7.13"
|
||||
},
|
||||
"babel": {
|
||||
"hashes": [
|
||||
"sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe",
|
||||
"sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.11.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.12.7"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b",
|
||||
"sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42",
|
||||
"sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d",
|
||||
"sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b",
|
||||
"sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a",
|
||||
"sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59",
|
||||
"sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154",
|
||||
"sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1",
|
||||
"sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c",
|
||||
"sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a",
|
||||
"sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d",
|
||||
"sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6",
|
||||
"sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b",
|
||||
"sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b",
|
||||
"sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783",
|
||||
"sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5",
|
||||
"sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918",
|
||||
"sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555",
|
||||
"sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639",
|
||||
"sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786",
|
||||
"sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e",
|
||||
"sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed",
|
||||
"sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820",
|
||||
"sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8",
|
||||
"sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3",
|
||||
"sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541",
|
||||
"sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14",
|
||||
"sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be",
|
||||
"sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e",
|
||||
"sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76",
|
||||
"sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b",
|
||||
"sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c",
|
||||
"sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b",
|
||||
"sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3",
|
||||
"sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc",
|
||||
"sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6",
|
||||
"sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59",
|
||||
"sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4",
|
||||
"sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d",
|
||||
"sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d",
|
||||
"sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3",
|
||||
"sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a",
|
||||
"sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea",
|
||||
"sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6",
|
||||
"sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e",
|
||||
"sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603",
|
||||
"sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24",
|
||||
"sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a",
|
||||
"sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58",
|
||||
"sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678",
|
||||
"sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a",
|
||||
"sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c",
|
||||
"sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6",
|
||||
"sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18",
|
||||
"sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174",
|
||||
"sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317",
|
||||
"sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f",
|
||||
"sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc",
|
||||
"sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837",
|
||||
"sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41",
|
||||
"sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c",
|
||||
"sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579",
|
||||
"sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753",
|
||||
"sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8",
|
||||
"sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291",
|
||||
"sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087",
|
||||
"sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866",
|
||||
"sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3",
|
||||
"sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d",
|
||||
"sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1",
|
||||
"sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca",
|
||||
"sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e",
|
||||
"sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db",
|
||||
"sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72",
|
||||
"sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d",
|
||||
"sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc",
|
||||
"sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539",
|
||||
"sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d",
|
||||
"sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af",
|
||||
"sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b",
|
||||
"sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602",
|
||||
"sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f",
|
||||
"sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478",
|
||||
"sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c",
|
||||
"sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e",
|
||||
"sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479",
|
||||
"sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7",
|
||||
"sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6",
|
||||
"sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.19"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.4"
|
||||
},
|
||||
"imagesize": {
|
||||
"hashes": [
|
||||
"sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b",
|
||||
"sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.4.1"
|
||||
},
|
||||
"jandd-sphinxext-ip": {
|
||||
"editable": true,
|
||||
"path": "."
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.1.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed",
|
||||
"sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc",
|
||||
"sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2",
|
||||
"sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460",
|
||||
"sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7",
|
||||
"sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0",
|
||||
"sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1",
|
||||
"sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa",
|
||||
"sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03",
|
||||
"sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323",
|
||||
"sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65",
|
||||
"sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013",
|
||||
"sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036",
|
||||
"sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f",
|
||||
"sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4",
|
||||
"sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419",
|
||||
"sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2",
|
||||
"sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619",
|
||||
"sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a",
|
||||
"sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a",
|
||||
"sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd",
|
||||
"sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7",
|
||||
"sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666",
|
||||
"sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65",
|
||||
"sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859",
|
||||
"sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625",
|
||||
"sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff",
|
||||
"sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156",
|
||||
"sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd",
|
||||
"sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba",
|
||||
"sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f",
|
||||
"sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1",
|
||||
"sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094",
|
||||
"sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a",
|
||||
"sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513",
|
||||
"sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed",
|
||||
"sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d",
|
||||
"sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3",
|
||||
"sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147",
|
||||
"sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c",
|
||||
"sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603",
|
||||
"sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601",
|
||||
"sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a",
|
||||
"sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1",
|
||||
"sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d",
|
||||
"sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3",
|
||||
"sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54",
|
||||
"sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2",
|
||||
"sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6",
|
||||
"sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.2"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2",
|
||||
"sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.0"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297",
|
||||
"sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.14.0"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0",
|
||||
"sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"
|
||||
],
|
||||
"version": "==2022.7.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
|
||||
"sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||
"version": "==2.28.2"
|
||||
},
|
||||
"snowballstemmer": {
|
||||
"hashes": [
|
||||
"sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1",
|
||||
"sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"
|
||||
],
|
||||
"version": "==2.2.0"
|
||||
},
|
||||
"sphinx": {
|
||||
"hashes": [
|
||||
"sha256:0dac3b698538ffef41716cf97ba26c1c7788dba73ce6f150c1ff5b4720786dd2",
|
||||
"sha256:807d1cb3d6be87eb78a381c3e70ebd8d346b9a25f3753e9947e866b2786865fc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==6.1.3"
|
||||
},
|
||||
"sphinxcontrib-applehelp": {
|
||||
"hashes": [
|
||||
"sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228",
|
||||
"sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"sphinxcontrib-devhelp": {
|
||||
"hashes": [
|
||||
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
|
||||
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.2"
|
||||
},
|
||||
"sphinxcontrib-htmlhelp": {
|
||||
"hashes": [
|
||||
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
|
||||
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"sphinxcontrib-jsmath": {
|
||||
"hashes": [
|
||||
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
|
||||
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"sphinxcontrib-qthelp": {
|
||||
"hashes": [
|
||||
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
|
||||
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"sphinxcontrib-serializinghtml": {
|
||||
"hashes": [
|
||||
"sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
|
||||
"sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==1.1.5"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72",
|
||||
"sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==1.26.14"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836",
|
||||
"sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==22.2.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320",
|
||||
"sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351",
|
||||
"sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350",
|
||||
"sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f",
|
||||
"sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf",
|
||||
"sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148",
|
||||
"sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4",
|
||||
"sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d",
|
||||
"sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc",
|
||||
"sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d",
|
||||
"sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2",
|
||||
"sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==22.12.0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414",
|
||||
"sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
"sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14",
|
||||
"sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"
|
||||
],
|
||||
"markers": "python_version ~= '3.7'",
|
||||
"version": "==5.3.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.12.7"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
|
||||
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
|
||||
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
|
||||
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
|
||||
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
|
||||
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
|
||||
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
|
||||
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
|
||||
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
|
||||
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
|
||||
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
|
||||
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
|
||||
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
|
||||
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
|
||||
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
|
||||
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
|
||||
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
|
||||
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
|
||||
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
|
||||
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
|
||||
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
|
||||
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
|
||||
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
|
||||
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
|
||||
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
|
||||
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
|
||||
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
|
||||
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
|
||||
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
|
||||
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
|
||||
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
|
||||
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
|
||||
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
|
||||
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
|
||||
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
|
||||
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
|
||||
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
|
||||
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
|
||||
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
|
||||
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
|
||||
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
|
||||
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
|
||||
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
|
||||
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
|
||||
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
|
||||
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
|
||||
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
|
||||
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
|
||||
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
|
||||
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
|
||||
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
|
||||
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
|
||||
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
|
||||
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
|
||||
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
|
||||
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
|
||||
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
|
||||
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
|
||||
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
|
||||
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
|
||||
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
|
||||
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
|
||||
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
|
||||
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
|
||||
],
|
||||
"version": "==1.15.1"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5",
|
||||
"sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b",
|
||||
"sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42",
|
||||
"sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d",
|
||||
"sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b",
|
||||
"sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a",
|
||||
"sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59",
|
||||
"sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154",
|
||||
"sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1",
|
||||
"sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c",
|
||||
"sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a",
|
||||
"sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d",
|
||||
"sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6",
|
||||
"sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b",
|
||||
"sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b",
|
||||
"sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783",
|
||||
"sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5",
|
||||
"sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918",
|
||||
"sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555",
|
||||
"sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639",
|
||||
"sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786",
|
||||
"sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e",
|
||||
"sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed",
|
||||
"sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820",
|
||||
"sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8",
|
||||
"sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3",
|
||||
"sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541",
|
||||
"sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14",
|
||||
"sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be",
|
||||
"sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e",
|
||||
"sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76",
|
||||
"sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b",
|
||||
"sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c",
|
||||
"sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b",
|
||||
"sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3",
|
||||
"sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc",
|
||||
"sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6",
|
||||
"sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59",
|
||||
"sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4",
|
||||
"sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d",
|
||||
"sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d",
|
||||
"sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3",
|
||||
"sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a",
|
||||
"sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea",
|
||||
"sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6",
|
||||
"sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e",
|
||||
"sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603",
|
||||
"sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24",
|
||||
"sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a",
|
||||
"sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58",
|
||||
"sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678",
|
||||
"sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a",
|
||||
"sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c",
|
||||
"sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6",
|
||||
"sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18",
|
||||
"sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174",
|
||||
"sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317",
|
||||
"sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f",
|
||||
"sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc",
|
||||
"sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837",
|
||||
"sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41",
|
||||
"sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c",
|
||||
"sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579",
|
||||
"sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753",
|
||||
"sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8",
|
||||
"sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291",
|
||||
"sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087",
|
||||
"sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866",
|
||||
"sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3",
|
||||
"sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d",
|
||||
"sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1",
|
||||
"sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca",
|
||||
"sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e",
|
||||
"sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db",
|
||||
"sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72",
|
||||
"sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d",
|
||||
"sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc",
|
||||
"sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539",
|
||||
"sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d",
|
||||
"sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af",
|
||||
"sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b",
|
||||
"sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602",
|
||||
"sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f",
|
||||
"sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478",
|
||||
"sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c",
|
||||
"sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e",
|
||||
"sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479",
|
||||
"sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7",
|
||||
"sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.3"
|
||||
},
|
||||
"colorama": {
|
||||
"hashes": [
|
||||
"sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44",
|
||||
"sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
|
||||
"version": "==0.4.6"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:04481245ef966fbd24ae9b9e537ce899ae584d521dfbe78f89cad003c38ca2ab",
|
||||
"sha256:0c45948f613d5d18c9ec5eaa203ce06a653334cf1bd47c783a12d0dd4fd9c851",
|
||||
"sha256:10188fe543560ec4874f974b5305cd1a8bdcfa885ee00ea3a03733464c4ca265",
|
||||
"sha256:218fe982371ac7387304153ecd51205f14e9d731b34fb0568181abaf7b443ba0",
|
||||
"sha256:29571503c37f2ef2138a306d23e7270687c0efb9cab4bd8038d609b5c2393a3a",
|
||||
"sha256:2a60d6513781e87047c3e630b33b4d1e89f39836dac6e069ffee28c4786715f5",
|
||||
"sha256:2bf1d5f2084c3932b56b962a683074a3692bce7cabd3aa023c987a2a8e7612f6",
|
||||
"sha256:3164d31078fa9efe406e198aecd2a02d32a62fecbdef74f76dad6a46c7e48311",
|
||||
"sha256:32df215215f3af2c1617a55dbdfb403b772d463d54d219985ac7cd3bf124cada",
|
||||
"sha256:33d1ae9d4079e05ac4cc1ef9e20c648f5afabf1a92adfaf2ccf509c50b85717f",
|
||||
"sha256:33ff26d0f6cc3ca8de13d14fde1ff8efe1456b53e3f0273e63cc8b3c84a063d8",
|
||||
"sha256:38da2db80cc505a611938d8624801158e409928b136c8916cd2e203970dde4dc",
|
||||
"sha256:3b155caf3760408d1cb903b21e6a97ad4e2bdad43cbc265e3ce0afb8e0057e73",
|
||||
"sha256:3b946bbcd5a8231383450b195cfb58cb01cbe7f8949f5758566b881df4b33baf",
|
||||
"sha256:3baf5f126f30781b5e93dbefcc8271cb2491647f8283f20ac54d12161dff080e",
|
||||
"sha256:4b14d5e09c656de5038a3f9bfe5228f53439282abcab87317c9f7f1acb280352",
|
||||
"sha256:51b236e764840a6df0661b67e50697aaa0e7d4124ca95e5058fa3d7cbc240b7c",
|
||||
"sha256:63ffd21aa133ff48c4dff7adcc46b7ec8b565491bfc371212122dd999812ea1c",
|
||||
"sha256:6a43c7823cd7427b4ed763aa7fb63901ca8288591323b58c9cd6ec31ad910f3c",
|
||||
"sha256:755e89e32376c850f826c425ece2c35a4fc266c081490eb0a841e7c1cb0d3bda",
|
||||
"sha256:7a726d742816cb3a8973c8c9a97539c734b3a309345236cd533c4883dda05b8d",
|
||||
"sha256:7c7c0d0827e853315c9bbd43c1162c006dd808dbbe297db7ae66cd17b07830f0",
|
||||
"sha256:7ed681b0f8e8bcbbffa58ba26fcf5dbc8f79e7997595bf071ed5430d8c08d6f3",
|
||||
"sha256:7ee5c9bb51695f80878faaa5598040dd6c9e172ddcf490382e8aedb8ec3fec8d",
|
||||
"sha256:8361be1c2c073919500b6601220a6f2f98ea0b6d2fec5014c1d9cfa23dd07038",
|
||||
"sha256:8ae125d1134bf236acba8b83e74c603d1b30e207266121e76484562bc816344c",
|
||||
"sha256:9817733f0d3ea91bea80de0f79ef971ae94f81ca52f9b66500c6a2fea8e4b4f8",
|
||||
"sha256:98b85dd86514d889a2e3dd22ab3c18c9d0019e696478391d86708b805f4ea0fa",
|
||||
"sha256:9ccb092c9ede70b2517a57382a601619d20981f56f440eae7e4d7eaafd1d1d09",
|
||||
"sha256:9d58885215094ab4a86a6aef044e42994a2bd76a446dc59b352622655ba6621b",
|
||||
"sha256:b643cb30821e7570c0aaf54feaf0bfb630b79059f85741843e9dc23f33aaca2c",
|
||||
"sha256:bc7c85a150501286f8b56bd8ed3aa4093f4b88fb68c0843d21ff9656f0009d6a",
|
||||
"sha256:beeb129cacea34490ffd4d6153af70509aa3cda20fdda2ea1a2be870dfec8d52",
|
||||
"sha256:c31b75ae466c053a98bf26843563b3b3517b8f37da4d47b1c582fdc703112bc3",
|
||||
"sha256:c4e4881fa9e9667afcc742f0c244d9364d197490fbc91d12ac3b5de0bf2df146",
|
||||
"sha256:c5b15ed7644ae4bee0ecf74fee95808dcc34ba6ace87e8dfbf5cb0dc20eab45a",
|
||||
"sha256:d12d076582507ea460ea2a89a8c85cb558f83406c8a41dd641d7be9a32e1274f",
|
||||
"sha256:d248cd4a92065a4d4543b8331660121b31c4148dd00a691bfb7a5cdc7483cfa4",
|
||||
"sha256:d47dd659a4ee952e90dc56c97d78132573dc5c7b09d61b416a9deef4ebe01a0c",
|
||||
"sha256:d4a5a5879a939cb84959d86869132b00176197ca561c664fc21478c1eee60d75",
|
||||
"sha256:da9b41d4539eefd408c46725fb76ecba3a50a3367cafb7dea5f250d0653c1040",
|
||||
"sha256:db61a79c07331e88b9a9974815c075fbd812bc9dbc4dc44b366b5368a2936063",
|
||||
"sha256:ddb726cb861c3117a553f940372a495fe1078249ff5f8a5478c0576c7be12050",
|
||||
"sha256:ded59300d6330be27bc6cf0b74b89ada58069ced87c48eaf9344e5e84b0072f7",
|
||||
"sha256:e2617759031dae1bf183c16cef8fcfb3de7617f394c813fa5e8e46e9b82d4222",
|
||||
"sha256:e5cdbb5cafcedea04924568d990e20ce7f1945a1dd54b560f879ee2d57226912",
|
||||
"sha256:ec8e767f13be637d056f7e07e61d089e555f719b387a7070154ad80a0ff31801",
|
||||
"sha256:ef382417db92ba23dfb5864a3fc9be27ea4894e86620d342a116b243ade5d35d",
|
||||
"sha256:f2cba5c6db29ce991029b5e4ac51eb36774458f0a3b8d3137241b32d1bb91f06",
|
||||
"sha256:f5b4198d85a3755d27e64c52f8c95d6333119e49fd001ae5798dac872c95e0f8",
|
||||
"sha256:ffeeb38ee4a80a30a6877c5c4c359e5498eec095878f1581453202bfacc8fbc2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.1.0"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b",
|
||||
"sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f",
|
||||
"sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190",
|
||||
"sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f",
|
||||
"sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f",
|
||||
"sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb",
|
||||
"sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c",
|
||||
"sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773",
|
||||
"sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72",
|
||||
"sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8",
|
||||
"sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717",
|
||||
"sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9",
|
||||
"sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856",
|
||||
"sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96",
|
||||
"sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288",
|
||||
"sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39",
|
||||
"sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e",
|
||||
"sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce",
|
||||
"sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1",
|
||||
"sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de",
|
||||
"sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df",
|
||||
"sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf",
|
||||
"sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==39.0.0"
|
||||
},
|
||||
"distlib": {
|
||||
"hashes": [
|
||||
"sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46",
|
||||
"sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"
|
||||
],
|
||||
"version": "==0.3.6"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6",
|
||||
"sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.19"
|
||||
},
|
||||
"exceptiongroup": {
|
||||
"hashes": [
|
||||
"sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e",
|
||||
"sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"
|
||||
],
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==1.1.0"
|
||||
},
|
||||
"filelock": {
|
||||
"hashes": [
|
||||
"sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de",
|
||||
"sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.9.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.4"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad",
|
||||
"sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"iniconfig": {
|
||||
"hashes": [
|
||||
"sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3",
|
||||
"sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"jaraco.classes": {
|
||||
"hashes": [
|
||||
"sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158",
|
||||
"sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.2.3"
|
||||
},
|
||||
"jeepney": {
|
||||
"hashes": [
|
||||
"sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806",
|
||||
"sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==0.8.0"
|
||||
},
|
||||
"keyring": {
|
||||
"hashes": [
|
||||
"sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd",
|
||||
"sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.13.1"
|
||||
},
|
||||
"markdown-it-py": {
|
||||
"hashes": [
|
||||
"sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27",
|
||||
"sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"mdurl": {
|
||||
"hashes": [
|
||||
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
|
||||
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.1.2"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41",
|
||||
"sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==9.0.0"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2",
|
||||
"sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.0"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229",
|
||||
"sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.11.0"
|
||||
},
|
||||
"pkginfo": {
|
||||
"hashes": [
|
||||
"sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546",
|
||||
"sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.9.6"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490",
|
||||
"sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.6.2"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.21"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297",
|
||||
"sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.14.0"
|
||||
},
|
||||
"pyproject-api": {
|
||||
"hashes": [
|
||||
"sha256:0962df21f3e633b8ddb9567c011e6c1b3dcdfc31b7860c0ede7e24c5a1200fbe",
|
||||
"sha256:4c111277dfb96bcd562c6245428f27250b794bfe3e210b8714c4f893952f2c17"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5",
|
||||
"sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.2.1"
|
||||
},
|
||||
"readme-renderer": {
|
||||
"hashes": [
|
||||
"sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273",
|
||||
"sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==37.3"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa",
|
||||
"sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||
"version": "==2.28.2"
|
||||
},
|
||||
"requests-toolbelt": {
|
||||
"hashes": [
|
||||
"sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7",
|
||||
"sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.1"
|
||||
},
|
||||
"rfc3986": {
|
||||
"hashes": [
|
||||
"sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd",
|
||||
"sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.0.0"
|
||||
},
|
||||
"rich": {
|
||||
"hashes": [
|
||||
"sha256:125d96d20c92b946b983d0d392b84ff945461e5a06d3867e9f9e575f8697b67f",
|
||||
"sha256:8aa57747f3fc3e977684f0176a88e789be314a99f99b43b75d1e9cb5dc6db9e9"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==13.3.1"
|
||||
},
|
||||
"secretstorage": {
|
||||
"hashes": [
|
||||
"sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77",
|
||||
"sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"
|
||||
],
|
||||
"markers": "sys_platform == 'linux'",
|
||||
"version": "==3.3.3"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_full_version < '3.11.0a7'",
|
||||
"version": "==2.0.1"
|
||||
},
|
||||
"tox": {
|
||||
"hashes": [
|
||||
"sha256:258895ba5de919490c03ef97467c4c8c42954537dfd96ae72cc2fb63dac67cf0",
|
||||
"sha256:3d8a8dd8a5afdc0d37af3e2b4959e427fe22530d0aa599baf0120e144b3defa3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.4.2"
|
||||
},
|
||||
"twine": {
|
||||
"hashes": [
|
||||
"sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8",
|
||||
"sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72",
|
||||
"sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==1.26.14"
|
||||
},
|
||||
"virtualenv": {
|
||||
"hashes": [
|
||||
"sha256:ce3b1684d6e1a20a3e5ed36795a97dfc6af29bc3970ca8dab93e11ac6094b3c4",
|
||||
"sha256:f8b927684efc6f1cc206c9db297a570ab9ad0e51c16fa9e45487d36d1905c058"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==20.17.1"
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||
],
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:73efd63936398aac78fd92b6f4865190119d6c91b531532e798977ea8dd402eb",
|
||||
"sha256:9eb0a4c5feab9b08871db0d672745b53450d7f26992fd1e4653aa43345e97b86"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.12.0"
|
||||
}
|
||||
}
|
||||
}
|
10
README.rst
10
README.rst
|
@ -8,16 +8,6 @@ directives to collect information regarding IP addresses in IP ranges.
|
|||
|
||||
.. _Sphinx: http://www.sphinx-doc.org/
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
The extension is developed in a git repository that can be cloned by running::
|
||||
|
||||
git clone https://git.dittberner.info/sphinxext-ip.git
|
||||
|
||||
A repository browser is available at
|
||||
https://git.dittberner.info/?p=sphinxext-ip.git.
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
__import__('pkg_resources').declare_namespace(__name__)
|
|
@ -1 +0,0 @@
|
|||
__import__('pkg_resources').declare_namespace(__name__)
|
|
@ -1,349 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jandd.sphinxext.ip
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The IP domain.
|
||||
|
||||
:copyright: Copyright (c) 2016 Jan Dittberner
|
||||
:license: GPLv3+, see COPYING file for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ipcalc import Network
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.domains import Domain, ObjType
|
||||
from sphinx.environment import NoUri
|
||||
from sphinx.locale import l_
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
__version__ = '0.2.2'
|
||||
|
||||
|
||||
def ip_object_anchor(typ, path):
|
||||
path = re.sub(r'[.:/]', '-', path)
|
||||
return typ.lower() + '-' + path
|
||||
|
||||
|
||||
class ip_node(nodes.Inline, nodes.TextElement):
|
||||
pass
|
||||
|
||||
|
||||
class ip_range(nodes.General, nodes.Element):
|
||||
pass
|
||||
|
||||
|
||||
class IPXRefRole(XRefRole):
|
||||
"""
|
||||
Cross referencing role for the IP domain.
|
||||
"""
|
||||
def __init__(self, method, index_type, **kwargs):
|
||||
self.method = method
|
||||
self.index_type = index_type
|
||||
innernodeclass = None
|
||||
if method in ('v4', 'v6'):
|
||||
innernodeclass = ip_node
|
||||
super(IPXRefRole, self).__init__(
|
||||
innernodeclass=innernodeclass, **kwargs)
|
||||
|
||||
def __call__(self, typ, rawtext, text, lineno, inliner,
|
||||
options={}, content=[]):
|
||||
try:
|
||||
Network(text)
|
||||
except ValueError as e:
|
||||
env = inliner.document.settings.env
|
||||
env.warn(env.docname, "invalid ip address/range %s" % text, lineno)
|
||||
return [nodes.literal(text, text), []]
|
||||
return super(IPXRefRole, self).__call__(
|
||||
typ, rawtext, text, lineno, inliner, options, content)
|
||||
|
||||
def process_link(self, env, refnode, has_explicit_title, title, target):
|
||||
domaindata = env.domaindata['ip']
|
||||
domaindata[self.method][target] = (target, refnode)
|
||||
return title, target
|
||||
|
||||
def result_nodes(self, document, env, node, is_ref):
|
||||
try:
|
||||
node['typ'] = self.method
|
||||
indexnode = addnodes.index()
|
||||
targetid = 'index-%s' % env.new_serialno('index')
|
||||
targetnode = nodes.target('', '', ids=[targetid])
|
||||
doctitle = document.traverse(nodes.title)[0].astext()
|
||||
idxtext = "%s; %s" % (node.astext(), doctitle)
|
||||
idxtext2 = "%s; %s" % (self.index_type, node.astext())
|
||||
indexnode['entries'] = [
|
||||
('single', idxtext, targetid, '', None),
|
||||
('single', idxtext2, targetid, '', None),
|
||||
]
|
||||
return [indexnode, targetnode, node], []
|
||||
except KeyError as e:
|
||||
return [node], [e.args[0]]
|
||||
|
||||
|
||||
class IPRange(Directive):
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = False
|
||||
option_spec = {}
|
||||
|
||||
def handle_rangespec(self, node):
|
||||
titlenode = nodes.title()
|
||||
node.append(titlenode)
|
||||
titlenode.append(nodes.inline('', self.get_prefix_title()))
|
||||
titlenode.append(nodes.literal('', self.rangespec))
|
||||
ids = ip_object_anchor(self.typ, self.rangespec)
|
||||
node['ids'].append(ids)
|
||||
self.env.domaindata[self.domain][self.typ][ids] = (
|
||||
self.env.docname,
|
||||
self.options.get('synopsis', ''))
|
||||
return ids
|
||||
|
||||
def run(self):
|
||||
if ':' in self.name:
|
||||
self.domain, self.objtype = self.name.split(':', 1)
|
||||
else:
|
||||
self.domain, self.objtype = '', self.name
|
||||
self.env = self.state.document.settings.env
|
||||
self.rangespec = self.arguments[0]
|
||||
node = nodes.section()
|
||||
name = self.handle_rangespec(node)
|
||||
if self.env.docname in self.env.titles:
|
||||
doctitle = self.env.titles[self.env.docname]
|
||||
else:
|
||||
doctitle = self.state.document.traverse(nodes.title)[0].astext()
|
||||
idx_text = "%s; %s" % (self.rangespec, doctitle)
|
||||
self.indexnode = addnodes.index(entries=[
|
||||
('single', idx_text, name, False, ''),
|
||||
('single', self.get_index_text(), name, False, '')
|
||||
])
|
||||
|
||||
if self.content:
|
||||
contentnode = nodes.paragraph('')
|
||||
node.append(contentnode)
|
||||
self.state.nested_parse(
|
||||
self.content, self.content_offset, contentnode)
|
||||
|
||||
iprange = ip_range()
|
||||
node.append(iprange)
|
||||
iprange['rangespec'] = self.rangespec
|
||||
return [self.indexnode, node]
|
||||
|
||||
|
||||
class IPv4Range(IPRange):
|
||||
typ = 'v4range'
|
||||
|
||||
def get_prefix_title(self):
|
||||
return l_('IPv4 address range ')
|
||||
|
||||
def get_index_text(self):
|
||||
return "%s; %s" % (l_('IPv4 range'), self.rangespec)
|
||||
|
||||
|
||||
class IPv6Range(IPRange):
|
||||
typ = 'v6range'
|
||||
|
||||
def get_prefix_title(self):
|
||||
return l_('IPv6 address range ')
|
||||
|
||||
def get_index_text(self):
|
||||
return "%s; %s" % (l_('IPv6 range'), self.rangespec)
|
||||
|
||||
|
||||
class IPDomain(Domain):
|
||||
"""
|
||||
IP address and range domain.
|
||||
"""
|
||||
name = 'ip'
|
||||
label = 'IP addresses and ranges.'
|
||||
|
||||
object_types = {
|
||||
'v4': ObjType(l_('v4'), 'v4', 'obj'),
|
||||
'v6': ObjType(l_('v6'), 'v6', 'obj'),
|
||||
'v4range': ObjType(l_('v4range'), 'v4range', 'obj'),
|
||||
'v6range': ObjType(l_('v6range'), 'v6range', 'obj'),
|
||||
}
|
||||
|
||||
directives = {
|
||||
'v4range': IPv4Range,
|
||||
'v6range': IPv6Range,
|
||||
}
|
||||
|
||||
roles = {
|
||||
'v4': IPXRefRole('v4', l_('IPv4 address')),
|
||||
'v6': IPXRefRole('v6', l_('IPv6 address')),
|
||||
'v4range': IPXRefRole('v4range', l_('IPv4 range')),
|
||||
'v6range': IPXRefRole('v6range', l_('IPv6 range')),
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
'v4': {},
|
||||
'v6': {},
|
||||
'v4range': {},
|
||||
'v6range': {},
|
||||
'ips': [],
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
to_remove = []
|
||||
for key, value in self.data['v4range'].items():
|
||||
if docname == value[0]:
|
||||
to_remove.append(key)
|
||||
for key in to_remove:
|
||||
del self.data['v4range'][key]
|
||||
|
||||
to_remove = []
|
||||
for key, value in self.data['v6range'].items():
|
||||
if docname == value[0]:
|
||||
to_remove.append(key)
|
||||
for key in to_remove:
|
||||
del self.data['v6range'][key]
|
||||
self.data['ips'] = [
|
||||
item for item in self.data['ips'] if item['docname'] != docname
|
||||
]
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder, typ, target, node,
|
||||
contnode):
|
||||
key = ip_object_anchor(typ, target)
|
||||
try:
|
||||
info = self.data[typ][key]
|
||||
except KeyError:
|
||||
text = contnode.rawsource
|
||||
role = self.roles.get(typ)
|
||||
if role is None:
|
||||
return None
|
||||
resnode = role.result_nodes(env.get_doctree(fromdocname),
|
||||
env, node, True)[0][2]
|
||||
if isinstance(resnode, addnodes.pending_xref):
|
||||
text = node[0][0]
|
||||
reporter = env.get_doctree(fromdocname).reporter
|
||||
reporter.warning('Cannot resolve reference to %r' % text,
|
||||
line=node.line)
|
||||
return node.children
|
||||
return resnode
|
||||
else:
|
||||
title = typ.upper() + ' ' + target
|
||||
return make_refnode(builder, fromdocname, info[0], key,
|
||||
contnode, title)
|
||||
|
||||
@property
|
||||
def items(self):
|
||||
return dict((key, self.data[key]) for key in self.object_types)
|
||||
|
||||
def get_objects(self):
|
||||
for typ, items in self.items.items():
|
||||
for path, info in items.items():
|
||||
anchor = ip_object_anchor(typ, path)
|
||||
yield (path, path, typ, info[0], anchor, 1)
|
||||
|
||||
|
||||
def process_ips(app, doctree):
|
||||
env = app.builder.env
|
||||
domaindata = env.domaindata[IPDomain.name]
|
||||
|
||||
for node in doctree.traverse(ip_node):
|
||||
ip = node.astext()
|
||||
domaindata['ips'].append({
|
||||
'docname': env.docname,
|
||||
'source': node.parent.source or env.doc2path(env.docname),
|
||||
'lineno': node.parent.line,
|
||||
'ip': ip,
|
||||
'typ': node.parent['typ'],
|
||||
})
|
||||
replacement = nodes.literal(ip, ip)
|
||||
node.replace_self(replacement)
|
||||
|
||||
|
||||
def sort_ip(item):
|
||||
return Network(item).ip
|
||||
|
||||
|
||||
def create_table_row(rowdata):
|
||||
row = nodes.row()
|
||||
for cell in rowdata:
|
||||
entry = nodes.entry()
|
||||
row += entry
|
||||
entry += cell
|
||||
return row
|
||||
|
||||
|
||||
def process_ip_nodes(app, doctree, fromdocname):
|
||||
env = app.builder.env
|
||||
domaindata = env.domaindata[IPDomain.name]
|
||||
|
||||
header = (l_('IP address'), l_('Used by'))
|
||||
colwidths = (1, 3)
|
||||
|
||||
for node in doctree.traverse(ip_range):
|
||||
content = []
|
||||
net = Network(node['rangespec'])
|
||||
ips = {}
|
||||
for key, value in [
|
||||
(ip_info['ip'], ip_info) for ip_info in
|
||||
domaindata['ips'] if ip_info['ip'] in net
|
||||
]:
|
||||
addrlist = ips.get(key, [])
|
||||
addrlist.append(value)
|
||||
ips[key] = addrlist
|
||||
if ips:
|
||||
table = nodes.table()
|
||||
tgroup = nodes.tgroup(cols=len(header))
|
||||
table += tgroup
|
||||
for colwidth in colwidths:
|
||||
tgroup += nodes.colspec(colwidth=colwidth)
|
||||
thead = nodes.thead()
|
||||
tgroup += thead
|
||||
thead += create_table_row([
|
||||
nodes.paragraph(text=label) for label in header])
|
||||
tbody = nodes.tbody()
|
||||
tgroup += tbody
|
||||
for ip, ip_info in [
|
||||
(ip, ips[ip]) for ip in sorted(ips, key=sort_ip)
|
||||
]:
|
||||
para = nodes.paragraph()
|
||||
para += nodes.literal('', ip)
|
||||
refnode = nodes.paragraph()
|
||||
refuris = set()
|
||||
refnodes = []
|
||||
for item in ip_info:
|
||||
ids = ip_object_anchor(item['typ'], item['ip'])
|
||||
if ids not in para['ids']:
|
||||
para['ids'].append(ids)
|
||||
|
||||
domaindata[item['typ']][ids] = (fromdocname, '')
|
||||
newnode = nodes.reference('', '', internal=True)
|
||||
try:
|
||||
newnode['refuri'] = app.builder.get_relative_uri(
|
||||
fromdocname, item['docname'])
|
||||
if newnode['refuri'] in refuris:
|
||||
continue
|
||||
refuris.add(newnode['refuri'])
|
||||
except NoUri:
|
||||
pass
|
||||
title = env.titles[item['docname']]
|
||||
innernode = nodes.Text(title.astext())
|
||||
newnode.append(innernode)
|
||||
refnodes.append(newnode)
|
||||
for count in range(len(refnodes)):
|
||||
refnode.append(refnodes[count])
|
||||
if count < len(refnodes) - 1:
|
||||
refnode.append(nodes.Text(", "))
|
||||
tbody += create_table_row([para, refnode])
|
||||
content.append(table)
|
||||
else:
|
||||
para = nodes.paragraph(l_('No IP addresses in this range'))
|
||||
content.append(para)
|
||||
node.replace_self(content)
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_domain(IPDomain)
|
||||
app.connect('doctree-read', process_ips)
|
||||
app.connect('doctree-resolved', process_ip_nodes)
|
||||
return {'version': __version__}
|
438
jandd/sphinxext/ip/__init__.py
Normal file
438
jandd/sphinxext/ip/__init__.py
Normal file
|
@ -0,0 +1,438 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
jandd.sphinxext.ip
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The IP domain.
|
||||
|
||||
:copyright: Copyright (c) Jan Dittberner
|
||||
:license: GPLv3+, see COPYING file for details.
|
||||
"""
|
||||
__version__ = "0.6.1"
|
||||
|
||||
import ipaddress
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, List, Optional, Tuple, Any, cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Element
|
||||
from sphinx import addnodes
|
||||
from sphinx.addnodes import desc_signature, pending_xref
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.directives import ObjectDescription
|
||||
from sphinx.domains import Domain, ObjType
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.errors import NoUri
|
||||
from sphinx.locale import _
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.nodes import make_refnode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ip_range(nodes.General, nodes.Element):
|
||||
pass
|
||||
|
||||
|
||||
class IPRangeDirective(ObjectDescription):
|
||||
"""A custom directive that describes an IP address range."""
|
||||
|
||||
has_content = True
|
||||
required_arguments = 1
|
||||
title_prefix: str = ""
|
||||
range_spec: str = ""
|
||||
|
||||
def get_title_prefix(self) -> str:
|
||||
return self.title_prefix
|
||||
|
||||
def handle_signature(self, sig: str, sig_node: desc_signature) -> str:
|
||||
sig_node += addnodes.desc_name(
|
||||
text="{} {}".format(self.get_title_prefix(), sig)
|
||||
)
|
||||
self.range_spec = sig
|
||||
return sig
|
||||
|
||||
def transform_content(self, content_node: addnodes.desc_content) -> None:
|
||||
ip_range_node = ip_range()
|
||||
ip_range_node["range_spec"] = self.range_spec
|
||||
content_node += ip_range_node
|
||||
|
||||
|
||||
class IPV4RangeDirective(IPRangeDirective):
|
||||
title_prefix = _("IPv4 range")
|
||||
|
||||
def add_target_and_index(self, name, sig: str, sig_node: desc_signature) -> None:
|
||||
anchor = "ip-ipv4range-{0}".format(sig)
|
||||
sig_node["ids"].append(anchor)
|
||||
ips = cast(IPDomain, self.env.get_domain("ip"))
|
||||
ips.add_ip4_range(sig)
|
||||
idx_text = "{}; {}".format(self.title_prefix, name)
|
||||
self.indexnode["entries"] = [
|
||||
("single", idx_text, anchor, "", None),
|
||||
]
|
||||
|
||||
|
||||
class IPV6RangeDirective(IPRangeDirective):
|
||||
title_prefix = _("IPv6 range")
|
||||
|
||||
def add_target_and_index(self, name, sig: str, signode: desc_signature) -> None:
|
||||
anchor = "ip-ipv6range-{0}".format(sig)
|
||||
signode["ids"].append(anchor)
|
||||
ips = cast(IPDomain, self.env.get_domain("ip"))
|
||||
ips.add_ip6_range(sig)
|
||||
idx_text = "{}; {}".format(self.title_prefix, name)
|
||||
self.indexnode["entries"] = [
|
||||
("single", idx_text, anchor, "", None),
|
||||
]
|
||||
|
||||
|
||||
class IPXRefRole(XRefRole):
|
||||
def __init__(self, index_type):
|
||||
self.index_type = index_type
|
||||
super().__init__()
|
||||
|
||||
def process_link(
|
||||
self,
|
||||
env: BuildEnvironment,
|
||||
refnode: nodes.Element,
|
||||
has_explicit_title: bool,
|
||||
title: str,
|
||||
target: str,
|
||||
) -> Tuple[str, str]:
|
||||
refnode.attributes.update(env.ref_context)
|
||||
|
||||
ips = cast(IPDomain, env.get_domain("ip"))
|
||||
if refnode["reftype"] == "v4":
|
||||
ips.add_ip4_address_reference(target)
|
||||
elif refnode["reftype"] == "v6":
|
||||
ips.add_ip6_address_reference(target)
|
||||
elif refnode["reftype"] == "v4range":
|
||||
ips.add_ip4_range_reference(target)
|
||||
elif refnode["reftype"] == "v6range":
|
||||
ips.add_ip6_range_reference(target)
|
||||
|
||||
return title, target
|
||||
|
||||
def result_nodes(
|
||||
self,
|
||||
document: nodes.document,
|
||||
env: BuildEnvironment,
|
||||
node: nodes.Element,
|
||||
is_ref: bool,
|
||||
) -> Tuple[List[nodes.Node], List[nodes.system_message]]:
|
||||
node_list, message = super().result_nodes(document, env, node, is_ref)
|
||||
|
||||
ip = cast(IPDomain, env.get_domain("ip"))
|
||||
if self.reftype in ["v4", "v6"] and self.target not in ip.data["ip_dict"]:
|
||||
return node_list, message
|
||||
if (
|
||||
self.reftype in ["v4range", "v6range"]
|
||||
and self.target not in ip.data["ranges"]
|
||||
):
|
||||
return node_list, message
|
||||
|
||||
index_node = addnodes.index()
|
||||
target_id = "ip-{}-{}".format(self.reftype, env.new_serialno("index"))
|
||||
|
||||
if self.reftype in ["v4", "v6"]:
|
||||
ip.add_ip_address_anchor(self.target, env.docname, target_id)
|
||||
|
||||
target_node = nodes.target("", "", ids=[target_id])
|
||||
doc_title = next(d for d in document.findall(nodes.title)).astext()
|
||||
|
||||
node_text = node.astext()
|
||||
|
||||
idx_text = "{}; {}".format(node_text, doc_title)
|
||||
idx_text_2 = "{}; {}".format(self.index_type, node.astext())
|
||||
index_node["entries"] = [
|
||||
("single", idx_text, target_id, "", None),
|
||||
("single", idx_text_2, target_id, "", None),
|
||||
]
|
||||
|
||||
node_list.insert(0, target_node)
|
||||
node_list.insert(0, index_node)
|
||||
return node_list, message
|
||||
|
||||
|
||||
class IPDomain(Domain):
|
||||
"""Custom domain for IP addresses and ranges."""
|
||||
|
||||
def merge_domaindata(self, docnames: list[str], otherdata: dict) -> None:
|
||||
# TODO: implement merge_domaindata
|
||||
print(
|
||||
f"merge_domaindata called for docnames: {docnames} with otherdata: {otherdata}"
|
||||
)
|
||||
|
||||
def resolve_any_xref(
|
||||
self,
|
||||
env: BuildEnvironment,
|
||||
fromdocname: str,
|
||||
builder: Builder,
|
||||
target: str,
|
||||
node: pending_xref,
|
||||
contnode: Element,
|
||||
) -> list[tuple[str, Element]]:
|
||||
# TODO: implement resolve_any_xref
|
||||
print("resolve_any_xref called")
|
||||
return []
|
||||
|
||||
name = "ip"
|
||||
label = "IP addresses and ranges."
|
||||
|
||||
object_types = {
|
||||
"v4": ObjType(_("v4"), "v4", "obj"),
|
||||
"v6": ObjType(_("v6"), "v6", "obj"),
|
||||
"v4range": ObjType(_("v4range"), "v4range", "obj"),
|
||||
"v6range": ObjType(_("v6range"), "v6range", "obj"),
|
||||
}
|
||||
|
||||
directives = {
|
||||
"v4range": IPV4RangeDirective,
|
||||
"v6range": IPV6RangeDirective,
|
||||
}
|
||||
|
||||
roles = {
|
||||
"v4": IPXRefRole("IPv4 address"),
|
||||
"v6": IPXRefRole("IPv6 address"),
|
||||
"v4range": IPXRefRole("IPv4 range"),
|
||||
"v6range": IPXRefRole("IPv6 range"),
|
||||
}
|
||||
|
||||
initial_data = {
|
||||
"range_nodes": [],
|
||||
"ip_refs": defaultdict(list),
|
||||
"range_refs": [],
|
||||
"ranges": defaultdict(list),
|
||||
"ip_dict": {},
|
||||
}
|
||||
|
||||
def get_full_qualified_name(self, node: nodes.Element) -> Optional[str]:
|
||||
return "{}.{}".format("ip", node)
|
||||
|
||||
def get_objects(self) -> Iterable[Tuple[str, str, str, str, str, int]]:
|
||||
for obj in self.data["range_nodes"]:
|
||||
yield obj
|
||||
|
||||
def add_ip4_range(self, sig: str):
|
||||
logger.debug("add_ip4_range: %s", sig)
|
||||
self._add_ip_range("v4", sig)
|
||||
|
||||
def add_ip6_range(self, sig: str):
|
||||
logger.debug("add_ip6_range: %s", sig)
|
||||
self._add_ip_range("v6", sig)
|
||||
|
||||
def _add_ip_range(self, family: str, sig: str):
|
||||
name = "ip{}range.{}".format(family, sig)
|
||||
anchor = "ip-ip{}range-{}".format(family, sig)
|
||||
try:
|
||||
new_ip_range = ipaddress.ip_network(sig)
|
||||
self.data["range_nodes"].append(
|
||||
(name, family, sig, self.env.docname, anchor, 0)
|
||||
)
|
||||
self.data["ranges"][sig].append((new_ip_range, self.env.docname, anchor))
|
||||
except ValueError as e:
|
||||
logger.error("invalid ip range address '%s': %s", sig, e)
|
||||
|
||||
def add_ip4_address_reference(self, ip_address: str):
|
||||
logger.debug("add_ip4_address_reference")
|
||||
self._add_ip_address_reference("v4", ip_address)
|
||||
|
||||
def add_ip6_address_reference(self, ip_address: str):
|
||||
logger.debug("add_ip4_address_reference")
|
||||
self._add_ip_address_reference("v6", ip_address)
|
||||
|
||||
def _add_ip_address_reference(self, family, sig):
|
||||
try:
|
||||
self.data["ip_dict"][sig] = ipaddress.ip_address(sig)
|
||||
except ValueError as e:
|
||||
logger.error("invalid ip address '%s': %s", sig, e)
|
||||
|
||||
def add_ip_address_anchor(self, sig, docname, anchor):
|
||||
try:
|
||||
ip = ipaddress.ip_address(sig)
|
||||
self.data["ip_refs"][sig].append((ip, docname, anchor))
|
||||
except ValueError as e:
|
||||
logger.error("invalid ip address '%s': %s", sig, e)
|
||||
|
||||
def add_ip4_range_reference(self, ip_range: str):
|
||||
logger.debug("add_ip4_range_reference")
|
||||
self._add_ip_range_reference("v4", ip_range)
|
||||
|
||||
def add_ip6_range_reference(self, ip_range: str):
|
||||
logger.debug("add_ip6_address_reference")
|
||||
self._add_ip_range_reference("v6", ip_range)
|
||||
|
||||
def _add_ip_range_reference(self, family, sig):
|
||||
name = "iprange{}.{}".format(family, sig)
|
||||
anchor = "ip-iprange{}-{}".format(family, sig)
|
||||
try:
|
||||
ip_range = ipaddress.ip_network(sig)
|
||||
self.data["range_refs"].append(
|
||||
(
|
||||
name,
|
||||
sig,
|
||||
"IP{} range".format(family),
|
||||
str(ip_range),
|
||||
self.env.docname,
|
||||
anchor,
|
||||
0,
|
||||
)
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error("invalid ip range '%s': %s", sig, e)
|
||||
|
||||
def resolve_xref(
|
||||
self,
|
||||
env: BuildEnvironment,
|
||||
fromdocname: str,
|
||||
builder: Builder,
|
||||
typ: str,
|
||||
target: str,
|
||||
node: pending_xref,
|
||||
contnode: nodes.Element,
|
||||
) -> Optional[nodes.Element]:
|
||||
match = []
|
||||
|
||||
def address_tuple(docname, anchor, ip_range: Any) -> Tuple[str, str, str]:
|
||||
return (
|
||||
docname,
|
||||
anchor,
|
||||
_("IPv{0} range {1}".format(ip_range.version, ip_range.compressed)),
|
||||
)
|
||||
|
||||
if typ in ("v4", "v6"):
|
||||
# reference to IP range
|
||||
if target not in self.data["ip_dict"]:
|
||||
# invalid ip address
|
||||
raise NoUri(target)
|
||||
match = [
|
||||
address_tuple(docname, anchor, ip_range)
|
||||
for ip_range, docname, anchor in [
|
||||
r
|
||||
for range_nodes in self.data["ranges"].values()
|
||||
for r in range_nodes
|
||||
]
|
||||
if self.data["ip_dict"][target] in ip_range
|
||||
]
|
||||
elif typ in ("v4range", "v6range"):
|
||||
if target in self.data["ranges"]:
|
||||
match = [
|
||||
address_tuple(docname, anchor, ip_range)
|
||||
for ip_range, docname, anchor in [
|
||||
range_nodes for range_nodes in self.data["ranges"][target]
|
||||
]
|
||||
]
|
||||
if len(match) > 0:
|
||||
todocname = match[0][0]
|
||||
targ = match[0][1]
|
||||
title = match[0][2]
|
||||
|
||||
return make_refnode(builder, fromdocname, todocname, targ, contnode, title)
|
||||
else:
|
||||
logger.error("found no link target for %s", target)
|
||||
return None
|
||||
|
||||
|
||||
def process_ip_nodes(app: Sphinx, doctree: nodes.Node, fromdocname: str):
|
||||
env = app.builder.env
|
||||
ips = env.get_domain(IPDomain.name)
|
||||
|
||||
header = (_("IP address"), _("Used by"))
|
||||
column_widths = (2, 5)
|
||||
|
||||
for node in doctree.findall(ip_range):
|
||||
content = []
|
||||
net = ipaddress.ip_network(node["range_spec"], strict=False)
|
||||
addresses = defaultdict(list)
|
||||
for ip_address_sig, refs in ips.data["ip_refs"].items():
|
||||
for ip_address, to_doc_name, anchor in refs:
|
||||
if ip_address in net:
|
||||
addresses[ip_address_sig].append((ip_address, to_doc_name, anchor))
|
||||
logger.debug(
|
||||
"found %s in network %s on %s",
|
||||
ip_address_sig,
|
||||
net.compressed,
|
||||
to_doc_name,
|
||||
)
|
||||
if addresses:
|
||||
table = nodes.table()
|
||||
table.attributes["classes"].append("indextable")
|
||||
table.attributes["align"] = "left"
|
||||
|
||||
tgroup = nodes.tgroup(cols=len(header))
|
||||
table += tgroup
|
||||
for column_width in column_widths:
|
||||
tgroup += nodes.colspec(colwidth=column_width)
|
||||
|
||||
thead = nodes.thead()
|
||||
tgroup += thead
|
||||
thead += create_table_row([nodes.paragraph(text=label) for label in header])
|
||||
|
||||
tbody = nodes.tbody()
|
||||
tgroup += tbody
|
||||
|
||||
for ip_address_sig, ip_info in [
|
||||
(key, addresses[key]) for key in sorted(addresses, key=sort_by_ip)
|
||||
]:
|
||||
para = nodes.paragraph()
|
||||
para += nodes.literal("", ip_info[0][0].compressed)
|
||||
|
||||
ref_node = nodes.paragraph()
|
||||
ref_nodes = []
|
||||
referenced_docs = set()
|
||||
|
||||
for item in ip_info:
|
||||
ip_address, to_doc_name, anchor = item
|
||||
if to_doc_name in referenced_docs:
|
||||
continue
|
||||
referenced_docs.add(to_doc_name)
|
||||
|
||||
title = env.titles[to_doc_name]
|
||||
inner_node = nodes.Text(title.astext())
|
||||
new_node = make_refnode(
|
||||
app.builder,
|
||||
fromdocname,
|
||||
to_doc_name,
|
||||
anchor,
|
||||
inner_node,
|
||||
title.astext(),
|
||||
)
|
||||
|
||||
ref_nodes.append(new_node)
|
||||
for count in range(len(ref_nodes)):
|
||||
ref_node.append(ref_nodes[count])
|
||||
if count < len(ref_nodes) - 1:
|
||||
ref_node.append(nodes.Text(", "))
|
||||
tbody += create_table_row([para, ref_node])
|
||||
content.append(table)
|
||||
else:
|
||||
para = nodes.paragraph(_("No IP addresses in this range"))
|
||||
content.append(para)
|
||||
|
||||
node.replace_self(content)
|
||||
|
||||
|
||||
def create_table_row(rowdata):
|
||||
row = nodes.row()
|
||||
for cell in rowdata:
|
||||
entry = nodes.entry()
|
||||
row += entry
|
||||
entry += cell
|
||||
return row
|
||||
|
||||
|
||||
def sort_by_ip(item):
|
||||
return ipaddress.ip_address(item)
|
||||
|
||||
|
||||
def setup(app: Sphinx):
|
||||
app.add_domain(IPDomain)
|
||||
app.connect("doctree-resolved", process_ip_nodes)
|
||||
return {
|
||||
"version": __version__,
|
||||
"env_version": 4,
|
||||
"parallel_read_safe": True,
|
||||
"parallel_write_safe": True,
|
||||
}
|
9
pyproject.toml
Normal file
9
pyproject.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools >= 35.0.2",
|
||||
"setuptools_scm[toml] >= 5.0",
|
||||
"wheel >= 0.29.0"
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools_scm]
|
35
setup.cfg
35
setup.cfg
|
@ -1,13 +1,34 @@
|
|||
[egg_info]
|
||||
tag_build = dev
|
||||
tag_date = true
|
||||
|
||||
[aliases]
|
||||
release = egg_info -RDb ''
|
||||
|
||||
[coverage:run]
|
||||
branch = true
|
||||
source = jandd.sphinxext.ip
|
||||
|
||||
[coverage:report]
|
||||
show_missing = true
|
||||
|
||||
[metadata]
|
||||
name = jandd.sphinxext.ip
|
||||
description = IP address extension for Sphinx
|
||||
long_description = file: README.rst, CHANGES.rst
|
||||
long_description_content_type = text/x-rst
|
||||
url = https://pypi.org/project/jandd.sphinxext.ip/
|
||||
author = Jan Dittberner
|
||||
author_email = jan@dittberner.info
|
||||
keywords = sphinx, extension, IP
|
||||
license = GPLv3+
|
||||
license_files = COPYING
|
||||
platforms = any
|
||||
version = 0.6.1
|
||||
classifiers =
|
||||
Development Status :: 5 - Production/Stable
|
||||
Framework :: Sphinx :: Extension
|
||||
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: Documentation
|
||||
Topic :: Internet
|
||||
|
||||
[options]
|
||||
zip_safe = False
|
||||
include_package_data = True
|
||||
packages = find_namespace:
|
||||
install_requires =
|
||||
Sphinx >= 5
|
||||
|
|
43
setup.py
43
setup.py
|
@ -1,42 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
from setuptools import setup
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
version = '0.2.2'
|
||||
|
||||
with open('README.rst') as readme:
|
||||
description = readme.read() + "\n\n"
|
||||
|
||||
with open('CHANGES.rst') as changes:
|
||||
description += changes.read()
|
||||
|
||||
requires = ['Sphinx>=1.4', 'ipcalc>=1.99']
|
||||
tests_requires = ['path.py>=8.2.1']
|
||||
|
||||
|
||||
setup(
|
||||
author="Jan Dittberner",
|
||||
author_email="jan@dittberner.info",
|
||||
description="IP address extension for Sphinx",
|
||||
long_description=description,
|
||||
include_package_data=True,
|
||||
install_requires=requires,
|
||||
keywords="sphinx extension IP",
|
||||
license="GPLv3+",
|
||||
url="https://pypi.python.org/pypi/jandd.sphinxext.ip",
|
||||
name="jandd.sphinxext.ip",
|
||||
namespace_packages=['jandd', 'jandd.sphinxext'],
|
||||
packages=find_packages(),
|
||||
platforms='any',
|
||||
tests_requires=tests_requires,
|
||||
version=version,
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Framework :: Sphinx :: Extension",
|
||||
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Documentation",
|
||||
"Topic :: Internet",
|
||||
],
|
||||
)
|
||||
setup()
|
||||
|
|
|
@ -12,52 +12,52 @@
|
|||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
#import sys
|
||||
#import os
|
||||
import os
|
||||
import sys
|
||||
from typing import Dict
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('..'))
|
||||
sys.path.insert(0, os.path.abspath(os.path.join("..", "..")))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['jandd.sphinxext.ip']
|
||||
extensions = ["jandd.sphinxext.ip"]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
# source_encoding = 'utf-8-ip_range'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = 'Sphinxext IP Tests'
|
||||
copyright = '2016, Jan Dittberner'
|
||||
author = 'Jan Dittberner'
|
||||
project = "Sphinxext IP Tests"
|
||||
copyright = "2016-2023, Jan Dittberner"
|
||||
author = "Jan Dittberner"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.1.0'
|
||||
version = "0.6.1"
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.1.0'
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -68,186 +68,187 @@ language = None
|
|||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
#default_role = None
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
#keep_warnings = False
|
||||
# keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'alabaster'
|
||||
html_theme = "alabaster"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents.
|
||||
# "<project> v<release> documentation" by default.
|
||||
#html_title = 'Sphinxext IP Tests v0.1.0'
|
||||
# html_title = 'Sphinxext IP Tests v0.1.0'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (relative to this directory) to use as a favicon of
|
||||
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
#html_extra_path = []
|
||||
# html_extra_path = []
|
||||
|
||||
# If not None, a 'Last updated on:' timestamp is inserted at every page
|
||||
# bottom, using the given strftime format.
|
||||
# The empty string is equivalent to '%b %d, %Y'.
|
||||
#html_last_updated_fmt = None
|
||||
# html_last_updated_fmt = None
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
# html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
|
||||
#html_search_language = 'en'
|
||||
# html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# 'ja' uses this config value.
|
||||
# 'zh' user can custom change `jieba` dictionary path.
|
||||
#html_search_options = {'type': 'default'}
|
||||
# html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
#html_search_scorer = 'scorer.js'
|
||||
# html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'SphinxextIPTestsdoc'
|
||||
htmlhelp_basename = "SphinxextIPTestsdoc"
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#'figure_align': 'htbp',
|
||||
latex_elements: Dict[str, str] = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
# Latex figure (float) alignment
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'SphinxextIPTests.tex', 'Sphinxext IP Tests Documentation',
|
||||
'Jan Dittberner', 'manual'),
|
||||
(
|
||||
master_doc,
|
||||
"SphinxextIPTests.tex",
|
||||
"Sphinxext IP Tests Documentation",
|
||||
"Jan Dittberner",
|
||||
"manual",
|
||||
),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
@ -255,12 +256,11 @@ latex_documents = [
|
|||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'sphinxextiptests', 'Sphinxext IP Tests Documentation',
|
||||
[author], 1)
|
||||
(master_doc, "sphinxextiptests", "Sphinxext IP Tests Documentation", [author], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
@ -269,19 +269,25 @@ man_pages = [
|
|||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'SphinxextIPTests', 'Sphinxext IP Tests Documentation',
|
||||
author, 'SphinxextIPTests', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
master_doc,
|
||||
"SphinxextIPTests",
|
||||
"Sphinxext IP Tests Documentation",
|
||||
author,
|
||||
"SphinxextIPTests",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#texinfo_appendices = []
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#texinfo_domain_indices = True
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
#texinfo_show_urls = 'footnote'
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
#texinfo_no_detailmenu = False
|
||||
# texinfo_no_detailmenu = False
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
Test page 3
|
||||
===========
|
||||
|
||||
This page contains :ip:v6:`2001:dead:beef::1` like :doc:`testpage2` does.
|
||||
This page contains :ip:v6:`2001:dead:beef::1` from :ip:v6range:`2001:dead:beef::/64`
|
||||
like :doc:`testpage2` does.
|
||||
|
|
24
tests/run.py
24
tests/run.py
|
@ -11,33 +11,33 @@ This script runs the jandd.sphinxext.ip unit test suite.
|
|||
"""
|
||||
|
||||
import sys
|
||||
from os import path
|
||||
import unittest
|
||||
from os import path
|
||||
|
||||
|
||||
def run(extra_args=[]):
|
||||
sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
|
||||
sys.path.insert(1, path.abspath(
|
||||
path.join(path.dirname(__file__), path.pardir,
|
||||
'jandd', 'sphinxext', 'ip'
|
||||
))
|
||||
sys.path.insert(
|
||||
1,
|
||||
path.abspath(
|
||||
path.join(path.dirname(__file__), path.pardir, "jandd", "sphinxext", "ip")
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
import sphinx
|
||||
except ImportError:
|
||||
print("The sphinx package is needed to run the jandd.sphinxext.ip "
|
||||
"test suite.")
|
||||
print(
|
||||
"The sphinx package is needed to run the jandd.sphinxext.ip " "test suite."
|
||||
)
|
||||
|
||||
import test_ip
|
||||
from tests.test_ip import TestIPExtension
|
||||
|
||||
print("Running jandd.sphinxext.ip test suite ...")
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(
|
||||
test_ip.TestIPExtension
|
||||
)
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestIPExtension)
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
|
|
@ -1,40 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from io import StringIO
|
||||
from util import TestApp, test_root
|
||||
import shutil
|
||||
import unittest
|
||||
from io import StringIO
|
||||
|
||||
from .util import SphinxTestApplication, test_root
|
||||
|
||||
IP4_ADDRESSES = ['127.0.0.1', '192.168.0.1']
|
||||
IP6_ADDRESSES = ['::1', '2001:dead:beef::1']
|
||||
IP4_RANGES = ['172.16.0.0/24', '192.168.0.0/24']
|
||||
IP6_RANGES = ['2001:dead:beef::/64', '2001:dada:b001::/64']
|
||||
IP4_ADDRESSES = ["127.0.0.1", "192.168.0.1"]
|
||||
IP6_ADDRESSES = ["::1", "2001:dead:beef::1"]
|
||||
IP4_RANGES = ["172.16.0.0/24", "192.168.0.0/24"]
|
||||
IP6_RANGES = ["2001:dead:beef::/64", "2001:dada:b001::/64"]
|
||||
|
||||
|
||||
class TestIPExtension(unittest.TestCase):
|
||||
def setUp(self):
|
||||
if not (test_root / '_static').exists():
|
||||
(test_root / '_static').mkdir()
|
||||
if not (test_root / "_static").exists():
|
||||
(test_root / "_static").mkdir()
|
||||
self.feed_warnfile = StringIO()
|
||||
self.app = TestApp(
|
||||
buildername='html', warning=self.feed_warnfile, cleanenv=True)
|
||||
self.app = SphinxTestApplication(
|
||||
buildername="html", warning=self.feed_warnfile, cleanenv=True
|
||||
)
|
||||
self.app.build(force_all=True, filenames=[])
|
||||
|
||||
def tearDown(self):
|
||||
self.app.cleanup()
|
||||
(test_root / '_build').rmtree(True)
|
||||
shutil.rmtree((test_root / "_build"))
|
||||
|
||||
def test_ip_domaindata(self):
|
||||
self.assertIn('ip', self.app.env.domaindata)
|
||||
ipdomdata = self.app.env.domaindata['ip']
|
||||
self.assertIn('v4', ipdomdata)
|
||||
self.assertIn('v6', ipdomdata)
|
||||
self.assertIn('v4range', ipdomdata)
|
||||
self.assertIn('v6range', ipdomdata)
|
||||
self.assertIn('ips', ipdomdata)
|
||||
self.assertIn("ip", self.app.env.domaindata)
|
||||
ipdomdata = self.app.env.domaindata["ip"]
|
||||
self.assertIn("ip_refs", ipdomdata)
|
||||
self.assertIn("range_refs", ipdomdata)
|
||||
self.assertIn("range_nodes", ipdomdata)
|
||||
self.assertIn("ranges", ipdomdata)
|
||||
self.assertIn("ip_dict", ipdomdata)
|
||||
|
||||
def find_in_index(self, entry):
|
||||
indexentries = self.app.env.indexentries
|
||||
indexentries = self.app.env.get_domain("index").entries
|
||||
for index in indexentries:
|
||||
for value in indexentries[index]:
|
||||
if value[1] == entry:
|
||||
|
@ -42,19 +43,15 @@ class TestIPExtension(unittest.TestCase):
|
|||
self.fail("%s not found in index" % entry)
|
||||
|
||||
def test_ip4_addresses(self):
|
||||
ipv4 = self.app.env.domaindata['ip']['v4']
|
||||
ips = self.app.env.domaindata['ip']['ips']
|
||||
ips = self.app.env.domaindata["ip"]["ip_refs"]
|
||||
for ip in IP4_ADDRESSES:
|
||||
self.assertIn(ip, ipv4)
|
||||
self.assertIn(ip, [item['ip'] for item in ips])
|
||||
self.assertIn(ip, ips)
|
||||
self.find_in_index("IPv4 address; %s" % ip)
|
||||
self.find_in_index("%s; Test page 2" % ip)
|
||||
|
||||
def test_ip6_addresses(self):
|
||||
ipv6 = self.app.env.domaindata['ip']['v6']
|
||||
ips = self.app.env.domaindata['ip']['ips']
|
||||
ips = self.app.env.domaindata["ip"]["ip_refs"]
|
||||
for ip in IP6_ADDRESSES:
|
||||
self.assertIn(ip, ipv6)
|
||||
self.assertIn(ip, [item['ip'] for item in ips])
|
||||
self.assertIn(ip, ips)
|
||||
self.find_in_index("IPv6 address; %s" % ip)
|
||||
self.find_in_index("%s; Test page 2" % ip)
|
||||
|
|
141
tests/util.py
141
tests/util.py
|
@ -7,32 +7,31 @@
|
|||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import io
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
try:
|
||||
from functools import wraps
|
||||
except ImportError:
|
||||
# functools is new in 2.4
|
||||
wraps = lambda f: (lambda w: w)
|
||||
import sys
|
||||
import tempfile
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
|
||||
from sphinx import application
|
||||
from sphinx.ext.autodoc import AutoDirective
|
||||
|
||||
from path import Path
|
||||
|
||||
__all__ = [
|
||||
'test_root',
|
||||
'raises', 'raises_msg', 'Struct',
|
||||
'ListOutput', 'TestApp', 'with_app', 'gen_with_app',
|
||||
'Path', 'with_tempdir', 'write_file',
|
||||
'sprint',
|
||||
"test_root",
|
||||
"raises",
|
||||
"raises_msg",
|
||||
"Struct",
|
||||
"ListOutput",
|
||||
"SphinxTestApplication",
|
||||
"with_app",
|
||||
"gen_with_app",
|
||||
"with_tempdir",
|
||||
"write_file",
|
||||
"sprint",
|
||||
]
|
||||
|
||||
|
||||
test_root = Path(__file__).parent.joinpath('root').abspath()
|
||||
test_root = Path(__file__).parent.joinpath("root").absolute()
|
||||
|
||||
|
||||
def _excstr(exc):
|
||||
|
@ -40,6 +39,7 @@ def _excstr(exc):
|
|||
return str(tuple(map(_excstr, exc)))
|
||||
return exc.__name__
|
||||
|
||||
|
||||
def raises(exc, func, *args, **kwds):
|
||||
"""
|
||||
Raise :exc:`AssertionError` if ``func(*args, **kwds)`` does not
|
||||
|
@ -50,8 +50,8 @@ def raises(exc, func, *args, **kwds):
|
|||
except exc:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('%s did not raise %s' %
|
||||
(func.__name__, _excstr(exc)))
|
||||
raise AssertionError("%s did not raise %s" % (func.__name__, _excstr(exc)))
|
||||
|
||||
|
||||
def raises_msg(exc, msg, func, *args, **kwds):
|
||||
"""
|
||||
|
@ -61,20 +61,21 @@ def raises_msg(exc, msg, func, *args, **kwds):
|
|||
try:
|
||||
func(*args, **kwds)
|
||||
except exc as err:
|
||||
assert msg in str(err), "\"%s\" not in \"%s\"" % (msg, err)
|
||||
assert msg in str(err), '"%s" not in "%s"' % (msg, err)
|
||||
else:
|
||||
raise AssertionError('%s did not raise %s' %
|
||||
(func.__name__, _excstr(exc)))
|
||||
raise AssertionError("%s did not raise %s" % (func.__name__, _excstr(exc)))
|
||||
|
||||
|
||||
class Struct(object):
|
||||
def __init__(self, **kwds):
|
||||
self.__dict__.update(kwds)
|
||||
|
||||
|
||||
class ListOutput(object):
|
||||
"""
|
||||
File-like object that collects written text in a list.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.content = []
|
||||
|
@ -85,42 +86,54 @@ class ListOutput(object):
|
|||
def write(self, text):
|
||||
self.content.append(text)
|
||||
|
||||
class TestApp(application.Sphinx):
|
||||
|
||||
class SphinxTestApplication(application.Sphinx):
|
||||
"""
|
||||
A subclass of :class:`Sphinx` that runs on the test root, with some
|
||||
better default values for the initialization parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None,
|
||||
buildername='html', confoverrides=None,
|
||||
status=None, warning=None, freshenv=None,
|
||||
warningiserror=None, tags=None,
|
||||
confname='conf.py', cleanenv=False):
|
||||
def __init__(
|
||||
self,
|
||||
src_dir=None,
|
||||
confdir=None,
|
||||
out_dir=None,
|
||||
doctreedir=None,
|
||||
buildername="html",
|
||||
confoverrides=None,
|
||||
status=None,
|
||||
warning=None,
|
||||
freshenv=None,
|
||||
warningiserror=None,
|
||||
tags=None,
|
||||
confname="conf.py",
|
||||
cleanenv=False,
|
||||
):
|
||||
|
||||
application.CONFIG_FILENAME = confname
|
||||
|
||||
self.cleanup_trees = [test_root / 'generated']
|
||||
self.cleanup_trees = [test_root / "generated"]
|
||||
|
||||
if srcdir is None:
|
||||
srcdir = test_root
|
||||
if srcdir == '(temp)':
|
||||
if src_dir is None:
|
||||
src_dir = test_root
|
||||
if src_dir == "(temp)":
|
||||
tempdir = Path(tempfile.mkdtemp())
|
||||
self.cleanup_trees.append(tempdir)
|
||||
temproot = tempdir / 'root'
|
||||
test_root.copytree(temproot)
|
||||
srcdir = temproot
|
||||
temp_root = tempdir / "root"
|
||||
shutil.copytree(test_root.resolve(), temp_root.resolve())
|
||||
src_dir = temp_root
|
||||
else:
|
||||
srcdir = Path(srcdir)
|
||||
self.builddir = srcdir.joinpath('_build')
|
||||
src_dir = Path(src_dir)
|
||||
self.builddir = src_dir.joinpath("_build")
|
||||
if confdir is None:
|
||||
confdir = srcdir
|
||||
if outdir is None:
|
||||
outdir = srcdir.joinpath(self.builddir, buildername)
|
||||
if not outdir.isdir():
|
||||
outdir.makedirs()
|
||||
self.cleanup_trees.insert(0, outdir)
|
||||
confdir = src_dir
|
||||
if out_dir is None:
|
||||
out_dir = src_dir.joinpath(self.builddir, buildername)
|
||||
if not out_dir.is_dir():
|
||||
out_dir.mkdir(parents=True)
|
||||
self.cleanup_trees.insert(0, out_dir)
|
||||
if doctreedir is None:
|
||||
doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees')
|
||||
doctreedir = src_dir.joinpath(src_dir, self.builddir, "doctrees")
|
||||
if cleanenv:
|
||||
self.cleanup_trees.insert(0, doctreedir)
|
||||
if confoverrides is None:
|
||||
|
@ -128,18 +141,28 @@ class TestApp(application.Sphinx):
|
|||
if status is None:
|
||||
status = io.StringIO()
|
||||
if warning is None:
|
||||
warning = ListOutput('stderr')
|
||||
warning = ListOutput("stderr")
|
||||
if freshenv is None:
|
||||
freshenv = False
|
||||
if warningiserror is None:
|
||||
warningiserror = False
|
||||
|
||||
application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir,
|
||||
buildername, confoverrides, status, warning,
|
||||
freshenv, warningiserror, tags)
|
||||
application.Sphinx.__init__(
|
||||
self,
|
||||
str(src_dir),
|
||||
confdir,
|
||||
out_dir,
|
||||
doctreedir,
|
||||
buildername,
|
||||
confoverrides,
|
||||
status,
|
||||
warning,
|
||||
freshenv,
|
||||
warningiserror,
|
||||
tags,
|
||||
)
|
||||
|
||||
def cleanup(self, doctrees=False):
|
||||
AutoDirective._registry.clear()
|
||||
for tree in self.cleanup_trees:
|
||||
shutil.rmtree(tree, True)
|
||||
|
||||
|
@ -149,14 +172,17 @@ def with_app(*args, **kwargs):
|
|||
Make a TestApp with args and kwargs, pass it to the test and clean up
|
||||
properly.
|
||||
"""
|
||||
|
||||
def generator(func):
|
||||
@wraps(func)
|
||||
def deco(*args2, **kwargs2):
|
||||
app = TestApp(*args, **kwargs)
|
||||
app = SphinxTestApplication(*args, **kwargs)
|
||||
func(app, *args2, **kwargs2)
|
||||
# don't execute cleanup if test failed
|
||||
app.cleanup()
|
||||
|
||||
return deco
|
||||
|
||||
return generator
|
||||
|
||||
|
||||
|
@ -165,29 +191,36 @@ def gen_with_app(*args, **kwargs):
|
|||
Make a TestApp with args and kwargs, pass it to the test and clean up
|
||||
properly.
|
||||
"""
|
||||
|
||||
def generator(func):
|
||||
@wraps(func)
|
||||
def deco(*args2, **kwargs2):
|
||||
app = TestApp(*args, **kwargs)
|
||||
app = SphinxTestApplication(*args, **kwargs)
|
||||
for item in func(app, *args2, **kwargs2):
|
||||
yield item
|
||||
# don't execute cleanup if test failed
|
||||
app.cleanup()
|
||||
|
||||
return deco
|
||||
|
||||
return generator
|
||||
|
||||
|
||||
def with_tempdir(func):
|
||||
def new_func():
|
||||
tempdir = Path(tempfile.mkdtemp())
|
||||
func(tempdir)
|
||||
tempdir.rmtree()
|
||||
tempdir.rmdir()
|
||||
|
||||
new_func.__name__ = func.__name__
|
||||
return new_func
|
||||
|
||||
|
||||
def write_file(name, contents):
|
||||
f = open(str(name), 'wb')
|
||||
f = open(str(name), "wb")
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
|
||||
def sprint(*args):
|
||||
sys.stderr.write(' '.join(map(str, args)) + '\n')
|
||||
sys.stderr.write(" ".join(map(str, args)) + "\n")
|
||||
|
|
26
tox.ini
Normal file
26
tox.ini
Normal file
|
@ -0,0 +1,26 @@
|
|||
[tox]
|
||||
requires =
|
||||
tox>=4
|
||||
env_list = lint, type, py{39,310,311,312}
|
||||
|
||||
[testenv]
|
||||
description = run unit tests
|
||||
deps =
|
||||
pytest>=7
|
||||
pytest-sugar
|
||||
commands =
|
||||
pytest {posargs:tests}
|
||||
|
||||
[testenv:lint]
|
||||
description = run linters
|
||||
skip_install = true
|
||||
deps =
|
||||
black==22.12
|
||||
commands = black {posargs:.}
|
||||
|
||||
[testenv:type]
|
||||
description = run type checks
|
||||
deps =
|
||||
mypy>=0.991
|
||||
commands =
|
||||
mypy {posargs:--install-types --non-interactive jandd tests}
|
Loading…
Reference in a new issue