1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# Copyright (C) 2012- Takafumi Arakaki
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from .py3compat import SimpleXMLRPCServer
def _get_logger():
"""
Generate a logger with a stream handler.
"""
logger = logging.getLogger('epc')
hndlr = logging.StreamHandler()
hndlr.setLevel(logging.INFO)
hndlr.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logger.addHandler(hndlr)
return logger
_logger = _get_logger()
class EPCDispatcher:
# This class will be mixed with `SocketServer.TCPServer`,
# which is an old style class.
# see also: SimpleXMLRPCServer.SimpleXMLRPCDispatcher
def __init__(self):
self.funcs = {}
self.instance = None
def register_instance(self, instance, allow_dotted_names=False):
"""
Register an instance to respond to EPC requests.
:type instance: object
:arg instance:
An object with methods to provide to peer. If this
instance has `_get_method` method, EPC method name
resolution can be done by this method.
:type allow_dotted_names: bool
:arg allow_dotted_names:
If it is true, method names containing dots are supported.
They are resolved using `getattr` for each part of the
name as long as it does not start with '_'.
Unlike :meth:`register_function`, only one instance can
be registered.
"""
self.instance = instance
self.allow_dotted_names = allow_dotted_names
def register_function(self, function, name=None):
"""
Register function to be called from EPC client.
:type function: callable
:arg function: Function to publish.
:type name: str
:arg name: Name by which function is published.
This method returns the given `function` as-is, so that you
can use it as a decorator.
"""
if name is None:
name = function.__name__
self.funcs[name] = function
return function
def get_method(self, name):
"""
Get registered method callend `name`.
"""
try:
return self.funcs[name]
except KeyError:
try:
return self.instance._get_method(name)
except AttributeError:
return SimpleXMLRPCServer.resolve_dotted_attribute(
self.instance, name, self.allow_dotted_names)
class EPCCore(EPCDispatcher):
"""
Core methods shared by `EPCServer` and `EPCClient`.
"""
logger = _logger
def __init__(self, debugger, log_traceback):
EPCDispatcher.__init__(self)
self.set_debugger(debugger)
self.log_traceback = log_traceback
def set_debugger(self, debugger):
"""
Set debugger to run when an error occurs in published method.
You can also set debugger by passing `debugger` argument to
the class constructor.
:type debugger: {'pdb', 'ipdb', None}
:arg debugger: type of debugger.
"""
if debugger == 'pdb':
import pdb
self.debugger = pdb
elif debugger == 'ipdb':
import ipdb
self.debugger = ipdb
else:
self.debugger = debugger
|