Coverage for server/endpoint.py : 63%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2from __future__ import annotations
4# -- stdlib --
5from typing import Optional, Sequence, TYPE_CHECKING, cast
6import logging
8# -- third party --
9from gevent import Greenlet, getcurrent
11# -- own --
12from endpoint import Endpoint, EndpointDied
13import wire
15# -- typing --
16if TYPE_CHECKING:
17 from server.core import Core # noqa: F401
20# -- code --
21log = logging.getLogger('server.core.endpoint')
24class Pivot(Exception):
25 pass
28class Client(object):
29 __slots__ = ('_ep', '_gr', 'core', '_')
31 def __init__(self, core: Core, ep: Optional[Endpoint]):
32 self._ep: Optional[Endpoint] = ep
33 self._gr: Optional[Greenlet] = None
34 self.core = core
36 self._: dict = {}
38 def _before_serve(self) -> None:
39 core = self.core
40 self._gr = getcurrent()
41 core.events.client_connected.emit(self)
43 def _serve(self) -> None:
44 core = self.core
45 tbl = core.events.client_command
47 while True:
48 if not self._ep: 48 ↛ 49line 48 didn't jump to line 49, because the condition on line 48 was never true
49 break
51 try:
52 for msg in self._ep.messages(timeout=90): 52 ↛ 48line 52 didn't jump to line 48, because the loop on line 52 didn't complete
53 tbl[msg.__class__].emit((self, msg))
55 except EndpointDied: 55 ↛ 58line 55 didn't jump to line 58
56 break
58 except Pivot:
59 continue
61 except Exception as e:
62 if core.options.testing:
63 core.crash(e)
64 raise
65 log.exception("Error occurred when handling client command")
67 self._ep and self._ep.close()
68 self._ep = None
69 self._gr = None
70 core.events.client_dropped.emit(self)
72 def serve(self) -> None:
73 self._before_serve()
74 self._serve()
76 def terminate(self) -> None:
77 self._gr and self._gr.kill(EndpointDied)
79 def is_dead(self) -> bool:
80 return not self._gr or self._gr.ready()
82 def pivot_to(self, other: Client) -> None:
83 if not self._ep:
84 raise Exception("self._ep is not valid!")
86 other._ep = self._ep
87 self._ep = None
88 self._gr and self._gr.kill() # this skips client_dropped event
90 core = self.core
91 if other._ep:
92 other._gr and other._gr.kill(Pivot)
93 else:
94 other._gr = core.runner.spawn(other._serve)
96 def __repr__(self) -> str:
97 if self._ep:
98 return '%s:%s:%s' % (
99 self.__class__.__name__,
100 *self._ep.address,
101 )
102 else:
103 return '%s:?' % (
104 self.__class__.__name__,
105 )
107 def get_greenlet(self) -> Optional[Greenlet]:
108 return self._gr
110 def write(self, v: wire.ServerToClient) -> None:
111 ep = self._ep
112 if ep: ep.write(v)
114 def write_bulk(self, vl: Sequence[wire.ServerToClient]) -> None:
115 ep = self._ep
116 if ep: ep.write_bulk(cast(Sequence[wire.Message], vl))
118 def raw_write(self, v: bytes) -> None:
119 ep = self._ep
120 if ep: ep.raw_write(v)