Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept path-like configuration options #888

Merged
merged 2 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion sphinxcontrib/confluencebuilder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from sphinxcontrib.confluencebuilder.util import first
from sphinxcontrib.confluencebuilder.util import handle_cli_file_subset
from sphinxcontrib.confluencebuilder.writer import ConfluenceWriter
import os
import tempfile


Expand Down Expand Up @@ -211,7 +212,7 @@ def prepare_subset(option):
if value is None:
return None

if isinstance(value, str):
if isinstance(value, (str, os.PathLike)):
files = extract_strings_from_file(value)
else:
files = value
Expand Down
13 changes: 5 additions & 8 deletions sphinxcontrib/confluencebuilder/config/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,6 @@ def validate_configuration(builder):
# confluence_footer_file
try:
validator.conf('confluence_footer_file') \
.string() \
.file()
except ConfluenceConfigurationError as e:
raise ConfluenceConfigurationError('''\
Expand Down Expand Up @@ -315,7 +314,6 @@ def validate_configuration(builder):
# confluence_header_file
try:
validator.conf('confluence_header_file') \
.string() \
.file()
except ConfluenceConfigurationError as e:
raise ConfluenceConfigurationError('''\
Expand Down Expand Up @@ -534,16 +532,15 @@ def validate_configuration(builder):
# if provided a file via command line, treat as a list
def conf_translate(value):
return handle_cli_file_subset(builder, option, value)

value = conf_translate(value)
option_val = validator.conf(option, conf_translate)

try:
validator.conf(option, conf_translate) \
.string_or_strings()

if isinstance(value, str):
validator.docnames_from_file()
if isinstance(value, (str, os.PathLike)):
option_val.docnames_from_file()
else:
validator.docnames()
option_val.docnames()
except ConfluenceConfigurationError as e:
raise ConfluenceConfigurationError('''\
{msg}
Expand Down
24 changes: 10 additions & 14 deletions sphinxcontrib/confluencebuilder/config/validation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: BSD-2-Clause
# Copyright Sphinx Confluence Builder Contributors (AUTHORS)

from pathlib import Path
from sphinxcontrib.confluencebuilder.exceptions import ConfluenceConfigurationError
from sphinxcontrib.confluencebuilder.util import extract_strings_from_file
from sphinxcontrib.confluencebuilder.util import str2bool
Expand Down Expand Up @@ -152,15 +153,14 @@ def docnames(self):
value = self._value()

if value is not None:
if not (isinstance(value, (list, set, tuple)) or not all(
isinstance(label, str) for label in value)):
if not (isinstance(value, (list, set, tuple))) or not all(
isinstance(label, (str, os.PathLike)) for label in value):
raise ConfluenceConfigurationError(
'%s is not a collection of filenames' % self.key)

for docname in value:
if not any(
os.path.isfile(
os.path.join(self.env.srcdir, docname + suffix))
Path(self.env.srcdir, docname + suffix).is_file()
for suffix in self.config.source_suffix):
raise ConfluenceConfigurationError(
f'{self.key} is missing document {docname}')
Expand All @@ -186,16 +186,12 @@ def docnames_from_file(self):
value = self._value()

if value is not None:
if not isinstance(value, str) or not os.path.isfile(
os.path.join(self.env.srcdir, value)):
raise ConfluenceConfigurationError(
'%s is not a file' % self.key)
self.file()

docnames = extract_strings_from_file(value)
for docname in docnames:
if not any(
os.path.isfile(
os.path.join(self.env.srcdir, docname + suffix))
Path(self.env.srcdir, docname + suffix).is_file()
for suffix in self.config.source_suffix):
raise ConfluenceConfigurationError(
f'{self.key} is missing document {docname}')
Expand Down Expand Up @@ -247,8 +243,8 @@ def file(self):
value = self._value()

if value is not None:
if not isinstance(value, str) or not os.path.isfile(
os.path.join(self.env.srcdir, value)):
if not isinstance(value, (str, os.PathLike)) or \
not Path(self.env.srcdir, value).is_file():
raise ConfluenceConfigurationError(
'%s is not a file' % self.key)

Expand Down Expand Up @@ -378,8 +374,8 @@ def path(self):
value = self._value()

if value is not None:
if not isinstance(value, str) or not os.path.exists(
os.path.join(self.env.srcdir, value)):
if not isinstance(value, (str, os.PathLike)) or \
not Path(self.env.srcdir, value).exists():
raise ConfluenceConfigurationError(
'%s is not a path' % self.key)

Expand Down
13 changes: 7 additions & 6 deletions sphinxcontrib/confluencebuilder/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright Sphinx Confluence Builder Contributors (AUTHORS)

from contextlib import contextmanager
from pathlib import Path
from sphinxcontrib.confluencebuilder.std.confluence import API_REST_BIND_PATH
from sphinxcontrib.confluencebuilder.std.confluence import FONT_SIZE
from sphinxcontrib.confluencebuilder.std.confluence import FONT_X_HEIGHT
Expand Down Expand Up @@ -321,19 +322,19 @@ def handle_cli_file_subset(builder, option, value):
the resolved configuration value
"""

if option in builder.config['overrides'] and isinstance(value, str):
if option in builder.config['overrides'] and \
isinstance(value, (str, os.PathLike)):
if not value:
# an empty command line subset is an "unset" request
# (and not an empty list); if no values are detected,
# return `None`
return None
else:
if os.path.isabs(value):
target_file = value
else:
target_file = os.path.join(builder.env.srcdir, value)
target_file = Path(value)
if not target_file.is_absolute():
target_file = Path(builder.env.srcdir, value)

if os.path.isfile(target_file):
if target_file.is_file():
value = target_file
else:
value = value.split(',')
Expand Down
60 changes: 51 additions & 9 deletions tests/unit-tests/test_config_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,34 +188,51 @@ def test_config_check_additional_mime_types(self):
self._try_config()

def test_config_check_ca_cert(self):
valid_cert_dir = str(self.test_dir)
valid_cert_file = str(self.dummy_exists)
missing_cert = str(self.dummy_missing)
valid_cert_dir = self.test_dir
valid_cert_file = self.dummy_exists
missing_cert = self.dummy_missing

self.config['confluence_ca_cert'] = valid_cert_dir
self._try_config()

self.config['confluence_ca_cert'] = str(valid_cert_dir)
self._try_config()

self.config['confluence_ca_cert'] = valid_cert_file
self._try_config()

self.config['confluence_ca_cert'] = str(valid_cert_file)
self._try_config()

self.config['confluence_ca_cert'] = missing_cert
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

self.config['confluence_ca_cert'] = str(missing_cert)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

def test_config_check_client_cert(self):
valid_cert = str(self.dummy_exists)
missing_cert = str(self.dummy_missing)
valid_cert = self.dummy_exists
missing_cert = self.dummy_missing

self.config['confluence_client_cert'] = valid_cert
self._try_config()

self.config['confluence_client_cert'] = str(valid_cert)
self._try_config()

self.config['confluence_client_cert'] = (valid_cert, valid_cert)
self._try_config()

self.config['confluence_client_cert'] = missing_cert
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

self.config['confluence_client_cert'] = str(missing_cert)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

self.config['confluence_client_cert'] = (valid_cert, missing_cert)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()
Expand Down Expand Up @@ -350,16 +367,23 @@ def mock_transform(docname):
self._try_config()

def test_config_check_footer_file(self):
valid_footer = str(self.dummy_exists)
missing_footer = str(self.dummy_missing)
valid_footer = self.dummy_exists
missing_footer = self.dummy_missing

self.config['confluence_footer_file'] = valid_footer
self._try_config()

self.config['confluence_footer_file'] = str(valid_footer)
self._try_config()

self.config['confluence_footer_file'] = missing_footer
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

self.config['confluence_footer_file'] = str(missing_footer)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

relbase = '../../templates/'
self.config['confluence_footer_file'] = relbase + 'sample-footer.tpl'
self._try_config()
Expand All @@ -373,16 +397,23 @@ def test_config_check_global_labels(self):
self._try_config()

def test_config_check_header_file(self):
valid_header = str(self.dummy_exists)
missing_header = str(self.dummy_missing)
valid_header = self.dummy_exists
missing_header = self.dummy_missing

self.config['confluence_header_file'] = valid_header
self._try_config()

self.config['confluence_header_file'] = str(valid_header)
self._try_config()

self.config['confluence_header_file'] = missing_header
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

self.config['confluence_header_file'] = str(missing_header)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config()

relbase = '../../templates/'
self.config['confluence_header_file'] = relbase + 'sample-header.tpl'
self._try_config()
Expand Down Expand Up @@ -671,6 +702,9 @@ def test_config_check_publish_list(self):

# file with a valid document list
self.assertTrue(valid_list.is_file())
self.config[option] = valid_list
self._try_config(dataset=dataset)

self.config[option] = str(valid_list)
self._try_config(dataset=dataset)

Expand All @@ -691,12 +725,20 @@ def test_config_check_publish_list(self):

# file with invalid document list
self.assertTrue(invalid_list.is_file())
self.config[option] = invalid_list
with self.assertRaises(ConfluenceConfigurationError):
self._try_config(dataset=dataset)

self.config[option] = str(invalid_list)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config(dataset=dataset)

# missing file
self.assertFalse(missing_list.is_file())
self.config[option] = missing_list
with self.assertRaises(ConfluenceConfigurationError):
self._try_config(dataset=dataset)

self.config[option] = str(missing_list)
with self.assertRaises(ConfluenceConfigurationError):
self._try_config(dataset=dataset)
Expand Down
4 changes: 2 additions & 2 deletions tests/unit-tests/test_config_header_footer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def test_storage_config_headerfooter_absolute(self):
config = dict(self.config)
footer_tpl = self.templates_dir / 'sample-footer.tpl'
header_tpl = self.templates_dir / 'sample-header.tpl'
config['confluence_footer_file'] = str(footer_tpl)
config['confluence_header_file'] = str(header_tpl)
config['confluence_footer_file'] = footer_tpl
config['confluence_header_file'] = header_tpl

out_dir = self.build(self.dataset, config=config)

Expand Down
8 changes: 4 additions & 4 deletions tests/unit-tests/test_config_publish_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_config_publishlist_allow_list_cli_empty(self):
], any_order=True)

def test_config_publishlist_allow_list_file_default_abs(self):
publish_list = str(self.dataset / 'publish-list-default')
publish_list = self.dataset / 'publish-list-default'

config = dict(self.config)
config['confluence_publish_allowlist'] = publish_list
Expand All @@ -93,7 +93,7 @@ def test_config_publishlist_allow_list_file_default_relative(self):
], any_order=True)

def test_config_publishlist_allow_list_file_empty(self):
publish_list = str(self.dataset / 'publish-list-empty')
publish_list = self.dataset / 'publish-list-empty'

config = dict(self.config)
config['confluence_publish_allowlist'] = publish_list
Expand Down Expand Up @@ -198,7 +198,7 @@ def test_config_publishlist_deny_list_cli_empty(self):
], any_order=True)

def test_config_publishlist_deny_list_file_default_abs(self):
publish_list = str(self.dataset / 'publish-list-default')
publish_list = self.dataset / 'publish-list-default'

config = dict(self.config)
config['confluence_publish_denylist'] = publish_list
Expand All @@ -222,7 +222,7 @@ def test_config_publishlist_deny_list_file_default_relative(self):
], any_order=True)

def test_config_publishlist_deny_list_file_empty(self):
publish_list = str(self.dataset / 'publish-list-empty')
publish_list = self.dataset / 'publish-list-empty'

config = dict(self.config)
config['confluence_publish_denylist'] = publish_list
Expand Down
4 changes: 2 additions & 2 deletions tests/unit-tests/test_sdoc_genindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def test_storage_sdoc_genindex_header_footer(self):
# defined header/footer data is also injected into the document.

dataset = self.datasets / 'sdoc' / 'genindex'
footer_tpl = str(self.templates_dir / 'sample-footer.tpl')
header_tpl = str(self.templates_dir / 'sample-header.tpl')
footer_tpl = self.templates_dir / 'sample-footer.tpl'
header_tpl = self.templates_dir / 'sample-header.tpl'

config = dict(self.config)
config['confluence_use_index'] = True
Expand Down
4 changes: 2 additions & 2 deletions tests/unit-tests/test_sdoc_modindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def test_storage_sdoc_modindex_header_footer(self):
# defined header/footer data is also injected into the document.

dataset = self.datasets / 'sdoc' / 'py-modindex'
footer_tpl = str(self.templates_dir / 'sample-footer.tpl')
header_tpl = str(self.templates_dir / 'sample-header.tpl')
footer_tpl = self.templates_dir / 'sample-footer.tpl'
header_tpl = self.templates_dir / 'sample-header.tpl'

config = dict(self.config)
config['confluence_domain_indices'] = True
Expand Down
4 changes: 2 additions & 2 deletions tests/unit-tests/test_sdoc_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def test_storage_sdoc_search_header_footer(self):
# defined header/footer data is also injected into the document.

dataset = self.datasets / 'minimal'
footer_tpl = str(self.templates_dir / 'sample-footer.tpl')
header_tpl = str(self.templates_dir / 'sample-header.tpl')
footer_tpl = self.templates_dir / 'sample-footer.tpl'
header_tpl = self.templates_dir / 'sample-header.tpl'

config = dict(self.config)
config['confluence_include_search'] = True
Expand Down