Skip to content

Commit d11545c

Browse files
authored
docs(attrs): Describe class fields (#548)
`NamedTuple`, dataclass, and `TypedDict` fields now describe what they hold, so the rendered API reference no longer shows "Alias for field number 0" or a bare name carrying only its type. - **Docstrings**: every field-bearing class documents each field in a NumPy `Attributes` section. - **Changelog**: a Documentation entry records the change.
2 parents 5663ec7 + bcccf11 commit d11545c

8 files changed

Lines changed: 313 additions & 13 deletions

File tree

CHANGES

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ $ uv add libvcs --prerelease allow
2020
_Notes on the upcoming release will go here._
2121
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
2222

23+
### Documentation
24+
25+
#### Class fields describe themselves in the API reference (#548)
26+
27+
The URL, command, and sync types — parsed URLs, rules, remotes, status, and
28+
the subprocess wrapper — now say what each field holds. They previously
29+
reached the rendered API reference as "Alias for field number 0" or as a
30+
bare name carrying only its type.
31+
2332
### Development
2433

2534
#### CI actions updated to current majors

src/libvcs/_internal/subprocess.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,91 @@ def __init__(self, output: str, *args: object) -> None:
7272
class SubprocessCommand(SkipDefaultFieldsReprMixin):
7373
"""Wraps a :mod:`subprocess` request. Inspect, mutate, control before invocation.
7474
75+
Fields mirror the parameters of :class:`subprocess.Popen`. Each is passed
76+
through as-is when :meth:`Popen`, :meth:`run`, :meth:`check_call`, or
77+
:meth:`check_output` fires, and the defaults match the ones
78+
:mod:`subprocess` uses.
79+
80+
Attributes
81+
----------
82+
args : _CMD
83+
Program and its arguments, as a sequence like ``['echo', 'hi']`` or
84+
as a single string when ``shell`` is set.
85+
bufsize : int
86+
Buffering policy for the pipe file objects: ``-1`` for
87+
:data:`io.DEFAULT_BUFFER_SIZE`, ``0`` for unbuffered, ``1`` for line
88+
buffered in text mode.
89+
executable : StrOrBytesPath | None
90+
Program to execute in place of ``args[0]``, or the shell to use when
91+
``shell`` is set. ``None`` runs ``args[0]`` itself.
92+
stdin : _FILE
93+
Child's standard input: a file descriptor, a file object,
94+
:data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, or ``None`` to
95+
inherit the parent's.
96+
stdout : _FILE
97+
Child's standard output, taking the same values as ``stdin``.
98+
stderr : _FILE
99+
Child's standard error, taking the same values as ``stdin``, plus
100+
:data:`subprocess.STDOUT` to fold it into ``stdout``.
101+
preexec_fn : t.Callable[[], t.Any] | None
102+
POSIX-only callable run in the child between fork and exec, or
103+
``None`` to run nothing.
104+
close_fds : bool
105+
Close inherited file descriptors above stderr in the child before
106+
exec.
107+
shell : bool
108+
Run ``args`` through the system shell instead of exec'ing it
109+
directly.
110+
cwd : StrOrBytesPath | None
111+
Directory to change into before running, or ``None`` to inherit the
112+
parent's working directory.
113+
env : _ENV | None
114+
Environment for the child, replacing the parent's, or ``None`` to
115+
inherit it.
116+
creationflags : int
117+
Windows-only process creation flags, e.g.
118+
:data:`subprocess.CREATE_NEW_CONSOLE`. ``0`` applies none.
119+
startupinfo : t.Any | None
120+
Windows-only :class:`subprocess.STARTUPINFO` controlling how the
121+
child's window appears, or ``None`` for the defaults.
122+
restore_signals : bool
123+
POSIX-only: reset signals Python set to ``SIG_IGN`` back to
124+
``SIG_DFL`` in the child before exec.
125+
start_new_session : bool
126+
POSIX-only: run :func:`os.setsid` in the child, detaching it from the
127+
parent's process group and controlling terminal.
128+
pass_fds : t.Any
129+
POSIX-only file descriptors to keep open in the child regardless of
130+
``close_fds``. The empty ``()`` passes none.
131+
umask : int
132+
POSIX-only umask to apply in the child before exec. ``-1`` leaves the
133+
inherited umask alone.
134+
pipesize : int
135+
Capacity of the pipes opened for ``stdin``, ``stdout``, and
136+
``stderr``. ``-1`` keeps the operating system default.
137+
user : str | None
138+
POSIX-only user to switch the child to, or ``None`` to stay as the
139+
calling user.
140+
group : str | None
141+
POSIX-only group to switch the child to, or ``None`` to keep the
142+
calling group.
143+
extra_groups : list[str] | None
144+
POSIX-only supplementary groups for the child, or ``None`` to leave
145+
them untouched.
146+
universal_newlines : bool | None
147+
Alias of ``text``, kept for backwards compatibility. ``None`` leaves
148+
the mode to the other text options.
149+
text : t.Literal[True] | None
150+
Open the pipe file objects in text mode. ``None`` leaves them binary
151+
unless ``encoding``, ``errors``, or ``universal_newlines`` selects
152+
text.
153+
encoding : str | None
154+
Codec for the text-mode pipe file objects. Setting it turns text mode
155+
on; ``None`` leaves the choice to the other text options.
156+
errors : str | None
157+
Decoding error handler for the text-mode pipe file objects, e.g.
158+
``"replace"``. Setting it turns text mode on.
159+
75160
Examples
76161
--------
77162
>>> cmd = SubprocessCommand("ls")

src/libvcs/sync/base.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
class SyncError:
2020
"""An error encountered during a sync step.
2121
22+
Attributes
23+
----------
24+
step : str
25+
Name of the sync step that failed, e.g. ``"fetch"`` or ``"checkout"``.
26+
message : str
27+
Human-readable description of what went wrong.
28+
exception : Exception | None
29+
Underlying exception, or ``None`` when the step reported a failure
30+
without raising.
31+
2232
Examples
2333
--------
2434
>>> error = SyncError(step="fetch", message="remote not found")
@@ -39,6 +49,15 @@ class SyncError:
3949
class SyncResult:
4050
"""Result of a repository synchronization.
4151
52+
Attributes
53+
----------
54+
ok : bool
55+
Whether every sync step succeeded. :meth:`SyncResult.add_error` flips
56+
this to ``False``.
57+
errors : list[SyncError]
58+
Errors recorded during the sync, in the order they happened. Empty
59+
while the sync is still clean.
60+
4261
Examples
4362
--------
4463
>>> result = SyncResult()
@@ -92,7 +111,15 @@ def add_error(
92111

93112

94113
class VCSLocation(t.NamedTuple):
95-
"""Generic VCS Location (URL and optional revision)."""
114+
"""Generic VCS Location (URL and optional revision).
115+
116+
Attributes
117+
----------
118+
url : str
119+
Repository URL, with any revision suffix stripped.
120+
rev : str | None
121+
Revision to check out, or ``None`` when unspecified.
122+
"""
96123

97124
url: str
98125
rev: str | None

src/libvcs/sync/git.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,18 @@ def __str__(self) -> str:
8686

8787
@dataclasses.dataclass
8888
class GitRemote:
89-
"""Structure containing git working copy information."""
89+
"""Structure containing git working copy information.
90+
91+
Attributes
92+
----------
93+
name : str
94+
Remote name as git records it, e.g. ``origin``.
95+
fetch_url : str
96+
URL git fetches from for this remote.
97+
push_url : str
98+
URL git pushes to for this remote. Same as ``fetch_url`` unless a
99+
separate push URL is configured.
100+
"""
90101

91102
name: str
92103
fetch_url: str
@@ -99,7 +110,31 @@ class GitRemote:
99110

100111
@dataclasses.dataclass
101112
class GitStatus:
102-
"""Git status information."""
113+
"""Git status information.
114+
115+
Fields hold the ``# branch.*`` headers of ``git status -sb
116+
--porcelain=2`` as strings, unconverted. Each is ``None`` when the header
117+
is absent from the output :meth:`GitStatus.from_stdout` parsed.
118+
119+
Attributes
120+
----------
121+
branch_oid : str | None
122+
Commit SHA of ``HEAD``. ``None`` on an unborn branch, where git
123+
reports ``(initial)`` instead of a SHA.
124+
branch_head : str | None
125+
Checked-out branch name, or ``(detached)`` when ``HEAD`` points at a
126+
commit rather than a branch.
127+
branch_upstream : str | None
128+
Upstream branch ``HEAD`` tracks, e.g. ``origin/master``. ``None``
129+
when the branch has no upstream configured.
130+
branch_ab : str | None
131+
Ahead/behind counts as git prints them, e.g. ``+0 -0``. ``None``
132+
without an upstream to compare against.
133+
branch_ahead : str | None
134+
Commit count ahead of the upstream, taken from ``branch_ab``.
135+
branch_behind : str | None
136+
Commit count behind the upstream, taken from ``branch_ab``.
137+
"""
103138

104139
branch_oid: str | None = None
105140
branch_head: str | None = None

src/libvcs/url/base.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,32 @@ def is_valid(cls, url: str, is_explicit: bool | None = None) -> bool:
2828

2929
@dataclasses.dataclass(repr=False)
3030
class Rule(SkipDefaultFieldsReprMixin):
31-
"""A Rule represents an eligible pattern mapping to URL."""
31+
"""A Rule represents an eligible pattern mapping to URL.
32+
33+
Attributes
34+
----------
35+
label : str
36+
Computer readable name / ID. Keys the rule inside a
37+
:class:`RuleMap` and is recorded on a matched URL's ``rule``.
38+
description : str
39+
Human readable description of the URL shape the rule covers.
40+
pattern : Pattern[str]
41+
Regex pattern. Its named groups are assigned onto the URL object's
42+
fields of the same name.
43+
defaults : dict[str, str]
44+
Values to apply to fields the pattern left unset, e.g. the
45+
``hostname`` and ``scheme`` a bare prefix such as ``github:`` implies.
46+
is_explicit : bool
47+
Is the match unambiguous with other VCS systems? e.g. git+ prefix
48+
weight : int
49+
Weight: Higher is more likely to win
50+
"""
3251

3352
label: str
34-
"""Computer readable name / ID"""
3553
description: str
36-
"""Human readable description"""
3754
pattern: Pattern[str]
38-
"""Regex pattern"""
3955
defaults: dict[str, str] = dataclasses.field(default_factory=dict)
40-
"""Is the match unambiguous with other VCS systems? e.g. git+ prefix"""
4156
is_explicit: bool = False
42-
"""Weight: Higher is more likely to win"""
4357
weight: int = 0
4458

4559

src/libvcs/url/git.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,32 @@ class GitBaseURL(
294294
):
295295
"""Git repository location. Parses URLs on initialization.
296296
297+
Attributes
298+
----------
299+
url : str
300+
Location as given, kept verbatim. Every other field is filled from it
301+
by the first :class:`~libvcs.url.base.Rule` that matches.
302+
scheme : str | None
303+
Transport scheme, e.g. ``https`` or ``ssh``. ``None`` for scp-style
304+
locations such as ``git@github.com:vcs-python/libvcs.git``, which
305+
carry no scheme.
306+
user : str | None
307+
User in front of the hostname, e.g. ``git``. ``None`` when the URL
308+
omits one; :meth:`to_url` falls back to ``git`` for scp-style output.
309+
hostname : str | None
310+
Server hosting the repository, e.g. ``github.com``.
311+
port : int | None
312+
Port the URL specifies, or ``None`` to use the transport's default.
313+
path : str | None
314+
Server-side path to the repository with the suffix split off, e.g.
315+
``vcs-python/libvcs``.
316+
suffix : str | None
317+
Trailing ``.git`` split off the path, or ``None`` when the URL has
318+
none. :meth:`to_url` re-appends it.
319+
rule : str | None
320+
:attr:`~libvcs.url.base.Rule.label` of the rule that matched, or
321+
``None`` when no rule did and the remaining fields stayed unset.
322+
297323
Examples
298324
--------
299325
>>> GitBaseURL(url='https://github.com/vcs-python/libvcs.git')
@@ -454,7 +480,21 @@ class GitAWSCodeCommitURL(
454480
URLProtocol,
455481
SkipDefaultFieldsReprMixin,
456482
):
457-
"""Supports AWS CodeCommit git URLs."""
483+
"""Supports AWS CodeCommit git URLs.
484+
485+
Parses the fields of :class:`GitBaseURL` plus the two below.
486+
487+
Attributes
488+
----------
489+
region : str | None
490+
AWS region from a ``codecommit::<region>://`` URL, e.g.
491+
``us-east-1``. ``None`` for region-less GRC URLs and for the HTTPS
492+
and SSH forms, which carry the region inside the hostname.
493+
rev : str | None
494+
Commit-ish (tag, branch, ref) trailing the URL as ``@rev``, or
495+
``None`` when the URL names no revision. :meth:`to_url` re-appends
496+
it.
497+
"""
458498

459499
# AWS CodeCommit Region
460500
region: str | None = None
@@ -589,7 +629,17 @@ class GitPipURL(
589629
URLProtocol,
590630
SkipDefaultFieldsReprMixin,
591631
):
592-
"""Supports pip git URLs."""
632+
"""Supports pip git URLs.
633+
634+
Parses the fields of :class:`GitBaseURL` plus the one below.
635+
636+
Attributes
637+
----------
638+
rev : str | None
639+
Commit-ish (tag, branch, ref) from a pip-style ``@rev``, e.g.
640+
``v0.10.0``. ``None`` when the URL names no revision.
641+
:meth:`to_url` re-appends it.
642+
"""
593643

594644
# commit-ish (rev): tag, branch, ref
595645
rev: str | None = None

src/libvcs/url/hg.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,38 @@ class HgBaseURL(
154154
):
155155
"""Mercurial repository location. Parses URLs on initialization.
156156
157+
Attributes
158+
----------
159+
url : str
160+
Location as given, kept verbatim. Every other field is filled from it
161+
by the first :class:`~libvcs.url.base.Rule` that matches.
162+
scheme : str | None
163+
Transport scheme, e.g. ``https``, ``ssh``, or ``hg+file``. ``None``
164+
when the matched rule captured none.
165+
user : str | None
166+
User in front of the hostname, e.g. ``hg``. ``None`` when the URL
167+
omits one; :meth:`to_url` falls back to ``hg`` for scp-style output.
168+
hostname : str
169+
Server hosting the repository, e.g. ``hg.mozilla.org``. Empty when
170+
the matched rule captures no host, as with ``hg+file://`` URLs.
171+
port : int | None
172+
Port the URL specifies, or ``None`` to use the transport's default.
173+
separator : str
174+
Character sitting between the host (and port) and the path, ``/``
175+
unless the URL used ``:`` or ``,``. :meth:`to_url` re-emits it.
176+
path : str
177+
Server-side path to the repository, e.g. ``mozilla-central/``. Empty
178+
when the URL carries no path.
179+
suffix : str | None
180+
Trailing ``.git``-style decoration split off the path, or ``None``
181+
when the URL has none.
182+
ref : str | None
183+
Commit-ish (tag, branch, ref, revision) for callers to set; the
184+
bundled rules capture no ref, so parsing leaves it ``None``.
185+
rule : str | None
186+
:attr:`~libvcs.url.base.Rule.label` of the rule that matched, or
187+
``None`` when no rule did and the remaining fields stayed unset.
188+
157189
Examples
158190
--------
159191
>>> HgBaseURL(url='https://hg.mozilla.org/mozilla-central/')
@@ -341,7 +373,16 @@ class HgPipURL(
341373
URLProtocol,
342374
SkipDefaultFieldsReprMixin,
343375
):
344-
"""Supports pip hg URLs."""
376+
"""Supports pip hg URLs.
377+
378+
Parses the fields of :class:`HgBaseURL` plus the one below.
379+
380+
Attributes
381+
----------
382+
rev : str | None
383+
Commit-ish (tag, branch, ref) from a pip-style ``@rev``, e.g.
384+
``v1.0``. ``None`` when the URL names no revision.
385+
"""
345386

346387
# commit-ish (rev): tag, branch, ref
347388
rev: str | None = None

0 commit comments

Comments
 (0)