Skip to content

Commit

Permalink
Begin to use pathlib
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Jul 30, 2024
1 parent 476cbcc commit 354a2fe
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 57 deletions.
32 changes: 12 additions & 20 deletions breathe/apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import os
import sys
import argparse
import errno
import xml.etree.ElementTree
from pathlib import Path

from breathe import __version__

Expand Down Expand Up @@ -46,32 +46,24 @@ def print_info(msg, args):

def write_file(name, text, args):
"""Write the output file for module/package <name>."""
fname = os.path.join(args.destdir, "%s.%s" % (name, args.suffix))
fname = Path(args.destdir, f"{name}.{args.suffix}")
if args.dryrun:
print_info("Would create file %s." % fname, args)
return
if not args.force and os.path.isfile(fname):
if not args.force and fname.is_file():
print_info("File %s already exists, skipping." % fname, args)
else:
print_info("Creating file %s." % fname, args)
if not os.path.exists(os.path.dirname(fname)):
try:
os.makedirs(os.path.dirname(fname))
except OSError as exc: # Guard against race condition
if exc.errno != errno.EEXIST:
raise
fname.parent.mkdir(parents=True, exist_ok=True)
try:
with open(fname) as target:
orig = target.read()
if orig == text:
print_info("File %s up to date, skipping." % fname, args)
return
orig = fname.read_text()
if orig == text:
print_info("File %s up to date, skipping." % fname, args)
return
except FileNotFoundError:
# Don't mind if it isn't there
pass

with open(fname, "w") as target:
target.write(text)
fname.write_text(text)


def format_heading(level, text):
Expand Down Expand Up @@ -100,12 +92,12 @@ def create_package_file(package, package_type, package_id, args):
text = format_heading(1, "%s %s" % (TYPEDICT[package_type], package))
text += format_directive(package_type, package, args)

write_file(os.path.join(package_type, package_id), text, args)
write_file(Path(package_type, package_id), text, args)


def create_modules_toc_file(key, value, args):
"""Create the module's index."""
if not os.path.isdir(os.path.join(args.destdir, key)):
if not Path(args.destdir, key).is_dir():
return
text = format_heading(1, "%s list" % value)
text += ".. toctree::\n"
Expand All @@ -120,7 +112,7 @@ def recurse_tree(args):
Look for every file in the directory tree and create the corresponding
ReST files.
"""
index = xml.etree.ElementTree.parse(os.path.join(args.rootpath, "index.xml"))
index = xml.etree.ElementTree.parse(Path(args.rootpath, "index.xml"))

# Assuming this is a valid Doxygen XML
for compound in index.getroot():
Expand Down
14 changes: 7 additions & 7 deletions breathe/directives/setup.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from breathe.directives.class_like import (
DoxygenStructDirective,
DoxygenClassDirective,
Expand Down Expand Up @@ -26,7 +28,6 @@

from sphinx.application import Sphinx

import os
import subprocess


Expand Down Expand Up @@ -94,17 +95,16 @@ def set_temp_data(
app.add_config_value("breathe_separate_member_pages", False, "env")

breathe_css = "breathe.css"
if os.path.exists(os.path.join(app.confdir, "_static", breathe_css)):
if Path(app.confdir, "_static", breathe_css).exists():
app.add_css_file(breathe_css)

def write_file(directory, filename, content):
# Check the directory exists
if not os.path.exists(directory):
os.makedirs(directory)
# Ensure that the directory exists
directory = Path(directory)
directory.mkdir(parents=True, exist_ok=True)

# Write the file with the provided contents
with open(os.path.join(directory, filename), "w") as f:
f.write(content)
(directory / filename).write_text(content)

doxygen_handle = AutoDoxygenProcessHandle(
subprocess.check_call, write_file, project_info_factory
Expand Down
11 changes: 7 additions & 4 deletions breathe/file_state_cache.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment

Expand Down Expand Up @@ -27,19 +29,20 @@ def _getmtime(filename: str):
raise MTimeError("Cannot find file: %s" % os.path.realpath(filename))


def update(app: Sphinx, source_file: str) -> None:
def update(app: Sphinx, source_file: str | os.PathLike[str]) -> None:
if not hasattr(app.env, "breathe_file_state"):
app.env.breathe_file_state = {} # type: ignore

new_mtime = _getmtime(source_file)
norm_source_file = Path(source_file).resolve().as_posix()
new_mtime = _getmtime(norm_source_file)
mtime, docnames = app.env.breathe_file_state.setdefault( # type: ignore
source_file, (new_mtime, set())
norm_source_file, (new_mtime, set())
)

assert app.env is not None
docnames.add(app.env.docname)

app.env.breathe_file_state[source_file] = (new_mtime, docnames) # type: ignore
app.env.breathe_file_state[norm_source_file] = (new_mtime, docnames) # type: ignore


def _get_outdated(
Expand Down
14 changes: 8 additions & 6 deletions breathe/parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pathlib import Path

from . import index
from . import compound

from breathe import file_state_cache, path_handler
from breathe import file_state_cache
from breathe.project import ProjectInfo

from sphinx.application import Sphinx
Expand Down Expand Up @@ -34,7 +36,7 @@ def __init__(self, app: Sphinx, cache):

class DoxygenIndexParser(Parser):
def parse(self, project_info: ProjectInfo):
filename = path_handler.resolve_path(self.app, project_info.project_path(), "index.xml")
filename = Path(self.app.confdir, project_info.project_path(), "index.xml").resolve()
file_state_cache.update(self.app, filename)

try:
Expand All @@ -60,11 +62,11 @@ def __init__(self, app: Sphinx, cache,
self.project_info = project_info

def parse(self, refid: str):
filename = path_handler.resolve_path(
self.app,
filename = Path(
self.app.confdir,
self.project_info.project_path(),
"%s.xml" % refid
)
f"{refid}.xml"
).resolve()

file_state_cache.update(self.app, filename)

Expand Down
9 changes: 4 additions & 5 deletions breathe/path_handler.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from sphinx.application import Sphinx
from pathlib import Path

import os
from sphinx.application import Sphinx


def includes_directory(file_path: str):
# Check for backslash or forward slash as we don't know what platform we're on and sometimes
# the doxygen paths will have forward slash even on Windows.
return bool(file_path.count("\\")) or bool(file_path.count("/"))
return bool(str(file_path).count("\\")) or bool(str(file_path).count("/"))


def resolve_path(app: Sphinx, directory: str, filename: str):
"""Returns a full path to the filename in the given directory assuming that if the directory
path is relative, then it is relative to the conf.py directory.
"""

# os.path.join does the appropriate handling if _project_path is an absolute path
return os.path.join(app.confdir, directory, filename)
return Path(app.confdir, directory, filename).resolve()
10 changes: 6 additions & 4 deletions breathe/process.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from breathe.project import AutoProjectInfo, ProjectInfoFactory

import os
Expand Down Expand Up @@ -38,7 +40,7 @@ class AutoDoxygenProcessHandle:
def __init__(
self,
run_process: Callable,
write_file: Callable[[str, str, str], None],
write_file: Callable[[str | os.PathLike[str], str, str], None],
project_info_factory: ProjectInfoFactory,
) -> None:
self.run_process = run_process
Expand Down Expand Up @@ -92,13 +94,13 @@ def process(
extra=f"{options}\n{aliases}",
)

build_dir = os.path.join(auto_project_info.build_dir(), "breathe", "doxygen")
cfgfile = "%s.cfg" % name
build_dir = Path(auto_project_info.build_dir(), "breathe", "doxygen")
cfgfile = f"{name}.cfg"
self.write_file(build_dir, cfgfile, cfg)

# Shell-escape the cfg file name to try to avoid any issue where the name might include
# malicious shell character - We have to use the shell=True option to make it work on
# Windows. See issue #271
self.run_process("doxygen %s" % quote(cfgfile), cwd=build_dir, shell=True)
self.run_process(f"doxygen {quote(cfgfile)}", cwd=build_dir, shell=True)

return os.path.join(build_dir, name, "xml")
5 changes: 3 additions & 2 deletions breathe/project.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from pathlib import Path

from .exception import BreatheError

from sphinx.application import Sphinx
Expand Down Expand Up @@ -40,8 +42,7 @@ def abs_path_to_source_file(self, file_):
projects conf.py directory as specified in the breathe_projects_source config variable.
"""

