Revision a14c36f9 ncclient/glue.py
b/ncclient/glue.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
"TODO: docstring" |
|
16 |
|
|
17 |
from cStringIO import StringIO |
|
18 |
from threading import Thread, Lock |
|
19 |
from xml.etree import cElementTree as ET |
|
20 |
|
|
21 |
import logging |
|
22 |
logger = logging.getLogger('ncclient.glue') |
|
23 |
|
|
24 |
|
|
25 |
def parse_root(raw): |
|
26 |
'''Internal use. |
|
27 |
Parse the top-level element from XML string. |
|
28 |
|
|
29 |
Returns a `(tag, attributes)` tuple, where `tag` is a string representing |
|
30 |
the qualified name of the root element and `attributes` is an |
|
31 |
`{attribute: value}` dictionary. |
|
32 |
''' |
|
33 |
fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd |
|
34 |
for event, element in ET.iterparse(fp, events=('start',)): |
|
35 |
return (element.tag, element.attrib) |
|
36 |
|
|
37 |
|
|
38 |
class Subject(Thread): |
|
39 |
|
|
40 |
'Meant for subclassing by transport.Session' |
|
41 |
|
|
42 |
def __init__(self): |
|
43 |
"TODO: docstring" |
|
44 |
Thread.__init__(self) |
|
45 |
self.setDaemon(True) |
|
46 |
self._listeners = set() # TODO(?) weakref |
|
47 |
self._lock = Lock() |
|
48 |
|
|
49 |
def _dispatch_message(self, raw): |
|
50 |
"TODO: docstring" |
|
51 |
try: |
|
52 |
root = parse_root(raw) |
|
53 |
except Exception as e: |
|
54 |
logger.error('error parsing dispatch message: %s' % e) |
|
55 |
return |
|
56 |
with self._lock: |
|
57 |
listeners = list(self._listeners) |
|
58 |
for l in listeners: |
|
59 |
logger.debug('dispatching message to %r' % l) |
|
60 |
try: |
|
61 |
l.callback(root, raw) |
|
62 |
except Exception as e: |
|
63 |
logger.warning('[error] %r' % e) |
|
64 |
|
|
65 |
def _dispatch_error(self, err): |
|
66 |
"TODO: docstring" |
|
67 |
with self._lock: |
|
68 |
listeners = list(self._listeners) |
|
69 |
for l in listeners: |
|
70 |
logger.debug('dispatching error to %r' % l) |
|
71 |
try: |
|
72 |
l.errback(err) |
|
73 |
except Exception as e: |
|
74 |
logger.warning('error %r' % e) |
|
75 |
|
|
76 |
def add_listener(self, listener): |
|
77 |
"TODO: docstring" |
|
78 |
logger.debug('installing listener %r' % listener) |
|
79 |
with self._lock: |
|
80 |
self._listeners.add(listener) |
|
81 |
|
|
82 |
def remove_listener(self, listener): |
|
83 |
"TODO: docstring" |
|
84 |
logger.debug('discarding listener %r' % listener) |
|
85 |
with self._lock: |
|
86 |
self._listeners.discard(listener) |
|
87 |
|
|
88 |
def get_listener_instance(self, cls): |
|
89 |
'''This is useful when we want to maintain one listener of a particular |
|
90 |
type per subject i.e. a multiton. |
|
91 |
''' |
|
92 |
with self._lock: |
|
93 |
for listener in self._listeners: |
|
94 |
if isinstance(listener, cls): |
|
95 |
return listener |
|
96 |
|
|
97 |
|
|
98 |
class Listener(object): |
|
99 |
|
|
100 |
"TODO: docstring" |
|
101 |
|
|
102 |
def callback(self, root, raw): |
|
103 |
raise NotImplementedError |
|
104 |
|
|
105 |
def errback(self, err): |
|
106 |
raise NotImplementedError |
Also available in: Unified diff