manifest: type annotate Project and ManifestProject

Add missing type annotations. Paths are generally 'str' for now,
though there is one place where we can use the PathType value we
defined in a previous commit.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-06-09 15:45:01 -07:00 committed by Marti Bolivar
parent 0020d8211e
commit 9492bb62c3
1 changed files with 41 additions and 34 deletions

View File

@ -16,7 +16,7 @@ import os
from pathlib import PurePath, PurePosixPath, Path
import shlex
import subprocess
from typing import Any, Callable, List, Optional, Union
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
from packaging.version import parse as parse_version
import pykwalify.core
@ -487,9 +487,13 @@ class Project:
path_repr = repr(self.abspath or self.path)
return f'<Project {self.name} ({path_repr}) at {self.revision}>'
def __init__(self, name, url, revision=None, path=None,
clone_depth=None, west_commands=None, topdir=None,
remote_name=None):
def __init__(self, name: str, url: str,
revision: Optional[str] = None,
path: Optional[str] = None,
clone_depth: Optional[int] = None,
west_commands: Optional[WestCommandsType] = None,
topdir: Optional[str] = None,
remote_name: Optional[str] = None):
'''Project constructor.
If *topdir* is ``None``, then absolute path attributes
@ -518,40 +522,40 @@ class Project:
self.remote_name = remote_name or 'origin'
@property
def path(self):
def path(self) -> str:
return self._path
@path.setter
def path(self, path):
self._path = str(path)
def path(self, path: PathType) -> None:
self._path: str = str(path)
# Invalidate the absolute path attributes. They'll get
# computed again next time they're accessed.
self._abspath = None
self._posixpath = None
self._abspath: Optional[str] = None
self._posixpath: Optional[str] = None
@property
def abspath(self):
def abspath(self) -> Optional[str]:
if self._abspath is None and self.topdir:
self._abspath = os.path.realpath(os.path.join(self.topdir,
self.path))
return self._abspath
@property
def posixpath(self):
def posixpath(self) -> Optional[str]:
if self._posixpath is None and self.abspath is not None:
self._posixpath = PurePath(self.abspath).as_posix()
return self._posixpath
@property
def name_and_path(self):
def name_and_path(self) -> str:
return f'{self.name} ({self.path})'
def as_dict(self):
def as_dict(self) -> Dict:
'''Return a representation of this object as a dict, as it
would be parsed from an equivalent YAML manifest.
'''
ret = {}
ret: Dict = {}
ret['name'] = self.name
ret['url'] = self.url
ret['revision'] = self.revision
@ -569,12 +573,14 @@ class Project:
# Git helpers
#
def git(self, cmd, extra_args=(), capture_stdout=False,
capture_stderr=False, check=True, cwd=None):
def git(self, cmd: Union[str, List[str]],
extra_args: Iterable[str] = (),
capture_stdout: bool = False,
capture_stderr: bool = False,
check: bool = True,
cwd: Optional[PathType] = None) -> subprocess.CompletedProcess:
'''Run a git command in the project repository.
Returns a ``subprocess.CompletedProcess``.
:param cmd: git command as a string (or list of strings)
:param extra_args: sequence of additional arguments to pass to
the git command (useful mostly if *cmd* is a string).
@ -626,7 +632,7 @@ class Project:
return subprocess.CompletedProcess(popen.args, popen.returncode,
stdout, stderr)
def sha(self, rev, cwd=None):
def sha(self, rev: str, cwd: Optional[PathType] = None) -> str:
'''Get the SHA for a project revision.
:param rev: git revision (HEAD, v2.0.0, etc.) as a string
@ -642,7 +648,8 @@ class Project:
# It'll be fun when we find out that was wrong and how...
return cp.stdout.decode('ascii').strip()
def is_ancestor_of(self, rev1, rev2, cwd=None):
def is_ancestor_of(self, rev1: str, rev2: str,
cwd: Optional[str] = None) -> bool:
'''Check if 'rev1' is an ancestor of 'rev2' in this project.
Returns True if rev1 is an ancestor commit of rev2 in the
@ -666,7 +673,7 @@ class Project:
else:
raise RuntimeError(f'unexpected git merge-base result {rc}')
def is_up_to_date_with(self, rev, cwd=None):
def is_up_to_date_with(self, rev: str, cwd: Optional[str] = None) -> bool:
'''Check if the project is up to date with *rev*, returning
``True`` if so.
@ -680,7 +687,7 @@ class Project:
'''
return self.is_ancestor_of(rev, 'HEAD', cwd=cwd)
def is_up_to_date(self, cwd=None):
def is_up_to_date(self, cwd: Optional[str] = None) -> bool:
'''Check if the project HEAD is up to date with the manifest.
This is equivalent to ``is_up_to_date_with(self.revision,
@ -691,7 +698,7 @@ class Project:
'''
return self.is_up_to_date_with(self.revision, cwd=cwd)
def is_cloned(self, cwd=None):
def is_cloned(self, cwd: Optional[str] = None) -> bool:
'''Returns ``True`` if ``self.abspath`` looks like a git
repository's top-level directory, and ``False`` otherwise.
@ -711,12 +718,10 @@ class Project:
return not (res.returncode or res.stdout.strip())
def read_at(self, path, rev=None, cwd=None):
def read_at(self, path: str, rev: Optional[str] = None,
cwd: Optional[str] = None) -> bytes:
'''Read file contents in the project at a specific revision.
The file contents are returned as a bytes object. The caller
should decode them if necessary.
:param path: relative path to file in this project
:param rev: revision to read *path* from (default: ``self.revision``)
:param cwd: directory to run command in (default: ``self.abspath``)
@ -727,11 +732,13 @@ class Project:
capture_stderr=True, cwd=cwd)
return cp.stdout
def listdir_at(self, path, rev=None, cwd=None, encoding=None):
'''List directory contents in the project at a specific revision.
def listdir_at(self, path: str, rev: Optional[str] = None,
cwd: Optional[str] = None,
encoding: Optional[str] = None) -> List[str]:
'''List of directory contents in the project at a specific revision.
The return value is a list of the directory's contents as
strings.
The return value is the directory contents as a list of files and
subdirectories.
:param path: relative path to file in this project
:param rev: revision to read *path* from (default: ``self.revision``)
@ -815,16 +822,16 @@ class ManifestProject(Project):
self.west_commands = _west_commands_list(west_commands)
@property
def abspath(self):
def abspath(self) -> Optional[str]:
if self._abspath is None and self.topdir and self.path:
self._abspath = os.path.realpath(os.path.join(self.topdir,
self.path))
return self._abspath
def as_dict(self):
def as_dict(self) -> Dict:
'''Return a representation of this object as a dict, as it would be
parsed from an equivalent YAML manifest.'''
ret = {}
ret: Dict = {}
if self.path:
ret['path'] = self.path
if self.west_commands: