Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ $ uv add libvcs --prerelease allow
_Notes on the upcoming release will go here._
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

### Documentation

#### Class fields describe themselves in the API reference (#548)

The URL, command, and sync types — parsed URLs, rules, remotes, status, and
the subprocess wrapper — now say what each field holds. They previously
reached the rendered API reference as "Alias for field number 0" or as a
bare name carrying only its type.

### Development

#### CI actions updated to current majors
Expand Down
85 changes: 85 additions & 0 deletions src/libvcs/_internal/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,91 @@ def __init__(self, output: str, *args: object) -> None:
class SubprocessCommand(SkipDefaultFieldsReprMixin):
"""Wraps a :mod:`subprocess` request. Inspect, mutate, control before invocation.

Fields mirror the parameters of :class:`subprocess.Popen`. Each is passed
through as-is when :meth:`Popen`, :meth:`run`, :meth:`check_call`, or
:meth:`check_output` fires, and the defaults match the ones
:mod:`subprocess` uses.

Attributes
----------
args : _CMD
Program and its arguments, as a sequence like ``['echo', 'hi']`` or
as a single string when ``shell`` is set.
bufsize : int
Buffering policy for the pipe file objects: ``-1`` for
:data:`io.DEFAULT_BUFFER_SIZE`, ``0`` for unbuffered, ``1`` for line
buffered in text mode.
executable : StrOrBytesPath | None
Program to execute in place of ``args[0]``, or the shell to use when
``shell`` is set. ``None`` runs ``args[0]`` itself.
stdin : _FILE
Child's standard input: a file descriptor, a file object,
:data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, or ``None`` to
inherit the parent's.
stdout : _FILE
Child's standard output, taking the same values as ``stdin``.
stderr : _FILE
Child's standard error, taking the same values as ``stdin``, plus
:data:`subprocess.STDOUT` to fold it into ``stdout``.
preexec_fn : t.Callable[[], t.Any] | None
POSIX-only callable run in the child between fork and exec, or
``None`` to run nothing.
close_fds : bool
Close inherited file descriptors above stderr in the child before
exec.
shell : bool
Run ``args`` through the system shell instead of exec'ing it
directly.
cwd : StrOrBytesPath | None
Directory to change into before running, or ``None`` to inherit the
parent's working directory.
env : _ENV | None
Environment for the child, replacing the parent's, or ``None`` to
inherit it.
creationflags : int
Windows-only process creation flags, e.g.
:data:`subprocess.CREATE_NEW_CONSOLE`. ``0`` applies none.
startupinfo : t.Any | None
Windows-only :class:`subprocess.STARTUPINFO` controlling how the
child's window appears, or ``None`` for the defaults.
restore_signals : bool
POSIX-only: reset signals Python set to ``SIG_IGN`` back to
``SIG_DFL`` in the child before exec.
start_new_session : bool
POSIX-only: run :func:`os.setsid` in the child, detaching it from the
parent's process group and controlling terminal.
pass_fds : t.Any
POSIX-only file descriptors to keep open in the child regardless of
``close_fds``. The empty ``()`` passes none.
umask : int
POSIX-only umask to apply in the child before exec. ``-1`` leaves the
inherited umask alone.
pipesize : int
Capacity of the pipes opened for ``stdin``, ``stdout``, and
``stderr``. ``-1`` keeps the operating system default.
user : str | None
POSIX-only user to switch the child to, or ``None`` to stay as the
calling user.
group : str | None
POSIX-only group to switch the child to, or ``None`` to keep the
calling group.
extra_groups : list[str] | None
POSIX-only supplementary groups for the child, or ``None`` to leave
them untouched.
universal_newlines : bool | None
Alias of ``text``, kept for backwards compatibility. ``None`` leaves
the mode to the other text options.
text : t.Literal[True] | None
Open the pipe file objects in text mode. ``None`` leaves them binary
unless ``encoding``, ``errors``, or ``universal_newlines`` selects
text.
encoding : str | None
Codec for the text-mode pipe file objects. Setting it turns text mode
on; ``None`` leaves the choice to the other text options.
errors : str | None
Decoding error handler for the text-mode pipe file objects, e.g.
``"replace"``. Setting it turns text mode on.

Examples
--------
>>> cmd = SubprocessCommand("ls")
Expand Down
29 changes: 28 additions & 1 deletion src/libvcs/sync/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
class SyncError:
"""An error encountered during a sync step.

