Skip to content

Commit 1b47b77

Browse files
committed
internal refactorings
1 parent 201dba6 commit 1b47b77

File tree

3 files changed

+132
-154
lines changed

3 files changed

+132
-154
lines changed

src/sphinx_fortran_domain/__init__.py

Lines changed: 7 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from importlib import metadata
44
from pathlib import Path
5-
import glob
65
import hashlib
76
import json
87
import os
@@ -14,6 +13,7 @@
1413

1514
from sphinx_fortran_domain.domain import FortranDomain
1615
from sphinx_fortran_domain.lexers import get_lexer, register_builtin_lexers
16+
from sphinx_fortran_domain._utils import collect_fortran_source_files, _as_chars, _as_list
1717

1818

1919
logger = logging.getLogger(__name__)
@@ -112,24 +112,6 @@ def _maybe_force_reread(app: Sphinx, env, added, changed, removed):
112112
return []
113113

114114

115-
def _as_list(value) -> List[str]:
116-
if value is None:
117-
return []
118-
if isinstance(value, str):
119-
return [value]
120-
return [str(v) for v in value]
121-
122-
123-
def _as_chars(value) -> List[str]:
124-
"""Normalize a config value into a list of single-character strings."""
125-
if value is None:
126-
return []
127-
if isinstance(value, str):
128-
# Allow ">!@" style strings.
129-
return [c for c in value if c.strip()]
130-
return [str(v) for v in value]
131-
132-
133115
def _doc_markers_from_config(app: Sphinx) -> List[str]:
134116
"""Return list of 2-character doc markers.
135117
@@ -159,70 +141,12 @@ def _collect_fortran_files(app: Sphinx) -> List[str]:
159141
exts = {e.lower() for e in _as_list(getattr(app.config, "fortran_file_extensions", []))}
160142
roots = _as_list(getattr(app.config, "fortran_sources", []))
161143
excludes = _as_list(getattr(app.config, "fortran_sources_exclude", []))
162-
if not roots:
163-
return []
164-
165-
files: List[str] = []
166-
for root in roots:
167-
if any(ch in root for ch in "*?["):
168-
pattern = str((Path(app.confdir) / root))
169-
for match in glob.glob(pattern, recursive=True):
170-
p = Path(match)
171-
if p.is_file() and (not exts or p.suffix.lower() in exts):
172-
files.append(str(p))
173-
continue
174-
175-
p = Path(root)
176-
if not p.is_absolute():
177-
p = Path(app.confdir) / p
178-
179-
if p.is_dir():
180-
for child in p.rglob("*"):
181-
if child.is_file() and (not exts or child.suffix.lower() in exts):
182-
files.append(str(child))
183-
elif p.is_file():
184-
if not exts or p.suffix.lower() in exts:
185-
files.append(str(p))
186-
187-
if excludes:
188-
def _norm(s: str) -> str:
189-
try:
190-
return os.path.normcase(str(Path(s).resolve()))
191-
except Exception:
192-
return os.path.normcase(str(Path(s)))
193-
194-
exclude_files: set[str] = set()
195-
confdir = Path(app.confdir)
196-
for raw in excludes:
197-
pat = str(raw)
198-
# Glob patterns
199-
if any(ch in pat for ch in "*?["):
200-
pattern = str(confdir / pat)
201-
for match in glob.glob(pattern, recursive=True):
202-
p = Path(match)
203-
if p.is_dir():
204-
for child in p.rglob("*"):
205-
if child.is_file() and (not exts or child.suffix.lower() in exts):
206-
exclude_files.add(_norm(str(child)))
207-
elif p.is_file() and (not exts or p.suffix.lower() in exts):
208-
exclude_files.add(_norm(str(p)))
209-
continue
210-
211-
p = Path(pat)
212-
if not p.is_absolute():
213-
p = confdir / p
214-
if p.is_dir():
215-
for child in p.rglob("*"):
216-
if child.is_file() and (not exts or child.suffix.lower() in exts):
217-
exclude_files.add(_norm(str(child)))
218-
elif p.is_file() and (not exts or p.suffix.lower() in exts):
219-
exclude_files.add(_norm(str(p)))
220-
221-
if exclude_files:
222-
files = [f for f in files if _norm(f) not in exclude_files]
223-
224-
# Deterministic order
225-
return sorted(set(files))
144+
return collect_fortran_source_files(
145+
confdir=Path(app.confdir),
146+
roots=roots,
147+
extensions=exts,
148+
excludes=excludes,
149+
)
226150

227151

228152
def _load_symbols(app: Sphinx) -> None:
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from __future__ import annotations
2+
3+
import glob
4+
import os
5+
from pathlib import Path
6+
from typing import Iterable, Sequence
7+
8+
9+
_WILDCARDS = "*?["
10+
11+
12+
def _has_wildcards(s: str) -> bool:
13+
return any(ch in s for ch in _WILDCARDS)
14+
15+
16+
def _as_list(value) -> list[str]:
17+
"""Normalize a config value into a list of strings."""
18+
if value is None:
19+
return []
20+
if isinstance(value, str):
21+
return [value]
22+
return [str(v) for v in value]
23+
24+
def _as_chars(value) -> List[str]:
25+
"""Normalize a config value into a list of single-character strings."""
26+
if value is None:
27+
return []
28+
if isinstance(value, str):
29+
# Allow ">!@" style strings.
30+
return [c for c in value if c.strip()]
31+
return [str(v) for v in value]
32+
33+
34+
def _norm_path(path: str) -> str:
35+
"""Normalize a path for comparison across platforms."""
36+
try:
37+
return os.path.normcase(str(Path(path).resolve()))
38+
except Exception:
39+
return os.path.normcase(str(Path(path)))
40+
41+
42+
def collect_fortran_source_files(
43+
*,
44+
confdir: Path,
45+
roots: Sequence[str],
46+
extensions: set[str],
47+
excludes: Sequence[str] = (),
48+
) -> list[str]:
49+
"""Collect Fortran source files from roots, honoring excludes.
50+
51+
- roots may be files, directories, or glob patterns (relative to confdir).
52+
- excludes may be files, directories, or glob patterns (relative to confdir).
53+
- extensions is a set of allowed suffixes (lower-cased). Empty means "allow any".
54+
55+
Returns a deterministic sorted list of file paths as strings.
56+
"""
57+
if not roots:
58+
return []
59+
60+
confdir = Path(confdir)
61+
files: list[str] = []
62+
63+
def _accept(p: Path) -> bool:
64+
return p.is_file() and (not extensions or p.suffix.lower() in extensions)
65+
66+
def _add_from_dir(d: Path) -> None:
67+
for child in d.rglob("*"):
68+
if _accept(child):
69+
files.append(str(child))
70+
71+
for raw_root in roots:
72+
root = str(raw_root)
73+
if _has_wildcards(root):
74+
pattern = str(confdir / root)
75+
for match in glob.glob(pattern, recursive=True):
76+
p = Path(match)
77+
if _accept(p):
78+
files.append(str(p))
79+
continue
80+
81+
p = Path(root)
82+
if not p.is_absolute():
83+
p = confdir / p
84+
if p.is_dir():
85+
_add_from_dir(p)
86+
elif _accept(p):
87+
files.append(str(p))
88+
89+
if excludes:
90+
exclude_files: set[str] = set()
91+
92+
def _exclude_path(p: Path) -> None:
93+
if p.is_dir():
94+
for child in p.rglob("*"):
95+
if _accept(child):
96+
exclude_files.add(_norm_path(str(child)))
97+
elif _accept(p):
98+
exclude_files.add(_norm_path(str(p)))
99+
100+
for raw_ex in excludes:
101+
pat = str(raw_ex)
102+
if _has_wildcards(pat):
103+
pattern = str(confdir / pat)
104+
for match in glob.glob(pattern, recursive=True):
105+
_exclude_path(Path(match))
106+
continue
107+
108+
p = Path(pat)
109+
if not p.is_absolute():
110+
p = confdir / p
111+
_exclude_path(p)
112+
113+
if exclude_files:
114+
files = [f for f in files if _norm_path(f) not in exclude_files]
115+
116+
# Deterministic order
117+
return sorted(set(files))

src/sphinx_fortran_domain/directives.py

Lines changed: 8 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import glob
43
import os
54
import re
65
from pathlib import Path
@@ -13,6 +12,8 @@
1312
from sphinx.directives import ObjectDescription
1413
from sphinx.util.parsing import nested_parse_to_nodes
1514

15+
from sphinx_fortran_domain._utils import collect_fortran_source_files, _as_list
16+
1617

1718
_RE_DOC_SECTION = re.compile(r"^\s*##\s+(?P<title>\S.*?\S)\s*$")
1819
_RE_FOOTNOTE_DEF = re.compile(r"^\s*\.\.\s*\[(?P<label>\d+|#)\]\s+")
@@ -24,14 +25,6 @@
2425
)
2526

2627

27-
def _as_list(value) -> list[str]:
28-
if value is None:
29-
return []
30-
if isinstance(value, str):
31-
return [value]
32-
return [str(v) for v in value]
33-
34-
3528
def _doc_markers_from_env(env) -> list[str]:
3629
"""Return configured Fortran doc markers (e.g. ['!>'])."""
3730
chars = getattr(getattr(env, "app", None), "config", None)
@@ -67,68 +60,12 @@ def _collect_fortran_files_from_env(env) -> list[str]:
6760
roots = _as_list(getattr(config, "fortran_sources", []))
6861
excludes = _as_list(getattr(config, "fortran_sources_exclude", []))
6962
exts = {e.lower() for e in _as_list(getattr(config, "fortran_file_extensions", []))}
70-
if not roots:
71-
return []
72-
73-
files: list[str] = []
74-
for root in roots:
75-
root = str(root)
76-
if any(ch in root for ch in "*?["):
77-
pattern = str(confdir / root)
78-
for match in glob.glob(pattern, recursive=True):
79-
p = Path(match)
80-
if p.is_file() and (not exts or p.suffix.lower() in exts):
81-
files.append(str(p))
82-
continue
83-
84-
p = Path(root)
85-
if not p.is_absolute():
86-
p = confdir / p
87-
if p.is_dir():
88-
for child in p.rglob("*"):
89-
if child.is_file() and (not exts or child.suffix.lower() in exts):
90-
files.append(str(child))
91-
continue
92-
if p.is_file() and (not exts or p.suffix.lower() in exts):
93-
files.append(str(p))
94-
95-
if excludes:
96-
def _norm(s: str) -> str:
97-
try:
98-
return os.path.normcase(str(Path(s).resolve()))
99-
except Exception:
100-
return os.path.normcase(str(Path(s)))
101-
102-
exclude_files: set[str] = set()
103-
for raw in excludes:
104-
pat = str(raw)
105-
if any(ch in pat for ch in "*?["):
106-
pattern = str(confdir / pat)
107-
for match in glob.glob(pattern, recursive=True):
108-
p = Path(match)
109-
if p.is_dir():
110-
for child in p.rglob("*"):
111-
if child.is_file() and (not exts or child.suffix.lower() in exts):
112-
exclude_files.add(_norm(str(child)))
113-
elif p.is_file() and (not exts or p.suffix.lower() in exts):
114-
exclude_files.add(_norm(str(p)))
115-
continue
116-
117-
p = Path(pat)
118-
if not p.is_absolute():
119-
p = confdir / p
120-
if p.is_dir():
121-
for child in p.rglob("*"):
122-
if child.is_file() and (not exts or child.suffix.lower() in exts):
123-
exclude_files.add(_norm(str(child)))
124-
elif p.is_file() and (not exts or p.suffix.lower() in exts):
125-
exclude_files.add(_norm(str(p)))
126-
127-
if exclude_files:
128-
files = [f for f in files if _norm(f) not in exclude_files]
129-
130-
# Deterministic order
131-
return sorted(set(files))
63+
return collect_fortran_source_files(
64+
confdir=confdir,
65+
roots=roots,
66+
extensions=exts,
67+
excludes=excludes,
68+
)
13269

13370

13471
def _find_program_in_file(lines: list[str], progname: str, *, start_at: int = 0) -> int | None:

0 commit comments

Comments
 (0)