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

Annotate core functions and remove sphinx style argument types #205

Merged
merged 33 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3337552
Annotate core functions and remove sphinx style argument types
tonybaloney Aug 1, 2023
0beb2e1
Tidy up
tonybaloney Aug 1, 2023
887794d
Fix type annotations on config object
tonybaloney Aug 1, 2023
aebc4a0
Remove variable reuse
tonybaloney Aug 1, 2023
5d2bd52
Add missing import for raw operator. Add pyright task
tonybaloney Aug 2, 2023
97e3d64
Be more explicit with the git command args to detect the overload
tonybaloney Aug 2, 2023
6e4cf11
Install dependencies. Fix potentially (although unreachable) unbound …
tonybaloney Aug 2, 2023
5ff9573
Patch mock git function
tonybaloney Aug 2, 2023
9b354f3
Annotate metrics and state modules. Pin gitpython since the arguments…
tonybaloney Aug 2, 2023
d1de047
Update pytest-cov
tonybaloney Aug 2, 2023
8a4087e
Start annotation of cache config and state modules
tonybaloney Aug 2, 2023
214d059
Annotate all of cache module
tonybaloney Aug 2, 2023
ecb8be5
fix types for archivers
tonybaloney Aug 2, 2023
72e8de1
use generics for base types and named tuples
tonybaloney Aug 2, 2023
f0a62ea
add missing docstrings
tonybaloney Aug 2, 2023
558c938
de-dataclass
tonybaloney Aug 2, 2023
abd41e2
reformat
tonybaloney Aug 2, 2023
1bd32b6
reformat
tonybaloney Aug 2, 2023
a7d5eb1
use abstract methods
tonybaloney Aug 2, 2023
036932e
remove unused import
tonybaloney Aug 2, 2023
2816e55
fix list-metrics command
tonybaloney Aug 2, 2023
9e5223d
Refactor wily config into submodule to reduce cyclical imports
tonybaloney Aug 3, 2023
8ca3cae
Move defaults into separate submodule
tonybaloney Aug 3, 2023
6551a4a
Update git imports
tonybaloney Aug 3, 2023
d2b93f0
Force tabulate range
tonybaloney Aug 3, 2023
876f525
Fix a bug in the archiver fallback sequence
tonybaloney Aug 3, 2023
5ddc780
Cleanup temp paths
tonybaloney Aug 3, 2023
17b7946
Update mypy call
tonybaloney Aug 3, 2023
5725932
Don't use argument cls in generics, not supported in Python 3.7,3.8
tonybaloney Aug 3, 2023
08a201d
Update metric type field name
tonybaloney Aug 3, 2023
7d068aa
Update list_metrics
tonybaloney Aug 3, 2023
0daa91a
Update some missing return values, remove more docstring types
tonybaloney Aug 3, 2023
477dd77
Add missing import
tonybaloney Aug 3, 2023
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
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,19 @@ jobs:
- uses: actions/checkout@v3
- run: pip install --user ruff
- run: ruff --format=github .

pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.11
- name: install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install flit
flit install --extras=all
- uses: jakebailey/pyright-action@v1
with:
working-directory: src
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ compile_messages:
lint_python:
ruff .
@# TODO(skarzi): fix type hints and require `mypy` to pass
mypy . || true
mypy --install-types --non-interactive src || true

