Преглед изворни кода

Add remaining missing docstrings

Complete the docstring documentation effort by adding all remaining
missing docstrings throughout the codebase. This commit brings the
docstring error count from 110 to 0, achieving 100% coverage.
Jelmer Vernooij пре 5 месеци
родитељ
комит
9f88c4a625

+ 3 - 2
dulwich/__init__.py

@@ -49,14 +49,15 @@ except ImportError:
         remove_in: Optional[Union[str, tuple[int, ...]]] = None,
         remove_in: Optional[Union[str, tuple[int, ...]]] = None,
     ) -> Callable[[F], F]:
     ) -> Callable[[F], F]:
         """Decorator to mark functions as deprecated.
         """Decorator to mark functions as deprecated.
-        
+
         Args:
         Args:
             since: Version when the function was deprecated
             since: Version when the function was deprecated
             remove_in: Version when the function will be removed
             remove_in: Version when the function will be removed
-        
+
         Returns:
         Returns:
             Decorator function
             Decorator function
         """
         """
+
         def decorator(func: F) -> F:
         def decorator(func: F) -> F:
             import functools
             import functools
             import warnings
             import warnings

+ 4 - 6
dulwich/archive.py

@@ -52,7 +52,7 @@ class ChunkedBytesIO:
 
 
     def __init__(self, contents: list[bytes]) -> None:
     def __init__(self, contents: list[bytes]) -> None:
         """Initialize ChunkedBytesIO.
         """Initialize ChunkedBytesIO.
-        
+
         Args:
         Args:
             contents: List of byte chunks
             contents: List of byte chunks
         """
         """
@@ -61,10 +61,10 @@ class ChunkedBytesIO:
 
 
     def read(self, maxbytes: Optional[int] = None) -> bytes:
     def read(self, maxbytes: Optional[int] = None) -> bytes:
         """Read bytes from the chunked stream.
         """Read bytes from the chunked stream.
-        
+
         Args:
         Args:
             maxbytes: Maximum number of bytes to read (None for all)
             maxbytes: Maximum number of bytes to read (None for all)
-        
+
         Returns:
         Returns:
             Bytes read
             Bytes read
         """
         """
