Skip to content

Commit b1cd711

Browse files
committed
Improve project_root detection
Project root now detected by walking the parent paths looking for .git. Can be overridden with new ``gitref_relative_project_root`` config value. Fixes #12
1 parent 2366ada commit b1cd711

File tree

4 files changed

+115
-41
lines changed

4 files changed

+115
-41
lines changed

Diff for: README.md

+67-35
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,17 @@ Key features:
1414
* Link to source code on github
1515
* Incorporate into tests or git hooks
1616

17-
Supports Python 3.6+
17+
Supports Python 3.7+
1818

1919

20-
Installation
21-
============
20+
## Installation
2221

2322
Install::
2423

2524
pip install sphinx-gitref
2625

2726

28-
Modify your Sphinx ``conf.py``:
29-
30-
1. Add ``sphinx_gitref`` to the ``extensions`` list in your Sphinx ``conf.py``:
31-
27+
In your Sphinx ``conf.py``, add ``sphinx_gitref`` to the ``extensions`` list:
3228

3329
```python
3430
extensions = [
@@ -37,35 +33,9 @@ Modify your Sphinx ``conf.py``:
3733
]
3834
```
3935

40-
2. Optional: Explicitly specify the remote URL.
41-
42-
Gitref will try to detect your remote origin URL from the ``.git`` dir in your docs'
43-
parent dir. If it can't find it, or detects the wrong remote, you can set or override
44-
the remote URL explicitly with::
45-
46-
```python
47-
gitref_remote_url = "https://github.com/username/repository.git"
48-
```
49-
50-
3. Optional: Explicitly specify the branch to link to.
51-
52-
Gitref will try to detect your current branch from the ``.git`` dir in your docs'
53-
parent dir. If it can't find it, or you'd like it to use a different branch, you can
54-
set or override it explicitly with::
55-
56-
```python
57-
gitref_branch = "master"
58-
```
59-
60-
4. Optional: Change the link label format when a coderef is provided without an
61-
explicit label, eg `` :gitref:`filename.py::coderef` ``
36+
Gitref should now work for most projects, but see "Configuration" below to customise its
37+
defaults.
6238

63-
Gitref defaults to using showing the coderef and dropping the filename. This can be
64-
overridden by setting a format string::
65-
66-
```python
67-
gitref_label_format = "{filename} {coderef}"
68-
```
6939

7040
## Usage
7141

