Skip to content

Commit

Permalink
minor fixes after linters
Browse files Browse the repository at this point in the history
  • Loading branch information
msztolcman committed Mar 24, 2021
1 parent 63ff0fa commit 7ae85e2
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 52 deletions.
18 changes: 9 additions & 9 deletions sendria/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pathlib
import signal
import sys
from typing import NoReturn
from typing import NoReturn, List, IO

import aiohttp.web
import daemon
Expand All @@ -27,12 +27,12 @@
SHUTDOWN = []


def exit_err(msg, exit_code=1, **kwargs):
def exit_err(msg: str, exit_code: int = 1, **kwargs) -> NoReturn:
logger.error(msg, **kwargs)
sys.exit(exit_code)


def parse_argv(argv):
def parse_argv(argv: List) -> argparse.Namespace:
parser = argparse.ArgumentParser('Sendria')
version = f'%(prog)s {__version__} (https://github.com/msztolcman/sendria (c) 2018 Marcin Sztolcman)'
parser.add_argument('-v', '--version', action='version', version=version,
Expand Down Expand Up @@ -123,7 +123,7 @@ def parse_argv(argv):
return args


def configure_logger(log_handler):
def configure_logger(log_handler: IO) -> NoReturn:
if log_handler.name == '<stdout>':
processors = (
structlog.dev.ConsoleRenderer(),
Expand All @@ -149,7 +149,7 @@ def configure_logger(log_handler):
)


def pid_exists(pid: int):
def pid_exists(pid: int) -> bool:
"""Check whether pid exists in the current process table.
UNIX only.
Source: https://stackoverflow.com/a/6940314/116153
Expand Down Expand Up @@ -201,7 +201,7 @@ async def terminate_server(sig: int, loop: asyncio.AbstractEventLoop) -> NoRetur
loop.stop()


def run_sendria_servers(loop, args: argparse.Namespace) -> NoReturn:
def run_sendria_servers(loop: asyncio.AbstractEventLoop, args: argparse.Namespace) -> NoReturn:
# initialize db
loop.run_until_complete(db.setup(args.db))

Expand All @@ -223,7 +223,7 @@ def run_sendria_servers(loop, args: argparse.Namespace) -> NoReturn:
logger.info('smtp server started', host=args.smtp_ip, port=args.smtp_port,
auth='enabled' if args.smtp_auth else 'disabled',
password_file=str(args.smtp_auth.path) if args.smtp_auth else None,
url=f'smtp://{args.smtp_ip}:{args.smtp_port}'
url=f'smtp://{args.smtp_ip}:{args.smtp_port}',
)

# initialize and start web server
Expand All @@ -244,7 +244,7 @@ def run_sendria_servers(loop, args: argparse.Namespace) -> NoReturn:
)

# prepare for clean terminate
async def _initialize_aiohttp_services__stop():
async def _initialize_aiohttp_services__stop() -> NoReturn:
for ws in set(app['websockets']):
await ws.close(code=aiohttp.WSCloseCode.GOING_AWAY, message='Server shutdown')
await app.shutdown()
Expand All @@ -271,7 +271,7 @@ def stop(pidfile: pathlib.Path) -> NoReturn:
exit_err('Could not send SIGTERM: %s' % e)


def main():
def main() -> NoReturn:
args = parse_argv(sys.argv[1:])
configure_logger(args.log_handler)

Expand Down
5 changes: 3 additions & 2 deletions sendria/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import argparse
import pathlib
from typing import Optional
from typing import Optional, NoReturn

import attr
from passlib.apache import HtpasswdFile
Expand Down Expand Up @@ -39,7 +40,7 @@ def get(key: Optional[str] = None) -> Optional[Config]:
return getattr(_CONFIG, key)


def setup(args):
def setup(args: argparse.Namespace) -> NoReturn:
global _CONFIG

_CONFIG = Config()
Expand Down
13 changes: 7 additions & 6 deletions sendria/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import pathlib
import sqlite3
from contextlib import asynccontextmanager
from email.message import Message as EmailMessage
from typing import Iterable, Optional, Union, List, NoReturn

import aiosqlite
Expand Down Expand Up @@ -118,7 +119,7 @@ async def store_message(conn: aiosqlite.Connection, message: Message) -> int:
message.type,
message.size,
message.peer,
)
),
)
message.id = cur.lastrowid
# Store parts (why do we do this for non-multipart at all?!)
Expand All @@ -136,7 +137,7 @@ async def store_message(conn: aiosqlite.Connection, message: Message) -> int:
return message.id


async def _save_message_part(cur: aiosqlite.Cursor, message_id: int, cid: str, part) -> int:
async def _save_message_part(cur: aiosqlite.Cursor, message_id: int, cid: str, part: EmailMessage) -> int:
sql = """
INSERT INTO message_part
(message_id, cid, type, is_attachment, filename, charset, body, size, created_at)
Expand All @@ -156,8 +157,8 @@ async def _save_message_part(cur: aiosqlite.Cursor, message_id: int, cid: str, p
part.get_filename(),
part.get_content_charset(),
body,
body_len
)
body_len,
),
)
return cur.lastrowid

