Skip to content

Add more types #1202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion doc/source/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
Changelog
=========

3.1.??
3.1.15 (UNRELEASED)
===================

* add deprectation warning for python 3.5

3.1.14
======

* git.Commit objects now have a ``replace`` method that will return a
Expand Down
12 changes: 8 additions & 4 deletions git/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
# flake8: noqa
#@PydevCodeAnalysisIgnore
from git.exc import * # @NoMove @IgnorePep8
import inspect
import os
import sys

import os.path as osp

from typing import Optional
from git.types import PathLike

__version__ = 'git'


#{ Initialization
def _init_externals():
def _init_externals() -> None:
"""Initialize external projects by putting them into the path"""
if __version__ == 'git' and 'PYOXIDIZER' not in os.environ:
sys.path.insert(1, osp.join(osp.dirname(__file__), 'ext', 'gitdb'))
Expand All @@ -29,13 +31,13 @@ def _init_externals():

#} END initialization


#################
_init_externals()
#################

#{ Imports

from git.exc import * # @NoMove @IgnorePep8
try:
from git.config import GitConfigParser # @NoMove @IgnorePep8
from git.objects import * # @NoMove @IgnorePep8
Expand Down Expand Up @@ -65,7 +67,8 @@ def _init_externals():
#{ Initialize git executable path
GIT_OK = None

def refresh(path=None):

def refresh(path: Optional[PathLike] = None) -> None:
"""Convenience method for setting the git executable path."""
global GIT_OK
GIT_OK = False
Expand All @@ -78,6 +81,7 @@ def refresh(path=None):
GIT_OK = True
#} END initialize git executable path


#################
try:
refresh()
Expand Down
15 changes: 11 additions & 4 deletions git/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import threading
from collections import OrderedDict
from textwrap import dedent
import warnings

from git.compat import (
defenc,
Expand Down Expand Up @@ -209,7 +210,7 @@ def refresh(cls, path=None):
# - a GitCommandNotFound error is spawned by ourselves
# - a PermissionError is spawned if the git executable provided
# cannot be executed for whatever reason

has_git = False
try:
cls().version()
Expand Down Expand Up @@ -497,7 +498,7 @@ def readlines(self, size=-1):
# skipcq: PYL-E0301
def __iter__(self):
return self

def __next__(self):
return self.next()

Expand Down Expand Up @@ -638,7 +639,7 @@ def execute(self, command,

:param env:
A dictionary of environment variables to be passed to `subprocess.Popen`.

:param max_chunk_size:
Maximum number of bytes in one chunk of data passed to the output_stream in
one invocation of write() method. If the given number is not positive then
Expand Down Expand Up @@ -902,8 +903,14 @@ def transform_kwarg(self, name, value, split_single_char_options):

def transform_kwargs(self, split_single_char_options=True, **kwargs):
"""Transforms Python style kwargs into git command line options."""
# Python 3.6 preserves the order of kwargs and thus has a stable
# order. For older versions sort the kwargs by the key to get a stable
# order.
if sys.version_info[:2] < (3, 6):
kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0]))
warnings.warn("Python 3.5 support is deprecated and will be removed 2021-09-05.\n" +
"It does not preserve the order for key-word arguments and enforce lexical sorting instead.")
args = []
kwargs = OrderedDict(sorted(kwargs.items(), key=lambda x: x[0]))
for k, v in kwargs.items():
if isinstance(v, (list, tuple)):
for value in v:
Expand Down
38 changes: 26 additions & 12 deletions git/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,57 +11,71 @@
import os
import sys


from gitdb.utils.encoding import (
force_bytes, # @UnusedImport
force_text # @UnusedImport
)

# typing --------------------------------------------------------------------

from typing import Any, AnyStr, Dict, Optional, Type
from git.types import TBD

# ---------------------------------------------------------------------------

is_win = (os.name == 'nt')

is_win = (os.name == 'nt') # type: bool
is_posix = (os.name == 'posix')
is_darwin = (os.name == 'darwin')
defenc = sys.getfilesystemencoding()


