Skip to content

Commit

Permalink
Merge d2761e2 into 60d8c2e
Browse files Browse the repository at this point in the history
  • Loading branch information
icgood committed Feb 16, 2020
2 parents 60d8c2e + d2761e2 commit 039a8af
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 52 deletions.
77 changes: 30 additions & 47 deletions pymap/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
from __future__ import annotations

import asyncio
import os
import os.path
import tempfile
from argparse import ArgumentParser, Namespace
from asyncio import Task, AbstractServer, CancelledError
from typing import Optional
from argparse import ArgumentParser
from asyncio import Task, CancelledError
from typing import Sequence

from grpclib.server import Server
from pymap.config import IMAPConfig
Expand All @@ -24,69 +21,55 @@ class AdminService(ServiceInterface): # pragma: no cover
"""

def __init__(self, path: str, server: AbstractServer) -> None:
def __init__(self, servers: Sequence[Server]) -> None:
super().__init__()
self._path = path
self._server = server
self._servers = servers
self._task = asyncio.create_task(self._run())

@classmethod
def get_socket_path(cls) -> str:
"""Return the default location of the UNIX socket file listening for
admin requests.
"""
return os.path.join(tempfile.gettempdir(), 'pymap-admin.sock')

@classmethod
def add_arguments(cls, parser: ArgumentParser) -> None:
group = parser.add_argument_group('admin service')
group.add_argument('--admin-socket', metavar='PATH', dest='admin_sock',
help='path to socket file')
group.add_argument('--admin-host', metavar='HOST', dest='admin_host',
action='append', help='host to listen on')
group.add_argument('--admin-port', metavar='PORT', dest='admin_port',
type=int, default=9090, help='port to listen on')
group.add_argument('--no-filter', action='store_true',
help='do not filter appended messages')

@classmethod
async def start(cls, backend: BackendInterface,
config: IMAPConfig) -> AdminService:
config = backend.config
path: Optional[str] = config.args.admin_sock
handlers = AdminHandlers(backend)
server = Server([handlers], loop=asyncio.get_event_loop())
path = await cls._start(path, server)
cls._chown(path, config.args)
return cls(path, server)
hosts: Sequence[str] = config.args.admin_host
port: int = config.args.admin_port
if not hosts:
hosts = ['127.0.0.1']
servers = [await cls._start(backend, host, port) for host in hosts]
return cls(servers)

@classmethod
async def _start(cls, path: Optional[str], server: Server) -> str:
if not path:
path = cls.get_socket_path()
await server.start(path=path)
return path
def _new_server(cls, backend: BackendInterface) -> Server:
loop = asyncio.get_event_loop()
handlers = AdminHandlers(backend)
return Server([handlers], loop=loop)

@classmethod
def _chown(cls, path: str, args: Namespace) -> None:
uid = args.set_uid or -1
gid = args.set_gid or -1
if uid >= 0 or gid >= 0:
os.chown(path, uid, gid)
async def _start(cls, backend: BackendInterface,
host: str, port: int) -> Server:
server = cls._new_server(backend)
await server.start(host=host, port=port, reuse_address=True)
return server

@property
def task(self) -> Task:
return self._task

def _unlink_path(self) -> None:
try:
os.unlink(self._path)
except OSError:
pass

async def _run(self) -> None:
server = self._server
servers = self._servers
try:
await server.wait_closed()
for server in servers:
await server.wait_closed()
except CancelledError:
server.close()
await server.wait_closed()
finally:
self._unlink_path()
for server in servers:
server.close()
await server.wait_closed()
9 changes: 5 additions & 4 deletions pymap/admin/client/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from pymap import __version__

from .command import ClientCommand
from .. import AdminService
from ..grpc.admin_grpc import AdminStub


Expand All @@ -23,7 +22,10 @@ def main() -> int:
parser.add_argument('--outfile', metavar='PATH',
type=FileType('w'), default=sys.stdout,
help='the output file (default: stdout)')
parser.add_argument('--socket', metavar='PATH', help='path to socket file')
parser.add_argument('--host', metavar='HOST', default='localhost',
help='host to connect to')
parser.add_argument('--port', metavar='PORT', type=int, default=9090,
help='port to connect to')

subparsers = parser.add_subparsers(dest='command',
help='which admin command to run')
Expand All @@ -42,8 +44,7 @@ def main() -> int:
async def run(parser: ArgumentParser, args: Namespace,
command_cls: Type[ClientCommand]) -> int:
loop = asyncio.get_event_loop()
path = args.socket or AdminService.get_socket_path()
channel = Channel(path=path, loop=loop)
channel = Channel(host=args.host, port=args.port, loop=loop)
stub = AdminStub(channel)
command = command_cls(stub, args)
try:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
license = f.read()

setup(name='pymap',
version='0.13.6',
version='0.14.0',
author='Ian Good',
author_email='icgood@gmail.com',
description='Lightweight, asynchronous IMAP serving in Python.',
Expand Down
1 change: 1 addition & 0 deletions test/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pytest-asyncio
pytest-cov
aioredis
hiredis
msgpack
grpclib
protobuf
sievelib

0 comments on commit 039a8af

Please sign in to comment.