@@ -164,9 +164,7 @@ def tar_stream(
 def _walk_tree(
 def _walk_tree(
     store: "BaseObjectStore", tree: "Tree", root: bytes = b""
     store: "BaseObjectStore", tree: "Tree", root: bytes = b""
 ) -> Generator[tuple[bytes, "TreeEntry"], None, None]:
 ) -> Generator[tuple[bytes, "TreeEntry"], None, None]:
-    """Recursively walk a dulwich Tree, yielding tuples of (absolute path, TreeEntry) along the way.
-
-    """
+    """Recursively walk a dulwich Tree, yielding tuples of (absolute path, TreeEntry) along the way."""
     for entry in tree.iteritems():
     for entry in tree.iteritems():
         entry_abspath = posixpath.join(root, entry.path)
         entry_abspath = posixpath.join(root, entry.path)
         if stat.S_ISDIR(entry.mode):
         if stat.S_ISDIR(entry.mode):

+ 1 - 1
dulwich/attrs.py

@@ -165,7 +165,7 @@ class Pattern:
 
 
     def __init__(self, pattern: bytes):
     def __init__(self, pattern: bytes):
         """Initialize GitAttributesPattern.
         """Initialize GitAttributesPattern.
-        
+
         Args:
         Args:
             pattern: Attribute pattern as bytes
             pattern: Attribute pattern as bytes
         """
         """

+ 1 - 1
dulwich/bisect.py

@@ -33,7 +33,7 @@ class BisectState:
 
 
     def __init__(self, repo: Repo) -> None:
     def __init__(self, repo: Repo) -> None:
         """Initialize BisectState.
         """Initialize BisectState.
-        
+
         Args:
         Args:
             repo: Repository to perform bisect on
             repo: Repository to perform bisect on
         """
         """

+ 2 - 2
dulwich/bundle.py

@@ -33,7 +33,7 @@ if TYPE_CHECKING:
 
 
 class Bundle:
 class Bundle:
     """Git bundle object representation."""
     """Git bundle object representation."""
-    
+
     version: Optional[int]
     version: Optional[int]
 
 
     capabilities: dict[str, Optional[str]]
     capabilities: dict[str, Optional[str]]
@@ -159,7 +159,7 @@ def read_bundle(f: BinaryIO) -> Bundle:
 
 
 def write_bundle(f: BinaryIO, bundle: Bundle) -> None:
 def write_bundle(f: BinaryIO, bundle: Bundle) -> None:
     """Write a bundle to a file.
     """Write a bundle to a file.
-    
+
     Args:
     Args:
         f: File-like object to write to
         f: File-like object to write to
         bundle: Bundle object to write
         bundle: Bundle object to write

+ 6 - 6
dulwich/cli.py

@@ -57,7 +57,7 @@ class CommitMessageError(Exception):
 
 
 def signal_int(signal, frame) -> None:
 def signal_int(signal, frame) -> None:
     """Handle interrupt signal by exiting.
     """Handle interrupt signal by exiting.
-    
+
     Args:
     Args:
         signal: Signal number
         signal: Signal number
         frame: Current stack frame
         frame: Current stack frame
@@ -67,7 +67,7 @@ def signal_int(signal, frame) -> None:
 
 
 def signal_quit(signal, frame) -> None:
 def signal_quit(signal, frame) -> None:
     """Handle quit signal by entering debugger.
     """Handle quit signal by entering debugger.
-    
+
     Args:
     Args:
         signal: Signal number
         signal: Signal number
         frame: Current stack frame
         frame: Current stack frame
@@ -178,7 +178,7 @@ class PagerBuffer:
 
 
     def __init__(self, pager):
     def __init__(self, pager):
         """Initialize PagerBuffer.
         """Initialize PagerBuffer.
-        
+
         Args:
         Args:
             pager: Pager instance to wrap
             pager: Pager instance to wrap
         """
         """
@@ -227,7 +227,7 @@ class Pager:
 
 
     def __init__(self, pager_cmd="cat"):
     def __init__(self, pager_cmd="cat"):
         """Initialize Pager.
         """Initialize Pager.
-        
+
         Args:
         Args:
             pager_cmd: Command to use for paging (default: "cat")
             pager_cmd: Command to use for paging (default: "cat")
         """
         """
@@ -470,7 +470,7 @@ class cmd_archive(Command):
 
 
     def run(self, args) -> None:
     def run(self, args) -> None:
         """Execute the archive command.
         """Execute the archive command.
-        
+
         Args:
         Args:
             args: Command line arguments
             args: Command line arguments
         """
         """
@@ -503,7 +503,7 @@ class cmd_add(Command):
 
 
     def run(self, argv) -> None:
     def run(self, argv) -> None:
         """Execute the add command.
         """Execute the add command.
-        
+
         Args:
         Args:
             argv: Command line arguments
             argv: Command line arguments
         """
         """

+ 31 - 0
dulwich/client.py

@@ -2151,6 +2151,7 @@ class BundleClient(GitClient):
           thin_packs: Whether or not thin packs should be retrieved
           thin_packs: Whether or not thin packs should be retrieved
           report_activity: Optional callback for reporting transport
           report_activity: Optional callback for reporting transport
             activity.
             activity.
+          config: Optional configuration object
         """
         """
         self._report_activity = report_activity
         self._report_activity = report_activity
 
 
@@ -2608,6 +2609,7 @@ class PLinkSSHVendor(SSHVendor):
 
 
 
 
 def ParamikoSSHVendor(**kwargs):
 def ParamikoSSHVendor(**kwargs):
+    """Create a ParamikoSSHVendor (deprecated)."""
     import warnings
     import warnings
 
 
     warnings.warn(
     warnings.warn(
@@ -2638,6 +2640,19 @@ class SSHGitClient(TraditionalGitClient):
         ssh_command=None,
         ssh_command=None,
         **kwargs,
         **kwargs,
     ) -> None:
     ) -> None:
+        """Initialize SSHGitClient.
+
+        Args:
+            host: SSH hostname
+            port: Optional SSH port
+            username: Optional username
+            vendor: Optional SSH vendor
+            config: Optional configuration
+            password: Optional password
+            key_filename: Optional SSH key file
+            ssh_command: Optional custom SSH command
+            **kwargs: Additional keyword arguments
+        """
         self.host = host
         self.host = host
         self.port = port
         self.port = port
         self.username = username
         self.username = username
@@ -2670,6 +2685,7 @@ class SSHGitClient(TraditionalGitClient):
             self.ssh_vendor = get_ssh_vendor()
             self.ssh_vendor = get_ssh_vendor()
 
 
     def get_url(self, path):
     def get_url(self, path):
+        """Get the SSH URL for a path."""
         netloc = self.host
         netloc = self.host
         if self.port is not None:
         if self.port is not None:
             netloc += f":{self.port}"
             netloc += f":{self.port}"
@@ -2681,6 +2697,7 @@ class SSHGitClient(TraditionalGitClient):
 
 
     @classmethod
     @classmethod
     def from_parsedurl(cls, parsedurl, **kwargs):
     def from_parsedurl(cls, parsedurl, **kwargs):
+        """Create an SSHGitClient from a parsed URL."""
         return cls(
         return cls(
             host=parsedurl.hostname,
             host=parsedurl.hostname,
             port=parsedurl.port,
             port=parsedurl.port,
@@ -2740,6 +2757,7 @@ class SSHGitClient(TraditionalGitClient):
 
 
 
 
 def default_user_agent_string():
 def default_user_agent_string():
+    """Return the default user agent string for Dulwich."""
     # Start user agent with "git/", because GitHub requires this. :-( See
     # Start user agent with "git/", because GitHub requires this. :-( See
     # https://github.com/jelmer/dulwich/issues/562 for details.
     # https://github.com/jelmer/dulwich/issues/562 for details.
     return "git/dulwich/{}".format(".".join([str(x) for x in dulwich.__version__]))
     return "git/dulwich/{}".format(".".join([str(x) for x in dulwich.__version__]))
@@ -2759,6 +2777,9 @@ def default_urllib3_manager(
 
 
     Args:
     Args:
       config: `dulwich.config.ConfigDict` instance with Git configuration.
       config: `dulwich.config.ConfigDict` instance with Git configuration.
+      pool_manager_cls: Pool manager class to use
+      proxy_manager_cls: Proxy manager class to use
+      base_url: Base URL for proxy bypass checks
       timeout: Timeout for HTTP requests in seconds
       timeout: Timeout for HTTP requests in seconds
       override_kwargs: Additional arguments for `urllib3.ProxyManager`
       override_kwargs: Additional arguments for `urllib3.ProxyManager`
 
 
@@ -2860,6 +2881,7 @@ def default_urllib3_manager(
 
 
 
 
 def check_for_proxy_bypass(base_url) -> bool:
 def check_for_proxy_bypass(base_url) -> bool:
+    """Check if proxy should be bypassed for the given URL."""
     # Check if a proxy bypass is defined with the no_proxy environment variable
     # Check if a proxy bypass is defined with the no_proxy environment variable
     if base_url:  # only check if base_url is provided
     if base_url:  # only check if base_url is provided
         no_proxy_str = os.environ.get("no_proxy")
         no_proxy_str = os.environ.get("no_proxy")
@@ -2923,6 +2945,7 @@ class AbstractHttpGitClient(GitClient):
     """
     """
 
 
     def __init__(self, base_url, dumb=False, **kwargs) -> None:
     def __init__(self, base_url, dumb=False, **kwargs) -> None:
+        """Initialize AbstractHttpGitClient."""
         self._base_url = base_url.rstrip("/") + "/"
         self._base_url = base_url.rstrip("/") + "/"
         self.dumb = dumb
         self.dumb = dumb
         GitClient.__init__(self, **kwargs)
         GitClient.__init__(self, **kwargs)
@@ -2934,6 +2957,7 @@ class AbstractHttpGitClient(GitClient):
           url: Request URL.
           url: Request URL.
           headers: Optional custom headers to override defaults.
           headers: Optional custom headers to override defaults.
           data: Request data.
           data: Request data.
+          raise_for_status: Whether to raise an exception for HTTP errors.
 
 
         Returns:
         Returns:
           Tuple (response, read), where response is an urllib3
           Tuple (response, read), where response is an urllib3
@@ -3334,6 +3358,7 @@ class AbstractHttpGitClient(GitClient):
         return LsRemoteResult(refs, symrefs)
         return LsRemoteResult(refs, symrefs)
 
 
     def get_url(self, path):
     def get_url(self, path):
+        """Get the HTTP URL for a path."""
         return self._get_url(path).rstrip("/")
         return self._get_url(path).rstrip("/")
 
 
     def _get_url(self, path):
     def _get_url(self, path):
@@ -3341,6 +3366,7 @@ class AbstractHttpGitClient(GitClient):
 
 
     @classmethod
     @classmethod
     def from_parsedurl(cls, parsedurl, **kwargs):
     def from_parsedurl(cls, parsedurl, **kwargs):
+        """Create an AbstractHttpGitClient from a parsed URL."""
         password = parsedurl.password
         password = parsedurl.password
         if password is not None:
         if password is not None:
             kwargs["password"] = urlunquote(password)
             kwargs["password"] = urlunquote(password)
@@ -3350,6 +3376,7 @@ class AbstractHttpGitClient(GitClient):
         return cls(urlunparse(parsedurl), **kwargs)
         return cls(urlunparse(parsedurl), **kwargs)
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of this client."""
         return f"{type(self).__name__}({self._base_url!r}, dumb={self.dumb!r})"
         return f"{type(self).__name__}({self._base_url!r}, dumb={self.dumb!r})"
 
 
 
 
@@ -3379,6 +3406,7 @@ class Urllib3HttpGitClient(AbstractHttpGitClient):
         timeout=None,
         timeout=None,
         **kwargs,
         **kwargs,
     ) -> None:
     ) -> None:
+        """Initialize Urllib3HttpGitClient."""
         self._username = username
         self._username = username
         self._password = password
         self._password = password
         self._timeout = timeout
         self._timeout = timeout
@@ -3495,6 +3523,7 @@ def get_transport_and_path_from_url(
       url: URL to open (a unicode string)
       url: URL to open (a unicode string)
       config: Optional config object
       config: Optional config object
       operation: Kind of operation that'll be performed; "pull" or "push"
       operation: Kind of operation that'll be performed; "pull" or "push"
+      **kwargs: Additional keyword arguments
 
 
     Keyword Args:
     Keyword Args:
       thin_packs: Whether or not thin packs should be retrieved
       thin_packs: Whether or not thin packs should be retrieved
@@ -3566,6 +3595,7 @@ def get_transport_and_path(
       location: URL or path (a string)
       location: URL or path (a string)
       config: Optional config object
       config: Optional config object
       operation: Kind of operation that'll be performed; "pull" or "push"
       operation: Kind of operation that'll be performed; "pull" or "push"
+      **kwargs: Additional keyword arguments
 
 
     Keyword Args:
     Keyword Args:
       thin_packs: Whether or not thin packs should be retrieved
       thin_packs: Whether or not thin packs should be retrieved
@@ -3614,6 +3644,7 @@ DEFAULT_GIT_CREDENTIALS_PATHS = [
 def get_credentials_from_store(
 def get_credentials_from_store(
     scheme, hostname, username=None, fnames=DEFAULT_GIT_CREDENTIALS_PATHS
     scheme, hostname, username=None, fnames=DEFAULT_GIT_CREDENTIALS_PATHS
 ):
 ):
+    """Read credentials from a Git credential store."""
     for fname in fnames:
     for fname in fnames:
         try:
         try:
             with open(fname, "rb") as f:
             with open(fname, "rb") as f:

+ 9 - 0
dulwich/cloud/gcs.py

@@ -32,12 +32,21 @@ from ..pack import PACK_SPOOL_FILE_MAX_SIZE, Pack, PackData, load_pack_index_fil
 
 
 
 
 class GcsObjectStore(BucketBasedObjectStore):
 class GcsObjectStore(BucketBasedObjectStore):
+    """Object store implementation for Google Cloud Storage."""
+
     def __init__(self, bucket, subpath="") -> None:
     def __init__(self, bucket, subpath="") -> None:
+        """Initialize GcsObjectStore.
+
+        Args:
+          bucket: GCS bucket instance
+          subpath: Subpath within the bucket
+        """
         super().__init__()
         super().__init__()
         self.bucket = bucket
         self.bucket = bucket
         self.subpath = subpath
         self.subpath = subpath
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of GcsObjectStore."""
         return f"{type(self).__name__}({self.bucket!r}, subpath={self.subpath!r})"
         return f"{type(self).__name__}({self.bucket!r}, subpath={self.subpath!r})"
 
 
     def _remove_pack(self, name) -> None:
     def _remove_pack(self, name) -> None:

+ 22 - 0
dulwich/commit_graph.py

@@ -68,6 +68,15 @@ class CommitGraphEntry:
         generation: int,
         generation: int,
         commit_time: int,
         commit_time: int,
     ) -> None:
     ) -> None:
+        """Initialize CommitGraphEntry.
+
+        Args:
+          commit_id: The commit object ID
+          tree_id: The tree object ID
+          parents: List of parent commit IDs
+          generation: Generation number
+          commit_time: Commit timestamp
+        """
         self.commit_id = commit_id
         self.commit_id = commit_id
         self.tree_id = tree_id
         self.tree_id = tree_id
         self.parents = parents
         self.parents = parents
@@ -75,6 +84,7 @@ class CommitGraphEntry:
         self.commit_time = commit_time
         self.commit_time = commit_time
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of CommitGraphEntry."""
         return (
         return (
             f"CommitGraphEntry(commit_id={self.commit_id!r}, "
             f"CommitGraphEntry(commit_id={self.commit_id!r}, "
             f"tree_id={self.tree_id!r}, parents={self.parents!r}, "
             f"tree_id={self.tree_id!r}, parents={self.parents!r}, "
@@ -86,10 +96,17 @@ class CommitGraphChunk:
     """Represents a chunk in the commit graph file."""
     """Represents a chunk in the commit graph file."""
 
 
     def __init__(self, chunk_id: bytes, data: bytes) -> None:
     def __init__(self, chunk_id: bytes, data: bytes) -> None:
+        """Initialize CommitGraphChunk.
+
+        Args:
+          chunk_id: Chunk identifier
+          data: Chunk data
+        """
         self.chunk_id = chunk_id
         self.chunk_id = chunk_id
         self.data = data
         self.data = data
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of CommitGraphChunk."""
         return f"CommitGraphChunk(chunk_id={self.chunk_id!r}, size={len(self.data)})"
         return f"CommitGraphChunk(chunk_id={self.chunk_id!r}, size={len(self.data)})"
 
 
 
 
@@ -97,6 +114,11 @@ class CommitGraph:
     """Git commit graph file reader/writer."""
     """Git commit graph file reader/writer."""
 
 
     def __init__(self, hash_version: int = HASH_VERSION_SHA1) -> None:
     def __init__(self, hash_version: int = HASH_VERSION_SHA1) -> None:
+        """Initialize CommitGraph.
+
+        Args:
+          hash_version: Hash version to use (SHA1 or SHA256)
+        """
         self.hash_version = hash_version
         self.hash_version = hash_version
         self.chunks: dict[bytes, CommitGraphChunk] = {}
         self.chunks: dict[bytes, CommitGraphChunk] = {}
         self.entries: list[CommitGraphEntry] = []
         self.entries: list[CommitGraphEntry] = []

+ 6 - 0
dulwich/config.py

@@ -664,6 +664,7 @@ class ConfigDict(Config):
         self._values.setdefault(section)[name] = value
         self._values.setdefault(section)[name] = value
 
 
     def items(self, section: SectionLike) -> Iterator[tuple[Name, Value]]:
     def items(self, section: SectionLike) -> Iterator[tuple[Name, Value]]:
+        """Get items in a section."""
         section_bytes, _ = self._check_section_and_name(section, b"")
         section_bytes, _ = self._check_section_and_name(section, b"")
         section_dict = self._values.get(section_bytes)
         section_dict = self._values.get(section_bytes)
         if section_dict is not None:
         if section_dict is not None:
@@ -671,6 +672,7 @@ class ConfigDict(Config):
         return iter([])
         return iter([])
 
 
     def sections(self) -> Iterator[Section]:
     def sections(self) -> Iterator[Section]:
+        """Get all sections."""
         return iter(self._values.keys())
         return iter(self._values.keys())
 
 
 
 
@@ -1434,6 +1436,7 @@ class StackedConfig(Config):
         return backends
         return backends
 
 
     def get(self, section: SectionLike, name: NameLike) -> Value:
     def get(self, section: SectionLike, name: NameLike) -> Value:
+        """Get value from configuration."""
         if not isinstance(section, tuple):
         if not isinstance(section, tuple):
             section = (section,)
             section = (section,)
         for backend in self.backends:
         for backend in self.backends:
@@ -1444,6 +1447,7 @@ class StackedConfig(Config):
         raise KeyError(name)
         raise KeyError(name)
 
 
     def get_multivar(self, section: SectionLike, name: NameLike) -> Iterator[Value]:
     def get_multivar(self, section: SectionLike, name: NameLike) -> Iterator[Value]:
+        """Get multiple values from configuration."""
         if not isinstance(section, tuple):
         if not isinstance(section, tuple):
             section = (section,)
             section = (section,)
         for backend in self.backends:
         for backend in self.backends:
@@ -1455,11 +1459,13 @@ class StackedConfig(Config):
     def set(
     def set(
         self, section: SectionLike, name: NameLike, value: Union[ValueLike, bool]
         self, section: SectionLike, name: NameLike, value: Union[ValueLike, bool]
     ) -> None:
     ) -> None:
+        """Set value in configuration."""
         if self.writable is None:
         if self.writable is None:
             raise NotImplementedError(self.set)
             raise NotImplementedError(self.set)
         return self.writable.set(section, name, value)
         return self.writable.set(section, name, value)
 
 
     def sections(self) -> Iterator[Section]:
     def sections(self) -> Iterator[Section]:
+        """Get all sections."""
         seen = set()
         seen = set()
         for backend in self.backends:
         for backend in self.backends:
             for section in backend.sections():
             for section in backend.sections():

+ 5 - 0
dulwich/contrib/diffstat.py

@@ -199,6 +199,11 @@ def diffstat(lines: list[bytes], max_width: int = 80) -> bytes:
 
 
 
 
 def main() -> int:
 def main() -> int:
+    """Main entry point for diffstat command line tool.
+
+    Returns:
+      Exit code (0 for success)
+    """
     argv = sys.argv
     argv = sys.argv
     # allow diffstat.py to also be used from the command line
     # allow diffstat.py to also be used from the command line
     if len(sys.argv) > 1:
     if len(sys.argv) > 1:

+ 20 - 0
dulwich/contrib/requests_vendor.py

@@ -49,6 +49,8 @@ from ..errors import GitProtocolError, NotGitRepository
 
 
 
 
 class RequestsHttpGitClient(AbstractHttpGitClient):
 class RequestsHttpGitClient(AbstractHttpGitClient):
+    """HTTP Git client using the requests library."""
+
     def __init__(
     def __init__(
         self,
         self,
         base_url: str,
         base_url: str,
@@ -58,6 +60,16 @@ class RequestsHttpGitClient(AbstractHttpGitClient):
         password: Optional[str] = None,
         password: Optional[str] = None,
         **kwargs: object,
         **kwargs: object,
     ) -> None:
     ) -> None:
+        """Initialize RequestsHttpGitClient.
+
+        Args:
+          base_url: Base URL of the Git repository
+          dumb: Whether to use dumb HTTP transport
+          config: Git configuration file
+          username: Username for authentication
+          password: Password for authentication
+          **kwargs: Additional keyword arguments
+        """
         self._username = username
         self._username = username
         self._password = password
         self._password = password
 
 
@@ -112,6 +124,14 @@ class RequestsHttpGitClient(AbstractHttpGitClient):
 
 
 
 
 def get_session(config: Optional["ConfigFile"]) -> Session:
 def get_session(config: Optional["ConfigFile"]) -> Session:
+    """Create a requests session with Git configuration.
+
+    Args:
+      config: Git configuration file
+
+    Returns:
+      Configured requests Session
+    """
     session = Session()
     session = Session()
     session.headers.update({"Pragma": "no-cache"})
     session.headers.update({"Pragma": "no-cache"})
 
 

+ 18 - 0
dulwich/contrib/swift.py

@@ -696,6 +696,12 @@ class SwiftPack(Pack):
     """
     """
 
 
     def __init__(self, *args: object, **kwargs: object) -> None:
     def __init__(self, *args: object, **kwargs: object) -> None:
+        """Initialize SwiftPack.
+
+        Args:
+          *args: Arguments to pass to parent class
+          **kwargs: Keyword arguments, must include 'scon' (SwiftConnector)
+        """
         self.scon = kwargs["scon"]
         self.scon = kwargs["scon"]
         del kwargs["scon"]
         del kwargs["scon"]
         super().__init__(*args, **kwargs)  # type: ignore
         super().__init__(*args, **kwargs)  # type: ignore
@@ -944,6 +950,12 @@ class SwiftInfoRefsContainer(InfoRefsContainer):
     """Manage references in info/refs object."""
     """Manage references in info/refs object."""
 
 
     def __init__(self, scon: SwiftConnector, store: object) -> None:
     def __init__(self, scon: SwiftConnector, store: object) -> None:
+        """Initialize SwiftInfoRefsContainer.
+
+        Args:
+          scon: Swift connector instance
+          store: Object store instance
+        """
         self.scon = scon
         self.scon = scon
         self.filename = "info/refs"
         self.filename = "info/refs"
         self.store = store
         self.store = store
@@ -1103,6 +1115,12 @@ class SwiftSystemBackend(Backend):
     """Backend for serving Git repositories from Swift."""
     """Backend for serving Git repositories from Swift."""
 
 
     def __init__(self, logger: "logging.Logger", conf: ConfigParser) -> None:
     def __init__(self, logger: "logging.Logger", conf: ConfigParser) -> None:
+        """Initialize SwiftSystemBackend.
+
+        Args:
+          logger: Logger instance
+          conf: Configuration parser instance
+        """
         self.conf = conf
         self.conf = conf
         self.logger = logger
         self.logger = logger
 
 

+ 9 - 0
dulwich/credentials.py

@@ -35,6 +35,15 @@ from .config import ConfigDict, SectionLike
 
 
 
 
 def match_urls(url: ParseResult, url_prefix: ParseResult) -> bool:
 def match_urls(url: ParseResult, url_prefix: ParseResult) -> bool:
+    """Check if a URL matches a URL prefix.
+
+    Args:
+      url: Parsed URL to check
+      url_prefix: Parsed URL prefix to match against
+
+    Returns:
+      True if url matches the prefix
+    """
     base_match = (
     base_match = (
         url.scheme == url_prefix.scheme
         url.scheme == url_prefix.scheme
         and url.hostname == url_prefix.hostname
         and url.hostname == url_prefix.hostname

+ 16 - 0
dulwich/diff_tree.py

@@ -54,10 +54,26 @@ class TreeChange(namedtuple("TreeChange", ["type", "old", "new"])):
 
 
     @classmethod
     @classmethod
     def add(cls, new: TreeEntry) -> "TreeChange":
     def add(cls, new: TreeEntry) -> "TreeChange":
+        """Create a TreeChange for an added entry.
+
+        Args:
+          new: New tree entry
+
+        Returns:
+          TreeChange instance
+        """
         return cls(CHANGE_ADD, _NULL_ENTRY, new)
         return cls(CHANGE_ADD, _NULL_ENTRY, new)
 
 
     @classmethod
     @classmethod
     def delete(cls, old: TreeEntry) -> "TreeChange":
     def delete(cls, old: TreeEntry) -> "TreeChange":
+        """Create a TreeChange for a deleted entry.
+
+        Args:
+          old: Old tree entry
+
+        Returns:
+          TreeChange instance
+        """
         return cls(CHANGE_DELETE, old, _NULL_ENTRY)
         return cls(CHANGE_DELETE, old, _NULL_ENTRY)
 
 
 
 

+ 7 - 0
dulwich/dumb.py

@@ -410,6 +410,11 @@ class DumbRemoteHTTPRepo:
         return dict(self._refs)
         return dict(self._refs)
 
 
     def get_head(self) -> Ref:
     def get_head(self) -> Ref:
+        """Get the current HEAD reference.
+
+        Returns:
+          HEAD reference name or commit ID
+        """
         head_resp_bytes = self._fetch_url("HEAD")
         head_resp_bytes = self._fetch_url("HEAD")
         head_split = head_resp_bytes.replace(b"\n", b"").split(b" ")
         head_split = head_resp_bytes.replace(b"\n", b"").split(b" ")
         head_target = head_split[1] if len(head_split) > 1 else head_split[0]
         head_target = head_split[1] if len(head_split) > 1 else head_split[0]
@@ -445,6 +450,8 @@ class DumbRemoteHTTPRepo:
           graph_walker: GraphWalker instance (not used for dumb HTTP)
           graph_walker: GraphWalker instance (not used for dumb HTTP)
           determine_wants: Function that returns list of wanted SHAs
           determine_wants: Function that returns list of wanted SHAs
           progress: Optional progress callback
           progress: Optional progress callback
+          get_tagged: Whether to get tagged objects
+          depth: Shallow clone depth
           depth: Depth for shallow clones (not supported for dumb HTTP)
           depth: Depth for shallow clones (not supported for dumb HTTP)
 
 
         Returns:
         Returns:

+ 34 - 0
dulwich/fastexport.py

@@ -177,6 +177,16 @@ class GitFastExporter:
     def emit_commit(
     def emit_commit(
         self, commit: Commit, ref: Ref, base_tree: Optional[ObjectID] = None
         self, commit: Commit, ref: Ref, base_tree: Optional[ObjectID] = None
     ) -> bytes:
     ) -> bytes:
+        """Emit a commit in fast-export format.
+
+        Args:
+          commit: Commit object to export
+          ref: Reference name for the commit
+          base_tree: Base tree for incremental export
+
+        Returns:
+          Marker for the commit
+        """
         cmd, marker = self._export_commit(commit, ref, base_tree)
         cmd, marker = self._export_commit(commit, ref, base_tree)
         self.print_cmd(cmd)
         self.print_cmd(cmd)
         return marker
         return marker
@@ -194,6 +204,14 @@ class GitImportProcessor(processor.ImportProcessor):
         verbose: bool = False,
         verbose: bool = False,
         outf: Optional[BinaryIO] = None,
         outf: Optional[BinaryIO] = None,
     ) -> None:
     ) -> None:
+        """Initialize GitImportProcessor.
+
+        Args:
+          repo: Repository to import into
+          params: Import parameters
+          verbose: Whether to enable verbose output
+          outf: Output file for verbose messages
+        """
         processor.ImportProcessor.__init__(self, params, verbose)
         processor.ImportProcessor.__init__(self, params, verbose)
         self.repo = repo
         self.repo = repo
         self.last_commit = ZERO_SHA
         self.last_commit = ZERO_SHA
@@ -201,11 +219,27 @@ class GitImportProcessor(processor.ImportProcessor):
         self._contents: dict[bytes, tuple[int, bytes]] = {}
         self._contents: dict[bytes, tuple[int, bytes]] = {}
 
 
     def lookup_object(self, objectish: bytes) -> ObjectID:
     def lookup_object(self, objectish: bytes) -> ObjectID:
+        """Look up an object by reference or marker.
+
+        Args:
+          objectish: Object reference or marker
+
+        Returns:
+          Object ID
+        """
         if objectish.startswith(b":"):
         if objectish.startswith(b":"):
             return self.markers[objectish[1:]]
             return self.markers[objectish[1:]]
         return objectish
         return objectish
 
 
     def import_stream(self, stream: BinaryIO) -> dict[bytes, bytes]:
     def import_stream(self, stream: BinaryIO) -> dict[bytes, bytes]:
+        """Import from a fast-import stream.
+
+        Args:
+          stream: Stream to import from
+
+        Returns:
+          Dictionary of markers to object IDs
+        """
         p = parser.ImportParser(stream)
         p = parser.ImportParser(stream)
         self.process(p.iter_commands)
         self.process(p.iter_commands)
         return self.markers
         return self.markers

+ 6 - 0
dulwich/file.py

@@ -125,6 +125,12 @@ class FileLocked(Exception):
     def __init__(
     def __init__(
         self, filename: Union[str, bytes, os.PathLike], lockfilename: Union[str, bytes]
         self, filename: Union[str, bytes, os.PathLike], lockfilename: Union[str, bytes]
     ) -> None:
     ) -> None:
+        """Initialize FileLocked.
+
+        Args:
+          filename: Name of the file that is locked
+          lockfilename: Name of the lock file
+        """
         self.filename = filename
         self.filename = filename
         self.lockfilename = lockfilename
         self.lockfilename = lockfilename
         super().__init__(filename, lockfilename)
         super().__init__(filename, lockfilename)

+ 22 - 0
dulwich/filters.py

@@ -59,6 +59,14 @@ class ProcessFilterDriver:
         required: bool = False,
         required: bool = False,
         cwd: Optional[str] = None,
         cwd: Optional[str] = None,
     ) -> None:
     ) -> None:
+        """Initialize ProcessFilterDriver.
+
+        Args:
+          clean_cmd: Command to run for clean filter
+          smudge_cmd: Command to run for smudge filter
+          required: Whether the filter is required
+          cwd: Working directory for filter execution
+        """
         self.clean_cmd = clean_cmd
         self.clean_cmd = clean_cmd
         self.smudge_cmd = smudge_cmd
         self.smudge_cmd = smudge_cmd
         self.required = required
         self.required = required
@@ -122,6 +130,12 @@ class FilterRegistry:
     def __init__(
     def __init__(
         self, config: Optional["StackedConfig"] = None, repo: Optional["Repo"] = None
         self, config: Optional["StackedConfig"] = None, repo: Optional["Repo"] = None
     ) -> None:
     ) -> None:
+        """Initialize FilterRegistry.
+
+        Args:
+          config: Git configuration stack
+          repo: Repository instance
+        """
         self.config = config
         self.config = config
         self.repo = repo
         self.repo = repo
         self._drivers: dict[str, FilterDriver] = {}
         self._drivers: dict[str, FilterDriver] = {}
@@ -377,6 +391,14 @@ class FilterBlobNormalizer:
         filter_registry: Optional[FilterRegistry] = None,
         filter_registry: Optional[FilterRegistry] = None,
         repo: Optional["Repo"] = None,
         repo: Optional["Repo"] = None,
     ) -> None:
     ) -> None:
