From e5e0d2f8a8ec5684b6f32fdd13490bedf7490d34 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Wed, 6 Nov 2019 22:27:14 -0800 Subject: [PATCH] Add stub with typing info. --- git_archive_all.py | 77 ++++++++++++++------------------------------- git_archive_all.pyi | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 53 deletions(-) create mode 100644 git_archive_all.pyi diff --git a/git_archive_all.py b/git_archive_all.py index 47675ff..e99acd5 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -32,24 +32,7 @@ import sys import re -__version__ = "1.19.4" - - -try: - # Python 3.3+ - from shlex import quote -except ImportError: - _find_unsafe = re.compile(r'[^a-zA-Z0-9_@%+=:,./-]').search - - def quote(s): - """Return a shell-escaped version of the string *s*.""" - if not s: - return "''" - - if _find_unsafe(s) is None: - return s - - return "'" + s.replace("'", "'\"'\"'") + "'" +__version__ = "1.20.0-dev0" try: @@ -62,17 +45,6 @@ def fsdecode(filename): else: return filename - -def git_fsdecode(filename): - """ - Decode filename from git output into str. - """ - if sys.platform.startswith('win32'): - return filename.decode('utf-8') - else: - return fsdecode(filename) - - try: # Python 3.2+ from os import fsencode @@ -84,6 +56,16 @@ def fsencode(filename): return filename +def git_fsdecode(filename): + """ + Decode filename from git output into str. + """ + if sys.platform.startswith('win32'): + return filename.decode('utf-8') + else: + return fsdecode(filename) + + def git_fsencode(filename): """ Encode filename from str into git input. @@ -99,20 +81,22 @@ def git_fsencode(filename): from os import fspath as _fspath def fspath(filename, decoder=fsdecode, encoder=fsencode): + """ + Convert filename into bytes or str, depending on what's the best type + to represent paths for current Python and platform. + """ # Python 3.6+: str can represent any path (PEP 383) # str is not required on Windows (PEP 529) # Decoding is still applied for consistency and to follow PEP 519 recommendation. return decoder(_fspath(filename)) except ImportError: def fspath(filename, decoder=fsdecode, encoder=fsencode): - """ - Python 3.4 and 3.5: str can represent any path (PEP 383), - but str is required on Windows (no PEP 529) - - Python 2.6 and 2.7: str cannot represent any path (no PEP 383), - str is required on Windows (no PEP 529) - bytes is required on POSIX (no PEP 383) - """ + # Python 3.4 and 3.5: str can represent any path (PEP 383), + # but str is required on Windows (no PEP 529) + # + # Python 2.6 and 2.7: str cannot represent any path (no PEP 383), + # str is required on Windows (no PEP 529) + # bytes is required on POSIX (no PEP 383) if sys.version_info > (3,): import pathlib if isinstance(filename, pathlib.PurePath): @@ -167,13 +151,10 @@ def __init__(self, prefix='', exclude=True, force_sub=False, extra=None, main_re bar @param exclude: Determines whether archiver should follow rules specified in .gitattributes files. - @type exclude: bool @param force_sub: Determines whether submodules are initialized and updated before archiving. - @type force_sub: bool @param extra: List of extra paths to include in the resulting archive. - @type extra: list @param main_repo_abspath: Absolute path to the main repository (or one of subdirectories). If given path is path to a subdirectory (but not a submodule directory!) it will be replaced @@ -182,7 +163,6 @@ def __init__(self, prefix='', exclude=True, force_sub=False, extra=None, main_re @param git_version: Version of Git that determines whether various workarounds are on. If None, tries to resolve via Git's CLI. - @type git_version: tuple or None """ self._should_decode_path = None self._check_attr_gens = {} @@ -217,11 +197,11 @@ def create(self, output_path, dry_run=False, output_format=None, compresslevel=N @param output_path: Output file path. @param dry_run: Determines whether create should do nothing but print what it would archive. - @type dry_run: bool @param output_format: Determines format of the output archive. If None, format is determined from extension of output_file_path. - @type output_format: str + + @param compresslevel: Optional compression level. Interpretation depends on the output format. """ output_path = fspath(output_path) @@ -291,7 +271,6 @@ def is_file_excluded(self, repo_abspath, repo_file_path): @param repo_file_path: Path to a file relative to repo_abspath. @return: True if file should be excluded. Otherwise False. - @rtype: bool """ next(self._check_attr_gens[repo_abspath]) attrs = self._check_attr_gens[repo_abspath].send(repo_file_path) @@ -303,7 +282,6 @@ def archive_all_files(self, archiver): @param archiver: Callable that accepts 2 arguments: abspath to file on the system and relative path within archive. - @type archiver: Callable """ for file_path in self.extra: archiver(path.abspath(file_path), path.join(self.prefix, file_path)) @@ -323,7 +301,6 @@ def walk_git_files(self, repo_path=fspath('')): @param repo_path: Path to the git submodule repository relative to main_repo_abspath. @return: Iterator to traverse files under git control relative to main_repo_abspath. - @rtype: Iterable """ repo_abspath = path.join(self.main_repo_abspath, fspath(repo_path)) assert repo_abspath not in self._check_attr_gens @@ -399,8 +376,6 @@ def check_git_attr(self, repo_abspath, attrs): @param repo_abspath: Absolute path to a git repository. @param attrs: Attributes to check - - @rtype: generator """ def make_process(): env = dict(environ, GIT_FLUSH='1') @@ -484,7 +459,7 @@ def read_attrs_old(process, repo_file_path): repo_file_attrs = {} for path, attr, value in reader(process, repo_file_path): - attr = attr if attr in attrs else attr.decode('utf-8') # use str or bytes depending on what user passed + attr = attr.decode('utf-8') repo_file_attrs[attr] = value yield repo_file_attrs @@ -508,12 +483,10 @@ def run_git_shell(cls, cmd, cwd=None): Run git shell command, read output and decode it into a unicode string. @param cmd: Command to be executed. - @type cmd: str @param cwd: Working directory. @return: Output of the command. - @rtype: bytes @raise CalledProcessError: Raises exception if return code of the command is non-zero. """ @@ -534,8 +507,6 @@ def get_git_version(cls): Return version of git current shell points to. If version cannot be parsed None is returned. - - @rtype: tuple or None """ try: output = cls.run_git_shell('git version') diff --git a/git_archive_all.pyi b/git_archive_all.pyi new file mode 100644 index 0000000..bec1678 --- /dev/null +++ b/git_archive_all.pyi @@ -0,0 +1,62 @@ +from os import PathLike as _PathLike +import logging +from typing import Callable, Collection, ClassVar, Dict, Generator, Iterable, List, Optional, Tuple, Union + +PathLike = Union[str, bytes, _PathLike] +PathStr = Union[str, bytes] + +def fsdecode(filename: PathLike) -> str: ... + +def fsencode(filename: PathLike) -> bytes: ... + +def git_fsdecode(filename: bytes) -> str: ... + +def git_fsencode(filename: str) -> bytes: ... + +def fspath(filename: PathLike, decoder=Callable[[PathLike], str], encoder=Callable[[PathLike], bytes]) -> PathStr: ... + +def git_fspath(filename: bytes) -> PathStr: ... + +class GitArchiver(object): + TARFILE_FORMATS: ClassVar[Dict[str, str]] + ZIPFILE_FORMATS: ClassVar[Tuple[str]] + LOG: ClassVar[logging.Logger] + + _should_decode_path: Optional[bool] + _check_attr_gens: Dict[str, Generator[Dict[str, bytes], PathStr, None]] + git_version: Optional[Tuple[int]] + main_repo_abspath: PathStr + prefix: PathStr + exclude: bool + extra: List[PathStr] + force_sub: bool + + def __init__(self, + prefix: PathLike, + exclude: bool, + force_sub: bool, + extra: Iterable[PathLike] = None, + main_repo_abspath: PathLike = None, + git_version: Tuple[int] = None) -> None: ... + + def create(self, + output_path: PathLike, + dry_run: bool, + output_format: str = None, + compresslevel: int = None) -> None: ... + + def is_file_excluded(self, repo_abspath: PathStr, repo_file_path: PathStr) -> bool: ... + + def archive_all_files(self, archiver: Callable[[PathStr, PathStr], None]) -> None: ... + + def walk_git_files(self, repo_path: PathStr = None) -> Generator[PathStr, None, None]: ... + + def check_git_attr(self, repo_abspath: PathStr, attrs: Collection[str]) -> Generator[Dict[str, bytes], PathStr, None]: ... + + def resolve_git_main_repo_abspath(self, abspath: PathLike) -> PathStr: ... + + @classmethod + def run_git_shell(cls, cmd: str, cwd: PathStr = None) -> bytes: ... + + @classmethod + def get_git_version(cls) -> Optional[Tuple[int]]: ... \ No newline at end of file