Attributes
----------
step : str
Name of the sync step that failed, e.g. ``"fetch"`` or ``"checkout"``.
message : str
Human-readable description of what went wrong.
exception : Exception | None
Underlying exception, or ``None`` when the step reported a failure
without raising.

Examples
--------
>>> error = SyncError(step="fetch", message="remote not found")
Expand All @@ -39,6 +49,15 @@ class SyncError:
class SyncResult:
"""Result of a repository synchronization.

Attributes
----------
ok : bool
Whether every sync step succeeded. :meth:`SyncResult.add_error` flips
this to ``False``.
errors : list[SyncError]
Errors recorded during the sync, in the order they happened. Empty
while the sync is still clean.

Examples
--------
>>> result = SyncResult()
Expand Down Expand Up @@ -92,7 +111,15 @@ def add_error(


class VCSLocation(t.NamedTuple):
"""Generic VCS Location (URL and optional revision)."""
"""Generic VCS Location (URL and optional revision).

Attributes
----------
url : str
Repository URL, with any revision suffix stripped.
rev : str | None
Revision to check out, or ``None`` when unspecified.
"""

url: str
rev: str | None
Expand Down
39 changes: 37 additions & 2 deletions src/libvcs/sync/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,18 @@ def __str__(self) -> str:

@dataclasses.dataclass
class GitRemote:
"""Structure containing git working copy information."""
"""Structure containing git working copy information.

Attributes
----------
name : str
Remote name as git records it, e.g. ``origin``.
fetch_url : str
URL git fetches from for this remote.
push_url : str
URL git pushes to for this remote. Same as ``fetch_url`` unless a
separate push URL is configured.
"""

name: str
fetch_url: str
Expand All @@ -99,7 +110,31 @@ class GitRemote:

@dataclasses.dataclass
class GitStatus:
"""Git status information."""
"""Git status information.

Fields hold the ``# branch.*`` headers of ``git status -sb
--porcelain=2`` as strings, unconverted. Each is ``None`` when the header
is absent from the output :meth:`GitStatus.from_stdout` parsed.

Attributes
----------
branch_oid : str | None
Commit SHA of ``HEAD``. ``None`` on an unborn branch, where git
reports ``(initial)`` instead of a SHA.
branch_head : str | None
Checked-out branch name, or ``(detached)`` when ``HEAD`` points at a
commit rather than a branch.
branch_upstream : str | None
Upstream branch ``HEAD`` tracks, e.g. ``origin/master``. ``None``
when the branch has no upstream configured.
branch_ab : str | None
Ahead/behind counts as git prints them, e.g. ``+0 -0``. ``None``
without an upstream to compare against.
branch_ahead : str | None
Commit count ahead of the upstream, taken from ``branch_ab``.
branch_behind : str | None
Commit count behind the upstream, taken from ``branch_ab``.
"""

branch_oid: str | None = None
branch_head: str | None = None
Expand Down
26 changes: 20 additions & 6 deletions src/libvcs/url/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,32 @@ def is_valid(cls, url: str, is_explicit: bool | None = None) -> bool:

@dataclasses.dataclass(repr=False)
class Rule(SkipDefaultFieldsReprMixin):
"""A Rule represents an eligible pattern mapping to URL."""
"""A Rule represents an eligible pattern mapping to URL.

Attributes
----------
label : str
Computer readable name / ID. Keys the rule inside a
:class:`RuleMap` and is recorded on a matched URL's ``rule``.
description : str
Human readable description of the URL shape the rule covers.
pattern : Pattern[str]
Regex pattern. Its named groups are assigned onto the URL object's
fields of the same name.
defaults : dict[str, str]
Values to apply to fields the pattern left unset, e.g. the
``hostname`` and ``scheme`` a bare prefix such as ``github:`` implies.
is_explicit : bool
Is the match unambiguous with other VCS systems? e.g. git+ prefix
weight : int
Weight: Higher is more likely to win
"""

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


Expand Down
54 changes: 52 additions & 2 deletions src/libvcs/url/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,32 @@ class GitBaseURL(
):
"""Git repository location. Parses URLs on initialization.