+        """Initialize FilterBlobNormalizer.
+
+        Args:
+          config_stack: Git configuration stack
+          gitattributes: GitAttributes instance
+          filter_registry: Optional filter registry to use
+          repo: Optional repository instance
+        """
         self.config_stack = config_stack
         self.config_stack = config_stack
         self.gitattributes = gitattributes
         self.gitattributes = gitattributes
         self.filter_registry = filter_registry or FilterRegistry(config_stack, repo)
         self.filter_registry = filter_registry or FilterRegistry(config_stack, repo)

+ 12 - 0
dulwich/greenthreads.py

@@ -89,6 +89,18 @@ class GreenThreadsMissingObjectFinder(MissingObjectFinder):
         concurrency: int = 1,
         concurrency: int = 1,
         get_parents: Optional[Callable[[ObjectID], list[ObjectID]]] = None,
         get_parents: Optional[Callable[[ObjectID], list[ObjectID]]] = None,
     ) -> None:
     ) -> None:
+        """Initialize GreenThreadsMissingObjectFinder.
+
+        Args:
+          object_store: Object store to search
+          haves: Objects we have
+          wants: Objects we want
+          progress: Optional progress callback
+          get_tagged: Optional function to get tagged objects
+          concurrency: Number of concurrent green threads
+          get_parents: Optional function to get commit parents
+        """
+
         def collect_tree_sha(sha: ObjectID) -> None:
         def collect_tree_sha(sha: ObjectID) -> None:
             self.sha_done.add(sha)
             self.sha_done.add(sha)
             obj = object_store[sha]
             obj = object_store[sha]

+ 11 - 0
dulwich/index.py

@@ -1332,6 +1332,7 @@ def index_entry_from_stat(
     Args:
     Args:
       stat_val: POSIX stat_result instance
       stat_val: POSIX stat_result instance
       hex_sha: Hex sha of the object
       hex_sha: Hex sha of the object
+      mode: Optional file mode, will be derived from stat if not provided
     """
     """
     if mode is None:
     if mode is None:
         mode = cleanup_mode(stat_val.st_mode)
         mode = cleanup_mode(stat_val.st_mode)
@@ -1366,6 +1367,7 @@ if sys.platform == "win32":
         """
         """
 
 
         def __init__(self, errno: int, msg: str, filename: Optional[str]) -> None:
         def __init__(self, errno: int, msg: str, filename: Optional[str]) -> None:
+            """Initialize WindowsSymlinkPermissionError."""
             super(PermissionError, self).__init__(
             super(PermissionError, self).__init__(
                 errno,
                 errno,
                 f"Unable to create symlink; do you have developer mode enabled? {msg}",
                 f"Unable to create symlink; do you have developer mode enabled? {msg}",
@@ -1419,6 +1421,7 @@ def build_file_from_blob(
       target_path: Path to write to
       target_path: Path to write to
       honor_filemode: An optional flag to honor core.filemode setting in
       honor_filemode: An optional flag to honor core.filemode setting in
         config file, default is core.filemode=True, change executable bit
         config file, default is core.filemode=True, change executable bit
+      tree_encoding: Encoding to use for tree contents
       symlink_fn: Function to use for creating symlinks
       symlink_fn: Function to use for creating symlinks
     Returns: stat object for the file
     Returns: stat object for the file
     """
     """
@@ -1608,6 +1611,7 @@ def build_index_from_tree(
         config file, default is core.filemode=True, change executable bit
         config file, default is core.filemode=True, change executable bit
       validate_path_element: Function to validate path elements to check
       validate_path_element: Function to validate path elements to check
         out; default just refuses .git and .. directories.
         out; default just refuses .git and .. directories.
+      symlink_fn: Function to use for creating symlinks
       blob_normalizer: An optional BlobNormalizer to use for converting line
       blob_normalizer: An optional BlobNormalizer to use for converting line
         endings when writing blobs to the working directory.
         endings when writing blobs to the working directory.
       tree_encoding: Encoding used for tree paths (default: utf-8)
       tree_encoding: Encoding used for tree paths (default: utf-8)
@@ -1681,6 +1685,7 @@ def blob_from_path_and_mode(
     Args:
     Args:
       fs_path: Full file system path to file
       fs_path: Full file system path to file
       mode: File mode
       mode: File mode
+      tree_encoding: Encoding to use for tree contents
     Returns: A `Blob` object
     Returns: A `Blob` object
     """
     """
     assert isinstance(fs_path, bytes)
     assert isinstance(fs_path, bytes)
@@ -1705,6 +1710,7 @@ def blob_from_path_and_stat(
     Args:
     Args:
       fs_path: Full file system path to file
       fs_path: Full file system path to file
       st: A stat object
       st: A stat object
+      tree_encoding: Encoding to use for tree contents
     Returns: A `Blob` object
     Returns: A `Blob` object
     """
     """
     return blob_from_path_and_mode(fs_path, st.st_mode, tree_encoding)
     return blob_from_path_and_mode(fs_path, st.st_mode, tree_encoding)
@@ -2440,6 +2446,7 @@ def get_unstaged_changes(
     Args:
     Args:
       index: index to check
       index: index to check
       root_path: path in which to find files
       root_path: path in which to find files
+      filter_blob_callback: Optional callback to filter blobs
     Returns: iterator over paths with unstaged changes
     Returns: iterator over paths with unstaged changes
     """
     """
     # For each entry in the index check the sha1 & ensure not staged
     # For each entry in the index check the sha1 & ensure not staged
@@ -2618,6 +2625,7 @@ def iter_fresh_objects(
     """Iterate over versions of objects on disk referenced by index.
     """Iterate over versions of objects on disk referenced by index.
 
 
     Args:
     Args:
+      paths: Paths to check
       root_path: Root path to access from
       root_path: Root path to access from
       include_deleted: Include deleted entries with sha and
       include_deleted: Include deleted entries with sha and
         mode set to None
         mode set to None
@@ -2655,9 +2663,11 @@ class locked_index:
     _file: "_GitFile"
     _file: "_GitFile"
 
 
     def __init__(self, path: Union[bytes, str]) -> None:
     def __init__(self, path: Union[bytes, str]) -> None:
+        """Initialize locked_index."""
         self._path = path
         self._path = path
 
 
     def __enter__(self) -> Index:
     def __enter__(self) -> Index:
+        """Enter context manager and lock index."""
         f = GitFile(self._path, "wb")
         f = GitFile(self._path, "wb")
         assert isinstance(f, _GitFile)  # GitFile in write mode always returns _GitFile
         assert isinstance(f, _GitFile)  # GitFile in write mode always returns _GitFile
         self._file = f
         self._file = f
@@ -2670,6 +2680,7 @@ class locked_index:
         exc_value: Optional[BaseException],
         exc_value: Optional[BaseException],
         traceback: Optional[types.TracebackType],
         traceback: Optional[types.TracebackType],
     ) -> None:
     ) -> None:
+        """Exit context manager and unlock index."""
         if exc_type is not None:
         if exc_type is not None:
             self._file.abort()
             self._file.abort()
             return
             return

+ 6 - 0
dulwich/lfs.py

@@ -91,10 +91,12 @@ class LFSStore:
     """Stores objects on disk, indexed by SHA256."""
     """Stores objects on disk, indexed by SHA256."""
 
 
     def __init__(self, path: str) -> None:
     def __init__(self, path: str) -> None:
+        """Initialize LFSStore."""
         self.path = path
         self.path = path
 
 
     @classmethod
     @classmethod
     def create(cls, lfs_dir: str) -> "LFSStore":
     def create(cls, lfs_dir: str) -> "LFSStore":
+        """Create a new LFS store."""
         if not os.path.isdir(lfs_dir):
         if not os.path.isdir(lfs_dir):
             os.mkdir(lfs_dir)
             os.mkdir(lfs_dir)
         tmp_dir = os.path.join(lfs_dir, "tmp")
         tmp_dir = os.path.join(lfs_dir, "tmp")
@@ -107,6 +109,7 @@ class LFSStore:
 
 
     @classmethod
     @classmethod
     def from_repo(cls, repo: "Repo", create: bool = False) -> "LFSStore":
     def from_repo(cls, repo: "Repo", create: bool = False) -> "LFSStore":
+        """Create LFS store from repository."""
         lfs_dir = os.path.join(repo.controldir(), "lfs")
         lfs_dir = os.path.join(repo.controldir(), "lfs")
         if create:
         if create:
             return cls.create(lfs_dir)
             return cls.create(lfs_dir)
@@ -114,6 +117,7 @@ class LFSStore:
 
 
     @classmethod
     @classmethod
     def from_controldir(cls, controldir: str, create: bool = False) -> "LFSStore":
     def from_controldir(cls, controldir: str, create: bool = False) -> "LFSStore":
+        """Create LFS store from control directory."""
         lfs_dir = os.path.join(controldir, "lfs")
         lfs_dir = os.path.join(controldir, "lfs")
         if create:
         if create:
             return cls.create(lfs_dir)
             return cls.create(lfs_dir)
@@ -158,6 +162,7 @@ class LFSPointer:
     """Represents an LFS pointer file."""
     """Represents an LFS pointer file."""
 
 
     def __init__(self, oid: str, size: int) -> None:
     def __init__(self, oid: str, size: int) -> None:
+        """Initialize LFSPointer."""
         self.oid = oid
         self.oid = oid
         self.size = size
         self.size = size
 
 
@@ -226,6 +231,7 @@ class LFSFilterDriver:
     def __init__(
     def __init__(
         self, lfs_store: "LFSStore", config: Optional["Config"] = None
         self, lfs_store: "LFSStore", config: Optional["Config"] = None
     ) -> None:
     ) -> None:
+        """Initialize LFSFilterDriver."""
         self.lfs_store = lfs_store
         self.lfs_store = lfs_store
         self.config = config
         self.config = config
 
 

+ 7 - 0
dulwich/lfs_server.py

@@ -246,6 +246,13 @@ class LFSServer(HTTPServer):
         lfs_store: LFSStore,
         lfs_store: LFSStore,
         log_requests: bool = False,
         log_requests: bool = False,
     ) -> None:
     ) -> None:
+        """Initialize LFSServer.
+
+        Args:
+          server_address: Tuple of (host, port) to bind to
+          lfs_store: LFS store instance to use
+          log_requests: Whether to log incoming requests
+        """
         super().__init__(server_address, LFSRequestHandler)
         super().__init__(server_address, LFSRequestHandler)
         self.lfs_store = lfs_store
         self.lfs_store = lfs_store
         self.log_requests = log_requests
         self.log_requests = log_requests

+ 7 - 3
dulwich/line_ending.py

@@ -163,6 +163,7 @@ class LineEndingFilter(FilterDriver):
         smudge_conversion: Optional[Callable[[bytes], bytes]] = None,
         smudge_conversion: Optional[Callable[[bytes], bytes]] = None,
         binary_detection: bool = True,
         binary_detection: bool = True,
     ):
     ):
+        """Initialize LineEndingFilter."""
         self.clean_conversion = clean_conversion
         self.clean_conversion = clean_conversion
         self.smudge_conversion = smudge_conversion
         self.smudge_conversion = smudge_conversion
         self.binary_detection = binary_detection
         self.binary_detection = binary_detection
@@ -335,6 +336,7 @@ class BlobNormalizer(FilterBlobNormalizer):
         core_eol: str = "native",
         core_eol: str = "native",
         autocrlf: bytes = b"false",
         autocrlf: bytes = b"false",
     ) -> None:
     ) -> None:
+        """Initialize FilteringBlobNormalizer."""
         # Set up a filter registry with line ending filters
         # Set up a filter registry with line ending filters
         filter_registry = FilterRegistry(config_stack)
         filter_registry = FilterRegistry(config_stack)
 
 
@@ -431,9 +433,7 @@ class BlobNormalizer(FilterBlobNormalizer):
 def normalize_blob(
 def normalize_blob(
     blob: Blob, conversion: Callable[[bytes], bytes], binary_detection: bool
     blob: Blob, conversion: Callable[[bytes], bytes], binary_detection: bool
 ) -> Blob:
 ) -> Blob:
-    """Takes a blob as input returns either the original blob if binary_detection is True and the blob content looks like binary, else return a new blob with converted data.
-
-    """
+    """Normalize blob by applying line ending conversion."""
     # Read the original blob
     # Read the original blob
     data = blob.data
     data = blob.data
 
 
@@ -454,6 +454,8 @@ def normalize_blob(
 
 
 
 
 class TreeBlobNormalizer(BlobNormalizer):
 class TreeBlobNormalizer(BlobNormalizer):
+    """Blob normalizer that tracks existing files in a tree."""
+
     def __init__(
     def __init__(
         self,
         self,
         config_stack: "StackedConfig",
         config_stack: "StackedConfig",
@@ -463,6 +465,7 @@ class TreeBlobNormalizer(BlobNormalizer):
         core_eol: str = "native",
         core_eol: str = "native",
         autocrlf: bytes = b"false",
         autocrlf: bytes = b"false",
     ) -> None:
     ) -> None:
+        """Initialize TreeBlobNormalizer."""
         super().__init__(config_stack, git_attributes, core_eol, autocrlf)
         super().__init__(config_stack, git_attributes, core_eol, autocrlf)
         if tree:
         if tree:
             self.existing_paths = {
             self.existing_paths = {
@@ -472,6 +475,7 @@ class TreeBlobNormalizer(BlobNormalizer):
             self.existing_paths = set()
             self.existing_paths = set()
 
 
     def checkin_normalize(self, blob: Blob, tree_path: bytes) -> Blob:
     def checkin_normalize(self, blob: Blob, tree_path: bytes) -> Blob:
+        """Normalize blob for checkin, considering existing tree state."""
         # Existing files should only be normalized on checkin if it was
         # Existing files should only be normalized on checkin if it was
         # previously normalized on checkout
         # previously normalized on checkout
         if (
         if (

+ 18 - 0
dulwich/lru_cache.py

@@ -78,6 +78,12 @@ class LRUCache(Generic[K, V]):
     def __init__(
     def __init__(
         self, max_cache: int = 100, after_cleanup_count: Optional[int] = None
         self, max_cache: int = 100, after_cleanup_count: Optional[int] = None
     ) -> None:
     ) -> None:
+        """Initialize LRUCache.
+
+        Args:
+          max_cache: Maximum number of entries to cache
+          after_cleanup_count: Number of entries to keep after cleanup
+        """
         self._cache: dict[K, _LRUNode[K, V]] = {}
         self._cache: dict[K, _LRUNode[K, V]] = {}
         # The "HEAD" of the lru linked list
         # The "HEAD" of the lru linked list
         self._most_recently_used = None
         self._most_recently_used = None
@@ -86,9 +92,11 @@ class LRUCache(Generic[K, V]):
         self._update_max_cache(max_cache, after_cleanup_count)
         self._update_max_cache(max_cache, after_cleanup_count)
 
 
     def __contains__(self, key: K) -> bool:
     def __contains__(self, key: K) -> bool:
+        """Check if key is in cache."""
         return key in self._cache
         return key in self._cache
 
 
     def __getitem__(self, key: K) -> V:
     def __getitem__(self, key: K) -> V:
+        """Get item from cache and mark as recently used."""
         cache = self._cache
         cache = self._cache
         node = cache[key]
         node = cache[key]
         # Inlined from _record_access to decrease the overhead of __getitem__
         # Inlined from _record_access to decrease the overhead of __getitem__
@@ -122,6 +130,7 @@ class LRUCache(Generic[K, V]):
         return node.value
         return node.value
 
 
     def __len__(self) -> int:
     def __len__(self) -> int:
+        """Return number of items in cache."""
         return len(self._cache)
         return len(self._cache)
 
 
     def _walk_lru(self) -> Iterator[_LRUNode[K, V]]:
     def _walk_lru(self) -> Iterator[_LRUNode[K, V]]:
@@ -196,6 +205,15 @@ class LRUCache(Generic[K, V]):
         return self._max_cache
         return self._max_cache
 
 
     def get(self, key: K, default: Optional[V] = None) -> Optional[V]:
     def get(self, key: K, default: Optional[V] = None) -> Optional[V]:
+        """Get value from cache with default if not found.
+
+        Args:
+          key: Key to look up
+          default: Default value if key not found
+
+        Returns:
+          Value from cache or default
+        """
         node = self._cache.get(key, None)
         node = self._cache.get(key, None)
         if node is None:
         if node is None:
             return default
             return default

+ 21 - 0
dulwich/mailmap.py

@@ -26,6 +26,14 @@ from typing import IO, Optional, Union
 
 
 
 
 def parse_identity(text: bytes) -> tuple[Optional[bytes], Optional[bytes]]:
 def parse_identity(text: bytes) -> tuple[Optional[bytes], Optional[bytes]]:
+    """Parse an identity string into name and email.
+
+    Args:
+      text: Identity string in format "Name <email>"
+
+    Returns:
+      Tuple of (name, email) where either can be None
+    """
     # TODO(jelmer): Integrate this with dulwich.fastexport.split_email and
     # TODO(jelmer): Integrate this with dulwich.fastexport.split_email and
     # dulwich.repo.check_user_identity
     # dulwich.repo.check_user_identity
     (name_str, email_str) = text.rsplit(b"<", 1)
     (name_str, email_str) = text.rsplit(b"<", 1)
@@ -81,6 +89,11 @@ class Mailmap:
             ]
             ]
         ] = None,
         ] = None,
     ) -> None:
     ) -> None:
