Remove the remote repository-access-through-zmq support

Modern methods such as the rqlcontroller cube + the cwclientlib library are the way forward.

Closes #2919297.

authorAurelien Campeas <aurelien.campeas@logilab.fr>
changesetd0314fa8bff4
branchdefault
phasedraft
hiddenyes
parent revision#5666e7891282 Remove remote repository-access-through-pyro support
child revision#66a355f56b2a [dbapi] remove the dbapi module and its immediate remaining users
files modified by this revision
dbapi.py
doc/3.21.rst
hooks/zmq.py
repoapi.py
server/cwzmq.py
server/serverconfig.py
server/serverctl.py
server/test/unittest_repository.py
test/unittest_utils.py
utils.py
zmqclient.py
# HG changeset patch
# User Aurelien Campeas <aurelien.campeas@logilab.fr>
# Date 1402056568 -7200
# Fri Jun 06 14:09:28 2014 +0200
# Node ID d0314fa8bff42a26917ed04cfadd5fab271bc256
# Parent 5666e7891282b785ac5c3934a95341ca5dc4ab45
Remove the remote repository-access-through-zmq support

Modern methods such as the rqlcontroller cube + the cwclientlib library are the way forward.

Closes #2919297.

diff --git a/dbapi.py b/dbapi.py
@@ -116,24 +116,21 @@
1 
2      `database` may be:
3 
4      * a simple instance id for in-memory connection
5 
6 -    * a uri like scheme://host:port/instanceid where scheme may be one of
7 -      'inmemory' or 'zmqpickle'
8 -
9 -      * if scheme is handled by ZMQ (eg 'tcp'), you should not specify an
10 -        instance id
11 +    * a uri like scheme://host:port/instanceid where scheme must be
12 +      'inmemory'
13 
14      Other arguments:
15 
16      :login:
17        the user login to use to authenticate.
18 
19      :cnxprops:
20        a :class:`ConnectionProperties` instance, allowing to specify
21 -      the connection method (eg in memory or zmq).
22 +      the connection method (eg in memory).
23 
24      :setvreg:
25        flag telling if a registry should be initialized for the connection.
26        Don't change this unless you know what you're doing.
27 
@@ -148,40 +145,22 @@
28        where it's already initialized.
29 
30      :kwargs:
31        there goes authentication tokens. You usually have to specify a password
32        for the given user, using a named 'password' argument.
33 +
34      """
35      if not urlparse(database).scheme:
36          warn('[3.16] give an qualified URI as database instead of using '
37               'host/cnxprops to specify the connection method',
38               DeprecationWarning, stacklevel=2)
39 -        if cnxprops and cnxprops.cnxtype == 'zmq':
40 -            database = kwargs.pop('host')
41 -        elif cnxprops and cnxprops.cnxtype == 'inmemory':
42 -            database = 'inmemory://' + database
43      puri = urlparse(database)
44      method = puri.scheme.lower()
45 -    if method == 'inmemory':
46 -        config = cwconfig.instance_configuration(puri.netloc)
47 -    else:
48 -        config = cwconfig.CubicWebNoAppConfiguration()
49 +    assert method == 'inmemory'
50 +    config = cwconfig.instance_configuration(puri.netloc)
51      repo = get_repository(database, config=config)
52 -    if method == 'inmemory':
53 -        vreg = repo.vreg
54 -    elif setvreg:
55 -        if mulcnx:
56 -            multiple_connections_fix()
57 -        vreg = cwvreg.CWRegistryStore(config, initlog=initlog)
58 -        schema = repo.get_schema()
59 -        for oldetype, newetype in ETYPE_NAME_MAP.items():
60 -            if oldetype in schema:
61 -                print 'aliasing', newetype, 'to', oldetype
62 -                schema._entities[newetype] = schema._entities[oldetype]
63 -        vreg.set_schema(schema)
64 -    else:
65 -        vreg = None
66 +    vreg = repo.vreg
67      cnx = _repo_connect(repo, login, cnxprops=cnxprops, **kwargs)
68      cnx.vreg = vreg
69      return cnx
70 
71  def in_memory_repo(config):
diff --git a/doc/3.21.rst b/doc/3.21.rst
@@ -12,5 +12,9 @@
72    transaction_data or session_data instead
73 
74  * the Pyro remote repository access method has been entirely removed
75    (emerging alternatives such as rqlcontroller and cwclientlib should
76    be used instead)
77 +
78 +* the `Pyro` and `Zmq-pickle` remote repository access methods have
79 +  been entirely removed (emerging alternatives such as rqlcontroller
80 +  and cwclientlib should be used instead)
diff --git a/hooks/zmq.py b/hooks/zmq.py
@@ -1,7 +1,7 @@
81  # -*- coding: utf-8 -*-
82 -# copyright 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
83 +# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
84  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
85  #
86  # This file is part of CubicWeb.
87  #
88  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -48,32 +48,5 @@
89          for address in address_sub:
90              self.repo.app_instances_bus.add_subscriber(address)
91          self.repo.app_instances_bus.start()
92 
93 
94 -class ZMQRepositoryServerStopHook(hook.Hook):
95 -    __regid__ = 'zmqrepositoryserverstop'
96 -    events = ('server_shutdown',)
97 -
98 -    def __call__(self):
99 -        server = getattr(self.repo, 'zmq_repo_server', None)
100 -        if server:
101 -            self.repo.zmq_repo_server.quit()
102 -
103 -class ZMQRepositoryServerStartHook(hook.Hook):
104 -    __regid__ = 'zmqrepositoryserverstart'
105 -    events = ('server_startup',)
106 -
107 -    def __call__(self):
108 -        config = self.repo.config
109 -        if config.name == 'repository':
110 -            # start-repository command already starts a zmq repo
111 -            return
112 -        address = config.get('zmq-repository-address')
113 -        if not address:
114 -            return
115 -        self.repo.warning('remote access to the repository via zmq/pickle is deprecated')
116 -        from cubicweb.server import cwzmq
117 -        self.repo.zmq_repo_server = server = cwzmq.ZMQRepositoryServer(self.repo)
118 -        server.connect(address)
119 -        self.repo.threaded_task(server.run)
120 -
diff --git a/repoapi.py b/repoapi.py
@@ -50,15 +50,11 @@
121 
122      if protocol == 'inmemory':
123          # me may have been called with a dummy 'inmemory://' uri ...
124          return _get_inmemory_repo(config, vreg)
125 
126 -    if protocol.startswith('zmqpickle-'):
127 -        from cubicweb.zmqclient import ZMQRepositoryClient
128 -        return ZMQRepositoryClient(uri)
129 -    else:
130 -        raise ConnectionError('unknown protocol: `%s`' % protocol)
131 +    raise ConnectionError('unknown protocol: `%s`' % protocol)
132 
133  def connect(repo, login, **kwargs):
134      """Take credential and return associated ClientConnection.
135 
136      The ClientConnection is associated to a new Session object that will be
diff --git a/server/cwzmq.py b/server/cwzmq.py
@@ -1,7 +1,7 @@
137  # -*- coding: utf-8 -*-
138 -# copyright 2012-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
139 +# copyright 2012-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
140  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
141  #
142  # This file is part of CubicWeb.
143  #
144  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -15,21 +15,18 @@
145  # details.
146  #
147  # You should have received a copy of the GNU Lesser General Public License along
148  # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
149 
150 -import cPickle
151 -import traceback
152  from threading import Thread
153  from logging import getLogger
154 
155  import zmq
156  from zmq.eventloop import ioloop
157  import zmq.eventloop.zmqstream
158 
159  from cubicweb import set_log_methods
160 -from cubicweb.server.server import QuitEvent, Finished
161 
162  ctx = zmq.Context()
163 
164  def cwproto_to_zmqaddr(address):
165      """ converts a cw-zmq address (like zmqpickle-tcp://<ip>:<port>)
@@ -132,134 +129,7 @@
166      def subscribe(self, topic, callback):
167          self.dispatch_table[topic] = callback
168          self.ioloop.add_callback(lambda: self.stream.setsockopt(zmq.SUBSCRIBE, topic))
169 
170 
171 -class ZMQRepositoryServer(object):
172 -
173 -    def __init__(self, repository):
174 -        """make the repository available as a PyRO object"""
175 -        self.address = None
176 -        self.repo = repository
177 -        self.socket = None
178 -        self.stream = None
179 -        self.loop = ioloop.IOLoop()
180 -
181 -        # event queue
182 -        self.events = []
183 -
184 -    def connect(self, address):
185 -        self.address = cwproto_to_zmqaddr(address)
186 -
187 -    def run(self):
188 -        """enter the service loop"""
189 -        # start repository looping tasks
190 -        self.socket = ctx.socket(zmq.REP)
191 -        self.stream = zmq.eventloop.zmqstream.ZMQStream(self.socket, io_loop=self.loop)
192 -        self.stream.bind(self.address)
193 -        self.info('ZMQ server bound on: %s', self.address)
194 -
195 -        self.stream.on_recv(self.process_cmds)
196 -
197 -        try:
198 -            self.loop.start()
199 -        except zmq.ZMQError:
200 -            self.warning('ZMQ event loop killed')
201 -        self.quit()
202 -
203 -    def trigger_events(self):
204 -        """trigger ready events"""
205 -        for event in self.events[:]:
206 -            if event.is_ready():
207 -                self.info('starting event %s', event)
208 -                event.fire(self)
209 -                try:
210 -                    event.update()
211 -                except Finished:
212 -                    self.events.remove(event)
213 -
214 -    def process_cmd(self, cmd):
215 -        """Delegate the given command to the repository.
216 -
217 -        ``cmd`` is a list of (method_name, args, kwargs)
218 -        where ``args`` is a list of positional arguments
219 -        and ``kwargs`` is a dictionnary of named arguments.
220 -
221 -        >>> rset = delegate_to_repo(["execute", [sessionid], {'rql': rql}])
222 -
223 -        :note1: ``kwargs`` may be ommited
224 -
225 -            >>> rset = delegate_to_repo(["execute", [sessionid, rql]])
226 -
227 -        :note2: both ``args`` and ``kwargs`` may be omitted
228 -
229 -            >>> schema = delegate_to_repo(["get_schema"])
230 -            >>> schema = delegate_to_repo("get_schema") # also allowed
231 -
232 -        """
233 -        cmd = cPickle.loads(cmd)
234 -        if not cmd:
235 -            raise AttributeError('function name required')
236 -        if isinstance(cmd, basestring):
237 -            cmd = [cmd]
238 -        if len(cmd) < 2:
239 -            cmd.append(())
240 -        if len(cmd) < 3:
241 -            cmd.append({})
242 -        cmd  = list(cmd) + [(), {}]
243 -        funcname, args, kwargs = cmd[:3]
244 -        result = getattr(self.repo, funcname)(*args, **kwargs)
245 -        return result
246 -
247 -    def process_cmds(self, cmds):
248 -        """Callback intended to be used with ``on_recv``.
249 -
250 -        Call ``delegate_to_repo`` on each command and send a pickled of
251 -        each result recursively.
252 -
253 -        Any exception are catched, pickled and sent.
254 -        """
255 -        try:
256 -            for cmd in cmds:
257 -                result = self.process_cmd(cmd)
258 -                self.send_data(result)
259 -        except Exception as exc:
260 -            traceback.print_exc()
261 -            self.send_data(exc)
262 -
263 -    def send_data(self, data):
264 -        self.socket.send_pyobj(data)
265 -
266 -    def quit(self, shutdown_repo=False):
267 -        """stop the server"""
268 -        self.info('Quitting ZMQ server')
269 -        try:
270 -            self.loop.add_callback(self.loop.stop)
271 -            self.stream.on_recv(None)
272 -            self.stream.close()
273 -        except Exception as e:
274 -            print e
275 -            pass
276 -        if shutdown_repo and not self.repo.shutting_down:
277 -            event = QuitEvent()
278 -            event.fire(self)
279 -
280 -    # server utilitities ######################################################
281 -
282 -    def install_sig_handlers(self):
283 -        """install signal handlers"""
284 -        import signal
285 -        self.info('installing signal handlers')
286 -        signal.signal(signal.SIGINT, lambda x, y, s=self: s.quit(shutdown_repo=True))
287 -        signal.signal(signal.SIGTERM, lambda x, y, s=self: s.quit(shutdown_repo=True))
288 -
289 -
290 -    # these are overridden by set_log_methods below
291 -    # only defining here to prevent pylint from complaining
292 -    @classmethod
293 -    def info(cls, msg, *a, **kw):
294 -        pass
295 -
296 -
297  set_log_methods(Publisher, getLogger('cubicweb.zmq.pub'))
298  set_log_methods(Subscriber, getLogger('cubicweb.zmq.sub'))
299 -set_log_methods(ZMQRepositoryServer, getLogger('cubicweb.zmq.repo'))
diff --git a/server/serverconfig.py b/server/serverconfig.py
@@ -195,18 +195,10 @@
300            'default': (),
301            'help': 'comma separated list of email addresses that will be \
302  notified of every changes.',
303            'group': 'email', 'level': 2,
304            }),
305 -        # zmq services config
306 -        ('zmq-repository-address',
307 -         {'type' : 'string',
308 -          'default': None,
309 -          'help': ('ZMQ URI on which the repository will be bound '
310 -                   'to (of the form `zmqpickle-tcp://<ipaddr>:<port>`).'),
311 -          'group': 'zmq', 'level': 3,
312 -          }),
313           ('zmq-address-sub',
314            {'type' : 'csv',
315             'default' : None,
316             'help': ('List of ZMQ addresses to subscribe to (requires pyzmq) '
317                      '(of the form `tcp://<ipaddr>:<port>`)'),
diff --git a/server/serverctl.py b/server/serverctl.py
@@ -36,11 +36,10 @@
318 
319  from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
320  from cubicweb.toolsutils import Command, CommandHandler, underline_title
321  from cubicweb.cwctl import CWCTL, check_options_consistency, ConfigureInstanceCommand
322  from cubicweb.server import SOURCE_TYPES
323 -from cubicweb.server.repository import Repository
324  from cubicweb.server.serverconfig import (
325      USER_OPTIONS, ServerConfiguration, SourceConfiguration,
326      ask_source_config, generate_source_config)
327 
328  # utility functions ###########################################################
@@ -674,76 +673,10 @@
329              cnx.commit()
330              print '-> password reset, sources file regenerated.'
331          cnx.close()
332 
333 
334 -class StartRepositoryCommand(Command):
335 -    """Start a CubicWeb RQL server for a given instance.
336 -
337 -    The server will be remotely accessible through ZMQ
338 -
339 -    <instance>
340 -      the identifier of the instance to initialize.
341 -    """
342 -    name = 'start-repository'
343 -    arguments = '<instance>'
344 -    min_args = max_args = 1
345 -    options = (
346 -        ('debug',
347 -         {'short': 'D', 'action' : 'store_true',
348 -          'help': 'start server in debug mode.'}),
349 -        ('loglevel',
350 -         {'short': 'l', 'type' : 'choice', 'metavar': '<log level>',
351 -          'default': None, 'choices': ('debug', 'info', 'warning', 'error'),
352 -          'help': 'debug if -D is set, error otherwise',
353 -          }),
354 -        ('address',
355 -         {'short': 'a', 'type': 'string', 'metavar': '<protocol>://<host>:<port>',
356 -          'default': '',
357 -          'help': ('specify a ZMQ URI on which to bind'),
358 -          }),
359 -        )
360 -
361 -    def create_repo(self, config):
362 -        address = self['address']
363 -        if not address:
364 -            address = config.get('zmq-repository-address')
365 -        from cubicweb.server.utils import TasksManager
366 -        from cubicweb.server.cwzmq import ZMQRepositoryServer
367 -        repo = Repository(config, TasksManager())
368 -        return ZMQRepositoryServer(repo), address
369 -
370 -    def run(self, args):
371 -        from logilab.common.daemon import daemonize, setugid
372 -        from cubicweb.cwctl import init_cmdline_log_threshold
373 -        print 'WARNING: Standalone repository with pyro or zmq access is deprecated'
374 -        appid = args[0]
375 -        debug = self['debug']
376 -        if sys.platform == 'win32' and not debug:
377 -            logger = logging.getLogger('cubicweb.ctl')
378 -            logger.info('Forcing debug mode on win32 platform')
379 -            debug = True
380 -        config = ServerConfiguration.config_for(appid, debugmode=debug)
381 -        init_cmdline_log_threshold(config, self['loglevel'])
382 -        # create the server
383 -        server, address = self.create_repo(config)
384 -        # ensure the directory where the pid-file should be set exists (for
385 -        # instance /var/run/cubicweb may be deleted on computer restart)
386 -        pidfile = config['pid-file']
387 -        piddir = os.path.dirname(pidfile)
388 -        # go ! (don't daemonize in debug mode)
389 -        if not os.path.exists(piddir):
390 -            os.makedirs(piddir)
391 -        if not debug and daemonize(pidfile, umask=config['umask']):
392 -            return
393 -        uid = config['uid']
394 -        if uid is not None:
395 -            setugid(uid)
396 -        server.install_sig_handlers()
397 -        server.connect(address)
398 -        server.run()
399 -
400 
401  def _remote_dump(host, appid, output, sudo=False):
402      # XXX generate unique/portable file name
403      from datetime import date
404      filename = '%s-%s.tgz' % (appid, date.today().strftime('%Y-%m-%d'))
@@ -1123,11 +1056,10 @@
405          schema_diff(fsschema, repo.schema, permissionshandler, diff_tool, ignore=('eid',))
406 
407 
408  for cmdclass in (CreateInstanceDBCommand, InitInstanceCommand,
409                   GrantUserOnInstanceCommand, ResetAdminPasswordCommand,
410 -                 StartRepositoryCommand,
411                   DBDumpCommand, DBRestoreCommand, DBCopyCommand,
412                   AddSourceCommand, CheckRepositoryCommand, RebuildFTICommand,
413                   SynchronizeSourceCommand, SchemaDiffCommand,
414                   ):
415      CWCTL.register(cmdclass)
diff --git a/server/test/unittest_repository.py b/server/test/unittest_repository.py
@@ -31,11 +31,11 @@
416                        UnknownEid, AuthenticationError, Unauthorized, QueryError)
417  from cubicweb.predicates import is_instance
418  from cubicweb.schema import RQLConstraint
419  from cubicweb.devtools.testlib import CubicWebTC
420  from cubicweb.devtools.repotest import tuplify
421 -from cubicweb.server import repository, hook
422 +from cubicweb.server import hook
423  from cubicweb.server.sqlutils import SQL_PREFIX
424  from cubicweb.server.hook import Hook
425  from cubicweb.server.sources import native
426  from cubicweb.server.session import SessionClosedError
427 
@@ -289,68 +289,10 @@
428          self.assertEqual(cstr.expression, 'O final TRUE')
429 
430          ownedby = schema.rschema('owned_by')
431          self.assertEqual(ownedby.objects('CWEType'), ('CWUser',))
432 
433 -    def test_zmq(self):
434 -        try:
435 -            import zmq
436 -        except ImportError:
437 -            self.skipTest("zmq in not available")
438 -        done = []
439 -        from cubicweb.devtools import TestServerConfiguration as ServerConfiguration
440 -        from cubicweb.server.cwzmq import ZMQRepositoryServer
441 -        # the client part has to be in a thread due to sqlite limitations
442 -        t = threading.Thread(target=self._zmq_client, args=(done,))
443 -        t.start()
444 -
445 -        zmq_server = ZMQRepositoryServer(self.repo)
446 -        zmq_server.connect('zmqpickle-tcp://127.0.0.1:41415')
447 -
448 -        t2 = threading.Thread(target=self._zmq_quit, args=(done, zmq_server,))
449 -        t2.start()
450 -
451 -        zmq_server.run()
452 -
453 -        t2.join(1)
454 -        t.join(1)
455 -
456 -        self.assertTrue(done[0])
457 -
458 -        if t.isAlive():
459 -            self.fail('something went wrong, thread still alive')
460 -
461 -    def _zmq_quit(self, done, srv):
462 -        while not done:
463 -            time.sleep(0.1)
464 -        srv.quit()
465 -
466 -    def _zmq_client(self, done):
467 -        try:
468 -            cnx = connect('zmqpickle-tcp://127.0.0.1:41415', u'admin', password=u'gingkow',
469 -                          initlog=False) # don't reset logging configuration
470 -            try:
471 -                cnx.load_appobjects(subpath=('entities',))
472 -                # check we can get the schema
473 -                schema = cnx.get_schema()
474 -                self.assertTrue(cnx.vreg)
475 -                self.assertTrue('etypes'in cnx.vreg)
476 -                cu = cnx.cursor()
477 -                rset = cu.execute('Any U,G WHERE U in_group G')
478 -                user = iter(rset.entities()).next()
479 -                self.assertTrue(user._cw)
480 -                self.assertTrue(user._cw.vreg)
481 -                from cubicweb.entities import authobjs
482 -                self.assertIsInstance(user._cw.user, authobjs.CWUser)
483 -                cnx.close()
484 -                done.append(True)
485 -            finally:
486 -                # connect monkey patch some method by default, remove them
487 -                multiple_connections_unfix()
488 -        finally:
489 -            done.append(False)
490 -
491      def test_internal_api(self):
492          repo = self.repo
493          cnxid = repo.connect(self.admlogin, password=self.admpassword)
494          session = repo._get_session(cnxid, setcnxset=True)
495          self.assertEqual(repo.type_and_source_from_eid(2, session),
diff --git a/test/unittest_utils.py b/test/unittest_utils.py
@@ -56,12 +56,10 @@
496      def test_parse_repo_uri(self):
497          self.assertEqual(('inmemory', None, 'myapp'),
498                           parse_repo_uri('myapp'))
499          self.assertEqual(('inmemory', None, 'myapp'),
500                           parse_repo_uri('inmemory://myapp'))
501 -        self.assertEqual(('zmqpickle-tcp', '127.0.0.1:666', ''),
502 -                         parse_repo_uri('zmqpickle-tcp://127.0.0.1:666'))
503          with self.assertRaises(NotImplementedError):
504              parse_repo_uri('foo://bar')
505 
506 
507 
diff --git a/utils.py b/utils.py
@@ -605,20 +605,17 @@
508 
509  def parse_repo_uri(uri):
510      """ transform a command line uri into a (protocol, hostport, appid), e.g:
511      <myapp>                      -> 'inmemory', None, '<myapp>'
512      inmemory://<myapp>           -> 'inmemory', None, '<myapp>'
513 -    zmqpickle://[host][:port]    -> 'zmqpickle', 'host:port', None
514      """
515      parseduri = urlparse(uri)
516      scheme = parseduri.scheme
517      if scheme == '':
518          return ('inmemory', None, parseduri.path)
519      if scheme == 'inmemory':
520          return (scheme, None, parseduri.netloc)
521 -    if scheme.startswith('zmqpickle-'):
522 -        return (scheme, parseduri.netloc, parseduri.path)
523      raise NotImplementedError('URI protocol not implemented for `%s`' % uri)
524 
525 
526 
527  logger = getLogger('cubicweb.utils')
diff --git a/zmqclient.py b/zmqclient.py
@@ -1,64 +0,0 @@
528 -# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
529 -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
530 -#
531 -# This file is part of CubicWeb.
532 -#
533 -# CubicWeb is free software: you can redistribute it and/or modify it under the
534 -# terms of the GNU Lesser General Public License as published by the Free
535 -# Software Foundation, either version 2.1 of the License, or (at your option)
536 -# any later version.
537 -#
538 -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
539 -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
540 -# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
541 -# details.
542 -#
543 -# You should have received a copy of the GNU Lesser General Public License along
544 -# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
545 -"""Source to query another RQL repository using ZMQ"""
546 -
547 -__docformat__ = "restructuredtext en"
548 -_ = unicode
549 -
550 -from functools import partial
551 -import zmq
552 -
553 -from cubicweb.server.cwzmq import cwproto_to_zmqaddr
554 -
555 -# XXX hack to overpass old zmq limitation that force to have
556 -# only one context per python process
557 -try:
558 -    from cubicweb.server.cwzmq import ctx
559 -except ImportError:
560 -    ctx = zmq.Context()
561 -
562 -class ZMQRepositoryClient(object):
563 -    """
564 -    This class delegates the overall repository stuff to a remote source.
565 -
566 -    So calling a method of this repository will result on calling the
567 -    corresponding method of the remote source repository.
568 -
569 -    Any raised exception on the remote source is propagated locally.
570 -
571 -    ZMQ is used as the transport layer and cPickle is used to serialize data.
572 -    """
573 -
574 -    def __init__(self, zmq_address):
575 -        """A zmq address provided here will be like
576 -        `zmqpickle-tcp://127.0.0.1:42000`.  W
577 -
578 -        We chop the prefix to get a real zmq address.
579 -        """
580 -        self.socket = ctx.socket(zmq.REQ)
581 -        self.socket.connect(cwproto_to_zmqaddr(zmq_address))
582 -
583 -    def __zmqcall__(self, name, *args, **kwargs):
584 -         self.socket.send_pyobj([name, args, kwargs])
585 -         result = self.socket.recv_pyobj()
586 -         if isinstance(result, BaseException):
587 -             raise result
588 -         return result
589 -
590 -    def __getattr__(self, name):
591 -        return partial(self.__zmqcall__, name)