Skip to content
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

Added type hints/annotations to all methods #32

Merged
merged 16 commits into from
Oct 14, 2019
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#24](https://github.com/greenbone/autohooks/pull/24)
* Add `poetry` mode to run autohooks via [poetry](https://poetry.eustace.io/)
[#29](https://github.com/greenbone/autohooks/pull/29)
* Added type hints/annotations to all methods [#32](https://github.com/greenbone/autohooks/pull/32)

### Changed

Expand Down
2 changes: 1 addition & 1 deletion autohooks/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
from autohooks.terminal import ok, fail, error, warning


def out(message):
def out(message: str):
print(message)
67 changes: 41 additions & 26 deletions autohooks/api/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,17 @@
from enum import Enum
from pathlib import Path
from tempfile import NamedTemporaryFile
from types import TracebackType
from typing import Any, List, Type, Optional, Generator, Union, TYPE_CHECKING

from autohooks.utils import exec_git, get_project_root_path, GitError

# https://stackoverflow.com/questions/49959656/typing-exit-in-3-5-fails-on-runtime-but-typechecks
if TYPE_CHECKING:
BaseExceptionType = Type[BaseException]
else:
BaseExceptionType = bool # don't care, as long is it doesn't error

__all__ = [
'exec_git',
'get_staged_status',
Expand Down Expand Up @@ -59,7 +67,7 @@ class Status(Enum):


class StatusEntry:
def __init__(self, status_string, root_path=None):
def __init__(self, status_string: str, root_path: Path = None) -> None:
status = status_string[:2]
filename = status_string[3:]

Expand All @@ -74,35 +82,35 @@ def __init__(self, status_string, root_path=None):
else:
self.path = Path(filename)

def __str__(self):
def __str__(self) -> str:
return '{}{} {}'.format(
self.index.value, self.working_tree.value, str(self.path)
)

def __repr__(self):
def __repr__(self) -> str:
return '<StatusEntry {}>'.format(str(self))

def absolute_path(self):
def absolute_path(self) -> Path:
if self.root_path:
return (self.root_path / self.path).resolve()
return self.path.resolve()


def _parse_status(output):
def _parse_status(output: str) -> Generator[str, None, None]:
output = output.rstrip('\0')
if not output:
return

output = output.split('\0')
while output:
line = output.pop(0)
output_list = output.split('\0')
while output_list:
line = output_list.pop(0)
if line[0] == Status.RENAMED.value:
yield '{}\0{}'.format(line, output.pop(0))
yield '{}\0{}'.format(line, output_list.pop(0))
else:
yield line


def is_staged_status(status):
def is_staged_status(status: StatusEntry) -> bool:
return (
status.index != Status.UNMODIFIED
and status.index != Status.UNTRACKED
Expand All @@ -111,7 +119,7 @@ def is_staged_status(status):
)


def is_partially_staged_status(status):
def is_partially_staged_status(status: StatusEntry) -> bool:
return (
status.index != Status.UNMODIFIED
and status.index != Status.UNTRACKED
Expand All @@ -123,7 +131,7 @@ def is_partially_staged_status(status):
)


def get_status(files=None):
def get_status(files: List[Union[Path, str]] = None) -> List[StatusEntry]:
args = [
'status',
'--porcelain=v1',
Expand All @@ -141,17 +149,19 @@ def get_status(files=None):
return [StatusEntry(f, root_path) for f in _parse_status(output)]


def get_staged_status(files=None):
def get_staged_status(
files: List[Union[Path, str]] = None
) -> List[StatusEntry]:
status = get_status(files)
return [s for s in status if is_staged_status(s)]


def stage_files_from_status_list(status_list):
def stage_files_from_status_list(status_list: List[StatusEntry]) -> None:
filenames = [str(s.path) for s in status_list]
exec_git('add', *filenames)


def get_diff(files=None):
def get_diff(files: List[StatusEntry] = None) -> str:
args = ['--no-pager', 'diff']

if files is not None:
Expand All @@ -161,24 +171,24 @@ def get_diff(files=None):
return exec_git(*args)


def _write_tree():
def _write_tree() -> str:
return exec_git('write-tree').strip()


def _read_tree(ref_or_hashid):
def _read_tree(ref_or_hashid: str) -> None:
exec_git('read-tree', ref_or_hashid)


def _checkout_from_index(status_list):
def _checkout_from_index(status_list: List[StatusEntry]) -> None:
filenames = [str(s.path) for s in status_list]
exec_git('checkout-index', '-f', '--', *filenames)


def _set_ref(name, hashid):
def _set_ref(name: str, hashid: str) -> None:
exec_git('update-ref', name, hashid)


def _get_tree_diff(tree1, tree2):
def _get_tree_diff(tree1: str, tree2: str) -> bytes:
return subprocess.check_output(
[
'git',
Expand All @@ -194,7 +204,7 @@ def _get_tree_diff(tree1, tree2):
)


def _apply_diff(patch):
def _apply_diff(patch: bytes) -> None:
with NamedTemporaryFile(mode='wb', buffering=0) as f:
f.write(patch)

Expand All @@ -214,12 +224,12 @@ def _apply_diff(patch):


class stash_unstaged_changes: # pylint: disable=invalid-name
def __init__(self, status_list):
def __init__(self, status_list: List[StatusEntry]) -> None:
self.partially_staged = [
s for s in status_list if is_partially_staged_status(s)
]

def stash_changes(self):
def stash_changes(self) -> None:
# save current staging area aka. index
self.index = _write_tree()
# add ref to be able to restore index manually
Expand All @@ -236,17 +246,22 @@ def stash_changes(self):
_read_tree(self.index)
_checkout_from_index(self.partially_staged)

def restore_working_tree(self):
def restore_working_tree(self) -> None:
# restore working tree
_read_tree(self.working_tree)
# checkout working tree
_checkout_from_index(self.partially_staged)

def __enter__(self):
def __enter__(self) -> None:
if self.partially_staged:
self.stash_changes()

def __exit__(self, exc_type, exc_value, traceback):
def __exit__(
self,
exc_type: Optional[BaseExceptionType],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> Any:
if not self.partially_staged:
return

Expand Down
7 changes: 5 additions & 2 deletions autohooks/api/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@

import fnmatch

from typing import Iterable
from pathlib import Path

def is_python_path(path):

def is_python_path(path: Path) -> bool:
if not path:
return False
return path.match('*.py')


def match(path, pattern_list):
def match(path: Path, pattern_list: Iterable) -> bool:
"""
Check if a Path matches to one of the patterns

Expand Down
4 changes: 3 additions & 1 deletion autohooks/cli/activate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import sys

from argparse import Namespace

from autohooks.config import (
load_config_from_pyproject_toml,
get_pyproject_toml_path,
Expand All @@ -30,7 +32,7 @@
from autohooks.setting import Mode


def install_hooks(args):
def install_hooks(args: Namespace) -> None:
pre_commit_hook_path = get_pre_commit_hook_path()
pyproject_toml = get_pyproject_toml_path()
config = load_config_from_pyproject_toml(pyproject_toml)
Expand Down
2 changes: 1 addition & 1 deletion autohooks/cli/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from autohooks.terminal import ok, error, warning


def check_hooks():
def check_hooks() -> None:
pre_commit_hook = get_pre_commit_hook_path()

if pre_commit_hook.is_file():
Expand Down
29 changes: 17 additions & 12 deletions autohooks/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import Dict, List
from pathlib import Path

import toml

from autohooks.setting import Mode
Expand All @@ -24,45 +27,45 @@


class Config:
def __init__(self, config_dict=None):
def __init__(self, config_dict: Dict = None) -> None:
self._config_dict = config_dict or {}

def get(self, *keys):
def get(self, *keys: str) -> 'Config':
config_dict = self._config_dict

for key in keys:
config_dict = config_dict.get(key, {})

return Config(config_dict)

def get_value(self, key, default=None):
def get_value(self, key: str, default: List[str] = None) -> List[str]:
return self._config_dict.get(key, default)

def is_empty(self):
return False if self._config_dict else True
def is_empty(self) -> bool:
return not bool(self._config_dict)


class AutohooksConfig:
def __init__(self, config_dict=None):
def __init__(self, config_dict: Dict = None) -> None:
self._config = Config(config_dict)
self._autohooks_config = self._config.get('tool').get('autohooks')

def has_config(self):
def has_config(self) -> bool:
return not self._config.is_empty()

def has_autohooks_config(self):
def has_autohooks_config(self) -> bool:
return not self._autohooks_config.is_empty()

def is_autohooks_enabled(self):
def is_autohooks_enabled(self) -> bool:
return self.has_autohooks_config()

def get_pre_commit_script_names(self):
def get_pre_commit_script_names(self) -> List[str]:
if self.has_autohooks_config():
return self._autohooks_config.get_value('pre-commit', [])

return []

def get_mode(self):
def get_mode(self) -> Mode:
if self.has_autohooks_config():
mode = self._autohooks_config.get_value('mode')
if not mode:
Expand All @@ -79,7 +82,9 @@ def get_config(self):
return self._config


def load_config_from_pyproject_toml(pyproject_toml=None):
def load_config_from_pyproject_toml(
pyproject_toml: Path = None
) -> AutohooksConfig:
if pyproject_toml is None:
pyproject_toml = get_pyproject_toml_path()

Expand Down
20 changes: 12 additions & 8 deletions autohooks/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,43 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from pathlib import Path

from setuptools.command.install import install
from setuptools.command.develop import develop

from autohooks.config import load_config_from_pyproject_toml
from autohooks.template import PreCommitTemplate

from autohooks.setting import Mode
from autohooks.utils import get_git_hook_directory_path


def get_pre_commit_hook_path():
def get_pre_commit_hook_path() -> Path:
git_hook_dir_path = get_git_hook_directory_path()
return git_hook_dir_path / 'pre-commit'


def get_autohooks_pre_commit_hook(mode):
def get_autohooks_pre_commit_hook(mode: Mode) -> str:
template = PreCommitTemplate()

return template.render(mode=mode)


def is_autohooks_pre_commit_hook(path):
def is_autohooks_pre_commit_hook(path: Path) -> bool:
hook = path.read_text()
lines = hook.split('\n')
return len(lines) > 5 and "autohooks.precommit" in lines[5]


def install_pre_commit_hook(pre_commit_hook, pre_commit_hook_path):
def install_pre_commit_hook(
pre_commit_hook: str, pre_commit_hook_path: Path
) -> None:
pre_commit_hook_path.write_text(pre_commit_hook)
pre_commit_hook_path.chmod(0o775)


class AutohooksInstall:
def install_git_hook(self):
def install_git_hook(self) -> None:
try:
pre_commit_hook_path = get_pre_commit_hook_path()
if not pre_commit_hook_path.exists():
Expand All @@ -63,12 +67,12 @@ def install_git_hook(self):


class PostInstall(install, AutohooksInstall):
def run(self):
def run(self) -> None:
super().run()
self.install_git_hook()


class PostDevelop(develop, AutohooksInstall):
def install_for_development(self):
def install_for_development(self) -> None:
super().install_for_development()
self.install_git_hook()
Loading