Expand Down Expand Up @@ -216,7 +217,7 @@ async def _get_message_part_types(conn: aiosqlite.Connection, message_id: int, t
is_attachment = 0
LIMIT
1
""".format(','.join('?' * len(types)))
""".format(','.join('?' * len(types))) # noqa: S608

async with conn.execute(sql, (message_id,) + types) as cur:
data = await cur.fetchone()
Expand Down Expand Up @@ -249,7 +250,7 @@ async def _message_has_types(conn: aiosqlite.Connection, message_id: int, types:
type IN ({0})
LIMIT
1
""".format(','.join('?' * len(types)))
""".format(','.join('?' * len(types))) # noqa: S608
async with conn.execute(sql, (message_id,) + types) as cur:
data = await cur.fetchone()
return data is not None
Expand Down
9 changes: 5 additions & 4 deletions sendria/errors.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import re
from typing import Optional, NoReturn


class SendriaException(Exception):
http_code = 400
_response_code_rxp = re.compile(r'[A-Z]+[a-z0-9]+')

def __init__(self, message=None):
def __init__(self, message: Optional[str] = None) -> NoReturn:
self.message = message

def get_response_code(self):
def get_response_code(self) -> str:
if hasattr(self, 'response_code'):
return self.response_code

cl = self.__class__.__name__[:-9]

def repl(m):
def repl(m: re.Match) -> str:
return m.group(0).upper() + '_'

cl = self._response_code_rxp.sub(repl, cl)
cl = cl.strip('_') + '_ERROR'

return cl

def get_message(self):
def get_message(self) -> Optional[str]:
return self.message
2 changes: 1 addition & 1 deletion sendria/http/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .core import setup
from .core import setup # noqa: F401
14 changes: 8 additions & 6 deletions sendria/http/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import asyncio
import re
import weakref
from typing import Union, NoReturn
from typing import Union, NoReturn, Optional

import aiohttp.web
import aiohttp_jinja2
Expand Down Expand Up @@ -81,11 +81,13 @@ async def delete_message(rq: aiohttp.web.Request) -> WebHandlerResponse:
return {}


async def _part_url(rq: aiohttp.web.Request, part) -> yarl.URL:
async def _part_url(rq: aiohttp.web.Request, part: dict) -> yarl.URL:
return rq.app.router['get-message-part'].url_for(message_id=str(part['message_id']), cid=part['cid'])


async def _part_response(rq: aiohttp.web.Request, part, body=None, charset=None) -> WebHandlerResponse:
async def _part_response(rq: aiohttp.web.Request, part: dict, body: Optional[Union[str, bytes]] = None,
charset: Optional[str] = None,
) -> WebHandlerResponse:
charset = charset or part['charset'] or 'utf-8'
if body is None:
body = part['body']
Expand Down Expand Up @@ -127,8 +129,8 @@ async def get_message_plain(rq: aiohttp.web.Request) -> WebHandlerResponse:
return await _part_response(rq, part) or {}


async def _fix_cid_links(rq: aiohttp.web.Request, soup, message_id) -> NoReturn:
def _url_from_cid_match(m):
async def _fix_cid_links(rq: aiohttp.web.Request, soup: bs4.BeautifulSoup, message_id: Union[str, bytes]) -> NoReturn:
def _url_from_cid_match(m: re.Match) -> str:
url = rq.app.router['get-message-part'].url_for(message_id=str(message_id), cid=m.group('cid'))
return m.group().replace(m.group('replace'), str(url))

Expand All @@ -148,7 +150,7 @@ def _url_from_cid_match(m):
tag.string = RE_CID_URL.sub(_url_from_cid_match, tag.string)


def _links_target_blank(soup) -> NoReturn:
def _links_target_blank(soup: bs4.BeautifulSoup) -> NoReturn:
for tag in soup.descendants:
if isinstance(tag, bs4.Tag) and tag.name == 'a':
tag.attrs['target'] = 'blank'
Expand Down
5 changes: 3 additions & 2 deletions sendria/http/json_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import datetime
import json
from typing import Any

import aiohttp.web
import yarl


class JSONEncoder(json.JSONEncoder):
def default(self, o):
def default(self, o: Any) -> Any:
if isinstance(o, datetime.datetime):
return o.isoformat()
elif isinstance(o, yarl.URL):
Expand All @@ -18,7 +19,7 @@ def default(self, o):


def json_response(*args, **kwargs) -> aiohttp.web.Response:
def dumps(*a, **b):
def dumps(*a, **b) -> str:
b['cls'] = JSONEncoder
return json.dumps(*a, **b)
kwargs['dumps'] = dumps
Expand Down
6 changes: 3 additions & 3 deletions sendria/http/middlewares.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__all__ = []

import traceback
from typing import Callable
from typing import Callable, NoReturn

import aiohttp.web
from aiohttp_basicauth import BasicAuthMiddleware
Expand Down Expand Up @@ -67,7 +67,7 @@ async def set_default_headers(rq: aiohttp.web.Request, handler: Callable) -> aio


class BasicAuth(BasicAuthMiddleware):
def __init__(self, http_auth: HtpasswdFile, *args, **kwargs):
def __init__(self, http_auth: HtpasswdFile, *args, **kwargs) -> NoReturn:
self._http_auth = http_auth
kwargs['realm'] = 'Sendria'
kwargs['force'] = False
Expand All @@ -82,7 +82,7 @@ async def authenticate(self, rq: aiohttp.web.Request) -> bool:
logger.info(
'request authentication failed',
uri=rq.url.human_repr(),
header=rq.headers.get('authorization', None)
header=rq.headers.get('authorization', None),
)

return res
Expand Down
2 changes: 1 addition & 1 deletion sendria/http/notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

def setup(*,
websockets: weakref.WeakSet,
debug_mode: bool
debug_mode: bool,
) -> NoReturn:
global WSHandlers, WebsocketMessagesQueue, DEBUG

Expand Down
18 changes: 9 additions & 9 deletions sendria/message.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
__all__ = ['Message', ]
__all__ = ['Message']

import uuid
from email.header import decode_header as _decode_header
from email.message import Message as EmailMessage
from email.utils import getaddresses
from typing import Union, List
from typing import Union, List, Dict, Any


class Message:
Expand All @@ -17,7 +17,7 @@ class Message:
'source',
'size', 'type', 'peer',
'parts',
'created_at'
'created_at',
)

@classmethod
Expand Down Expand Up @@ -46,13 +46,13 @@ def from_email(cls, email: EmailMessage) -> 'Message':

return o

def to_dict(self):
def to_dict(self) -> Dict[str, Any]:
return {
k: getattr(self, k)
for k in self.__slots__
}

def __repr__(self):
def __repr__(self) -> str:
r = []
for k in self.__slots__:
if k not in ('source', 'parts'):
Expand All @@ -74,15 +74,15 @@ def decode_header(cls, value: Union[str, bytes, None]) -> str:
return (b''.join(headers)).decode()

@classmethod
def split_addresses(cls, value) -> List[str]:
def split_addresses(cls, value: str) -> List[str]:
return [('{0} <{1}>'.format(name, addr) if name else addr)
for name, addr in getaddresses([value])]

@classmethod
def iter_message_parts(cls, email: EmailMessage):
def iter_message_parts(cls, email: EmailMessage) -> EmailMessage:
if email.is_multipart():
for email in email.get_payload():
for part in cls.iter_message_parts(email):
for payload in email.get_payload():
for part in cls.iter_message_parts(payload):
yield part
else:
yield email
Loading

0 comments on commit 7ae85e2

Please sign in to comment.