diff --git a/sphinxcontrib/confluencebuilder/__main__.py b/sphinxcontrib/confluencebuilder/__main__.py index bcb44072..275cd70c 100644 --- a/sphinxcontrib/confluencebuilder/__main__.py +++ b/sphinxcontrib/confluencebuilder/__main__.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinx.util.console import color_terminal from sphinx.util.console import nocolor # pylint: disable=no-name-in-module from sphinxcontrib.confluencebuilder import __version__ as version @@ -29,7 +30,7 @@ def main(): parser.add_argument('--verbose', '-V', action='count', default=0) parser.add_argument('--version', action='version', version='%(prog)s ' + version) - parser.add_argument('--work-dir') + parser.add_argument('--work-dir', type=Path) args, _ = parser.parse_known_args() if args.help: diff --git a/sphinxcontrib/confluencebuilder/assets.py b/sphinxcontrib/confluencebuilder/assets.py index 0d4bcf5e..4ba3307d 100644 --- a/sphinxcontrib/confluencebuilder/assets.py +++ b/sphinxcontrib/confluencebuilder/assets.py @@ -2,6 +2,7 @@ # Copyright Sphinx Confluence Builder Contributors (AUTHORS) from docutils import nodes +from pathlib import Path from sphinx import addnodes from sphinx.util.osutil import canon_path from sphinx.util.images import guess_mimetype @@ -11,7 +12,6 @@ from sphinxcontrib.confluencebuilder.std.confluence import SUPPORTED_IMAGE_TYPES from sphinxcontrib.confluencebuilder.util import ConfluenceUtil from sphinxcontrib.confluencebuilder.util import find_env_abspath -import os # default content type to use if a type cannot be detected for an asset DEFAULT_CONTENT_TYPE = 'application/octet-stream' @@ -54,15 +54,15 @@ class ConfluenceAssetManager: Args: config: the active configuration env: the build environment - outdir: configured output directory (where assets may be stored) + out_dir: configured output directory (where assets may be stored) """ - def __init__(self, config, env, outdir): + def __init__(self, config, env, out_dir): self.assets = [] self.env = env self.force_standalone = config.confluence_asset_force_standalone self.hash2asset = {} self.keys = set() - self.outdir = outdir + self.out_dir = out_dir self.path2asset = {} self.root_doc = config.root_doc @@ -83,8 +83,8 @@ def add(self, path, docname): the key, document name and path """ logger.verbose('adding manual attachment: %s' % path) - abspath = find_env_abspath(self.env, self.outdir, path) - return self._handle_entry(abspath, docname, standalone=True) + abs_path = find_env_abspath(self.env, self.out_dir, path) + return self._handle_entry(abs_path, docname, standalone=True) def build(self): """ @@ -274,7 +274,8 @@ def _handle_entry(self, path, docname, standalone=False): if path not in self.path2asset: hash_ = ConfluenceUtil.hash_asset(path) - type_ = guess_mimetype(path, default=DEFAULT_CONTENT_TYPE) + # str-cast for sphinx-6.1 + type_ = guess_mimetype(str(path), default=DEFAULT_CONTENT_TYPE) else: hash_ = self.path2asset[path].hash type_ = self.path2asset[path].type @@ -284,18 +285,18 @@ def _handle_entry(self, path, docname, standalone=False): hash_exists = hash_ in self.hash2asset if not hash_exists or standalone: # no asset entry and no hash entry (or standalone); new asset - key = os.path.basename(path) + key = path.name # Confluence does not allow attachments with select characters. # Filter out the asset name to a compatible key value. for rep in INVALID_CHARS: key = key.replace(rep, '_') - filename, file_ext = os.path.splitext(key) + new_path = Path(key) idx = 1 while key in self.keys: idx += 1 - key = f'{filename}_{idx}{file_ext}' + key = f'{new_path.stem}_{idx}{new_path.suffix}' self.keys.add(key) asset = ConfluenceAsset(key, path, type_, hash_) @@ -332,20 +333,20 @@ def _interpret_asset_path(self, node): path = None if isinstance(node, nodes.image): # uri's will be relative to documentation root. - path = str(node['uri']) + path = Path(node['uri']) elif isinstance(node, addnodes.download_reference): # reftarget will be a reference to the asset with respect to the # document (refdoc) holding this reference. Use reftarget and refdoc # to find a proper path. - docdir = os.path.dirname(node['refdoc']) - path = os.path.join(docdir, node['reftarget']) + docdir = Path(node['refdoc']).parent + path = docdir / node['reftarget'] - abspath = find_env_abspath(self.env, self.outdir, path) + abs_path = find_env_abspath(self.env, self.out_dir, path) - if not abspath: + if not abs_path: logger.verbose('failed to find path: %s' % path) - return abspath + return abs_path class ConfluenceSupportedImages: diff --git a/sphinxcontrib/confluencebuilder/builder.py b/sphinxcontrib/confluencebuilder/builder.py index cf305157..ba62408f 100644 --- a/sphinxcontrib/confluencebuilder/builder.py +++ b/sphinxcontrib/confluencebuilder/builder.py @@ -6,7 +6,6 @@ from docutils import nodes from docutils.io import StringOutput from pathlib import Path -from os import path from sphinx import addnodes from sphinx import version_info as sphinx_version_info from sphinx.builders import Builder @@ -152,7 +151,7 @@ def init(self): if new_url: self.cloud = new_url.endswith('.atlassian.net/wiki/') - self.assets = ConfluenceAssetManager(config, self.env, self.outdir) + self.assets = ConfluenceAssetManager(config, self.env, self.out_dir) self.writer = ConfluenceWriter(self) self.config.sphinx_verbosity = self._verbose self.publisher.init(self.config, self.cloud) @@ -789,13 +788,13 @@ def to_asset_name(asset): for asset in status_iterator(assets, 'publishing assets... ', length=len(assets), verbosity=self._verbose, stringify_func=to_asset_name): - key, absfile, type_, hash_, docname = asset + key, abs_file, type_, hash_, docname = asset if self._check_publish_skip(docname): self.verbose(key + ' skipped due to configuration') continue try: - with open(absfile, 'rb') as file: + with abs_file.open('rb') as file: output = file.read() self.publish_asset(key, docname, output, type_, hash_) except OSError as err: @@ -910,13 +909,13 @@ def _generate_special_document(self, docname, generator): header_template_data = '' if self.config.confluence_header_file is not None: - fname = path.join(self.env.srcdir, + header_file = Path(self.env.srcdir, self.config.confluence_header_file) try: - with open(fname, encoding='utf-8') as file: + with header_file.open(encoding='utf-8') as file: header_template_data = file.read() + '\n' except OSError as err: - self.warn(f'error reading file {fname}: {err}') + self.warn(f'error reading file {header_file}: {err}') # if no data is supplied, the file is plain text if self.config.confluence_header_data is None: @@ -932,13 +931,13 @@ def _generate_special_document(self, docname, generator): footer_template_data = '' if self.config.confluence_footer_file is not None: - fname = path.join(self.env.srcdir, + footer_file = Path(self.env.srcdir, self.config.confluence_footer_file) try: - with open(fname, encoding='utf-8') as file: + with footer_file.open(encoding='utf-8') as file: footer_template_data = file.read() + '\n' except OSError as err: - self.warn(f'error reading file {fname}: {err}') + self.warn(f'error reading file {footer_file}: {err}') # if no data is supplied, the file is plain text if self.config.confluence_footer_data is None: diff --git a/sphinxcontrib/confluencebuilder/cmd/build.py b/sphinxcontrib/confluencebuilder/cmd/build.py index ca4c51bb..8aba544a 100644 --- a/sphinxcontrib/confluencebuilder/cmd/build.py +++ b/sphinxcontrib/confluencebuilder/cmd/build.py @@ -2,10 +2,10 @@ # Copyright Sphinx Confluence Builder Contributors (AUTHORS) from contextlib import suppress +from pathlib import Path from sphinx.application import Sphinx from sphinx.util.docutils import docutils_namespace from sphinxcontrib.confluencebuilder.logger import ConfluenceLogger as logger -import os import sys #: default builder to invoke when one is not specified @@ -26,7 +26,7 @@ def build_main(args_parser): """ args_parser.add_argument('-D', action='append', default=[], dest='define') - args_parser.add_argument('--output-dir', '-o') + args_parser.add_argument('--output-dir', '-o', type=Path) known_args = sys.argv[1:] args, unknown_args = args_parser.parse_known_args(known_args) @@ -42,12 +42,12 @@ def build_main(args_parser): logger.error('invalid define provided in command line') return 1 - work_dir = args.work_dir if args.work_dir else os.getcwd() + work_dir = args.work_dir if args.work_dir else Path.cwd() if args.output_dir: output_dir = args.output_dir else: - output_dir = os.path.join(work_dir, '_build', 'confluence') - doctrees_dir = os.path.join(output_dir, '.doctrees') + output_dir = work_dir / '_build' / 'confluence' + doctrees_dir = output_dir / '.doctrees' builder = args.action if args.action else DEFAULT_BUILDER verbosity = 0 diff --git a/sphinxcontrib/confluencebuilder/cmd/report.py b/sphinxcontrib/confluencebuilder/cmd/report.py index 9c4b9d88..10607c4d 100644 --- a/sphinxcontrib/confluencebuilder/cmd/report.py +++ b/sphinxcontrib/confluencebuilder/cmd/report.py @@ -3,6 +3,7 @@ from collections import OrderedDict from docutils import __version__ as docutils_version +from pathlib import Path from requests import __version__ as requests_version from sphinx import __version__ as sphinx_version from sphinx.application import Sphinx @@ -19,7 +20,6 @@ from urllib.parse import urlparse from urllib3 import __version__ as urllib3_version from xml.etree import ElementTree -import os import platform import sys import traceback @@ -68,7 +68,7 @@ def report_main(args_parser): rv = 0 offline = args.offline - work_dir = args.work_dir if args.work_dir else os.getcwd() + work_dir = args.work_dir if args.work_dir else Path.cwd() # setup sphinx engine to extract configuration config = {} @@ -81,10 +81,10 @@ def report_main(args_parser): print('fetching configuration information...') builder = ConfluenceReportBuilder.name app = Sphinx( - work_dir, # document sources - work_dir, # directory with configuration - tmp_dir, # output for built documents - tmp_dir, # output for doctree files + str(work_dir), # document sources + str(work_dir), # directory with configuration + str(tmp_dir), # output for built documents + str(tmp_dir), # output for doctree files builder, # builder to execute status=sys.stdout, # sphinx status output warning=sys.stderr) # sphinx warning output @@ -126,7 +126,7 @@ def report_main(args_parser): sys.stdout.flush() tb_msg = traceback.format_exc() logger.error(tb_msg) - if os.path.isfile(os.path.join(work_dir, 'conf.py')): + if Path(work_dir / 'conf.py').is_file(): configuration_load_issue = 'unable to load configuration' configuration_load_issue += '\n\n' + tb_msg.strip() else: diff --git a/sphinxcontrib/confluencebuilder/cmd/wipe.py b/sphinxcontrib/confluencebuilder/cmd/wipe.py index 639f4e9c..9c580475 100644 --- a/sphinxcontrib/confluencebuilder/cmd/wipe.py +++ b/sphinxcontrib/confluencebuilder/cmd/wipe.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinx.application import Sphinx from sphinx.locale import __ from sphinx.util.docutils import docutils_namespace @@ -8,7 +9,6 @@ from sphinxcontrib.confluencebuilder.logger import ConfluenceLogger as logger from sphinxcontrib.confluencebuilder.publisher import ConfluencePublisher from sphinxcontrib.confluencebuilder.util import temp_dir -import os import sys import traceback @@ -34,7 +34,7 @@ def wipe_main(args_parser): if unknown_args: logger.warn('unknown arguments: {}'.format(' '.join(unknown_args))) - work_dir = args.work_dir if args.work_dir else os.getcwd() + work_dir = args.work_dir if args.work_dir else Path.cwd() # protection warning if not args.danger: @@ -63,10 +63,10 @@ def wipe_main(args_parser): try: with temp_dir() as tmp_dir, docutils_namespace(): app = Sphinx( - work_dir, # document sources - work_dir, # directory with configuration - tmp_dir, # output for built documents - tmp_dir, # output for doctree files + str(work_dir), # document sources + str(work_dir), # directory with configuration + str(tmp_dir), # output for built documents + str(tmp_dir), # output for doctree files 'confluence', # builder to execute status=sys.stdout, # sphinx status output warning=sys.stderr) # sphinx warning output @@ -86,7 +86,7 @@ def wipe_main(args_parser): except Exception: # noqa: BLE001 sys.stdout.flush() logger.error(traceback.format_exc()) - if os.path.isfile(os.path.join(work_dir, 'conf.py')): + if Path(work_dir / 'conf.py').is_file(): logger.error('unable to load configuration') else: logger.error('no documentation/missing configuration') diff --git a/sphinxcontrib/confluencebuilder/config/checks.py b/sphinxcontrib/confluencebuilder/config/checks.py index c77ef93b..a71e8052 100644 --- a/sphinxcontrib/confluencebuilder/config/checks.py +++ b/sphinxcontrib/confluencebuilder/config/checks.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinxcontrib.confluencebuilder.config.notifications import deprecated from sphinxcontrib.confluencebuilder.config.notifications import warnings from sphinxcontrib.confluencebuilder.config.validation import ConfigurationValidation @@ -170,7 +171,7 @@ def validate_configuration(builder): ''') for cert in cert_files: - if cert and not os.path.isfile(os.path.join(env.srcdir, cert)): + if cert and not Path(env.srcdir, cert).is_file(): raise ConfluenceConfigurationError('''\ confluence_client_cert missing certificate file diff --git a/sphinxcontrib/confluencebuilder/config/defaults.py b/sphinxcontrib/confluencebuilder/config/defaults.py index 86655e5b..cc04c526 100644 --- a/sphinxcontrib/confluencebuilder/config/defaults.py +++ b/sphinxcontrib/confluencebuilder/config/defaults.py @@ -1,9 +1,9 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinxcontrib.confluencebuilder.debug import PublishDebug from sphinxcontrib.confluencebuilder.util import str2bool -import os # configures the default editor to publication @@ -43,13 +43,13 @@ def apply_defaults(builder): if conf.confluence_adv_restricted is None: conf.confluence_adv_restricted = [] - if conf.confluence_ca_cert and not os.path.isabs(conf.confluence_ca_cert): - # if the ca path is not an absolute path, the path is a relative - # path based on the source directory (i.e. passed configuration - # checks); resolve the file here before it eventually gets provided - # to Requests - conf.confluence_ca_cert = os.path.join( - env.srcdir, conf.confluence_ca_cert) + if conf.confluence_ca_cert: + if not Path(conf.confluence_ca_cert).is_absolute(): + # if the ca path is not an absolute path, the path is a relative + # path based on the source directory (i.e. passed configuration + # checks); resolve the file here before it eventually gets provided + # to Requests + conf.confluence_ca_cert = Path(env.srcdir, conf.confluence_ca_cert) if conf.confluence_cleanup_search_mode is None: # the default is `search`, since on Confluence Server/DC; the `direct` diff --git a/sphinxcontrib/confluencebuilder/env.py b/sphinxcontrib/confluencebuilder/env.py index 4b28e61a..86596607 100644 --- a/sphinxcontrib/confluencebuilder/env.py +++ b/sphinxcontrib/confluencebuilder/env.py @@ -135,7 +135,7 @@ def track_page_hash(self, docname): return doc_hash # determine the hash of the document based on data + config-hash - src_file = self.env.doc2path(docname) + src_file = Path(self.env.doc2path(docname)) src_file_hash = ConfluenceUtil.hash_asset(src_file) doc_hash_data = self._active_hash + src_file_hash doc_hash = ConfluenceUtil.hash(doc_hash_data) diff --git a/sphinxcontrib/confluencebuilder/logger.py b/sphinxcontrib/confluencebuilder/logger.py index 50084cc6..f18c6cf0 100644 --- a/sphinxcontrib/confluencebuilder/logger.py +++ b/sphinxcontrib/confluencebuilder/logger.py @@ -3,6 +3,7 @@ from collections import deque from contextlib import suppress +from pathlib import Path from sphinx.util import logging from sphinx.util.console import bold # pylint: disable=no-name-in-module import sys @@ -118,7 +119,8 @@ def trace(container, data): This is solely for manually debugging unexpected scenarios. """ try: - with open('trace.log', 'a', encoding='utf-8') as file: + trace_file = Path('trace.log') + with trace_file.open('a', encoding='utf-8') as file: file.write('[%s]\n' % container) file.write(data) file.write('\n') diff --git a/sphinxcontrib/confluencebuilder/storage/index.py b/sphinxcontrib/confluencebuilder/storage/index.py index 5f5a58fb..25431ce8 100644 --- a/sphinxcontrib/confluencebuilder/storage/index.py +++ b/sphinxcontrib/confluencebuilder/storage/index.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinx.environment.adapters.indexentries import IndexEntries from sphinxcontrib.confluencebuilder.locale import L as sccb_translation from sphinxcontrib.confluencebuilder.state import ConfluenceState from sphinxcontrib.confluencebuilder.storage import intern_uri_anchor_value -import os import pkgutil import posixpath @@ -48,8 +48,8 @@ def generate_storage_format_domainindex(builder, docname, f): else: domainindex_fname = 'domainindex.html' - domainindex_template = os.path.join('templates', domainindex_fname) - template_data = pkgutil.get_data(__name__, domainindex_template) + domainindex_template = Path('templates', domainindex_fname) + template_data = pkgutil.get_data(__name__, str(domainindex_template)) # process the template with the generated index ctx = { @@ -96,8 +96,8 @@ def generate_storage_format_genindex(builder, docname, f): else: genindex_fname = 'genindex.html' - genindex_template = os.path.join('templates', genindex_fname) - template_data = pkgutil.get_data(__name__, genindex_template) + genindex_template = Path('templates', genindex_fname) + template_data = pkgutil.get_data(__name__, str(genindex_template)) # process the template with the generated index ctx = { @@ -125,7 +125,9 @@ def process_doclink(config, refuri): the document's title and anchor value """ - docname = posixpath.normpath(os.path.splitext(refuri.split('#')[0])[0]) + doc_path = Path(refuri.split('#')[0]) + doc_raw_id = doc_path.parent / doc_path.stem + docname = posixpath.normpath(doc_raw_id.as_posix()) doctitle = ConfluenceState.title(docname) anchor_value = intern_uri_anchor_value(docname, refuri) diff --git a/sphinxcontrib/confluencebuilder/storage/search.py b/sphinxcontrib/confluencebuilder/storage/search.py index b39fff52..cfdf2287 100644 --- a/sphinxcontrib/confluencebuilder/storage/search.py +++ b/sphinxcontrib/confluencebuilder/storage/search.py @@ -1,8 +1,8 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) +from pathlib import Path from sphinxcontrib.confluencebuilder.locale import L as sccb_translation -import os import pkgutil @@ -23,8 +23,8 @@ def generate_storage_format_search(builder, docname, f): space_name = builder.config.confluence_space_name # fetch raw template data - search_template = os.path.join('templates', 'search.html') - template_data = pkgutil.get_data(__name__, search_template) + search_template = Path('templates', 'search.html') + template_data = pkgutil.get_data(__name__, str(search_template)) # process the template with the generated index ctx = { diff --git a/sphinxcontrib/confluencebuilder/storage/translator.py b/sphinxcontrib/confluencebuilder/storage/translator.py index b52f9ccd..7a42380f 100644 --- a/sphinxcontrib/confluencebuilder/storage/translator.py +++ b/sphinxcontrib/confluencebuilder/storage/translator.py @@ -4,7 +4,7 @@ from contextlib import suppress from docutils import nodes -from os import path +from pathlib import Path from sphinx import addnodes from sphinx.locale import _ as SL from sphinx.locale import admonitionlabels @@ -1340,8 +1340,9 @@ def _visit_reference_intern_id(self, node): self._reference_context.append(self._end_ac_link_body(node)) def _visit_reference_intern_uri(self, node): - docname = posixpath.normpath( - self.docparent + path.splitext(node['refuri'].split('#')[0])[0]) + doc_path = Path(node['refuri'].split('#')[0]) + doc_raw_id = Path(self.docparent) / doc_path.parent / doc_path.stem + docname = posixpath.normpath(doc_raw_id.as_posix()) doctitle = self.state.title(docname) if not doctitle: self.warn('unable to build link to document due to ' @@ -2517,8 +2518,9 @@ def visit_confluence_doc_card(self, node): self.body.append(self._start_ac_macro(node, 'panel')) self.body.append(self._start_ac_rich_text_body_macro(node)) - docname = posixpath.normpath(self.docparent + - path.splitext(PARAMS(node)['href'].split('#')[0])[0]) + doc_path = Path(PARAMS(node)['href'].split('#')[0]) + doc_raw_id = Path(self.docparent) / doc_path.parent / doc_path.stem + docname = posixpath.normpath(doc_raw_id.as_posix()) doctitle = self.state.title(docname) if doctitle: attribs = {} @@ -2550,8 +2552,9 @@ def visit_confluence_doc_card(self, node): raise nodes.SkipNode def visit_confluence_doc_card_inline(self, node): - docname = posixpath.normpath(self.docparent + - path.splitext(node['reftarget'].split('#')[0])[0]) + doc_path = Path(node['reftarget'].split('#')[0]) + doc_raw_id = Path(self.docparent) / doc_path.parent / doc_path.stem + docname = posixpath.normpath(doc_raw_id.as_posix()) doctitle = self.state.title(docname) if doctitle: doctitle = self.encode(doctitle) diff --git a/sphinxcontrib/confluencebuilder/svg.py b/sphinxcontrib/confluencebuilder/svg.py index d68b807b..4f3e28b7 100644 --- a/sphinxcontrib/confluencebuilder/svg.py +++ b/sphinxcontrib/confluencebuilder/svg.py @@ -60,17 +60,17 @@ def confluence_supported_svg(builder, node): return # invalid uri/path - uri_abspath = find_env_abspath(builder.env, builder.outdir, uri) - if not uri_abspath: + abs_path = find_env_abspath(builder.env, builder.out_dir, uri) + if not abs_path: return # ignore non-svgs - mimetype = guess_mimetype(uri_abspath) + mimetype = guess_mimetype(str(abs_path)) # cast for sphinx-6.1 if mimetype != 'image/svg+xml': return try: - with open(uri_abspath, 'rb') as f: + with abs_path.open('rb') as f: svg_data = f.read() except (IOError, OSError) as err: builder.warn('error reading svg: %s' % err) diff --git a/sphinxcontrib/confluencebuilder/translator.py b/sphinxcontrib/confluencebuilder/translator.py index 6f753eaf..a10dac62 100644 --- a/sphinxcontrib/confluencebuilder/translator.py +++ b/sphinxcontrib/confluencebuilder/translator.py @@ -3,7 +3,7 @@ from docutils import nodes from docutils.nodes import NodeVisitor as BaseTranslator -from os import path +from pathlib import Path from sphinx.util.images import get_image_size from sphinx.util.images import guess_mimetype from sphinx.util.osutil import SEP @@ -49,9 +49,10 @@ def __init__(self, document, builder): # for relative document uris # (see '_visit_reference_intern_uri') if SEP in self.docname: - self.docparent = self.docname[0 : self.docname.rfind(SEP) + 1] + docparent = self.docname[0 : self.docname.rfind(SEP) + 1] else: - self.docparent = '' + docparent = '' + self.docparent = Path(docparent) self.assets = builder.assets self.body = [] @@ -88,10 +89,10 @@ def depart_document(self, node): # prepend header (if any) if self.builder.config.confluence_header_file is not None: header_template_data = '' - header_file = path.join(self.builder.env.srcdir, + header_file = Path(self.builder.env.srcdir, self.builder.config.confluence_header_file) try: - with open(header_file, encoding='utf-8') as file: + with header_file.open(encoding='utf-8') as file: header_template_data = file.read() except (IOError, OSError) as err: self.warn(f'error reading file {header_file}: {err}') @@ -113,10 +114,10 @@ def depart_document(self, node): # append footer (if any) if self.builder.config.confluence_footer_file is not None: footer_template_data = '' - footer_file = path.join(self.builder.env.srcdir, + footer_file = Path(self.builder.env.srcdir, self.builder.config.confluence_footer_file) try: - with open(footer_file, encoding='utf-8') as file: + with footer_file.open(encoding='utf-8') as file: footer_template_data = file.read() except (IOError, OSError) as err: self.warn(f'error reading file {footer_file}: {err}') diff --git a/sphinxcontrib/confluencebuilder/transmute/ext_sphinx_toolbox.py b/sphinxcontrib/confluencebuilder/transmute/ext_sphinx_toolbox.py index a3f05ba0..b54b492b 100644 --- a/sphinxcontrib/confluencebuilder/transmute/ext_sphinx_toolbox.py +++ b/sphinxcontrib/confluencebuilder/transmute/ext_sphinx_toolbox.py @@ -2,7 +2,7 @@ # Copyright Sphinx Confluence Builder Contributors (AUTHORS) from docutils import nodes -from os import path +from pathlib import Path from sphinx import addnodes from sphinxcontrib.confluencebuilder.compat import docutils_findall as findall from sphinxcontrib.confluencebuilder.nodes import confluence_expand @@ -68,11 +68,11 @@ def replace_sphinx_toolbox_nodes(builder, doctree): # directory; which the processing of a download_reference will # strip and use the asset directory has the container folder to find # the file in - mock_docname = path.join(builder.config.assets_dir, 'mock') + mock_docname = Path(builder.config.assets_dir, 'mock') new_node = addnodes.download_reference( node.astext(), node.astext(), - refdoc=mock_docname, + refdoc=str(mock_docname), refexplicit=True, reftarget=node['refuri'], ) diff --git a/sphinxcontrib/confluencebuilder/util.py b/sphinxcontrib/confluencebuilder/util.py index 1aea30db..d646d253 100644 --- a/sphinxcontrib/confluencebuilder/util.py +++ b/sphinxcontrib/confluencebuilder/util.py @@ -56,7 +56,7 @@ def hash_asset(asset): """ BLOCKSIZE = 65536 sha = sha256() - with open(asset, 'rb') as file: + with asset.open('rb') as file: buff = file.read(BLOCKSIZE) while len(buff) > 0: sha.update(buff) @@ -185,8 +185,9 @@ def extract_strings_from_file(filename): """ filelist = [] - if os.path.isfile(filename): - with open(filename) as f: + file = Path(filename) if isinstance(filename, str) else filename + if file.is_file(): + with file.open() as f: for raw_line in f: line = raw_line.strip() if not line or line.startswith('#'): @@ -196,7 +197,7 @@ def extract_strings_from_file(filename): return filelist -def find_env_abspath(env, outdir, path): +def find_env_abspath(env, out_dir, path: str): """ find an existing absolute path for a provided path in a sphinx environment @@ -206,43 +207,51 @@ def find_env_abspath(env, outdir, path): Args: env: the build environment - outdir: the build's output directory + out_dir: the build's output directory path: the path to use Returns: the absolute path """ - abspath = None - if path: - path = os.path.normpath(path) - if os.path.isabs(path): - abspath = path - - # some generated nodes will prefix the path of an asset with `/`, - # with the intent of that path being the root from the - # configured source directory instead of an absolute path on the - # system -- check the environment's source directory first before - # checking the full system's path - if path[0] == os.sep: - new_path = os.path.join(env.srcdir, path[1:]) - - if os.path.isfile(new_path): - abspath = new_path - else: - abspath = os.path.join(env.srcdir, path) + if not path: + return None + + try: + normalized_path = Path(os.path.normpath(path)) + except OSError: + # ignore paths that may not resolve; this may include paths with + # with wildcard hints to be used for candidate fetching at a + # later stage + return None + + if normalized_path.is_absolute(): + abs_path = normalized_path + + # some generated nodes will prefix the path of an asset with `/`, + # with the intent of that path being the root from the + # configured source directory instead of an absolute path on the + # system -- check the environment's source directory first before + # checking the full system's path + if normalized_path.name[0] == os.sep: + new_path = Path(env.srcdir, normalized_path.name[1:]) + + if new_path.is_file(): + abs_path = new_path + else: + abs_path = Path(env.srcdir, normalized_path) - # extensions may dump a generated asset in the output directory; if - # the absolute mapping to the source directory does not find the - # asset, attempt to bind the path based on the output directory - if not os.path.isfile(abspath): - abspath = os.path.join(outdir, path) + # extensions may dump a generated asset in the output directory; if + # the absolute mapping to the source directory does not find the + # asset, attempt to bind the path based on the output directory + if not abs_path.is_file(): + abs_path = out_dir / normalized_path # if no asset can be found, ensure a `None` path is returned - if not os.path.isfile(abspath): - abspath = None + if not abs_path.is_file(): + abs_path = None - return abspath + return abs_path def first(it): @@ -399,6 +408,6 @@ def temp_dir(): """ dir_ = tempfile.mkdtemp() try: - yield dir_ + yield Path(dir_) finally: shutil.rmtree(dir_, ignore_errors=True) diff --git a/tests/unit-tests/test_cache.py b/tests/unit-tests/test_cache.py index bfc897b6..ffe24b1f 100644 --- a/tests/unit-tests/test_cache.py +++ b/tests/unit-tests/test_cache.py @@ -1,7 +1,6 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) -from pathlib import Path from sphinxcontrib.confluencebuilder.util import temp_dir from tests.lib import prepare_dirs from tests.lib.testcase import ConfluenceTestCase @@ -98,7 +97,6 @@ def write_doc(file, data): pass with temp_dir() as src_dir: - src_dir = Path(src_dir) index_file = src_dir / 'index.rst' write_doc(index_file, '''\ index diff --git a/tests/unit-tests/test_legacy_pages.py b/tests/unit-tests/test_legacy_pages.py index 9495a36b..080b6985 100644 --- a/tests/unit-tests/test_legacy_pages.py +++ b/tests/unit-tests/test_legacy_pages.py @@ -1,7 +1,6 @@ # SPDX-License-Identifier: BSD-2-Clause # Copyright Sphinx Confluence Builder Contributors (AUTHORS) -from pathlib import Path from sphinxcontrib.confluencebuilder.builder import ConfluenceBuilder from sphinxcontrib.confluencebuilder.util import temp_dir from tests.lib import prepare_dirs @@ -39,7 +38,6 @@ def wrapped_init(builder): out_dir = prepare_dirs() with temp_dir() as src_dir: - src_dir = Path(src_dir) conf_file = src_dir / 'conf.py' write_doc(conf_file, '') diff --git a/tests/unit-tests/test_translator.py b/tests/unit-tests/test_translator.py index 7691ba12..8b6679f9 100644 --- a/tests/unit-tests/test_translator.py +++ b/tests/unit-tests/test_translator.py @@ -33,4 +33,4 @@ def test_translator_docname_and_docparent(self): translator = ConfluenceBaseTranslator(doc, app.builder) self.assertEqual(translator.docname, 'foo/bar/baz') - self.assertEqual(translator.docparent, 'foo/bar/') + self.assertEqual(translator.docparent.as_posix(), 'foo/bar')