setup: Disallow untyped defs.

Mostly this meant giving annotations to low-value functions like
the error classes and __init_subclass__, but it's worth it for
the future strictness+documentation value.
This commit is contained in:
Brett Smith 2020-03-29 23:18:40 -04:00
parent e6894c2b46
commit 747ef25da6
5 changed files with 71 additions and 14 deletions

View file

@ -14,24 +14,53 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import beancount.core.data as bc_data
from typing import (
Any,
Iterable,
Optional,
)
from .beancount_types import (
Directive,
MetaKey,
MetaValue,
Posting,
Transaction,
Type,
)
Meta = Optional[bc_data.Meta]
class Error(Exception):
def __init__(self, message, entry, source=None):
def __init__(self,
message: str,
entry: Optional[Directive],
source: Meta=None,
) -> None:
self.message = message
self.entry = entry
self.source = entry.meta if source is None else source
if source:
self.source = source
elif entry is not None:
self.source = entry.meta
else:
self.source = {}
self._fill_source(self.source, '<unknown>')
def __repr__(self):
def __repr__(self) -> str:
return "{clsname}<{source[filename]}:{source[lineno]}: {message}>".format(
clsname=type(self).__name__,
message=self.message,
source=self.source,
)
def _fill_source(self, source, filename='conservancy_beancount', lineno=0):
def _fill_source(self,
source: bc_data.Meta,
filename: str='conservancy_beancount',
lineno: int=0,
) -> None:
source.setdefault('filename', filename)
source.setdefault('lineno', lineno)
@ -39,15 +68,27 @@ class Error(Exception):
Iter = Iterable[Error]
class BrokenLinkError(Error):
def __init__(self, txn, key, link, source=None):
def __init__(self,
txn: Transaction,
key: MetaKey,
link: str,
source: Meta=None,
) -> None:
super().__init__(
"{} not found in repository: {}".format(key, link),
txn,
source,
)
class BrokenRTLinkError(Error):
def __init__(self, txn, key, link, parsed=True, source=None):
def __init__(self,
txn: Transaction,
key: MetaKey,
link: str,
parsed: Any=True,
source: Meta=None,
) -> None:
if parsed:
msg_fmt = "{} not found in RT: {}"
else:
@ -58,8 +99,13 @@ class BrokenRTLinkError(Error):
source,
)
class ConfigurationError(Error):
def __init__(self, message, entry=None, source=None):
def __init__(self,
message: str,
entry: Optional[Directive]=None,
source: Meta=None,
) -> None:
if source is None:
source = {}
self._fill_source(source)
@ -67,7 +113,14 @@ class ConfigurationError(Error):
class InvalidMetadataError(Error):
def __init__(self, txn, key, value=None, post=None, need_type=str, source=None):
def __init__(self,
txn: Transaction,
key: MetaKey,
value: Optional[MetaValue]=None,
post: Optional[bc_data.Posting]=None,
need_type: Type=str,
source: Meta=None,
) -> None:
if post is None:
srcname = 'transaction'
else:

View file

@ -69,8 +69,9 @@ class Hook(Generic[Entry], metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self, entry: Entry) -> errormod.Iter: ...
def __init_subclass__(cls):
cls.DIRECTIVE = cls.__orig_bases__[0].__args__[0]
def __init_subclass__(cls) -> None:
# cls.__orig_bases__ comes from the ABCMeta metaclass
cls.DIRECTIVE = cls.__orig_bases__[0].__args__[0] # type:ignore[attr-defined]
TransactionHook = Hook[Transaction]

View file

@ -31,6 +31,7 @@ from ..beancount_types import (
from typing import (
Any,
Dict,
NoReturn,
Optional,
Set,
)
@ -43,7 +44,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
def __init__(self, config: configmod.Config, source_path: Path=PROJECT_DATA_PATH) -> None:
repo_path = config.repository_path()
if repo_path is None:
raise self._config_error("no repository configured")
self._config_error("no repository configured")
project_data_path = repo_path / source_path
source = {'filename': str(project_data_path)}
try:
@ -68,7 +69,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
else:
self.VALUES_ENUM = core.MetadataEnum(self.METADATA_KEY, names, aliases)
def _config_error(self, msg: str, filename: Optional[Path]=None):
def _config_error(self, msg: str, filename: Optional[Path]=None) -> NoReturn:
source = {}
if filename is not None:
source['filename'] = str(filename)

View file

@ -196,11 +196,12 @@ class RT:
# to be a method, it's just an internal decrator.
def _cache_method(func: _URLLookup) -> _URLLookup: # type:ignore[misc]
@functools.wraps(func)
def caching_wrapper(self,
def caching_wrapper(self: 'RT',
ticket_id: RTId,
attachment_id: Optional[RTId]=None,
) -> Optional[str]:
cache_key = (str(ticket_id), attachment_id and str(attachment_id))
cache_key = (str(ticket_id),
None if attachment_id is None else str(attachment_id))
url: Optional[str]
try:
url = self._cache[cache_key]

View file

@ -4,6 +4,7 @@ typecheck=pytest --addopts="--mypy conservancy_beancount"
[mypy]
disallow_any_unimported = True
disallow_untyped_defs = True
show_error_codes = True
strict_equality = True
warn_redundant_casts = True