# Coverage for server/endpoint.py : 63%

## 70 statements 50 run 20 missing 7 excluded 3 partial

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 -*-

2 from __future__ import annotations

3

4# -- stdlib --

5 from typing import Optional, Sequence, TYPE_CHECKING, cast

6 import logging

7

8# -- third party --

9 from gevent import Greenlet, getcurrent

10

11# -- own --

12 from endpoint import Endpoint, EndpointDied

13 import wire

14

15# -- typing --

16 if TYPE_CHECKING:

17 from server.core import Core # noqa: F401

18

19

20# -- code --

21 log = logging.getLogger('server.core.endpoint')

22

23

24 class Pivot(Exception):

25 pass

26

27

28 class Client(object):

29 __slots__ = ('_ep', '_gr', 'core', '_')

30

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

35

36 self._: dict = {}

37

38 def _before_serve(self) -> None:

39 core = self.core

40 self._gr = getcurrent()

41 core.events.client_connected.emit(self)

42

43 def _serve(self) -> None:

44 core = self.core

45 tbl = core.events.client_command

46

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

50

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))

54

55 except EndpointDied: 55 ↛ 58line 55 didn't jump to line 58

56 break

57

58 except Pivot:

59 continue

60

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")

66

67 self._ep and self._ep.close()

68 self._ep = None

69 self._gr = None

70 core.events.client_dropped.emit(self)

71

72 def serve(self) -> None:

73 self._before_serve()

74 self._serve()

75

76 def terminate(self) -> None:

77 self._gr and self._gr.kill(EndpointDied)

78

79 def is_dead(self) -> bool:

80 return not self._gr or self._gr.ready()

81

82 def pivot_to(self, other: Client) -> None:

83 if not self._ep:

84 raise Exception("self._ep is not valid!")

85

86 other._ep = self._ep

87 self._ep = None

88 self._gr and self._gr.kill() # this skips client_dropped event

89

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)

95

96 def __repr__(self) -> str:

97 if self._ep:

98 return '%s:%s:%s' % (

99 self.__class__.__name__,

100 *self._ep.address,

101 ) # noqa

102 else:

103 return '%s:?' % (

104 self.__class__.__name__,

105 )

106

107 def get_greenlet(self) -> Optional[Greenlet]:

108 return self._gr

109

110 def write(self, v: wire.ServerToClient) -> None:

111 ep = self._ep

112 if ep: ep.write(v)

113

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))

117

118 def raw_write(self, v: bytes) -> None:

119 ep = self._ep

120 if ep: ep.raw_write(v)