1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 Operations on a running LyX instance.
22
23 LyX only allows you to insert keys at the current cursor position.
24 """
25
26 import os, string, select, signal
27
28 from gettext import gettext as _
29
30 from Pyblio.Cite.WP import CommunicationError, OperationError
31
33 - def __init__(self, pipe='~/.lyx/lyxpipe'):
34 self.pipe = pipe
35 self.pin = None
36 self.pout = None
37
38 self.buffer = ''
39 return
40
42 """Establish connection with LyX.
43
44 Common failures are: nonexitent pipe, or LyX not running...
45 """
46 if self.is_connected():
47 return
48
49 pin = os.path.expanduser(self.pipe + '.in')
50 pout = os.path.expanduser(self.pipe + '.out')
51
52 def safe_connect(f, mode):
53 try:
54 fd = os.open(f, os.O_NONBLOCK | mode)
55 except OSError, msg:
56 if msg.errno == 6:
57 os.unlink(f)
58 raise CommunicationError(_("pipe %r is not connected to LyX") % f)
59 if msg.errno == 2:
60 raise CommunicationError(_("Cannot find pipe %r") % f)
61 else:
62 raise
63 return fd
64
65 self.pin = safe_connect(pin, os.O_WRONLY)
66 self.pout = safe_connect(pout, os.O_RDONLY)
67
68 self._send('hello', base='LYXSRV')
69 return
70
72 return self.pin is not None
73
79
80 - def cite(self, keys, db):
81 """ Insert the keys in the actual wordprocessor document. Each
82 key contains the actual key and a readable representation for
83 the reference.
84
85 Args:
86 keys: [(Pyblio.Store.Key, str)]
87 db: Pyblio.Store.Database
88 """
89 if not self.is_connected():
90 raise OperationError(_("Cannot cite when not connected"))
91
92
93
94
95
96
97 if db.schema.id != 'org.pybliographer/bibtex/0.1':
98 raise OperationError(_("LyX only allows citing from BibTeX"))
99
100 full = [str(db[key]['id'][0]) for (key, plain, extra) in keys]
101
102 self._send('citation-insert:' + ','.join(full))
103 return
104
107
110
113
115 os.close(self.pin)
116 os.close(self.pout)
117 self.pin = None
118 self.pout = None
119 return
120
121 - def _send(self, msg, base='LYXCMD'):
122 """Send a command 'msg' to the LyX process.
123
124 Once a client has identified itself, it can issue commands in
125 its own context 'base'.
126
127 Args:
128 msg: string
129 base: string
130 """
131 raw = msg.encode('latin-1', 'replace')
132
133 try:
134 os.write(self.pin, '%s:pyblio:%s\n' % (base, raw))
135 except OSError, msg:
136 self._close()
137 raise CommunicationError(_("cannot perform operation (%s)") % msg)
138
139
140 if msg == 'bye':
141 return
142
143 answer = None
144
145 while 1:
146 r = select.select([self.pout], [], [], 2)
147 if not r[0]:
148 break
149 self.buffer += os.read(self.pout, 1024)
150
151 if '\n' in self.buffer:
152 answer, self.buffer = self.buffer.split('\n', 1)
153 break
154
155
156
157 if not answer:
158 self._close()
159 raise CommunicationError(_("LyX did not answer to our request"))
160
161 parts = answer.split(':')
162 if parts[0] == 'ERROR':
163 raise OperationError(_("LyX rejected the command: %s") % parts[-1])
164 return
165
169