@@ -131,9 +101,71 @@ class Gitea(Remote):
131101
```
132102

133103

104+
## Configuration
105+
106+
Define the following variables in Sphinx ``conf.py``.
107+
108+
109+
### ``gitref_relative_project_root``
110+
111+
Explicitly specify the relative path to the project root form your docs' source dir.
112+
113+
The project root is the root directory of your git repository.
114+
115+
Gitref will walk up the directory tree from your documentation source, looking for the
116+
first directory with a ``.git`` dir. It will use this as the project root.
117+
118+
If it mis-detects the path, you can configure it with a relative path. For example, if
119+
your docs are in ``docs/``, you can specify one parent up as:
120+
121+
```python
122+
gitref_relative_project_root = ".."
123+
```
124+
125+
126+
### ``gitref_remote_url``
127+
128+
Explicitly specify the remote URL.
129+
130+
Gitref will try to detect your remote origin URL from the ``.git`` dir in your project
131+
root. If it can't find it, or detects the wrong remote, you can set or override the
132+
remote URL explicitly with:
133+
134+
```python
135+
gitref_remote_url = "https://github.com/username/repository.git"
136+
```
137+
138+
139+
### ``gitref_branch``
140+
141+
Explicitly specify the branch to link to.
142+
143+
Gitref will try to detect your current branch from the ``.git`` dir in your project
144+
root. If it can't find it, or you'd like it to use a different branch, you can set or
145+
override it explicitly with::
146+
147+
```python
148+
gitref_branch = "master"
149+
```
150+
151+
### ``gitref_label_format``
152+
153+
Change the link label format when a coderef is provided without an explicit label, eg
154+
`` :gitref:`filename.py::coderef` ``
155+
156+
Gitref defaults to using showing the coderef and dropping the filename. This can be
157+
overridden by setting a format string::
158+
159+
```python
160+
gitref_label_format = "{filename} {coderef}"
161+
```
162+
163+
164+
134165
## Changelog
135166

136167
0.3.0 - 2024-05-19
168+
* Better project root detection with override support (fixes #12)
137169
* Support latest Sphinx
138170

139171
0.2.1 - 2022-02-19

Diff for: pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ classifiers = [
1818
"Operating System :: OS Independent",
1919
]
2020
keywords = ["django"]
21-
requires-python = ">=3.6"
21+
requires-python = ">=3.7"
2222
dependencies = [
2323
"docutils",
2424
"sphinx",

Diff for: sphinx_gitref/role.py

+46-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,57 @@
11
"""
22
Sphinx role
33
"""
4+
from __future__ import annotations
5+
6+
from functools import lru_cache
47
from pathlib import Path
8+
from typing import TYPE_CHECKING
59

610
from docutils import nodes, utils
711
from sphinx.util.nodes import split_explicit_title
812

913
from .exceptions import ParseError
1014
from .parser import python_to_lineno
1115

16+
if TYPE_CHECKING:
17+
from docutils.parsers.rst.states import Inliner
18+
1219

13-
def gitref(name, rawtext, text, lineno, inliner, options={}, content=[]):
20+
@lru_cache
21+
def get_project_root(doc_root: Path, option: str | None):
22+
# See if gitref_relative_project_root has told us where to look
23+
if option is not None:
24+
project_root = (doc_root / option).absolute()
25+
if not project_root.is_dir():
26+
raise ValueError(
27+
f"Project root {project_root} does not exist"
28+
" - check your gitref_relative_project_root"
29+
)
30+
return project_root
31+
32+
# Try to detect it
33+
project_root = doc_root
34+
while project_root != project_root.parent:
35+
if (project_root / ".git").is_dir():
36+
return project_root
37+
project_root = project_root.parent
38+
39+
# Couldn't find it
40+
raise ValueError(
41+
"Could not find ancestor path containing a .git dir"
42+
" - configure with gitref_relative_project_root"
43+
)
44+
45+
46+
def gitref(
47+
name: str,
48+
rawtext: str,
49+
text: str,
50+
lineno: int,
51+
inliner: Inliner,
52+
options: dict | None = None,
53+
content=[],
54+
):
1455
"""
1556
Reference a file in git
1657
@@ -40,9 +81,9 @@ def gitref(name, rawtext, text, lineno, inliner, options={}, content=[]):
4081
except AttributeError:
4182
raise ValueError("Config does not specify gitref_remote")
4283

43-
# Assume the project root is one dir up from the docs dir
44-
doc_root = inliner.document.settings.env.srcdir
45-
project_root = Path(doc_root).parent
84+
# Assume the project root is the first ancestor dir from docs to contain a .git dir
85+
doc_root = Path(inliner.document.settings.env.srcdir)
86+
project_root = get_project_root(doc_root, app.config.gitref_relative_project_root)
4687

4788
# Process text value
4889
has_t, title, target = split_explicit_title(text)
@@ -83,5 +124,5 @@ def gitref(name, rawtext, text, lineno, inliner, options={}, content=[]):
83124

84125
ref = remote.get_url(filename=filename, line=ref_start)
85126

86-
node = nodes.reference(rawtext, title, refuri=ref, **options)
127+
node = nodes.reference(rawtext, title, refuri=ref, **(options or {}))
87128
return [node], []

Diff for: sphinx_gitref/setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def setup(app):
4242
repo = Repo(git_root)
4343

4444
# Add config variables
45+
app.add_config_value("gitref_relative_project_root", default=None, rebuild="html")
4546
app.add_config_value("gitref_remote_url", repo.get_remote_url(), "html")
4647
app.add_config_value("gitref_branch", repo.get_local_branch(), "html")
4748
app.add_config_value("gitref_label_format", DEFAULT_LABEL_FORMAT, "html")

0 commit comments

Comments
 (0)