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:
parent
e6894c2b46
commit
747ef25da6
5 changed files with 71 additions and 14 deletions
|
@ -14,24 +14,53 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import beancount.core.data as bc_data
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
|
Any,
|
||||||
Iterable,
|
Iterable,
|
||||||
|
Optional,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .beancount_types import (
|
||||||
|
Directive,
|
||||||
|
MetaKey,
|
||||||
|
MetaValue,
|
||||||
|
Posting,
|
||||||
|
Transaction,
|
||||||
|
Type,
|
||||||
|
)
|
||||||
|
|
||||||
|
Meta = Optional[bc_data.Meta]
|
||||||
|
|
||||||
class Error(Exception):
|
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.message = message
|
||||||
self.entry = entry
|
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(
|
return "{clsname}<{source[filename]}:{source[lineno]}: {message}>".format(
|
||||||
clsname=type(self).__name__,
|
clsname=type(self).__name__,
|
||||||
message=self.message,
|
message=self.message,
|
||||||
source=self.source,
|
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('filename', filename)
|
||||||
source.setdefault('lineno', lineno)
|
source.setdefault('lineno', lineno)
|
||||||
|
|
||||||
|
@ -39,15 +68,27 @@ class Error(Exception):
|
||||||
Iter = Iterable[Error]
|
Iter = Iterable[Error]
|
||||||
|
|
||||||
class BrokenLinkError(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__(
|
super().__init__(
|
||||||
"{} not found in repository: {}".format(key, link),
|
"{} not found in repository: {}".format(key, link),
|
||||||
txn,
|
txn,
|
||||||
source,
|
source,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BrokenRTLinkError(Error):
|
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:
|
if parsed:
|
||||||
msg_fmt = "{} not found in RT: {}"
|
msg_fmt = "{} not found in RT: {}"
|
||||||
else:
|
else:
|
||||||
|
@ -58,8 +99,13 @@ class BrokenRTLinkError(Error):
|
||||||
source,
|
source,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationError(Error):
|
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:
|
if source is None:
|
||||||
source = {}
|
source = {}
|
||||||
self._fill_source(source)
|
self._fill_source(source)
|
||||||
|
@ -67,7 +113,14 @@ class ConfigurationError(Error):
|
||||||
|
|
||||||
|
|
||||||
class InvalidMetadataError(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:
|
if post is None:
|
||||||
srcname = 'transaction'
|
srcname = 'transaction'
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -69,8 +69,9 @@ class Hook(Generic[Entry], metaclass=abc.ABCMeta):
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def run(self, entry: Entry) -> errormod.Iter: ...
|
def run(self, entry: Entry) -> errormod.Iter: ...
|
||||||
|
|
||||||
def __init_subclass__(cls):
|
def __init_subclass__(cls) -> None:
|
||||||
cls.DIRECTIVE = cls.__orig_bases__[0].__args__[0]
|
# cls.__orig_bases__ comes from the ABCMeta metaclass
|
||||||
|
cls.DIRECTIVE = cls.__orig_bases__[0].__args__[0] # type:ignore[attr-defined]
|
||||||
|
|
||||||
|
|
||||||
TransactionHook = Hook[Transaction]
|
TransactionHook = Hook[Transaction]
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ..beancount_types import (
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Dict,
|
Dict,
|
||||||
|
NoReturn,
|
||||||
Optional,
|
Optional,
|
||||||
Set,
|
Set,
|
||||||
)
|
)
|
||||||
|
@ -43,7 +44,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
||||||
def __init__(self, config: configmod.Config, source_path: Path=PROJECT_DATA_PATH) -> None:
|
def __init__(self, config: configmod.Config, source_path: Path=PROJECT_DATA_PATH) -> None:
|
||||||
repo_path = config.repository_path()
|
repo_path = config.repository_path()
|
||||||
if repo_path is None:
|
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
|
project_data_path = repo_path / source_path
|
||||||
source = {'filename': str(project_data_path)}
|
source = {'filename': str(project_data_path)}
|
||||||
try:
|
try:
|
||||||
|
@ -68,7 +69,7 @@ class MetaProject(core._NormalizePostingMetadataHook):
|
||||||
else:
|
else:
|
||||||
self.VALUES_ENUM = core.MetadataEnum(self.METADATA_KEY, names, aliases)
|
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 = {}
|
source = {}
|
||||||
if filename is not None:
|
if filename is not None:
|
||||||
source['filename'] = str(filename)
|
source['filename'] = str(filename)
|
||||||
|
|
|
@ -196,11 +196,12 @@ class RT:
|
||||||
# to be a method, it's just an internal decrator.
|
# to be a method, it's just an internal decrator.
|
||||||
def _cache_method(func: _URLLookup) -> _URLLookup: # type:ignore[misc]
|
def _cache_method(func: _URLLookup) -> _URLLookup: # type:ignore[misc]
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def caching_wrapper(self,
|
def caching_wrapper(self: 'RT',
|
||||||
ticket_id: RTId,
|
ticket_id: RTId,
|
||||||
attachment_id: Optional[RTId]=None,
|
attachment_id: Optional[RTId]=None,
|
||||||
) -> Optional[str]:
|
) -> 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]
|
url: Optional[str]
|
||||||
try:
|
try:
|
||||||
url = self._cache[cache_key]
|
url = self._cache[cache_key]
|
||||||
|
|
|
@ -4,6 +4,7 @@ typecheck=pytest --addopts="--mypy conservancy_beancount"
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
disallow_any_unimported = True
|
disallow_any_unimported = True
|
||||||
|
disallow_untyped_defs = True
|
||||||
show_error_codes = True
|
show_error_codes = True
|
||||||
strict_equality = True
|
strict_equality = True
|
||||||
warn_redundant_casts = True
|
warn_redundant_casts = True
|
||||||
|
|
Loading…
Reference in a new issue