def safe_decode(s):
def safe_decode(s: Optional[AnyStr]) -> Optional[str]:
"""Safely decodes a binary string to unicode"""
if isinstance(s, str):
return s
elif isinstance(s, bytes):
return s.decode(defenc, 'surrogateescape')
elif s is not None:
elif s is None:
return None
else:
raise TypeError('Expected bytes or text, but got %r' % (s,))


def safe_encode(s):
"""Safely decodes a binary string to unicode"""
def safe_encode(s: Optional[AnyStr]) -> Optional[bytes]:
"""Safely encodes a binary string to unicode"""
if isinstance(s, str):
return s.encode(defenc)
elif isinstance(s, bytes):
return s
elif s is not None:
elif s is None:
return None
else:
raise TypeError('Expected bytes or text, but got %r' % (s,))


def win_encode(s):
def win_encode(s: Optional[AnyStr]) -> Optional[bytes]:
"""Encode unicodes for process arguments on Windows."""
if isinstance(s, str):
return s.encode(locale.getpreferredencoding(False))
elif isinstance(s, bytes):
return s
elif s is not None:
raise TypeError('Expected bytes or text, but got %r' % (s,))
return None



def with_metaclass(meta, *bases):
def with_metaclass(meta: Type[Any], *bases: Any) -> 'metaclass': # type: ignore ## mypy cannot understand dynamic class creation
"""copied from https://github.com/Byron/bcore/blob/master/src/python/butility/future.py#L15"""
class metaclass(meta):

class metaclass(meta): # type: ignore
__call__ = type.__call__
__init__ = type.__init__
__init__ = type.__init__ # type: ignore

def __new__(cls, name, nbases, d):
def __new__(cls, name: str, nbases: Optional[int], d: Dict[str, Any]) -> TBD:
if nbases is None:
return type.__new__(cls, name, (), d)
return meta(name, bases, d)

return metaclass(meta.__name__ + 'Helper', None, {})
4 changes: 3 additions & 1 deletion git/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import fnmatch
from collections import OrderedDict

from typing_extensions import Literal

from git.compat import (
defenc,
force_text,
Expand Down Expand Up @@ -194,7 +196,7 @@ def items_all(self):
return [(k, self.getall(k)) for k in self]


def get_config_path(config_level):
def get_config_path(config_level: Literal['system', 'global', 'user', 'repository']) -> str:

# we do not support an absolute path of the gitconfig on windows ,
# use the global config instead
Expand Down
24 changes: 16 additions & 8 deletions git/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@
from gitdb.db import GitDB # @UnusedImport
from gitdb.db import LooseObjectDB

from .exc import (
GitCommandError,
BadObject
)
from gitdb.exc import BadObject
from git.exc import GitCommandError

# typing-------------------------------------------------

from typing import TYPE_CHECKING, AnyStr
from git.types import PathLike

if TYPE_CHECKING:
from git.cmd import Git


# --------------------------------------------------------

__all__ = ('GitCmdObjectDB', 'GitDB')

Expand All @@ -28,23 +36,23 @@ class GitCmdObjectDB(LooseObjectDB):
have packs and the other implementations
"""

def __init__(self, root_path, git):
def __init__(self, root_path: PathLike, git: 'Git') -> None:
"""Initialize this instance with the root and a git command"""
super(GitCmdObjectDB, self).__init__(root_path)
self._git = git

def info(self, sha):
def info(self, sha: bytes) -> OInfo:
hexsha, typename, size = self._git.get_object_header(bin_to_hex(sha))
return OInfo(hex_to_bin(hexsha), typename, size)

def stream(self, sha):
def stream(self, sha: bytes) -> OStream:
"""For now, all lookup is done by git itself"""
hexsha, typename, size, stream = self._git.stream_object_data(bin_to_hex(sha))
return OStream(hex_to_bin(hexsha), typename, size, stream)

# { Interface

def partial_to_complete_sha_hex(self, partial_hexsha):
def partial_to_complete_sha_hex(self, partial_hexsha: AnyStr) -> bytes:
""":return: Full binary 20 byte sha from the given partial hexsha
:raise AmbiguousObjectName:
:raise BadObject:
Expand Down
Loading