[hgext] use **only** cwclientlib

thus remove any endpoint configuration in hgrc but the endopint id of the forge.

authorDavid Douard <david.douard@logilab.fr>
changeseta4a911afcb78
branchdefault
phasedraft
hiddenyes
parent revision#0a171b4a2225 [hgext] allow using cwclientlib's config file instead of hgrc
child revision#b166ef0b2324 [hgext] add a new "start-test" command (closes #290804)
files modified by this revision
hgext/jpl/__init__.py
hgext/jpl/jplproxy.py
hgext/jpl/review.py
hgext/jpl/tasks.py
# HG changeset patch
# User David Douard <david.douard@logilab.fr>
# Date 1434374537 -7200
# Mon Jun 15 15:22:17 2015 +0200
# Node ID a4a911afcb78de42c7902d9c2004b29c443c7a37
# Parent 0a171b4a2225cdce61d35d8d9d6aa37a1b36eb00
[hgext] use **only** cwclientlib

thus remove any endpoint configuration in hgrc but the endopint id of the forge.

diff --git a/hgext/jpl/__init__.py b/hgext/jpl/__init__.py
@@ -12,37 +12,29 @@
1  mercurial changesets.
2 
3  The forge url can be permanently defined into one of the mercurial
4  configuration file::
5 
6 -  [lglb]
7 -  forge-url = https://www.cubicweb.org/
8 -  auth-mech = signedrequest
9 -  auth-token = my token
10 -  auth-secret = 0123456789abcdef
11 +  [jpl]
12 +  endpoint = https://www.cubicweb.org/
13 
14 -or for kerberos authentication::
15 +or, according cwo is the id of the endpoint in your cwclientlib_ config file::
16 
17 -  [lglb]
18 -  forge-url = https://my.intranet.com/
19 -  auth-mech = kerberos
20 +  [jpl]
21 +  endpoint = cwo 
22 
23 -Note that you need `python-requests-kerberos`_ for this later
24 -configuration to work.
25 -
26 -You may also need `python-ndg-httpsclient`_ and `python-openssl`_ if
27 +You may need `python-ndg-httpsclient`_ and `python-openssl`_ if
28  the forge application is using a SNI_ ssl configuration (ie. if you
29  get errors like::
30 
31    abort: error: hostname 'www.logilab.org' doesn't match either of
32           'demo.cubicweb.org', 'cubicweb.org'
33 
34 -.. _`python-requests-kerberos`: https://pypi.python.org/pypi/requests-kerberos
35  .. _`python-ndg-httpsclient`: https://pypi.python.org/pypi/ndg-httpsclient
36  .. _`python-openssl`:https://pypi.python.org/pypi/pyOpenSSL
37  .. _SNI: https://en.wikipedia.org/wiki/Server_Name_Indication
38 -.. _`cwclientlib: https://www.cubicweb.org/project/cwclientlib
39 +.. _cwclientlib: https://www.cubicweb.org/project/cwclientlib
40 
41  '''
42  from cStringIO import StringIO
43  from mercurial import cmdutil, scmutil, util, node, demandimport
44  from mercurial.i18n import _
@@ -52,11 +44,11 @@
45  try:
46      enabled = demandimport.isenabled()
47  except AttributeError:
48      enabled = demandimport._import is __import__
49  demandimport.disable()
50 -from .jplproxy import build_proxy, RequestError
51 +from .jplproxy import build_proxy
52  from .tasks import print_tasks
53  from .review import ask_review, show_review, sudo_make_me_a_ticket, assign
54  if enabled:
55      demandimport.enable()
56 
@@ -126,63 +118,56 @@
57  def reviewed(repo, subset, x):
58      """
59      return changesets that are linked to reviewed patch in the jpl forge
60      """
61      mercurial.revset.getargs(x, 0, 0, _("reviewed takes no arguments"))
62 -    base_url = repo.ui.config('lglb', 'forge-url')
63 -    url = '%s/view?vid=jsonexport&rql=rql:%s' % (base_url, quote(RQL))
64 -    raw_data = urlopen(url)
65 -    data = json.load(raw_data)
66 +    with build_proxy(repo.ui) as client:
67 +        data = client.rql(RQL).json()
68      all = set(short for po, short, p in data)
69      return [r for r in subset if str(repo[r]) in all]
70 
71  def inversion(repo, subset, x):
72      """
73      return changesets that are linked to patches linked to tickets of given version+project
74      """
75      version = mercurial.revset.getargs(x, 1, 1, _("inversion takes one argument"))[0][1]
76 -    base_url = repo.ui.config('lglb', 'forge-url')
77 -    url = '%s/view?vid=jsonexport&rql=rql:%s' % (base_url, quote(IVRQL % {'version': version}))
78 -    raw_data = urlopen(url)
79 -    data = json.load(raw_data)
80 +    with build_proxy(repo.ui) as client:
81 +        data = client.execute(IVRQL, {'version': version}).json()
82      all = set(short for po, short, p in data)
83      return [r for r in subset if str(repo[r]) in all]
84 
85  def tasks_predicate(repo, subset, x=None):
86      """``tasks(*states)``
87      Changesets linked to tasks to be done.
88 
89      The optional state arguments are task states to filter
90      (default to 'todo').
91      """
92 -    base_url = repo.ui.config('lglb', 'forge-url')
93      states = None
94      if x is not None:
95          states = [val for typ, val in mercurial.revset.getlist(x)]
96      if not states:
97          states = '!= "done"'
98      elif len(states) == 1:
99          states = '"{}"'.format(states[0])
100      else:
101          states = 'IN ({})'.format(','.join('"{}"'.format(state) for state in states))
102      rql = TASKSRQL.format(states=states)
103 -    url = '%s/view?vid=jsonexport&rql=rql:%s' % (base_url, quote(rql))
104 -    raw_data = urlopen(url)
105 -    data = json.load(raw_data)
106 +    with build_proxy(repo.ui) as client:
107 +        data = client.execute(rql, {}).json()
108      all = set(short[0] for short in data)
109      return [r for r in subset if str(repo[r]) in all]
110 
111  def showtasks(**args):
112      """:tasks: List of Strings. The text of the tasks and comments of a patch."""
113      output = _MockOutput()
114      with build_proxy(output, args) as client:
115          try:
116              print_tasks(client, output, iter([node.short(args['ctx'].node())]), {})
117 -        except RequestError:
118 +        except Exception:
119              return ''
120      return mercurial.templatekw.showlist('task', list(output), **args)
121 -    #return str(output).strip()
122 
123  class _MockOutput(object):
124      def __init__(self):
125          self._ios = [StringIO()]
126      def write(self, msg, label=None):
@@ -192,22 +177,18 @@
127      def __iter__(self):
128          for io in self._ios:
129              yield io.getvalue()
130 
131  def extsetup(ui):
132 -    if ui.config('lglb', 'forge-url'):
133 +    if ui.config('jpl', 'endpoint'):
134          mercurial.revset.symbols['reviewed'] = reviewed
135          mercurial.revset.symbols['tasks'] = tasks_predicate
136          mercurial.revset.symbols['inversion'] = inversion
137          mercurial.templatekw.keywords['tasks'] = showtasks
138 
139  cnxopts  = [
140 -    ('U', 'forge-url', '', _('base url of the forge (jpl) server'), _('URL')),
141 -    ('S', 'no-verify-ssl', None, _('do NOT verify server SSL certificate')),
142 -    ('Y', 'auth-mech', '', _('authentication mechanism used to connect to the forge'), _('MECH')),
143 -    ('t', 'auth-token', '', _('authentication token (when using signed request)'), _('TOKEN')),
144 -    ('s', 'auth-secret', '', _('authentication secret (when using signed request)'), ('SECRET')),
145 +    ('U', 'endpoint', '', _('endpoint (ID or URL) of the configured cwclientlib forge (jpl) server'), _('ENDPOINT')),
146      ]
147 
148  @command('^tasks', [
149      ('r', 'rev', [], _('tasks for the given revision(s)'), _('REV')),
150      ('a', 'all', False, _('also display done tasks')),
@@ -217,19 +198,21 @@
151      """show tasks related to the given revision.
152 
153      By default, the revision used is the parent of the working
154      directory: use -r/--rev to specify a different revision.
155 
156 -    By default, the forge url used is https://www.cubicweb.org/: use
157 -    -U/--forge-url to specify a different url. The forge url can be
158 -    permanently defined into one of the mercurial configuration file::
159 +    By default, the forge url used is https://www.cubicweb.org/. Use
160 +    -U/--endpoint to specify a different cwclientlib endpoint. The
161 +    endpoint id of the forge can be permanently defined into one of
162 +    the mercurial configuration file::
163 
164 -    [lglb]
165 -    forge-url = https://www.cubicweb.org/
166 +    [jpl]
167 +    endpoint = https://www.cubicweb.org/
168 
169      By default, done tasks are not displayed: use -a/--all to not filter
170      tasks and display all.
171 +
172      """
173      changesets += tuple(opts.get('rev', []))
174      if not changesets:
175          changesets = ('.')
176      revs = scmutil.revrange(repo, changesets)
@@ -243,11 +226,11 @@
177          ctxhexs = list((node.short(repo.lookup(lrev)) for lrev in precs))
178          showall = opts.get('all', None)
179          with build_proxy(ui, opts) as client:
180              try:
181                  print_tasks(client, ui, ctxhexs, showall=showall)
182 -            except RequestError, e:
183 +            except Exception as e:
184                  ui.write('no patch or no tasks for %s\n' % node.short(repo.lookup(rev)))
185 
186 
187  @command('^ask-review', [
188      ('r', 'rev', [], _('ask review for the given revision(s)'), _('REV')),
diff --git a/hgext/jpl/jplproxy.py b/hgext/jpl/jplproxy.py
@@ -7,13 +7,12 @@
189  from contextlib import contextmanager
190  from mercurial import util
191  from mercurial.i18n import _
192  from requests import ConnectionError, HTTPError
193 
194 -import itertools
195 +from cwclientlib import cwproxy, cwproxy_for
196 
197 -from cwclientlib import cwproxy, cwproxy_for
198 
199  def wraprql(meth):
200      def wrapper(*args, **kwargs):
201          reply = meth(*args, **kwargs)
202          try:
@@ -25,64 +24,37 @@
203              return None
204          except HTTPError as exc:
205              return '\n'.join(("%s" % exc, reply.json()['reason']))
206      return wrapper
207 
208 -class RequestError(IOError):
209 -    """Exception raised when the request fails."""
210 +
211 +class JplProxy(cwproxy.CWProxy):
212 +    rql = wraprql(cwproxy.CWProxy.rql)
213 +    rqlio = wraprql(cwproxy.CWProxy.rqlio)
214 
215 -def getlglbopt(name, ui, opts, default=None, isbool=False):
216 +
217 +def getcwcliopt(name, ui, opts, default=None, isbool=False):
218      value = default
219 -    if getattr(ui, 'config', None) and ui.config('lglb', name):
220 -        value = ui.config('lglb', name)
221 +    if getattr(ui, 'config', None) and ui.config('cwclientlib', name):
222 +        value = ui.config('cwclientlib', name)
223      name = name.replace('-', '_')
224      if opts.get(name):
225          value = opts[name]
226      if isbool and value not in (None, True, False):
227          value = value.lower() in ('t','true','1','y','yes')
228      return value
229 
230 +
231  @contextmanager
232 -def build_proxy(ui, opts):
233 +def build_proxy(ui, opts=None):
234      """Build a cwproxy"""
235 -
236      try:
237 -        base_url = getlglbopt('forge-url', ui, opts, default=URL)
238 -        verify = not getlglbopt('no-verify-ssl', ui, opts, isbool=True)
239 -        mech = getlglbopt('auth-mech', ui, opts)
240 -        auth = None
241 -
242 -        if base_url.startswith('http'):
243 -            # legacy, all config in hgrc
244 -            if mech and mech not in ('signedrequest', 'kerberos'):
245 -                raise util.Abort(_('unknown authentication mechanisme specified with --auth-mech'))
246 -
247 -            if mech == 'signedrequest':
248 -                token = getlglbopt('auth-token', ui, opts)
249 -                secret = getlglbopt('auth-secret', ui, opts)
250 -                if not token or not secret:
251 -                    raise util.Abort(_('you must provide your authentication token and secret'))
252 -
253 -                auth = cwproxy.SignedRequestAuth(token, secret)
254 -            if mech == 'kerberos':
255 -                from requests_kerberos import HTTPKerberosAuth, OPTIONAL
256 -                auth = HTTPKerberosAuth(mutual_authentication=OPTIONAL)
257 -
258 -            proxy = cwproxy.CWProxy(base_url, auth=auth, verify=verify)
259 -        else:
260 -            # use cwclientlib config file
261 -            proxy = cwproxy_for(base_url)
262 -
263 -        proxy.rql = wraprql(proxy.rql)
264 -        proxy.rqlio = wraprql(proxy.rqlio)
265 -        yield proxy
266 -
267 +        endpoint = getcwcliopt('endpoint', ui, opts, default=URL)
268 +        yield cwproxy_for(endpoint, proxycls=JplProxy)
269      except ConnectionError as exc:
270          if ui.tracebackflag:
271              raise
272          try:
273              msg = exc[0].reason
274          except (AttributeError, IndexError):
275              msg = str(exc)
276          ui.warn(_('abort: error: %s\n') % msg)
277 -
278 -
diff --git a/hgext/jpl/review.py b/hgext/jpl/review.py
@@ -4,11 +4,11 @@
279  import itertools
280  import sys
281  enc = sys.stdout.encoding or 'ascii'
282 
283  from cwclientlib import builders
284 -from .jplproxy import build_proxy, RequestError
285 +from .jplproxy import build_proxy
286 
287  def ask_review(client, revs):
288      eids = client.rql(
289          '''Any P WHERE P patch_revision R, R changeset IN ({revs}),
290                         P in_state S, S name 'in-progress'
diff --git a/hgext/jpl/tasks.py b/hgext/jpl/tasks.py
@@ -3,11 +3,11 @@
291 
292  import itertools
293  import sys
294  enc = sys.stdout.encoding or 'ascii'
295 
296 -from .jplproxy import build_proxy, RequestError
297 +from .jplproxy import build_proxy
298 
299  INDENT = '  '
300 
301  PATCH_RQL = """
302  rql:
@@ -80,11 +80,11 @@
303          rql = PATCH_RQL.format(unions='UNION'.join(UNIONS[:2]))
304          taskstate = TASKNOTDONE_RQL
305 
306      patchesdata = client.rql(rql.format(revs=revs, taskstate=taskstate), vid='jsonexport')
307      if not patchesdata:
308 -        raise RequestError("no tasks found for revisions: %r" % ','.join(revs))
309 +        raise ValueError("no tasks found for revisions: %r" % ','.join(revs))
310 
311      patchesdata = itertools.groupby(patchesdata, lambda x:x[:3])
312      for (peid, pname, pstate), patchdata in patchesdata:
313          msg = '{name} {url}/{eid:d} ({state})\n\n'.format(url=client.base_url, eid=peid, state=pstate, name=pname)
314          ui.write(msg, label='jpl.tasks.patch')
@@ -122,12 +122,12 @@
315      epilog=('The `colorama <https://pypi.python.org/pypi/colorama>`_ module is '
316              'required to enable colored output.')
317      parser = argparse.ArgumentParser(epilog=epilog)
318      parser.add_argument('revs', default=[], metavar='REVS', nargs='+',
319                          help='tasks for the given revision (short hex)', ),
320 -    parser.add_argument('-U', '--forge-url', default=URL, metavar='URL',
321 -                        help='base url of the forge (jpl) server [%s]' % URL)
322 +    parser.add_argument('-U', '--endpoint', default=URL, metavar='URL',
323 +                        help='cwclientlib endpoint ID of the forge (jpl) server [%s]' % URL)
324      parser.add_argument('-c', '--color', default='auto', metavar='WHEN',
325                          choices=('auto', 'never', 'always'),
326                          help='display data with color [auto]')
327      parser.add_argument('-a', '--all', default=False, action='store_true',
328                          help='display data with color [auto]')