.PHONY: lint_formatting
lint_formatting:
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ classifiers = [
"Programming Language :: Python :: 3 :: Only",
]
dependencies = [
"gitpython>=3.0.0,<4.0.0",
"gitpython>=3.1.32,<4.0.0",
"radon>=5.1,<5.2",
"click>=7.0,<9.0",
"nbformat>=5.1.3,<6.0.0",
"colorlog>=4.0.0,<5.0.0",
"tabulate>=0.8.2,<1.0.0",
"tabulate>=0.9.0,<1.0.0",
"plotly>=4.0.0,<6.0.0",
"progress>=1.4,<2.0",
"dataclasses; python_version == '3.6'",
Expand All @@ -37,7 +37,7 @@ dynamic=["version", "description"]
[project.optional-dependencies]
test = [
"pytest~=7.2",
"pytest-cov~=3.0.0",
"pytest-cov~=4.1.0",
]
dev = [
"black~=22.6.0",
Expand Down
6 changes: 3 additions & 3 deletions src/wily/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@
MAX_MESSAGE_WIDTH = 50


def format_date(timestamp):
def format_date(timestamp: float) -> str:
"""Reusable timestamp -> date."""
return datetime.date.fromtimestamp(timestamp).isoformat()


def format_datetime(timestamp):
def format_datetime(timestamp: float) -> str:
"""Reusable timestamp -> datetime."""
return datetime.datetime.fromtimestamp(timestamp).isoformat()


def format_revision(sha):
def format_revision(sha: str) -> str:
"""Return a shorter git sha."""
return sha[:7]
4 changes: 2 additions & 2 deletions src/wily/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from wily import WILY_LOG_NAME, __version__, logger
from wily.archivers import resolve_archiver
from wily.cache import exists, get_default_metrics
from wily.config import DEFAULT_CONFIG_PATH, DEFAULT_GRID_STYLE
from wily.config import load as load_config
from wily.defaults import DEFAULT_CONFIG_PATH, DEFAULT_GRID_STYLE
from wily.helper import get_style
from wily.helper.custom_enums import ReportFormat
from wily.lang import _
Expand Down Expand Up @@ -503,7 +503,7 @@ def handle_no_cache(context):

if __name__ == "__main__": # pragma: no cover
try:
cli()
cli() # type: ignore
except Exception as runtime:
logger.error(f"Oh no, Wily crashed! See {WILY_LOG_NAME} for information.")
logger.info(
Expand Down
65 changes: 44 additions & 21 deletions src/wily/archivers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
Specifies a standard interface for finding revisions (versions) of a path and switching to them.
"""

from collections import namedtuple
from dataclasses import dataclass
from typing import List
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar

from wily.config.types import WilyConfig


@dataclass
class Revision:
"""Represents a revision in the archiver."""

key: str
author_name: str
author_email: str
date: str
author_name: Optional[str]
author_email: Optional[str]
date: int
message: str
tracked_files: List[str]
tracked_dirs: List[str]
Expand All @@ -28,32 +29,31 @@ class Revision:
class BaseArchiver:
"""Abstract Archiver Class."""

name: str

def __init__(self, config: "WilyConfig"):
"""Initialise the archiver."""
...

def revisions(self, path: str, max_revisions: int) -> List[Revision]:
"""
Get the list of revisions.

:param path: the path to target.
:type path: ``str``

:param max_revisions: the maximum number of revisions.
:type max_revisions: ``int``

:return: A list of revisions.
:rtype: ``list`` of :class:`Revision`
"""
raise NotImplementedError
...

def checkout(self, revision: Revision, **options):
def checkout(self, revision: Revision, options: Dict[Any, Any]) -> None:
"""
Checkout a specific revision.

:param revision: The revision identifier.
:type revision: :class:`Revision`

:param options: Any additional options.
:type options: ``dict``
"""
raise NotImplementedError
...

def finish(self):
"""Clean up any state if processing completed/failed."""
Expand All @@ -69,22 +69,45 @@ def find(self, search: str) -> Revision:
:return: An instance of revision.
:rtype: Instance of :class:`Revision`
"""
raise NotImplementedError
...


from wily.archivers.filesystem import FilesystemArchiver
from wily.archivers.git import GitArchiver

"""Type for an operator"""
Archiver = namedtuple("Archiver", "name cls description")
"""Type for an Archiver"""

T = TypeVar("T")


"""Git Operator defined in `wily.archivers.git`"""
ARCHIVER_GIT = Archiver(name="git", cls=GitArchiver, description="Git archiver")
class Archiver(Generic[T]):
"""Holder for archivers."""

name: str
archiver_cls: Type[T]
description: str

def __init__(self, name: str, archiver_cls: Type[T], description: str):
"""Initialise the archiver."""
self.name = name
self.archiver_cls = archiver_cls
self.description = description

def __str__(self):
"""Return the name of the archiver."""
return self.name


"""Git Archiver defined in `wily.archivers.git`"""
ARCHIVER_GIT = Archiver(
name="git", archiver_cls=GitArchiver, description="Git archiver"
)

"""Filesystem archiver"""
ARCHIVER_FILESYSTEM = Archiver(
name="filesystem", cls=FilesystemArchiver, description="Filesystem archiver"
name="filesystem",
archiver_cls=FilesystemArchiver,
description="Filesystem archiver",
)

"""Set of all available archivers"""
Expand Down
16 changes: 4 additions & 12 deletions src/wily/archivers/filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
import hashlib
import logging
import os.path
from typing import List
from typing import Any, Dict, List

from wily.archivers import BaseArchiver, Revision
from wily.config.types import WilyConfig

logger = logging.getLogger(__name__)

Expand All @@ -18,12 +19,11 @@ class FilesystemArchiver(BaseArchiver):

name = "filesystem"

def __init__(self, config):
def __init__(self, config: "WilyConfig"):
"""
Instantiate a new Filesystem Archiver.

:param config: The wily configuration
:type config: :class:`wily.config.WilyConfig`
"""
self.config = config

Expand All @@ -32,13 +32,8 @@ def revisions(self, path: str, max_revisions: int) -> List[Revision]:
Get the list of revisions.

:param path: the path to target.
:type path: ``str``

:param max_revisions: the maximum number of revisions.
:type max_revisions: ``int``

:return: A list of revisions.
:rtype: ``list`` of :class:`Revision`
"""
mtime = os.path.getmtime(path)
key = hashlib.sha1(str(mtime).encode()).hexdigest()[:7]
Expand All @@ -57,15 +52,12 @@ def revisions(self, path: str, max_revisions: int) -> List[Revision]:
)
]

def checkout(self, revision: Revision, options):
def checkout(self, revision: Revision, options: Dict[Any, Any]) -> None:
"""
Checkout a specific revision.

:param revision: The revision identifier.
:type revision: :class:`Revision`

:param options: Any additional options.
:type options: ``dict``
"""
# effectively noop since there are no revision
pass
35 changes: 16 additions & 19 deletions src/wily/archivers/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
Implementation of the archiver API for the gitpython module.
"""
import logging
from typing import Dict, List, Tuple
from typing import Any, Dict, List, Tuple

import git.exc
from git import Commit
from git.repo import Repo

from wily.archivers import BaseArchiver, Revision
from wily.config.types import WilyConfig

logger = logging.getLogger(__name__)

Expand All @@ -24,7 +25,7 @@ class InvalidGitRepositoryError(Exception):
class DirtyGitRepositoryError(Exception):
"""Error for a dirty git repository (untracked files)."""

def __init__(self, untracked_files):
def __init__(self, untracked_files: List[str]):
"""
Raise error for untracked files.

Expand All @@ -38,10 +39,16 @@ def __init__(self, untracked_files):
def get_tracked_files_dirs(repo: Repo, commit: Commit) -> Tuple[List[str], List[str]]:
"""Get tracked files in a repo for a commit hash using ls-tree."""
paths = repo.git.execute(
["git", "ls-tree", "--name-only", "--full-tree", "-r", commit.hexsha]
["git", "ls-tree", "--name-only", "--full-tree", "-r", commit.hexsha],
with_extended_output=False,
as_process=False,
stdout_as_string=True,
).split("\n")
dirs = [""] + repo.git.execute(
["git", "ls-tree", "--name-only", "--full-tree", "-r", "-d", commit.hexsha]
["git", "ls-tree", "--name-only", "--full-tree", "-r", "-d", commit.hexsha],
with_extended_output=False,
as_process=False,
stdout_as_string=True,
).split("\n")
return paths, dirs

Expand Down Expand Up @@ -72,12 +79,11 @@ class GitArchiver(BaseArchiver):

name = "git"

def __init__(self, config):
def __init__(self, config: "WilyConfig"):
"""
Instantiate a new Git Archiver.

:param config: The wily configuration
:type config: :class:`wily.config.WilyConfig`
"""
try:
self.repo = Repo(config.path)
Expand All @@ -96,18 +102,14 @@ def revisions(self, path: str, max_revisions: int) -> List[Revision]:
Get the list of revisions.

:param path: the path to target.
:type path: ``str``

:param max_revisions: the maximum number of revisions.
:type max_revisions: ``int``

:return: A list of revisions.
:rtype: ``list`` of :class:`Revision`
"""
if self.repo.is_dirty():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some typing information remains in the above docstring and that on find().

raise DirtyGitRepositoryError(self.repo.untracked_files)

revisions = []
revisions: List[Revision] = []
for commit in self.repo.iter_commits(
self.current_branch, max_count=max_revisions, reverse=True
):
Expand All @@ -133,7 +135,7 @@ def revisions(self, path: str, max_revisions: int) -> List[Revision]:
author_name=commit.author.name,
author_email=commit.author.email,
date=commit.committed_date,
message=commit.message,
message=str(commit.message),
tracked_files=tracked_files,
tracked_dirs=tracked_dirs,
added_files=added_files,
Expand All @@ -143,15 +145,12 @@ def revisions(self, path: str, max_revisions: int) -> List[Revision]:
revisions.append(rev)
return revisions[::-1]

def checkout(self, revision: Revision, options: Dict):
def checkout(self, revision: Revision, options: Dict[Any, Any]) -> None:
"""
Checkout a specific revision.

:param revision: The revision identifier.
:type revision: :class:`Revision`

:param options: Any additional options.
:type options: ``dict``
"""
rev = revision.key
self.repo.git.checkout(rev)
Expand All @@ -170,10 +169,8 @@ def find(self, search: str) -> Revision:
Search a string and return a single revision.

:param search: The search term.
:type search: ``str``

:return: An instance of revision.
:rtype: Instance of :class:`Revision`
"""
commit = self.repo.commit(search)
tracked_files, tracked_dirs = get_tracked_files_dirs(self.repo, commit)
Expand All @@ -191,7 +188,7 @@ def find(self, search: str) -> Revision:
author_name=commit.author.name,
author_email=commit.author.email,
date=commit.committed_date,
message=commit.message,
message=str(commit.message),
tracked_files=tracked_files,
tracked_dirs=tracked_dirs,
added_files=added_files,
Expand Down
Loading
Loading