Attributes
----------
url : str
Location as given, kept verbatim. Every other field is filled from it
by the first :class:`~libvcs.url.base.Rule` that matches.
scheme : str | None
Transport scheme, e.g. ``https`` or ``ssh``. ``None`` for scp-style
locations such as ``git@github.com:vcs-python/libvcs.git``, which
carry no scheme.
user : str | None
User in front of the hostname, e.g. ``git``. ``None`` when the URL
omits one; :meth:`to_url` falls back to ``git`` for scp-style output.
hostname : str | None
Server hosting the repository, e.g. ``github.com``.
port : int | None
Port the URL specifies, or ``None`` to use the transport's default.
path : str | None
Server-side path to the repository with the suffix split off, e.g.
``vcs-python/libvcs``.
suffix : str | None
Trailing ``.git`` split off the path, or ``None`` when the URL has
none. :meth:`to_url` re-appends it.
rule : str | None
:attr:`~libvcs.url.base.Rule.label` of the rule that matched, or
``None`` when no rule did and the remaining fields stayed unset.

Examples
--------
>>> GitBaseURL(url='https://github.com/vcs-python/libvcs.git')
Expand Down Expand Up @@ -454,7 +480,21 @@ class GitAWSCodeCommitURL(
URLProtocol,
SkipDefaultFieldsReprMixin,
):
"""Supports AWS CodeCommit git URLs."""
"""Supports AWS CodeCommit git URLs.

Parses the fields of :class:`GitBaseURL` plus the two below.

Attributes
----------
region : str | None
AWS region from a ``codecommit::<region>://`` URL, e.g.
``us-east-1``. ``None`` for region-less GRC URLs and for the HTTPS
and SSH forms, which carry the region inside the hostname.
rev : str | None
Commit-ish (tag, branch, ref) trailing the URL as ``@rev``, or
``None`` when the URL names no revision. :meth:`to_url` re-appends
it.
"""

# AWS CodeCommit Region
region: str | None = None
Expand Down Expand Up @@ -589,7 +629,17 @@ class GitPipURL(
URLProtocol,
SkipDefaultFieldsReprMixin,
):
"""Supports pip git URLs."""
"""Supports pip git URLs.

Parses the fields of :class:`GitBaseURL` plus the one below.

Attributes
----------
rev : str | None
Commit-ish (tag, branch, ref) from a pip-style ``@rev``, e.g.
``v0.10.0``. ``None`` when the URL names no revision.
:meth:`to_url` re-appends it.
"""

# commit-ish (rev): tag, branch, ref
rev: str | None = None
Expand Down
43 changes: 42 additions & 1 deletion src/libvcs/url/hg.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,38 @@ class HgBaseURL(
):
"""Mercurial repository location. Parses URLs on initialization.

Attributes
----------
url : str
Location as given, kept verbatim. Every other field is filled from it
by the first :class:`~libvcs.url.base.Rule` that matches.
scheme : str | None
Transport scheme, e.g. ``https``, ``ssh``, or ``hg+file``. ``None``
when the matched rule captured none.
user : str | None
User in front of the hostname, e.g. ``hg``. ``None`` when the URL
omits one; :meth:`to_url` falls back to ``hg`` for scp-style output.
hostname : str
Server hosting the repository, e.g. ``hg.mozilla.org``. Empty when
the matched rule captures no host, as with ``hg+file://`` URLs.
port : int | None
Port the URL specifies, or ``None`` to use the transport's default.
separator : str
Character sitting between the host (and port) and the path, ``/``
unless the URL used ``:`` or ``,``. :meth:`to_url` re-emits it.
path : str
Server-side path to the repository, e.g. ``mozilla-central/``. Empty
when the URL carries no path.
suffix : str | None
Trailing ``.git``-style decoration split off the path, or ``None``
when the URL has none.
ref : str | None
Commit-ish (tag, branch, ref, revision) for callers to set; the
bundled rules capture no ref, so parsing leaves it ``None``.
rule : str | None
:attr:`~libvcs.url.base.Rule.label` of the rule that matched, or
``None`` when no rule did and the remaining fields stayed unset.

Examples
--------
>>> HgBaseURL(url='https://hg.mozilla.org/mozilla-central/')
Expand Down Expand Up @@ -341,7 +373,16 @@ class HgPipURL(
URLProtocol,
SkipDefaultFieldsReprMixin,
):
"""Supports pip hg URLs."""
"""Supports pip hg URLs.

Parses the fields of :class:`HgBaseURL` plus the one below.

Attributes
----------
rev : str | None
Commit-ish (tag, branch, ref) from a pip-style ``@rev``, e.g.
``v1.0``. ``None`` when the URL names no revision.
"""

# commit-ish (rev): tag, branch, ref
rev: str | None = None
Expand Down
Loading