+        """Initialize Mailmap.
+
+        Args:
+          map: Optional iterator of (canonical_identity, from_identity) tuples
+        """
         self._table: dict[
         self._table: dict[
             tuple[Optional[bytes], Optional[bytes]],
             tuple[Optional[bytes], Optional[bytes]],
             tuple[Optional[bytes], Optional[bytes]],
             tuple[Optional[bytes], Optional[bytes]],
@@ -143,5 +156,13 @@ class Mailmap:
 
 
     @classmethod
     @classmethod
     def from_path(cls, path: str) -> "Mailmap":
     def from_path(cls, path: str) -> "Mailmap":
+        """Create Mailmap from file path.
+
+        Args:
+          path: Path to mailmap file
+
+        Returns:
+          Mailmap instance
+        """
         with open(path, "rb") as f:
         with open(path, "rb") as f:
             return cls(read_mailmap(f))
             return cls(read_mailmap(f))

+ 6 - 0
dulwich/merge.py

@@ -18,6 +18,12 @@ class MergeConflict(Exception):
     """Raised when a merge conflict occurs."""
     """Raised when a merge conflict occurs."""
 
 
     def __init__(self, path: bytes, message: str) -> None:
     def __init__(self, path: bytes, message: str) -> None:
+        """Initialize MergeConflict.
+
+        Args:
+          path: Path to the conflicted file
+          message: Conflict description
+        """
         self.path = path
         self.path = path
         super().__init__(f"Merge conflict in {path!r}: {message}")
         super().__init__(f"Merge conflict in {path!r}: {message}")
 
 

+ 15 - 0
dulwich/object_store.py

@@ -956,6 +956,7 @@ class PackBasedObjectStore(BaseObjectStore, PackedObjectContainer):
 
 
         Args:
         Args:
           sha1: sha for the object.
           sha1: sha for the object.
+          include_comp: Whether to include compression metadata.
         """
         """
         if sha1 == ZERO_SHA:
         if sha1 == ZERO_SHA:
             raise KeyError(sha1)
             raise KeyError(sha1)
@@ -998,6 +999,7 @@ class PackBasedObjectStore(BaseObjectStore, PackedObjectContainer):
         Args:
         Args:
           objects: Iterable over (object, path) tuples, should support
           objects: Iterable over (object, path) tuples, should support
             __len__.
             __len__.
+          progress: Optional progress reporting function.
         Returns: Pack object of the objects written.
         Returns: Pack object of the objects written.
         """
         """
         count = len(objects)
         count = len(objects)
@@ -1357,7 +1359,9 @@ class DiskObjectStore(PackBasedObjectStore):
         Args:
         Args:
           f: Open file object for the pack.
           f: Open file object for the pack.
           path: Path to the pack file.
           path: Path to the pack file.
+          num_objects: Number of objects in the pack.
           indexer: A PackIndexer for indexing the pack.
           indexer: A PackIndexer for indexing the pack.
+          progress: Optional progress reporting function.
         """
         """
         entries = []
         entries = []
         for i, entry in enumerate(indexer):
         for i, entry in enumerate(indexer):
@@ -1432,6 +1436,7 @@ class DiskObjectStore(PackBasedObjectStore):
             requested bytes are read.
             requested bytes are read.
           read_some: Read function that returns at least one byte, but may
           read_some: Read function that returns at least one byte, but may
             not return the number of bytes requested.
             not return the number of bytes requested.
+          progress: Optional progress reporting function.
         Returns: A Pack object pointing at the now-completed thin pack in the
         Returns: A Pack object pointing at the now-completed thin pack in the
             objects/pack directory.
             objects/pack directory.
         """
         """
@@ -1771,6 +1776,7 @@ class MemoryObjectStore(BaseObjectStore):
 
 
         Args:
         Args:
           objects: Iterable over a list of (object, path) tuples
           objects: Iterable over a list of (object, path) tuples
+          progress: Optional progress reporting function.
         """
         """
         for obj, path in objects:
         for obj, path in objects:
             self.add_object(obj)
             self.add_object(obj)
@@ -1812,6 +1818,8 @@ class MemoryObjectStore(BaseObjectStore):
 
 
         Args:
         Args:
           count: Number of items to add
           count: Number of items to add
+          unpacked_objects: Iterator of UnpackedObject instances
+          progress: Optional progress reporting function.
         """
         """
         if count == 0:
         if count == 0:
             return
             return
@@ -1845,6 +1853,7 @@ class MemoryObjectStore(BaseObjectStore):
             requested bytes are read.
             requested bytes are read.
           read_some: Read function that returns at least one byte, but may
           read_some: Read function that returns at least one byte, but may
             not return the number of bytes requested.
             not return the number of bytes requested.
+          progress: Optional progress reporting function.
         """
         """
         f, commit, abort = self.add_pack()
         f, commit, abort = self.add_pack()
         try:
         try:
@@ -2142,6 +2151,8 @@ class ObjectStoreGraphWalker:
         Args:
         Args:
           local_heads: Heads to start search with
           local_heads: Heads to start search with
           get_parents: Function for finding the parents of a SHA1.
           get_parents: Function for finding the parents of a SHA1.
+          shallow: Set of shallow commits.
+          update_shallow: Function to update shallow commits.
         """
         """
         self.heads = set(local_heads)
         self.heads = set(local_heads)
         self.get_parents = get_parents
         self.get_parents = get_parents
@@ -2539,9 +2550,11 @@ def _collect_ancestors(
     """Collect all ancestors of heads up to (excluding) those in common.
     """Collect all ancestors of heads up to (excluding) those in common.
 
 
     Args:
     Args:
+      store: Object store to get commits from
       heads: commits to start from
       heads: commits to start from
       common: commits to end at, or empty set to walk repository
       common: commits to end at, or empty set to walk repository
         completely
         completely
+      shallow: Set of shallow commits
       get_parents: Optional function for getting the parents of a
       get_parents: Optional function for getting the parents of a
         commit.
         commit.
     Returns: a tuple (A, B) where A - all commits reachable
     Returns: a tuple (A, B) where A - all commits reachable
@@ -2587,6 +2600,7 @@ def iter_tree_contents(
     Iteration is depth-first pre-order, as in e.g. os.walk.
     Iteration is depth-first pre-order, as in e.g. os.walk.
 
 
     Args:
     Args:
+      store: Object store to get trees from
       tree_id: SHA1 of the tree.
       tree_id: SHA1 of the tree.
       include_trees: If True, include tree objects in the iteration.
       include_trees: If True, include tree objects in the iteration.
     Returns: Iterator over TreeEntry namedtuples for all the objects in a
     Returns: Iterator over TreeEntry namedtuples for all the objects in a
@@ -2614,6 +2628,7 @@ def peel_sha(store: ObjectContainer, sha: bytes) -> tuple[ShaFile, ShaFile]:
     """Peel all tags from a SHA.
     """Peel all tags from a SHA.
 
 
     Args:
     Args:
+      store: Object store to get objects from
       sha: The object SHA to peel.
       sha: The object SHA to peel.
     Returns: The fully-peeled SHA1 of a tag object, after peeling all
     Returns: The fully-peeled SHA1 of a tag object, after peeling all
         intermediate tags; if the original ref does not point to a tag,
         intermediate tags; if the original ref does not point to a tag,

+ 8 - 0
dulwich/objects.py

@@ -650,6 +650,7 @@ class ShaFile:
         return self.sha().hexdigest().encode("ascii")
         return self.sha().hexdigest().encode("ascii")
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of this object."""
         return f"<{self.__class__.__name__} {self.id!r}>"
         return f"<{self.__class__.__name__} {self.id!r}>"
 
 
     def __ne__(self, other: object) -> bool:
     def __ne__(self, other: object) -> bool:
@@ -1251,6 +1252,7 @@ class Tree(ShaFile):
     __slots__ = "_entries"
     __slots__ = "_entries"
 
 
     def __init__(self) -> None:
     def __init__(self) -> None:
+        """Initialize an empty Tree."""
         super().__init__()
         super().__init__()
         self._entries: dict[bytes, tuple[int, bytes]] = {}
         self._entries: dict[bytes, tuple[int, bytes]] = {}
 
 
@@ -1273,9 +1275,11 @@ class Tree(ShaFile):
         return tree
         return tree
 
 
     def __contains__(self, name: bytes) -> bool:
     def __contains__(self, name: bytes) -> bool:
+        """Check if name exists in tree."""
         return name in self._entries
         return name in self._entries
 
 
     def __getitem__(self, name: bytes) -> tuple[int, ObjectID]:
     def __getitem__(self, name: bytes) -> tuple[int, ObjectID]:
+        """Get tree entry by name."""
         return self._entries[name]
         return self._entries[name]
 
 
     def __setitem__(self, name: bytes, value: tuple[int, ObjectID]) -> None:
     def __setitem__(self, name: bytes, value: tuple[int, ObjectID]) -> None:
@@ -1292,13 +1296,16 @@ class Tree(ShaFile):
         self._needs_serialization = True
         self._needs_serialization = True
 
 
     def __delitem__(self, name: bytes) -> None:
     def __delitem__(self, name: bytes) -> None:
+        """Delete tree entry by name."""
         del self._entries[name]
         del self._entries[name]
         self._needs_serialization = True
         self._needs_serialization = True
 
 
     def __len__(self) -> int:
     def __len__(self) -> int:
+        """Return number of entries in tree."""
         return len(self._entries)
         return len(self._entries)
 
 
     def __iter__(self) -> Iterator[bytes]:
     def __iter__(self) -> Iterator[bytes]:
+        """Iterate over tree entry names."""
         return iter(self._entries)
         return iter(self._entries)
 
 
     def add(self, name: bytes, mode: int, hexsha: bytes) -> None:
     def add(self, name: bytes, mode: int, hexsha: bytes) -> None:
@@ -1611,6 +1618,7 @@ class Commit(ShaFile):
     )
     )
 
 
     def __init__(self) -> None:
     def __init__(self) -> None:
+        """Initialize an empty Commit."""
         super().__init__()
         super().__init__()
         self._parents: list[bytes] = []
         self._parents: list[bytes] = []
         self._encoding: Optional[bytes] = None
         self._encoding: Optional[bytes] = None

+ 15 - 0
dulwich/objectspec.py

@@ -32,6 +32,14 @@ if TYPE_CHECKING:
 
 
 
 
 def to_bytes(text: Union[str, bytes]) -> bytes:
 def to_bytes(text: Union[str, bytes]) -> bytes:
+    """Convert text to bytes.
+
+    Args:
+      text: Text to convert (str or bytes)
+
+    Returns:
+      Bytes representation of text
+    """
     if getattr(text, "encode", None) is not None:
     if getattr(text, "encode", None) is not None:
         text = text.encode("ascii")  # type: ignore
         text = text.encode("ascii")  # type: ignore
     return text  # type: ignore
     return text  # type: ignore
@@ -233,6 +241,7 @@ def parse_reftuple(
       lh_container: A RefsContainer object
       lh_container: A RefsContainer object
       rh_container: A RefsContainer object
       rh_container: A RefsContainer object
       refspec: A string
       refspec: A string
+      force: Whether to force the operation
     Returns: A tuple with left and right ref
     Returns: A tuple with left and right ref
     Raises:
     Raises:
       KeyError: If one of the refs can not be found
       KeyError: If one of the refs can not be found
@@ -348,6 +357,12 @@ class AmbiguousShortId(Exception):
     """The short id is ambiguous."""
     """The short id is ambiguous."""
 
 
     def __init__(self, prefix: bytes, options: list[ShaFile]) -> None:
     def __init__(self, prefix: bytes, options: list[ShaFile]) -> None:
+        """Initialize AmbiguousShortId.
+
+        Args:
+          prefix: The ambiguous prefix
+          options: List of matching objects
+        """
         self.prefix = prefix
         self.prefix = prefix
         self.options = options
         self.options = options
 
 

+ 65 - 3
dulwich/pack.py

@@ -2436,6 +2436,7 @@ def write_pack_object(write, type, object, sha=None, compression_level=-1):
       write: Write function to use
       write: Write function to use
       type: Numeric type of the object
       type: Numeric type of the object
       object: Object to write
       object: Object to write
+      sha: Optional SHA-1 hasher to update
       compression_level: the zlib compression level
       compression_level: the zlib compression level
     Returns: Tuple with offset at which the object was written, and crc32
     Returns: Tuple with offset at which the object was written, and crc32
     """
     """
@@ -2460,6 +2461,7 @@ def write_pack(
 
 
     Args:
     Args:
       filename: Path to the new pack file (without .pack extension)
       filename: Path to the new pack file (without .pack extension)
+      objects: Objects to write to the pack
       delta_window_size: Delta window size
       delta_window_size: Delta window size
       deltify: Whether to deltify pack objects
       deltify: Whether to deltify pack objects
       compression_level: the zlib compression level
       compression_level: the zlib compression level
@@ -2505,6 +2507,17 @@ def find_reusable_deltas(
     other_haves: Optional[set[bytes]] = None,
     other_haves: Optional[set[bytes]] = None,
     progress=None,
     progress=None,
 ) -> Iterator[UnpackedObject]:
 ) -> Iterator[UnpackedObject]:
+    """Find deltas in a pack that can be reused.
+
+    Args:
+      container: Pack container to search for deltas
+      object_ids: Set of object IDs to find deltas for
+      other_haves: Set of other object IDs we have
+      progress: Optional progress reporting callback
+
+    Returns:
+      Iterator of UnpackedObject entries that can be reused
+    """
     if other_haves is None:
     if other_haves is None:
         other_haves = set()
         other_haves = set()
     reused = 0
     reused = 0
@@ -2535,6 +2548,7 @@ def deltify_pack_objects(
     Args:
     Args:
       objects: An iterable of (object, path) tuples to deltify.
       objects: An iterable of (object, path) tuples to deltify.
       window_size: Window size; None for default
       window_size: Window size; None for default
+      progress: Optional progress reporting callback
     Returns: Iterator over type_num, object id, delta_base, content
     Returns: Iterator over type_num, object id, delta_base, content
         delta_base is None for full text entries
         delta_base is None for full text entries
     """
     """
@@ -2556,6 +2570,14 @@ def deltify_pack_objects(
 def sort_objects_for_delta(
 def sort_objects_for_delta(
     objects: Union[Iterator[ShaFile], Iterator[tuple[ShaFile, Optional[PackHint]]]],
     objects: Union[Iterator[ShaFile], Iterator[tuple[ShaFile, Optional[PackHint]]]],
 ) -> Iterator[ShaFile]:
 ) -> Iterator[ShaFile]:
+    """Sort objects for optimal delta compression.
+
+    Args:
+      objects: Iterator of objects or (object, hint) tuples
+
+    Returns:
+      Iterator of sorted ShaFile objects
+    """
     magic = []
     magic = []
     for entry in objects:
     for entry in objects:
         if isinstance(entry, tuple):
         if isinstance(entry, tuple):
@@ -2577,6 +2599,16 @@ def sort_objects_for_delta(
 def deltas_from_sorted_objects(
 def deltas_from_sorted_objects(
     objects, window_size: Optional[int] = None, progress=None
     objects, window_size: Optional[int] = None, progress=None
 ):
 ):
+    """Create deltas from sorted objects.
+
+    Args:
+      objects: Iterator of sorted objects to deltify
+      window_size: Delta window size; None for default
+      progress: Optional progress reporting callback
+
+    Returns:
+      Iterator of UnpackedObject entries
+    """
     # TODO(jelmer): Use threads
     # TODO(jelmer): Use threads
     if window_size is None:
     if window_size is None:
         window_size = DEFAULT_PACK_DELTA_WINDOW_SIZE
         window_size = DEFAULT_PACK_DELTA_WINDOW_SIZE
@@ -2627,6 +2659,10 @@ def pack_objects_to_data(
 
 
     Args:
     Args:
       objects: Pack objects
       objects: Pack objects
+      deltify: Whether to deltify pack objects
+      delta_window_size: Delta window size
+      ofs_delta: Whether to use offset deltas
+      progress: Optional progress reporting callback
     Returns: Tuples with (type_num, hexdigest, delta base, object chunks)
     Returns: Tuples with (type_num, hexdigest, delta base, object chunks)
     """
     """
     # TODO(jelmer): support deltaifying
     # TODO(jelmer): support deltaifying
@@ -2728,10 +2764,13 @@ def write_pack_from_container(
     Args:
     Args:
       write: write function to use
       write: write function to use
       container: PackedObjectContainer
       container: PackedObjectContainer
+      object_ids: Sequence of (object_id, hint) tuples to write
       delta_window_size: Sliding window size for searching for deltas;
       delta_window_size: Sliding window size for searching for deltas;
                          Set to None for default window size.
                          Set to None for default window size.
       deltify: Whether to deltify objects
       deltify: Whether to deltify objects
+      reuse_deltas: Whether to reuse existing deltas
       compression_level: the zlib compression level to use
       compression_level: the zlib compression level to use
+      other_haves: Set of additional object IDs the receiver has
     Returns: Dict mapping id -> (offset, crc32 checksum), pack checksum
     Returns: Dict mapping id -> (offset, crc32 checksum), pack checksum
     """
     """
     pack_contents_count = len(object_ids)
     pack_contents_count = len(object_ids)
@@ -2835,6 +2874,7 @@ class PackChunkGenerator:
           num_records: Number of records (defaults to len(records) if not specified)
           num_records: Number of records (defaults to len(records) if not specified)
           progress: Function to report progress to
           progress: Function to report progress to
           compression_level: the zlib compression level
           compression_level: the zlib compression level
+          reuse_compressed: Whether to reuse compressed chunks
         Returns: Dict mapping id -> (offset, crc32 checksum), pack checksum
         Returns: Dict mapping id -> (offset, crc32 checksum), pack checksum
         """
         """
         # Write the pack
         # Write the pack
@@ -3260,6 +3300,18 @@ class Pack:
         threads=None,
         threads=None,
         big_file_threshold=None,
         big_file_threshold=None,
     ) -> None:
     ) -> None:
+        """Initialize a Pack object.
+
+        Args:
+          basename: Base path for pack files (without .pack/.idx extension)
+          resolve_ext_ref: Optional function to resolve external references
+          delta_window_size: Size of the delta compression window
+          window_memory: Memory limit for delta compression window
+          delta_cache_size: Size of the delta cache
+          depth: Maximum depth for delta chains
+          threads: Number of threads to use for operations
+          big_file_threshold: Size threshold for big file handling
+        """
         self._basename = basename
         self._basename = basename
         self._data = None
         self._data = None
         self._idx = None
         self._idx = None
@@ -3285,9 +3337,7 @@ class Pack:
 
 
     @classmethod
     @classmethod
     def from_lazy_objects(cls, data_fn, idx_fn):
     def from_lazy_objects(cls, data_fn, idx_fn):
-        """Create a new pack object from callables to load pack data and index objects.
-
-        """
+        """Create a new pack object from callables to load pack data and index objects."""
         ret = cls("")
         ret = cls("")
         ret._data_load = data_fn
         ret._data_load = data_fn
         ret._idx_load = idx_fn
         ret._idx_load = idx_fn
@@ -3329,18 +3379,22 @@ class Pack:
         return self._idx
         return self._idx
 
 
     def close(self) -> None:
     def close(self) -> None:
+        """Close the pack file and index."""
         if self._data is not None:
         if self._data is not None:
             self._data.close()
             self._data.close()
         if self._idx is not None:
         if self._idx is not None:
             self._idx.close()
             self._idx.close()
 
 
     def __enter__(self):
     def __enter__(self):
+        """Enter context manager."""
         return self
         return self
 
 
     def __exit__(self, exc_type, exc_val, exc_tb):
     def __exit__(self, exc_type, exc_val, exc_tb):
+        """Exit context manager."""
         self.close()
         self.close()
 
 
     def __eq__(self, other):
     def __eq__(self, other):
+        """Check equality with another pack."""
         return isinstance(self, type(other)) and self.index == other.index
         return isinstance(self, type(other)) and self.index == other.index
 
 
     def __len__(self) -> int:
     def __len__(self) -> int:
@@ -3348,6 +3402,7 @@ class Pack:
         return len(self.index)
         return len(self.index)
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of this pack."""
         return f"{self.__class__.__name__}({self._basename!r})"
         return f"{self.__class__.__name__}({self._basename!r})"
 
 
     def __iter__(self):
     def __iter__(self):
@@ -3383,9 +3438,11 @@ class Pack:
         # TODO: object connectivity checks
         # TODO: object connectivity checks
 
 
     def get_stored_checksum(self) -> bytes:
     def get_stored_checksum(self) -> bytes:
+        """Return the stored checksum of the pack data."""
         return self.data.get_stored_checksum()
         return self.data.get_stored_checksum()
 
 
     def pack_tuples(self):
     def pack_tuples(self):
+        """Return pack tuples for all objects in pack."""
         return [(o, None) for o in self.iterobjects()]
         return [(o, None) for o in self.iterobjects()]
 
 
     def __contains__(self, sha1: bytes) -> bool:
     def __contains__(self, sha1: bytes) -> bool:
@@ -3397,6 +3454,7 @@ class Pack:
             return False
             return False
 
 
     def get_raw(self, sha1: bytes) -> tuple[int, bytes]:
     def get_raw(self, sha1: bytes) -> tuple[int, bytes]:
+        """Get raw object data by SHA1."""
         offset = self.index.object_offset(sha1)
         offset = self.index.object_offset(sha1)
         obj_type, obj = self.data.get_object_at(offset)
         obj_type, obj = self.data.get_object_at(offset)
         type_num, chunks = self.resolve_object(offset, obj_type, obj)
         type_num, chunks = self.resolve_object(offset, obj_type, obj)
@@ -3416,6 +3474,7 @@ class Pack:
     def iterobjects_subset(
     def iterobjects_subset(
         self, shas: Iterable[ObjectID], *, allow_missing: bool = False
         self, shas: Iterable[ObjectID], *, allow_missing: bool = False
     ) -> Iterator[ShaFile]:
     ) -> Iterator[ShaFile]:
+        """Iterate over a subset of objects in this pack."""
         return (
         return (
             uo
             uo
             for uo in PackInflater.for_pack_subset(
             for uo in PackInflater.for_pack_subset(
@@ -3435,6 +3494,7 @@ class Pack:
         allow_missing: bool = False,
         allow_missing: bool = False,
         convert_ofs_delta: bool = False,
         convert_ofs_delta: bool = False,
     ) -> Iterator[UnpackedObject]:
     ) -> Iterator[UnpackedObject]:
+        """Iterate over unpacked objects in subset."""
         ofs_pending: dict[int, list[UnpackedObject]] = defaultdict(list)
         ofs_pending: dict[int, list[UnpackedObject]] = defaultdict(list)
         ofs: dict[bytes, int] = {}
         ofs: dict[bytes, int] = {}
         todo = set(shas)
         todo = set(shas)
@@ -3464,6 +3524,7 @@ class Pack:
             raise UnresolvedDeltas(list(todo))
             raise UnresolvedDeltas(list(todo))
 
 
     def iter_unpacked(self, include_comp=False):
     def iter_unpacked(self, include_comp=False):
+        """Iterate over all unpacked objects in this pack."""
         ofs_to_entries = {
         ofs_to_entries = {
             ofs: (sha, crc32) for (sha, ofs, crc32) in self.index.iterentries()
             ofs: (sha, crc32) for (sha, ofs, crc32) in self.index.iterentries()
         }
         }
@@ -3580,6 +3641,7 @@ class Pack:
         Args:
         Args:
           sha: SHA of object to fetch
           sha: SHA of object to fetch
           include_comp: Whether to include compression data in UnpackedObject
           include_comp: Whether to include compression data in UnpackedObject
+          convert_ofs_delta: Whether to convert offset deltas to ref deltas
         """
         """
         offset = self.index.object_offset(sha)
         offset = self.index.object_offset(sha)
         unpacked = self.data.get_unpacked_object_at(offset, include_comp=include_comp)
         unpacked = self.data.get_unpacked_object_at(offset, include_comp=include_comp)

+ 5 - 0
dulwich/patch.py

@@ -58,8 +58,12 @@ def write_commit_patch(
     """Write a individual file patch.
     """Write a individual file patch.
 
 
     Args:
     Args:
+      f: File-like object to write to
       commit: Commit object
       commit: Commit object
+      contents: Contents of the patch
       progress: tuple with current patch number and total.
       progress: tuple with current patch number and total.
+      version: Version string to include in patch header
+      encoding: Encoding to use for the patch
 
 
     Returns:
     Returns:
       tuple with filename and contents
       tuple with filename and contents
@@ -409,6 +413,7 @@ def write_tree_diff(
 
 
     Args:
     Args:
       f: File-like object to write to.
       f: File-like object to write to.
+      store: Object store to read from
       old_tree: Old tree id
       old_tree: Old tree id
       new_tree: New tree id
       new_tree: New tree id
       diff_binary: Whether to diff files even if they
       diff_binary: Whether to diff files even if they

+ 10 - 1
dulwich/porcelain.py

@@ -1182,6 +1182,7 @@ def print_commit(commit, decode, outstream=sys.stdout) -> None:
 
 
     Args:
     Args:
       commit: A `Commit` object
       commit: A `Commit` object
+      decode: Function to decode commit data
       outstream: A stream file to write to
       outstream: A stream file to write to
     """
     """
     outstream.write("-" * 50 + "\n")
     outstream.write("-" * 50 + "\n")
@@ -1653,6 +1654,7 @@ def submodule_update(
       paths: Optional list of specific submodule paths to update. If None, updates all.
       paths: Optional list of specific submodule paths to update. If None, updates all.
       init: If True, initialize submodules first
       init: If True, initialize submodules first
       force: Force update even if local changes exist
       force: Force update even if local changes exist
+      errstream: Error stream for error messages
     """
     """
     from .submodule import iter_cached_submodules
     from .submodule import iter_cached_submodules
 
 
@@ -1795,6 +1797,7 @@ def tag_create(
       sign: GPG Sign the tag (bool, defaults to False,
       sign: GPG Sign the tag (bool, defaults to False,
         pass True to use default GPG key,
         pass True to use default GPG key,
         pass a str containing Key ID to use a specific GPG key)
         pass a str containing Key ID to use a specific GPG key)
+      encoding: Encoding to use for tag messages
     """
     """
     with open_repo_closing(repo) as r:
     with open_repo_closing(repo) as r:
         object = parse_object(r, objectish)
         object = parse_object(r, objectish)
@@ -2187,6 +2190,7 @@ def push(
       outstream: A stream file to write output
       outstream: A stream file to write output
       errstream: A stream file to write errors
       errstream: A stream file to write errors
       force: Force overwriting refs
       force: Force overwriting refs
+      **kwargs: Additional keyword arguments for the client
     """
     """
     # Open the repo
     # Open the repo
     with open_repo_closing(repo) as r:
     with open_repo_closing(repo) as r:
@@ -2315,6 +2319,7 @@ def pull(
         feature, and ignored otherwise.
         feature, and ignored otherwise.
       protocol_version: desired Git protocol version. By default the highest
       protocol_version: desired Git protocol version. By default the highest
         mutually supported protocol version will be used
         mutually supported protocol version will be used
+      **kwargs: Additional keyword arguments for the client
     """
     """
     # Open the repo
     # Open the repo
     with open_repo_closing(repo) as r:
     with open_repo_closing(repo) as r:
@@ -3033,6 +3038,8 @@ def fetch(
       depth: Depth to fetch at
       depth: Depth to fetch at
       prune: Prune remote removed refs
       prune: Prune remote removed refs
       prune_tags: Prune reomte removed tags
       prune_tags: Prune reomte removed tags
+      force: Force fetching even if it would overwrite local changes
+      **kwargs: Additional keyword arguments for the client
     Returns:
     Returns:
       Dictionary with refs on the remote
       Dictionary with refs on the remote
     """
     """
@@ -3122,6 +3129,7 @@ def ls_remote(remote, config: Optional[Config] = None, **kwargs):
     Args:
     Args:
       remote: Remote repository location
       remote: Remote repository location
       config: Configuration to use
       config: Configuration to use
+      **kwargs: Additional keyword arguments for the client
     Returns:
     Returns:
       LsRemoteResult object with refs and symrefs
       LsRemoteResult object with refs and symrefs
     """
     """
@@ -3630,6 +3638,7 @@ def reset_file(
       repo: dulwich Repo object
       repo: dulwich Repo object
       file_path: file to reset, relative to the repository path
       file_path: file to reset, relative to the repository path
       target: branch or commit or b'HEAD' to reset
       target: branch or commit or b'HEAD' to reset
+      symlink_fn: Function to use for creating symlinks
     """
     """
     tree = parse_tree(repo, treeish=target)
     tree = parse_tree(repo, treeish=target)
     tree_path = _fs_to_tree_path(file_path)
     tree_path = _fs_to_tree_path(file_path)
@@ -4276,7 +4285,7 @@ def cherry_pick(
       repo: Repository to cherry-pick into
       repo: Repository to cherry-pick into
       committish: Commit to cherry-pick (can be None only when ``continue_`` or abort is True)
       committish: Commit to cherry-pick (can be None only when ``continue_`` or abort is True)
       no_commit: If True, do not create a commit after applying changes
       no_commit: If True, do not create a commit after applying changes
-      ``continue_``: Continue an in-progress cherry-pick after resolving conflicts
+      continue_: Continue an in-progress cherry-pick after resolving conflicts
       abort: Abort an in-progress cherry-pick
       abort: Abort an in-progress cherry-pick
 
 
     Returns:
     Returns:

+ 24 - 0
dulwich/protocol.py

@@ -289,6 +289,14 @@ class Protocol:
         close: Optional[Callable[[], None]] = None,
         close: Optional[Callable[[], None]] = None,
         report_activity: Optional[Callable[[int, str], None]] = None,
         report_activity: Optional[Callable[[int, str], None]] = None,
     ) -> None:
     ) -> None:
+        """Initialize Protocol.
+
+        Args:
+          read: Function to read bytes from the transport
+          write: Function to write bytes to the transport
+          close: Optional function to close the transport
+          report_activity: Optional function to report activity
+        """
         self.read = read
         self.read = read
         self.write = write
         self.write = write
         self._close = close
         self._close = close
@@ -301,6 +309,7 @@ class Protocol:
             self._close()
             self._close()
 
 
     def __enter__(self) -> "Protocol":
     def __enter__(self) -> "Protocol":
+        """Enter context manager."""
         return self
         return self
 
 
     def __exit__(
     def __exit__(
@@ -309,6 +318,7 @@ class Protocol:
         exc_val: Optional[BaseException],
         exc_val: Optional[BaseException],
         exc_tb: Optional[types.TracebackType],
         exc_tb: Optional[types.TracebackType],
     ) -> None:
     ) -> None:
+        """Exit context manager and close transport."""
         self.close()
         self.close()
 
 
     def read_pkt_line(self) -> Optional[bytes]:
     def read_pkt_line(self) -> Optional[bytes]:
@@ -466,6 +476,15 @@ class ReceivableProtocol(Protocol):
         report_activity: Optional[Callable[[int, str], None]] = None,
         report_activity: Optional[Callable[[int, str], None]] = None,
         rbufsize: int = _RBUFSIZE,
         rbufsize: int = _RBUFSIZE,
     ) -> None:
     ) -> None:
+        """Initialize ReceivableProtocol.
+
+        Args:
+          recv: Function to receive bytes from the transport
+          write: Function to write bytes to the transport
+          close: Optional function to close the transport
+          report_activity: Optional function to report activity
+          rbufsize: Read buffer size
+        """
         super().__init__(self.read, write, close=close, report_activity=report_activity)
         super().__init__(self.read, write, close=close, report_activity=report_activity)
         self._recv = recv
         self._recv = recv
         self._rbuf = BytesIO()
         self._rbuf = BytesIO()
@@ -662,6 +681,11 @@ class PktLineParser:
     """Packet line parser that hands completed packets off to a callback."""
     """Packet line parser that hands completed packets off to a callback."""
 
 
     def __init__(self, handle_pkt: Callable[[Optional[bytes]], None]) -> None:
     def __init__(self, handle_pkt: Callable[[Optional[bytes]], None]) -> None:
+        """Initialize PktLineParser.
+
+        Args:
+          handle_pkt: Callback function to handle completed packets
+        """
         self.handle_pkt = handle_pkt
         self.handle_pkt = handle_pkt
         self._readahead = BytesIO()
         self._readahead = BytesIO()
 
 

+ 10 - 0
dulwich/rebase.py

@@ -38,6 +38,11 @@ class RebaseConflict(RebaseError):
     """Raised when a rebase conflict occurs."""
     """Raised when a rebase conflict occurs."""
 
 
     def __init__(self, conflicted_files: list[bytes]):
     def __init__(self, conflicted_files: list[bytes]):
+        """Initialize RebaseConflict.
+
+        Args:
+          conflicted_files: List of conflicted file paths
+        """
         self.conflicted_files = conflicted_files
         self.conflicted_files = conflicted_files
         super().__init__(
         super().__init__(
             f"Conflicts in: {', '.join(f.decode('utf-8', 'replace') for f in conflicted_files)}"
             f"Conflicts in: {', '.join(f.decode('utf-8', 'replace') for f in conflicted_files)}"
@@ -197,6 +202,11 @@ class MemoryRebaseStateManager:
     """Manages rebase state in memory for MemoryRepo."""
     """Manages rebase state in memory for MemoryRepo."""
 
 
     def __init__(self, repo: Repo) -> None:
     def __init__(self, repo: Repo) -> None:
+        """Initialize MemoryRebaseStateManager.
+
+        Args:
+          repo: Repository instance
+        """
         self.repo = repo
         self.repo = repo
         self._state: Optional[dict] = None
         self._state: Optional[dict] = None
 
 

+ 14 - 0
dulwich/refs.py

@@ -530,6 +530,7 @@ class DictRefsContainer(RefsContainer):
     """
     """
 
 
     def __init__(self, refs, logger=None) -> None:
     def __init__(self, refs, logger=None) -> None:
+        """Initialize DictRefsContainer."""
         super().__init__(logger=logger)
         super().__init__(logger=logger)
         self._refs = refs
         self._refs = refs
         self._peeled: dict[bytes, ObjectID] = {}
         self._peeled: dict[bytes, ObjectID] = {}
@@ -818,6 +819,7 @@ class DiskRefsContainer(RefsContainer):
         worktree_path: Optional[Union[str, bytes, os.PathLike]] = None,
         worktree_path: Optional[Union[str, bytes, os.PathLike]] = None,
         logger=None,
         logger=None,
     ) -> None:
     ) -> None:
+        """Initialize DiskRefsContainer."""
         super().__init__(logger=logger)
         super().__init__(logger=logger)
         # Convert path-like objects to strings, then to bytes for Git compatibility
         # Convert path-like objects to strings, then to bytes for Git compatibility
         self.path = os.fsencode(os.fspath(path))
         self.path = os.fsencode(os.fspath(path))
@@ -1046,6 +1048,9 @@ class DiskRefsContainer(RefsContainer):
         Args:
         Args:
           name: Name of the ref to set
           name: Name of the ref to set
           other: Name of the ref to point at
           other: Name of the ref to point at
+          committer: Optional committer name
+          timestamp: Optional timestamp
+          timezone: Optional timezone
           message: Optional message to describe the change
           message: Optional message to describe the change
         """
         """
         self._check_refname(name)
         self._check_refname(name)
@@ -1090,6 +1095,9 @@ class DiskRefsContainer(RefsContainer):
           old_ref: The old sha the refname must refer to, or None to set
           old_ref: The old sha the refname must refer to, or None to set
             unconditionally.
             unconditionally.
           new_ref: The new sha the refname will refer to.
           new_ref: The new sha the refname will refer to.
+          committer: Optional committer name
+          timestamp: Optional timestamp
+          timezone: Optional timezone
           message: Set message for reflog
           message: Set message for reflog
         Returns: True if the set was successful, False otherwise.
         Returns: True if the set was successful, False otherwise.
         """
         """
@@ -1168,6 +1176,9 @@ class DiskRefsContainer(RefsContainer):
         Args:
         Args:
           name: The refname to set.
           name: The refname to set.
           ref: The new sha the refname will refer to.
           ref: The new sha the refname will refer to.
+          committer: Optional committer name
+          timestamp: Optional timestamp
+          timezone: Optional timezone
           message: Optional message for reflog
           message: Optional message for reflog
         Returns: True if the add was successful, False otherwise.
         Returns: True if the add was successful, False otherwise.
         """
         """
@@ -1220,6 +1231,9 @@ class DiskRefsContainer(RefsContainer):
           name: The refname to delete.
           name: The refname to delete.
           old_ref: The old sha the refname must refer to, or None to
           old_ref: The old sha the refname must refer to, or None to
             delete unconditionally.
             delete unconditionally.
+          committer: Optional committer name
+          timestamp: Optional timestamp
+          timezone: Optional timezone
           message: Optional message
           message: Optional message
         Returns: True if the delete was successful, False otherwise.
         Returns: True if the delete was successful, False otherwise.
         """
         """

+ 28 - 0
dulwich/reftable.py

@@ -225,6 +225,13 @@ class RefUpdate:
     """A reference update operation."""
     """A reference update operation."""
 
 
     def __init__(self, name: bytes, value_type: int, value: bytes):
     def __init__(self, name: bytes, value_type: int, value: bytes):
+        """Initialize RefUpdate.
+
+        Args:
+          name: Reference name
+          value_type: Type of reference value
+          value: Reference value
+        """
         self.name = name
         self.name = name
         self.value_type = value_type
         self.value_type = value_type
         self.value = value
         self.value = value
@@ -236,6 +243,14 @@ class RefRecord:
     def __init__(
     def __init__(
         self, refname: bytes, value_type: int, value: bytes, update_index: int = 0
         self, refname: bytes, value_type: int, value: bytes, update_index: int = 0
     ):
     ):
+        """Initialize RefRecord.
+
+        Args:
+          refname: Reference name
+          value_type: Type of reference value
+          value: Reference value
+          update_index: Update index for the reference
+        """
         self.refname = refname
         self.refname = refname
         self.value_type = value_type
         self.value_type = value_type
         self.value = value
         self.value = value
@@ -323,6 +338,7 @@ class RefBlock:
     """A block containing reference records."""
     """A block containing reference records."""
 
 
     def __init__(self):
     def __init__(self):
+        """Initialize RefBlock."""
         self.refs = []
         self.refs = []
 
 
     def add_ref(
     def add_ref(
@@ -478,6 +494,13 @@ class ReftableWriter:
         auto_create_head: bool = True,
         auto_create_head: bool = True,
         is_batch_operation: bool = False,
         is_batch_operation: bool = False,
     ):
     ):
+        """Initialize ReftableWriter.
+
+        Args:
+          f: Binary file object to write to
+          auto_create_head: Whether to automatically create HEAD reference
+          is_batch_operation: Whether this is a batch operation
+        """
         self.f = f
         self.f = f
         self.refs: dict[bytes, tuple[int, bytes]] = {}
         self.refs: dict[bytes, tuple[int, bytes]] = {}
         self.refs_order: list[bytes] = []  # Track insertion order for update indices
         self.refs_order: list[bytes] = []  # Track insertion order for update indices
@@ -662,6 +685,11 @@ class ReftableReader:
     """Reader for reftable files."""
     """Reader for reftable files."""
 
 
     def __init__(self, f: BinaryIO):
     def __init__(self, f: BinaryIO):
+        """Initialize ReftableReader.
+
+        Args:
+          f: Binary file object to read from
+        """
         self.f = f
         self.f = f
         self._read_header()
         self._read_header()
         self.refs: dict[bytes, tuple[int, bytes]] = {}
         self.refs: dict[bytes, tuple[int, bytes]] = {}

+ 12 - 1
dulwich/repo.py

@@ -1512,7 +1512,7 @@ class Repo(BaseRepo):
 
 
     @replace_me(remove_in="0.26.0")
     @replace_me(remove_in="0.26.0")
     def unstage(self, fs_paths: list[str]) -> None:
     def unstage(self, fs_paths: list[str]) -> None:
-        """Unstage specific file in the index
+        """Unstage specific file in the index.
 
 
         Args:
         Args:
           fs_paths: a list of files to unstage,
           fs_paths: a list of files to unstage,
@@ -1770,6 +1770,7 @@ class Repo(BaseRepo):
             return None
             return None
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of this repository."""
         return f"<Repo at {self.path!r}>"
         return f"<Repo at {self.path!r}>"
 
 
     def set_description(self, description) -> None:
     def set_description(self, description) -> None:
@@ -1832,6 +1833,9 @@ class Repo(BaseRepo):
         Args:
         Args:
           path: Path in which to create the repository
           path: Path in which to create the repository
           mkdir: Whether to create the directory
           mkdir: Whether to create the directory
+          config: Configuration object
+          default_branch: Default branch name
+          symlinks: Whether to support symlinks
           format: Repository format version (defaults to 0)
           format: Repository format version (defaults to 0)
         Returns: `Repo` instance
         Returns: `Repo` instance
         """
         """
@@ -1919,6 +1923,10 @@ class Repo(BaseRepo):
 
 
         Args:
         Args:
           path: Path to create bare repository in
           path: Path to create bare repository in
+          mkdir: Whether to create the directory
+          object_store: Object store to use
+          config: Configuration object
+          default_branch: Default branch name
           format: Repository format version (defaults to 0)
           format: Repository format version (defaults to 0)
         Returns: a `Repo` instance
         Returns: a `Repo` instance
         """
         """
@@ -1944,9 +1952,11 @@ class Repo(BaseRepo):
         self.object_store.close()
         self.object_store.close()
 
 
     def __enter__(self):
     def __enter__(self):
+        """Enter context manager."""
         return self
         return self
 
 
     def __exit__(self, exc_type, exc_val, exc_tb):
     def __exit__(self, exc_type, exc_val, exc_tb):
+        """Exit context manager and close repository."""
         self.close()
         self.close()
 
 
     def _read_gitattributes(self) -> dict[bytes, dict[bytes, bytes]]:
     def _read_gitattributes(self) -> dict[bytes, dict[bytes, bytes]]:
@@ -2189,6 +2199,7 @@ class MemoryRepo(BaseRepo):
 
 
         Args:
         Args:
           path: The path to the file, relative to the control dir.
           path: The path to the file, relative to the control dir.
+          basedir: Optional base directory for the path
         Returns: An open file object, or None if the file does not exist.
         Returns: An open file object, or None if the file does not exist.
         """
         """
         contents = self._named_files.get(path, None)
         contents = self._named_files.get(path, None)

+ 17 - 0
dulwich/server.py

@@ -172,6 +172,8 @@ class BackendRepo(TypingProtocol):
         """Yield the objects required for a list of commits.
         """Yield the objects required for a list of commits.
 
 
         Args:
         Args:
+          determine_wants: Function to determine which objects the client wants
+          graph_walker: Object used to walk the commit graph
           progress: is a callback to send progress messages to the client
           progress: is a callback to send progress messages to the client
           get_tagged: Function that returns a dict of pointed-to sha ->
           get_tagged: Function that returns a dict of pointed-to sha ->
             tag sha for including tags.
             tag sha for including tags.
@@ -191,6 +193,17 @@ class DictBackend(Backend):
         self.repos = repos
         self.repos = repos
 
 
     def open_repository(self, path: str) -> BackendRepo:
     def open_repository(self, path: str) -> BackendRepo:
+        """Open repository at given path.
+
+        Args:
+          path: Path to the repository
+
+        Returns:
+          Repository object
+
+        Raises:
+          NotGitRepository: If no repository found at path
+        """
         logger.debug("Opening repository at %s", path)
         logger.debug("Opening repository at %s", path)
         try:
         try:
             return self.repos[path]
             return self.repos[path]
@@ -672,6 +685,10 @@ class _ProtocolGraphWalker:
 
 
         If the client has the 'shallow' capability, this method also reads and
         If the client has the 'shallow' capability, this method also reads and
         responds to the 'shallow' and 'deepen' lines from the client. These are
         responds to the 'shallow' and 'deepen' lines from the client. These are
+
+        Args:
+          heads: Dictionary of heads to advertise
+          depth: Maximum depth for shallow clones
         not part of the wants per se, but they set up necessary state for
         not part of the wants per se, but they set up necessary state for
         walking the graph. Additionally, later code depends on this method
         walking the graph. Additionally, later code depends on this method
         consuming everything up to the first 'have' line.
         consuming everything up to the first 'have' line.

+ 13 - 0
dulwich/stash.py

@@ -68,6 +68,12 @@ class Stash:
     """
     """
 
 
     def __init__(self, repo: "Repo", ref: Ref = DEFAULT_STASH_REF) -> None:
     def __init__(self, repo: "Repo", ref: Ref = DEFAULT_STASH_REF) -> None:
+        """Initialize Stash.
+
+        Args:
+          repo: Repository object
+          ref: Stash reference name
+        """
         self._ref = ref
         self._ref = ref
         self._repo = repo
         self._repo = repo
 
 
@@ -76,6 +82,11 @@ class Stash:
         return os.path.join(self._repo.commondir(), "logs", os.fsdecode(self._ref))
         return os.path.join(self._repo.commondir(), "logs", os.fsdecode(self._ref))
 
 
     def stashes(self) -> list["Entry"]:
     def stashes(self) -> list["Entry"]:
+        """Get list of stash entries.
+
+        Returns:
+          List of stash entries in chronological order
+        """
         try:
         try:
             with GitFile(self._reflog_path, "rb") as f:
             with GitFile(self._reflog_path, "rb") as f:
                 return list(reversed(list(read_reflog(f))))
                 return list(reversed(list(read_reflog(f))))
@@ -330,7 +341,9 @@ class Stash:
         return cid
         return cid
 
 
     def __getitem__(self, index: int) -> "Entry":
     def __getitem__(self, index: int) -> "Entry":
+        """Get stash entry by index."""
         return list(self.stashes())[index]
         return list(self.stashes())[index]
 
 
     def __len__(self) -> int:
     def __len__(self) -> int:
+        """Return number of stash entries."""
         return len(list(self.stashes()))
         return len(list(self.stashes()))

+ 41 - 3
dulwich/tests/test_object_store.py

@@ -50,6 +50,8 @@ testobject = make_object(Blob, data=b"yummy data")
 
 
 
 
 class ObjectStoreTests:
 class ObjectStoreTests:
+    """Base class for testing object store implementations."""
+
     store: "BaseObjectStore"
     store: "BaseObjectStore"
 
 
     assertEqual: Callable[[object, object], None]
     assertEqual: Callable[[object, object], None]
@@ -62,17 +64,20 @@ class ObjectStoreTests:
     assertFalse: Callable[[bool], None]
     assertFalse: Callable[[bool], None]
 
 
     def test_determine_wants_all(self) -> None:
     def test_determine_wants_all(self) -> None:
+        """Test determine_wants_all with valid ref."""
         self.assertEqual(
         self.assertEqual(
             [b"1" * 40],
             [b"1" * 40],
             self.store.determine_wants_all({b"refs/heads/foo": b"1" * 40}),
             self.store.determine_wants_all({b"refs/heads/foo": b"1" * 40}),
         )
         )
 
 
     def test_determine_wants_all_zero(self) -> None:
     def test_determine_wants_all_zero(self) -> None:
+        """Test determine_wants_all with zero ref."""
         self.assertEqual(
         self.assertEqual(
             [], self.store.determine_wants_all({b"refs/heads/foo": b"0" * 40})
             [], self.store.determine_wants_all({b"refs/heads/foo": b"0" * 40})
         )
         )
 
 
     def test_determine_wants_all_depth(self) -> None:
     def test_determine_wants_all_depth(self) -> None:
+        """Test determine_wants_all with depth parameter."""
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         refs = {b"refs/heads/foo": testobject.id}
         refs = {b"refs/heads/foo": testobject.id}
         with patch.object(self.store, "_get_depth", return_value=1) as m:
         with patch.object(self.store, "_get_depth", return_value=1) as m:
@@ -90,6 +95,7 @@ class ObjectStoreTests:
             )
             )
 
 
     def test_get_depth(self) -> None:
     def test_get_depth(self) -> None:
+        """Test getting object depth."""
         self.assertEqual(0, self.store._get_depth(testobject.id))
         self.assertEqual(0, self.store._get_depth(testobject.id))
 
 
         self.store.add_object(testobject)
         self.store.add_object(testobject)
@@ -108,26 +114,29 @@ class ObjectStoreTests:
         )
         )
 
 
     def test_iter(self) -> None:
     def test_iter(self) -> None:
+        """Test iterating over empty store."""
         self.assertEqual([], list(self.store))
         self.assertEqual([], list(self.store))
 
 
     def test_get_nonexistant(self) -> None:
     def test_get_nonexistant(self) -> None:
+        """Test getting non-existent object raises KeyError."""
         self.assertRaises(KeyError, lambda: self.store[b"a" * 40])
         self.assertRaises(KeyError, lambda: self.store[b"a" * 40])
 
 
     def test_contains_nonexistant(self) -> None:
     def test_contains_nonexistant(self) -> None:
+        """Test checking for non-existent object."""
         self.assertNotIn(b"a" * 40, self.store)
         self.assertNotIn(b"a" * 40, self.store)
 
 
     def test_add_objects_empty(self) -> None:
     def test_add_objects_empty(self) -> None:
+        """Test adding empty list of objects."""
         self.store.add_objects([])
         self.store.add_objects([])
 
 
     def test_add_commit(self) -> None:
     def test_add_commit(self) -> None:
+        """Test adding commit objects."""
         # TODO: Argh, no way to construct Git commit objects without
         # TODO: Argh, no way to construct Git commit objects without
         # access to a serialized form.
         # access to a serialized form.
         self.store.add_objects([])
         self.store.add_objects([])
 
 
     def test_store_resilience(self) -> None:
     def test_store_resilience(self) -> None:
-        """Test if updating an existing stored object doesn't erase the object from the store.
-
-        """
+        """Test if updating an existing stored object doesn't erase the object from the store."""
         test_object = make_object(Blob, data=b"data")
         test_object = make_object(Blob, data=b"data")
 
 
         self.store.add_object(test_object)
         self.store.add_object(test_object)
@@ -139,6 +148,7 @@ class ObjectStoreTests:
         self.assertEqual(stored_test_object.id, test_object_id)
         self.assertEqual(stored_test_object.id, test_object_id)
 
 
     def test_add_object(self) -> None:
     def test_add_object(self) -> None:
+        """Test adding a single object to store."""
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         self.assertEqual({testobject.id}, set(self.store))
         self.assertEqual({testobject.id}, set(self.store))
         self.assertIn(testobject.id, self.store)
         self.assertIn(testobject.id, self.store)
@@ -146,6 +156,7 @@ class ObjectStoreTests:
         self.assertEqual(r, testobject)
         self.assertEqual(r, testobject)
 
 
     def test_add_objects(self) -> None:
     def test_add_objects(self) -> None:
+        """Test adding multiple objects to store."""
         data = [(testobject, "mypath")]
         data = [(testobject, "mypath")]
         self.store.add_objects(data)
         self.store.add_objects(data)
         self.assertEqual({testobject.id}, set(self.store))
         self.assertEqual({testobject.id}, set(self.store))
@@ -154,6 +165,7 @@ class ObjectStoreTests:
         self.assertEqual(r, testobject)
         self.assertEqual(r, testobject)
 
 
     def test_tree_changes(self) -> None:
     def test_tree_changes(self) -> None:
+        """Test detecting changes between trees."""
         blob_a1 = make_object(Blob, data=b"a1")
         blob_a1 = make_object(Blob, data=b"a1")
         blob_a2 = make_object(Blob, data=b"a2")
         blob_a2 = make_object(Blob, data=b"a2")
         blob_b = make_object(Blob, data=b"b")
         blob_b = make_object(Blob, data=b"b")
@@ -179,6 +191,7 @@ class ObjectStoreTests:
         )
         )
 
 
     def test_iter_tree_contents(self) -> None:
     def test_iter_tree_contents(self) -> None:
+        """Test iterating over tree contents."""
         blob_a = make_object(Blob, data=b"a")
         blob_a = make_object(Blob, data=b"a")
         blob_b = make_object(Blob, data=b"b")
         blob_b = make_object(Blob, data=b"b")
         blob_c = make_object(Blob, data=b"c")
         blob_c = make_object(Blob, data=b"c")
@@ -200,6 +213,7 @@ class ObjectStoreTests:
         self.assertEqual([], list(iter_tree_contents(self.store, None)))
         self.assertEqual([], list(iter_tree_contents(self.store, None)))
 
 
     def test_iter_tree_contents_include_trees(self) -> None:
     def test_iter_tree_contents_include_trees(self) -> None:
+        """Test iterating tree contents including tree objects."""
         blob_a = make_object(Blob, data=b"a")
         blob_a = make_object(Blob, data=b"a")
         blob_b = make_object(Blob, data=b"b")
         blob_b = make_object(Blob, data=b"b")
         blob_c = make_object(Blob, data=b"c")
         blob_c = make_object(Blob, data=b"c")
@@ -230,11 +244,13 @@ class ObjectStoreTests:
         self.assertEqual(expected, list(actual))
         self.assertEqual(expected, list(actual))
 
 
     def make_tag(self, name, obj):
     def make_tag(self, name, obj):
+        """Helper to create and add a tag object."""
         tag = make_tag(obj, name=name)
         tag = make_tag(obj, name=name)
         self.store.add_object(tag)
         self.store.add_object(tag)
         return tag
         return tag
 
 
     def test_peel_sha(self) -> None:
     def test_peel_sha(self) -> None:
+        """Test peeling SHA to get underlying object."""
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         tag1 = self.make_tag(b"1", testobject)
         tag1 = self.make_tag(b"1", testobject)
         tag2 = self.make_tag(b"2", testobject)
         tag2 = self.make_tag(b"2", testobject)
@@ -243,17 +259,20 @@ class ObjectStoreTests:
             self.assertEqual((obj, testobject), peel_sha(self.store, obj.id))
             self.assertEqual((obj, testobject), peel_sha(self.store, obj.id))
 
 
     def test_get_raw(self) -> None:
     def test_get_raw(self) -> None:
+        """Test getting raw object data."""
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         self.assertEqual(
         self.assertEqual(
             (Blob.type_num, b"yummy data"), self.store.get_raw(testobject.id)
             (Blob.type_num, b"yummy data"), self.store.get_raw(testobject.id)
         )
         )
 
 
     def test_close(self) -> None:
     def test_close(self) -> None:
+        """Test closing the object store."""
         # For now, just check that close doesn't barf.
         # For now, just check that close doesn't barf.
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         self.store.close()
         self.store.close()
 
 
     def test_iter_prefix(self) -> None:
     def test_iter_prefix(self) -> None:
+        """Test iterating objects by prefix."""
         self.store.add_object(testobject)
         self.store.add_object(testobject)
         self.assertEqual([testobject.id], list(self.store.iter_prefix(testobject.id)))
         self.assertEqual([testobject.id], list(self.store.iter_prefix(testobject.id)))
         self.assertEqual(
         self.assertEqual(
@@ -296,20 +315,26 @@ class ObjectStoreTests:
         self.assertEqual(blob1.id, objects[0].id)
         self.assertEqual(blob1.id, objects[0].id)
 
 
     def test_iter_prefix_not_found(self) -> None:
     def test_iter_prefix_not_found(self) -> None:
+        """Test iterating with prefix that doesn't match any objects."""
         self.assertEqual([], list(self.store.iter_prefix(b"1" * 40)))
         self.assertEqual([], list(self.store.iter_prefix(b"1" * 40)))
 
 
 
 
 class PackBasedObjectStoreTests(ObjectStoreTests):
 class PackBasedObjectStoreTests(ObjectStoreTests):
+    """Tests for pack-based object stores."""
+
     store: PackBasedObjectStore
     store: PackBasedObjectStore
 
 
     def tearDown(self) -> None:
     def tearDown(self) -> None:
+        """Clean up by closing all packs."""
         for pack in self.store.packs:
         for pack in self.store.packs:
             pack.close()
             pack.close()
 
 
     def test_empty_packs(self) -> None:
     def test_empty_packs(self) -> None:
+        """Test that new store has no packs."""
         self.assertEqual([], list(self.store.packs))
         self.assertEqual([], list(self.store.packs))
 
 
     def test_pack_loose_objects(self) -> None:
     def test_pack_loose_objects(self) -> None:
+        """Test packing loose objects into packs."""
         b1 = make_object(Blob, data=b"yummy data")
         b1 = make_object(Blob, data=b"yummy data")
         self.store.add_object(b1)
         self.store.add_object(b1)
         b2 = make_object(Blob, data=b"more yummy data")
         b2 = make_object(Blob, data=b"more yummy data")
@@ -324,6 +349,7 @@ class PackBasedObjectStoreTests(ObjectStoreTests):
         self.assertEqual(0, self.store.pack_loose_objects())
         self.assertEqual(0, self.store.pack_loose_objects())
 
 
     def test_repack(self) -> None:
     def test_repack(self) -> None:
+        """Test repacking multiple packs into one."""
         b1 = make_object(Blob, data=b"yummy data")
         b1 = make_object(Blob, data=b"yummy data")
         self.store.add_object(b1)
         self.store.add_object(b1)
         b2 = make_object(Blob, data=b"more yummy data")
         b2 = make_object(Blob, data=b"more yummy data")
@@ -341,6 +367,7 @@ class PackBasedObjectStoreTests(ObjectStoreTests):
         self.assertEqual(0, self.store.pack_loose_objects())
         self.assertEqual(0, self.store.pack_loose_objects())
 
 
     def test_repack_existing(self) -> None:
     def test_repack_existing(self) -> None:
+        """Test repacking with existing objects."""
         b1 = make_object(Blob, data=b"yummy data")
         b1 = make_object(Blob, data=b"yummy data")
         self.store.add_object(b1)
         self.store.add_object(b1)
         b2 = make_object(Blob, data=b"more yummy data")
         b2 = make_object(Blob, data=b"more yummy data")
@@ -401,16 +428,21 @@ class PackBasedObjectStoreTests(ObjectStoreTests):
 
 
 
 
 class FindShallowTests(TestCase):
 class FindShallowTests(TestCase):
+    """Tests for finding shallow commits."""
+
     def setUp(self):
     def setUp(self):
+        """Set up test fixture."""
         super().setUp()
         super().setUp()
         self._store = MemoryObjectStore()
         self._store = MemoryObjectStore()
 
 
     def make_commit(self, **attrs):
     def make_commit(self, **attrs):
+        """Helper to create and store a commit."""
         commit = make_commit(**attrs)
         commit = make_commit(**attrs)
         self._store.add_object(commit)
         self._store.add_object(commit)
         return commit
         return commit
 
 
     def make_linear_commits(self, n, message=b""):
     def make_linear_commits(self, n, message=b""):
+        """Create a linear chain of commits."""
         commits = []
         commits = []
         parents = []
         parents = []
         for _ in range(n):
         for _ in range(n):
@@ -419,9 +451,11 @@ class FindShallowTests(TestCase):
         return commits
         return commits
 
 
     def assertSameElements(self, expected, actual):
     def assertSameElements(self, expected, actual):
+        """Assert that two sequences contain the same elements."""
         self.assertEqual(set(expected), set(actual))
         self.assertEqual(set(expected), set(actual))
 
 
     def test_linear(self):
     def test_linear(self):
+        """Test finding shallow commits in a linear history."""
         c1, c2, c3 = self.make_linear_commits(3)
         c1, c2, c3 = self.make_linear_commits(3)
 
 
         self.assertEqual((set([c3.id]), set([])), find_shallow(self._store, [c3.id], 1))
         self.assertEqual((set([c3.id]), set([])), find_shallow(self._store, [c3.id], 1))
@@ -439,6 +473,7 @@ class FindShallowTests(TestCase):
         )
         )
 
 
     def test_multiple_independent(self):
     def test_multiple_independent(self):
+        """Test finding shallow commits with multiple independent branches."""
         a = self.make_linear_commits(2, message=b"a")
         a = self.make_linear_commits(2, message=b"a")
         b = self.make_linear_commits(2, message=b"b")
         b = self.make_linear_commits(2, message=b"b")
         c = self.make_linear_commits(2, message=b"c")
         c = self.make_linear_commits(2, message=b"c")
@@ -450,6 +485,7 @@ class FindShallowTests(TestCase):
         )
         )
 
 
     def test_multiple_overlapping(self):
     def test_multiple_overlapping(self):
+        """Test finding shallow commits with overlapping branches."""
         # Create the following commit tree:
         # Create the following commit tree:
         # 1--2
         # 1--2
         #  \
         #  \
@@ -465,6 +501,7 @@ class FindShallowTests(TestCase):
         )
         )
 
 
     def test_merge(self):
     def test_merge(self):
+        """Test finding shallow commits with merge commits."""
         c1 = self.make_commit()
         c1 = self.make_commit()
         c2 = self.make_commit()
         c2 = self.make_commit()
         c3 = self.make_commit(parents=[c1.id, c2.id])
         c3 = self.make_commit(parents=[c1.id, c2.id])
@@ -475,6 +512,7 @@ class FindShallowTests(TestCase):
         )
         )
 
 
     def test_tag(self):
     def test_tag(self):
+        """Test finding shallow commits with tags."""
         c1, c2 = self.make_linear_commits(2)
         c1, c2 = self.make_linear_commits(2)
         tag = make_tag(c2, name=b"tag")
         tag = make_tag(c2, name=b"tag")
         self._store.add_object(tag)
         self._store.add_object(tag)

+ 1 - 0
dulwich/tests/utils.py

@@ -87,6 +87,7 @@ def make_object(cls, **attrs):
     __slots__.
     __slots__.
 
 
     Args:
     Args:
+      cls: The class to create an instance of
       attrs: dict of attributes to set on the new object.
       attrs: dict of attributes to set on the new object.
     Returns: A newly initialized object of type cls.
     Returns: A newly initialized object of type cls.
     """
     """

+ 8 - 0
dulwich/walk.py

@@ -53,6 +53,12 @@ class WalkEntry:
     """Object encapsulating a single result from a walk."""
     """Object encapsulating a single result from a walk."""
 
 
     def __init__(self, walker: "Walker", commit: Commit) -> None:
     def __init__(self, walker: "Walker", commit: Commit) -> None:
+        """Initialize WalkEntry.
+
+        Args:
+          walker: Walker instance that created this entry
+          commit: Commit object for this entry
+        """
         self.commit = commit
         self.commit = commit
         self._store = walker.store
         self._store = walker.store
         self._get_parents = walker.get_parents
         self._get_parents = walker.get_parents
@@ -141,6 +147,7 @@ class WalkEntry:
         return self._changes[path_prefix]
         return self._changes[path_prefix]
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of WalkEntry."""
         return f"<WalkEntry commit={self.commit.id.decode('ascii')}, changes={self.changes()!r}>"
         return f"<WalkEntry commit={self.commit.id.decode('ascii')}, changes={self.changes()!r}>"
 
 
 
 
@@ -435,6 +442,7 @@ class Walker:
         return results
         return results
 
 
     def __iter__(self) -> Iterator[WalkEntry]:
     def __iter__(self) -> Iterator[WalkEntry]:
+        """Iterate over walk entries."""
         return iter(self._reorder(iter(self._next, None)))
         return iter(self._reorder(iter(self._next, None)))
 
 
 
 

+ 8 - 9
dulwich/web.py

@@ -574,6 +574,7 @@ class HTTPGitApplication:
             self.handlers.update(handlers)
             self.handlers.update(handlers)
 
 
     def __call__(self, environ, start_response):
     def __call__(self, environ, start_response):
+        """Handle WSGI request."""
         path = environ["PATH_INFO"]
         path = environ["PATH_INFO"]
         method = environ["REQUEST_METHOD"]
         method = environ["REQUEST_METHOD"]
         req = HTTPGitRequest(
         req = HTTPGitRequest(
@@ -599,14 +600,14 @@ class HTTPGitApplication:
 
 
 
 
 class GunzipFilter:
 class GunzipFilter:
-    """WSGI middleware that unzips gzip-encoded requests before passing on to the underlying application.
-
-    """
+    """WSGI middleware that unzips gzip-encoded requests before passing on to the underlying application."""
 
 
     def __init__(self, application) -> None:
     def __init__(self, application) -> None:
+        """Initialize GunzipFilter."""
         self.app = application
         self.app = application
 
 
     def __call__(self, environ, start_response):
     def __call__(self, environ, start_response):
+        """Handle WSGI request."""
         import gzip
         import gzip
 
 
         if environ.get("HTTP_CONTENT_ENCODING", "") == "gzip":
         if environ.get("HTTP_CONTENT_ENCODING", "") == "gzip":
@@ -621,14 +622,14 @@ class GunzipFilter:
 
 
 
 
 class LimitedInputFilter:
 class LimitedInputFilter:
-    """WSGI middleware that limits the input length of a request to that specified in Content-Length.
-
-    """
+    """WSGI middleware that limits the input length of a request to that specified in Content-Length."""
 
 
     def __init__(self, application) -> None:
     def __init__(self, application) -> None:
+        """Initialize LimitedInputFilter."""
         self.app = application
         self.app = application
 
 
     def __call__(self, environ, start_response):
     def __call__(self, environ, start_response):
+        """Handle WSGI request."""
         # This is not necessary if this app is run from a conforming WSGI
         # This is not necessary if this app is run from a conforming WSGI
         # server. Unfortunately, there's no way to tell that at this point.
         # server. Unfortunately, there's no way to tell that at this point.
         # TODO: git may used HTTP/1.1 chunked encoding instead of specifying
         # TODO: git may used HTTP/1.1 chunked encoding instead of specifying
@@ -642,9 +643,7 @@ class LimitedInputFilter:
 
 
 
 
 def make_wsgi_chain(*args, **kwargs):
 def make_wsgi_chain(*args, **kwargs):
-    """Factory function to create an instance of HTTPGitApplication, correctly wrapped with needed middleware.
-
-    """
+    """Factory function to create an instance of HTTPGitApplication, correctly wrapped with needed middleware."""
     app = HTTPGitApplication(*args, **kwargs)
     app = HTTPGitApplication(*args, **kwargs)
     wrapped_app = LimitedInputFilter(GunzipFilter(app))
     wrapped_app = LimitedInputFilter(GunzipFilter(app))
     return wrapped_app
     return wrapped_app

+ 15 - 1
dulwich/worktree.py

@@ -72,6 +72,18 @@ class WorkTreeInfo:
         prunable: bool = False,
         prunable: bool = False,
         lock_reason: str | None = None,
         lock_reason: str | None = None,
     ):
     ):
+        """Initialize WorkTreeInfo.
+
+        Args:
+          path: Path to the worktree
+          head: Current HEAD commit SHA
+          branch: Current branch (if not detached)
+          bare: Whether this is a bare repository
+          detached: Whether HEAD is detached
+          locked: Whether the worktree is locked
+          prunable: Whether the worktree can be pruned
+          lock_reason: Reason for locking (if locked)
+        """
         self.path = path
         self.path = path
         self.head = head
         self.head = head
         self.branch = branch
         self.branch = branch
@@ -82,9 +94,11 @@ class WorkTreeInfo:
         self.lock_reason = lock_reason
         self.lock_reason = lock_reason
 
 
     def __repr__(self) -> str:
     def __repr__(self) -> str:
+        """Return string representation of WorkTreeInfo."""
         return f"WorkTreeInfo(path={self.path!r}, branch={self.branch!r}, detached={self.detached})"
         return f"WorkTreeInfo(path={self.path!r}, branch={self.branch!r}, detached={self.detached})"
 
 
     def __eq__(self, other: object) -> bool:
     def __eq__(self, other: object) -> bool:
+        """Check equality with another WorkTreeInfo."""
         if not isinstance(other, WorkTreeInfo):
         if not isinstance(other, WorkTreeInfo):
             return NotImplemented
             return NotImplemented
         return (
         return (
@@ -311,7 +325,7 @@ class WorkTree:
         index.write()
         index.write()
 
 
     def unstage(self, fs_paths: list[str]) -> None:
     def unstage(self, fs_paths: list[str]) -> None:
-        """Unstage specific file in the index
+        """Unstage specific file in the index.
 
 
         Args:
         Args:
           fs_paths: a list of files to unstage,
           fs_paths: a list of files to unstage,