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

Use pathlib (Part 2) #889

Merged
merged 9 commits into from
Feb 27, 2024
3 changes: 2 additions & 1 deletion sphinxcontrib/confluencebuilder/__main__.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 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
Expand Down Expand Up @@ -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:
Expand Down
33 changes: 17 additions & 16 deletions sphinxcontrib/confluencebuilder/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'
Expand Down Expand Up @@ -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

Expand All @@ -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):
"""
Expand Down Expand Up @@ -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
Expand All @@ -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_)
Expand Down Expand Up @@ -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:
Expand Down
19 changes: 9 additions & 10 deletions sphinxcontrib/confluencebuilder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down
10 changes: 5 additions & 5 deletions sphinxcontrib/confluencebuilder/cmd/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down
14 changes: 7 additions & 7 deletions sphinxcontrib/confluencebuilder/cmd/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 = {}
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down
14 changes: 7 additions & 7 deletions sphinxcontrib/confluencebuilder/cmd/wipe.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# 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
from sphinxcontrib.confluencebuilder.config import process_ask_configs
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

Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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')
Expand Down
3 changes: 2 additions & 1 deletion sphinxcontrib/confluencebuilder/config/checks.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.config.notifications import deprecated
from sphinxcontrib.confluencebuilder.config.notifications import warnings
from sphinxcontrib.confluencebuilder.config.validation import ConfigurationValidation
Expand Down Expand Up @@ -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

Expand Down
16 changes: 8 additions & 8 deletions sphinxcontrib/confluencebuilder/config/defaults.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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`
Expand Down
2 changes: 1 addition & 1 deletion sphinxcontrib/confluencebuilder/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion sphinxcontrib/confluencebuilder/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
Loading