# os.path.join does the appropriate handling if _source_path is an absolute path
return os.path.join(self.app.confdir, self._source_path, file_)
return Path(self.app.confdir, self._source_path, file_).resolve()

def create_project_info(self, project_path):
"""Creates a proper ProjectInfo object based on the information in this AutoProjectInfo"""
Expand Down
2 changes: 1 addition & 1 deletion breathe/renderer/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ def allow(self, node_stack) -> bool:
# If the target_file contains directory separators then
# match against the same length at the end of the location
#
location_match = location[-len(self.target_file) :]
location_match = location[-len(self.target_file):]
return location_match == self.target_file
else:
# If there are no separators, match against the whole filename
Expand Down
13 changes: 5 additions & 8 deletions breathe/renderer/sphinxrenderer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
from pathlib import Path

from breathe.parser import compound, compoundsuper, DoxygenCompoundParser
from breathe.project import ProjectInfo
Expand Down Expand Up @@ -2348,21 +2348,18 @@ def visit_docdot(self, node) -> List[Node]:
def visit_docdotfile(self, node) -> List[Node]:
"""Translate node from doxygen's dotfile command to sphinx's graphviz directive."""
dotcode = ""
dot_file_path = node.name # type: str
dot_file_path = Path(node.name)
# Doxygen v1.9.3+ uses a relative path to specify the dot file.
# Previously, Doxygen used an absolute path.
# This relative path is with respect to the XML_OUTPUT path.
# Furthermore, Doxygen v1.9.3+ will copy the dot file into the XML_OUTPUT
if not os.path.isabs(dot_file_path):
if not dot_file_path.is_absolute():
# Use self.project_info.project_path as the XML_OUTPUT path, and
# make it absolute with consideration to the conf.py path
project_path = self.project_info.project_path()
dot_file_path = os.path.abspath(
os.path.join(self.app.confdir, project_path, dot_file_path)
)
dot_file_path = Path(self.app.confdir, project_path, dot_file_path).resolve()
try:
with open(dot_file_path, encoding="utf-8") as fp:
dotcode = fp.read()
dotcode = dot_file_path.read_text(encoding="utf-8")
if not dotcode.rstrip("\n"):
raise RuntimeError("%s found but without any content" % dot_file_path)
except OSError as exc:
Expand Down

0 comments on commit 354a2fe

Please sign in to comment.