~blamar/+junk/openstack-api-arrrg

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/zshcomp.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_zshcomp -*-
 
2
# Copyright (c) 2006 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
Rebuild the completion functions for the currently active version of Twisted::
 
7
    $ python zshcomp.py -i
 
8
 
 
9
This module implements a zsh code generator which generates completion code for
 
10
commands that use twisted.python.usage. This is the stuff that makes pressing
 
11
Tab at the command line work.
 
12
 
 
13
Maintainer: Eric Mangold
 
14
 
 
15
To build completion functions for your own commands, and not Twisted commands,
 
16
then just do something like this::
 
17
 
 
18
    o = mymodule.MyOptions()
 
19
    f = file('_mycommand', 'w')
 
20
    Builder("mycommand", o, f).write()
 
21
 
 
22
Then all you have to do is place the generated file somewhere in your
 
23
C{$fpath}, and restart zsh. Note the "site-functions" directory in your
 
24
C{$fpath} where you may install 3rd-party completion functions (like the one
 
25
you're building). Call C{siteFunctionsPath} to locate this directory
 
26
programmatically.
 
27
 
 
28
SPECIAL CLASS VARIABLES. You may set these on your usage.Options subclass::
 
29
 
 
30
    zsh_altArgDescr
 
31
    zsh_multiUse
 
32
    zsh_mutuallyExclusive
 
33
    zsh_actions
 
34
    zsh_actionDescr
 
35
    zsh_extras
 
36
 
 
37
Here is what they mean (with examples)::
 
38
 
 
39
    zsh_altArgDescr = {"foo":"use this description for foo instead"}
 
40
        A dict mapping long option names to alternate descriptions.  When this
 
41
        variable is present, the descriptions contained here will override
 
42
        those descriptions provided in the optFlags and optParameters
 
43
        variables.
 
44
 
 
45
    zsh_multiUse = ["foo", "bar"]
 
46
        A sequence containing those long option names which may appear on the
 
47
        command line more than once. By default, options will only be completed
 
48
        one time.
 
49
 
 
50
    zsh_mutuallyExclusive = [("foo", "bar"), ("bar", "baz")]
 
51
        A sequence of sequences, with each sub-sequence containing those long
 
52
        option names that are mutually exclusive. That is, those options that
 
53
        cannot appear on the command line together.
 
54
 
 
55
    zsh_actions = {"foo":'_files -g "*.foo"', "bar":"(one two three)",
 
56
            "colors":"_values -s , 'colors to use' red green blue"}
 
57
        A dict mapping long option names to Zsh "actions". These actions
 
58
        define what will be completed as the argument to the given option.  By
 
59
        default, all files/dirs will be completed if no action is given.
 
60
 
 
61
        Callables may instead be given for the values in this dict. The
 
62
        callable should accept no arguments, and return a string that will be
 
63
        used as the zsh "action" in the same way as the literal strings in the
 
64
        examples above.
 
65
 
 
66
        As you can see in the example above. The "foo" option will have files
 
67
        that end in .foo completed when the user presses Tab. The "bar"
 
68
        option will have either of the strings "one", "two", or "three"
 
69
        completed when the user presses Tab.
 
70
 
 
71
        "colors" will allow multiple arguments to be completed, seperated by
 
72
        commas. The possible arguments are red, green, and blue. Examples::
 
73
 
 
74
            my_command --foo some-file.foo --colors=red,green
 
75
            my_command --colors=green
 
76
            my_command --colors=green,blue
 
77
 
 
78
        Actions may take many forms, and it is beyond the scope of this
 
79
        document to illustrate them all. Please refer to the documention for
 
80
        the Zsh _arguments function. zshcomp is basically a front-end to Zsh's
 
81
        _arguments completion function.
 
82
 
 
83
        That documentation is available on the zsh web site at this URL:
 
84
        U{http://zsh.sunsite.dk/Doc/Release/zsh_19.html#SEC124}
 
85
 
 
86
    zsh_actionDescr = {"logfile":"log file name", "random":"random seed"}
 
87
        A dict mapping long option names to a description for the corresponding
 
88
        zsh "action". These descriptions are show above the generated matches
 
89
        when the user is doing completions for this option.
 
90
 
 
91
        Normally Zsh does not show these descriptions unless you have
 
92
        "verbose" completion turned on. Turn on verbosity with this in your
 
93
        ~/.zshrc::
 
94
 
 
95
            zstyle ':completion:*' verbose yes
 
96
            zstyle ':completion:*:descriptions' format '%B%d%b'
 
97
 
 
98
    zsh_extras = [":file to read from:action", ":file to write to:action"]
 
99
        A sequence of extra arguments that will be passed verbatim to Zsh's
 
100
        _arguments completion function. The _arguments function does all the
 
101
        hard work of doing command line completions. You can see how zshcomp
 
102
        invokes the _arguments call by looking at the generated completion
 
103
        files that this module creates.
 
104
 
 
105
   *** NOTE ***
 
106
 
 
107
        You will need to use this variable to describe completions for normal
 
108
        command line arguments. That is, those arguments that are not
 
109
        associated with an option. That is, the arguments that are given to the
 
110
        parseArgs method of your usage.Options subclass.
 
111
 
 
112
        In the example above, the 1st non-option argument will be described as
 
113
        "file to read from" and completion options will be generated in
 
114
        accordance with the "action". (See above about zsh "actions") The
 
115
        2nd non-option argument will be described as "file to write to" and
 
116
        the action will be interpreted likewise.
 
117
 
 
118
        Things you can put here are all documented under the _arguments
 
119
        function here: U{http://zsh.sunsite.dk/Doc/Release/zsh_19.html#SEC124}
 
120
 
 
121
Zsh Notes:
 
122
 
 
123
To enable advanced completion add something like this to your ~/.zshrc::
 
124
 
 
125
    autoload -U compinit
 
126
    compinit
 
127
 
 
128
For some extra verbosity, and general niceness add these lines too::
 
129
 
 
130
    zstyle ':completion:*' verbose yes
 
131
    zstyle ':completion:*:descriptions' format '%B%d%b'
 
132
    zstyle ':completion:*:messages' format '%d'
 
133
    zstyle ':completion:*:warnings' format 'No matches for: %d'
 
134
 
 
135
Have fun!
 
136
"""
 
137
import itertools, sys, commands, os.path
 
138
 
 
139
from twisted.python import reflect, util, usage
 
140
from twisted.scripts.mktap import IServiceMaker
 
141
 
 
142
class MyOptions(usage.Options):
 
143
    """
 
144
    Options for this file
 
145
    """
 
146
    longdesc = ""
 
147
    synopsis = "Usage: python zshcomp.py [--install | -i] | <output directory>"
 
148
    optFlags = [["install", "i",
 
149
                 'Output files to the "installation" directory ' \
 
150
                 '(twisted/python/zsh in the currently active ' \
 
151
                 'Twisted package)']]
 
152
    optParameters = [["directory", "d", None,
 
153
                      "Output files to this directory"]]
 
154
    def postOptions(self):
 
155
        if self['install'] and self['directory']:
 
156
            raise usage.UsageError, "Can't have --install and " \
 
157
                                    "--directory at the same time"
 
158
        if not self['install'] and not self['directory']:
 
159
            raise usage.UsageError, "Not enough arguments"
 
160
        if self['directory'] and not os.path.isdir(self['directory']):
 
161
            raise usage.UsageError, "%s is not a directory" % self['directory']
 
162
 
 
163
class Builder:
 
164
    def __init__(self, cmd_name, options, file):
 
165
        """
 
166
        @type cmd_name: C{str}
 
167
        @param cmd_name: The name of the command
 
168
 
 
169
        @type options: C{twisted.usage.Options}
 
170
        @param options: The C{twisted.usage.Options} instance defined for
 
171
                        this command
 
172
 
 
173
        @type file: C{file}
 
174
        @param file: The C{file} to write the completion function to
 
175
        """
 
176
 
 
177
        self.cmd_name = cmd_name
 
178
        self.options = options
 
179
        self.file = file
 
180
 
 
181
    def write(self):
 
182
        """
 
183
        Write the completion function to the file given to __init__
 
184
        @return: C{None}
 
185
        """
 
186
        # by default, we just write out a single call to _arguments
 
187
        self.file.write('#compdef %s\n' % (self.cmd_name,))
 
188
        gen = ArgumentsGenerator(self.cmd_name, self.options, self.file)
 
189
        gen.write()
 
190
 
 
191
class SubcommandBuilder(Builder):
 
192
    """
 
193
    Use this builder for commands that have sub-commands. twisted.python.usage
 
194
    has the notion of sub-commands that are defined using an entirely seperate
 
195
    Options class.
 
196
    """
 
197
    interface = None
 
198
    subcmdLabel = None
 
199
 
 
200
    def write(self):
 
201
        """
 
202
        Write the completion function to the file given to __init__
 
203
        @return: C{None}
 
204
        """
 
205
        self.file.write('#compdef %s\n' % (self.cmd_name,))
 
206
        self.file.write('local _zsh_subcmds_array\n_zsh_subcmds_array=(\n')
 
207
        from twisted import plugin as newplugin
 
208
        plugins = newplugin.getPlugins(self.interface)
 
209
 
 
210
        for p in plugins:
 
211
            self.file.write('"%s:%s"\n' % (p.tapname, p.description))
 
212
        self.file.write(")\n\n")
 
213
 
 
214
        self.options.__class__.zsh_extras = ['*::subcmd:->subcmd']
 
215
        gen = ArgumentsGenerator(self.cmd_name, self.options, self.file)
 
216
        gen.write()
 
217
 
 
218
        self.file.write("""if (( CURRENT == 1 )); then
 
219
  _describe "%s" _zsh_subcmds_array && ret=0
 
220
fi
 
221
(( ret )) || return 0
 
222
 
 
223
service="$words[1]"
 
224
 
 
225
case $service in\n""" % (self.subcmdLabel,))
 
226
 
 
227
        plugins = newplugin.getPlugins(self.interface)
 
228
        for p in plugins:
 
229
            self.file.write(p.tapname + ")\n")
 
230
            gen = ArgumentsGenerator(p.tapname, p.options(), self.file)
 
231
            gen.write()
 
232
            self.file.write(";;\n")
 
233
        self.file.write("*) _message \"don't know how to" \
 
234
                        " complete $service\";;\nesac")
 
235
 
 
236
class MktapBuilder(SubcommandBuilder):
 
237
    """
 
238
    Builder for the mktap command
 
239
    """
 
240
    interface = IServiceMaker
 
241
    subcmdLabel = 'tap to build'
 
242
 
 
243
class TwistdBuilder(SubcommandBuilder):
 
244
    """
 
245
    Builder for the twistd command
 
246
    """
 
247
    interface = IServiceMaker
 
248
    subcmdLabel = 'service to run'
 
249
 
 
250
class ArgumentsGenerator:
 
251
    """
 
252
    Generate a call to the zsh _arguments completion function
 
253
    based on data in a usage.Options subclass
 
254
    """
 
255
    def __init__(self, cmd_name, options, file):
 
256
        """
 
257
        @type cmd_name: C{str}
 
258
        @param cmd_name: The name of the command
 
259
 
 
260
        @type options: C{twisted.usage.Options}
 
261
        @param options: The C{twisted.usage.Options} instance defined
 
262
                        for this command
 
263
 
 
264
        @type file: C{file}
 
265
        @param file: The C{file} to write the completion function to
 
266
        """
 
267
        self.cmd_name = cmd_name
 
268
        self.options = options
 
269
        self.file = file
 
270
 
 
271
        self.altArgDescr = {}
 
272
        self.actionDescr = {}
 
273
        self.multiUse = []
 
274
        self.mutuallyExclusive = []
 
275
        self.actions = {}
 
276
        self.extras = []
 
277
 
 
278
        aCL = reflect.accumulateClassList
 
279
        aCD = reflect.accumulateClassDict
 
280
 
 
281
        aCD(options.__class__, 'zsh_altArgDescr', self.altArgDescr)
 
282
        aCD(options.__class__, 'zsh_actionDescr', self.actionDescr)
 
283
        aCL(options.__class__, 'zsh_multiUse', self.multiUse)
 
284
        aCL(options.__class__, 'zsh_mutuallyExclusive',
 
285
            self.mutuallyExclusive)
 
286
        aCD(options.__class__, 'zsh_actions', self.actions)
 
287
        aCL(options.__class__, 'zsh_extras', self.extras)
 
288
 
 
289
        optFlags = []
 
290
        optParams = []
 
291
 
 
292
        aCL(options.__class__, 'optFlags', optFlags)
 
293
        aCL(options.__class__, 'optParameters', optParams)
 
294
 
 
295
        for i, optList in enumerate(optFlags):
 
296
            if len(optList) != 3:
 
297
                optFlags[i] = util.padTo(3, optList)
 
298
 
 
299
        for i, optList in enumerate(optParams):
 
300
            if len(optList) != 4:
 
301
                optParams[i] = util.padTo(4, optList)
 
302
 
 
303
 
 
304
        self.optFlags = optFlags
 
305
        self.optParams = optParams
 
306
 
 
307
        optParams_d = {}
 
308
        for optList in optParams:
 
309
            optParams_d[optList[0]] = optList[1:]
 
310
        self.optParams_d = optParams_d
 
311
 
 
312
        optFlags_d = {}
 
313
        for optList in optFlags:
 
314
            optFlags_d[optList[0]] = optList[1:]
 
315
        self.optFlags_d = optFlags_d
 
316
 
 
317
        optAll_d = {}
 
318
        optAll_d.update(optParams_d)
 
319
        optAll_d.update(optFlags_d)
 
320
        self.optAll_d = optAll_d
 
321
 
 
322
        self.addAdditionalOptions()
 
323
 
 
324
        # makes sure none of the zsh_ data structures reference option
 
325
        # names that don't exist. (great for catching typos)
 
326
        self.verifyZshNames()
 
327
 
 
328
        self.excludes = self.makeExcludesDict()
 
329
 
 
330
    def write(self):
 
331
        """
 
332
        Write the zsh completion code to the file given to __init__
 
333
        @return: C{None}
 
334
        """
 
335
        self.writeHeader()
 
336
        self.writeExtras()
 
337
        self.writeOptions()
 
338
        self.writeFooter()
 
339
 
 
340
    def writeHeader(self):
 
341
        """
 
342
        This is the start of the code that calls _arguments
 
343
        @return: C{None}
 
344
        """
 
345
        self.file.write('_arguments -s -A "-*" \\\n')
 
346
 
 
347
    def writeOptions(self):
 
348
        """
 
349
        Write out zsh code for each option in this command
 
350
        @return: C{None}
 
351
        """
 
352
        optNames = self.optAll_d.keys()
 
353
        optNames.sort()
 
354
        for long in optNames:
 
355
            self.writeOpt(long)
 
356
 
 
357
    def writeExtras(self):
 
358
        """
 
359
        Write out the "extras" list. These are just passed verbatim to the
 
360
        _arguments call
 
361
        @return: C{None}
 
362
        """
 
363
        for s in self.extras:
 
364
            self.file.write(escape(s))
 
365
            self.file.write(' \\\n')
 
366
 
 
367
    def writeFooter(self):
 
368
        """
 
369
        Write the last bit of code that finishes the call to _arguments
 
370
        @return: C{None}
 
371
        """
 
372
        self.file.write('&& return 0\n')
 
373
 
 
374
    def verifyZshNames(self):
 
375
        """
 
376
        Ensure that none of the names given in zsh_* variables are typoed
 
377
        @return: C{None}
 
378
        @raise ValueError: Raised if unknown option names have been given in
 
379
                           zsh_* variables
 
380
        """
 
381
        def err(name):
 
382
            raise ValueError, "Unknown option name \"%s\" found while\n" \
 
383
                "examining zsh_ attributes for the %s command" % (
 
384
                    name, self.cmd_name)
 
385
 
 
386
        for name in itertools.chain(self.altArgDescr, self.actionDescr,
 
387
        self.actions, self.multiUse):
 
388
            if name not in self.optAll_d:
 
389
                err(name)
 
390
 
 
391
        for seq in self.mutuallyExclusive:
 
392
            for name in seq:
 
393
                if name not in self.optAll_d:
 
394
                    err(name)
 
395
 
 
396
    def excludeStr(self, long, buildShort=False):
 
397
        """
 
398
        Generate an "exclusion string" for the given option
 
399
 
 
400
        @type long: C{str}
 
401
        @param long: The long name of the option
 
402
                     (i.e. "verbose" instead of "v")
 
403
 
 
404
        @type buildShort: C{bool}
 
405
        @param buildShort: May be True to indicate we're building an excludes
 
406
                           string for the short option that correspondes to
 
407
                           the given long opt
 
408
 
 
409
        @return: The generated C{str}
 
410
        """
 
411
        if long in self.excludes:
 
412
            exclusions = self.excludes[long][:]
 
413
        else:
 
414
            exclusions = []
 
415
 
 
416
        # if long isn't a multiUse option (can't appear on the cmd line more
 
417
        # than once), then we have to exclude the short option if we're
 
418
        # building for the long option, and vice versa.
 
419
        if long not in self.multiUse:
 
420
            if buildShort is False:
 
421
                short = self.getShortOption(long)
 
422
                if short is not None:
 
423
                    exclusions.append(short)
 
424
            else:
 
425
                exclusions.append(long)
 
426
 
 
427
        if not exclusions:
 
428
            return ''
 
429
 
 
430
        strings = []
 
431
        for optName in exclusions:
 
432
            if len(optName) == 1:
 
433
                # short option
 
434
                strings.append("-" + optName)
 
435
            else:
 
436
                strings.append("--" + optName)
 
437
        return "(%s)" % " ".join(strings)
 
438
 
 
439
    def makeExcludesDict(self):
 
440
        """
 
441
        @return: A C{dict} that maps each option name appearing in
 
442
        self.mutuallyExclusive to a list of those option names that
 
443
        is it mutually exclusive with (can't appear on the cmd line with)
 
444
        """
 
445
 
 
446
        #create a mapping of long option name -> single character name
 
447
        longToShort = {}
 
448
        for optList in itertools.chain(self.optParams, self.optFlags):
 
449
            try:
 
450
                if optList[1] != None:
 
451
                    longToShort[optList[0]] = optList[1]
 
452
            except IndexError:
 
453
                pass
 
454
 
 
455
        excludes = {}
 
456
        for lst in self.mutuallyExclusive:
 
457
            for i, long in enumerate(lst):
 
458
                tmp = []
 
459
                tmp.extend(lst[:i])
 
460
                tmp.extend(lst[i+1:])
 
461
                for name in tmp[:]:
 
462
                    if name in longToShort:
 
463
                        tmp.append(longToShort[name])
 
464
 
 
465
                if long in excludes:
 
466
                    excludes[long].extend(tmp)
 
467
                else:
 
468
                    excludes[long] = tmp
 
469
        return excludes
 
470
 
 
471
    def writeOpt(self, long):
 
472
        """
 
473
        Write out the zsh code for the given argument. This is just part of the
 
474
        one big call to _arguments
 
475
 
 
476
        @type long: C{str}
 
477
        @param long: The long name of the option
 
478
                     (i.e. "verbose" instead of "v")
 
479
 
 
480
        @return: C{None}
 
481
        """
 
482
        if long in self.optFlags_d:
 
483
            # It's a flag option. Not one that takes a parameter.
 
484
            long_field = "--%s" % long
 
485
        else:
 
486
            long_field = "--%s=" % long
 
487
 
 
488
        short = self.getShortOption(long)
 
489
        if short != None:
 
490
            short_field = "-" + short
 
491
        else:
 
492
            short_field = ''
 
493
 
 
494
        descr = self.getDescription(long)
 
495
        descr_field = descr.replace("[", "\[")
 
496
        descr_field = descr_field.replace("]", "\]")
 
497
        descr_field = '[%s]' % descr_field
 
498
 
 
499
        if long in self.actionDescr:
 
500
            actionDescr_field = self.actionDescr[long]
 
501
        else:
 
502
            actionDescr_field = descr
 
503
 
 
504
        action_field = self.getAction(long)
 
505
        if long in self.multiUse:
 
506
            multi_field = '*'
 
507
        else:
 
508
            multi_field = ''
 
509
 
 
510
        longExclusions_field = self.excludeStr(long)
 
511
 
 
512
        if short:
 
513
            #we have to write an extra line for the short option if we have one
 
514
            shortExclusions_field = self.excludeStr(long, buildShort=True)
 
515
            self.file.write(escape('%s%s%s%s%s' % (shortExclusions_field,
 
516
                multi_field, short_field, descr_field, action_field)))
 
517
            self.file.write(' \\\n')
 
518
 
 
519
        self.file.write(escape('%s%s%s%s%s' % (longExclusions_field,
 
520
            multi_field, long_field, descr_field, action_field)))
 
521
        self.file.write(' \\\n')
 
522
 
 
523
    def getAction(self, long):
 
524
        """
 
525
        Return a zsh "action" string for the given argument
 
526
        @return: C{str}
 
527
        """
 
528
        if long in self.actions:
 
529
            if callable(self.actions[long]):
 
530
                action = self.actions[long]()
 
531
            else:
 
532
                action = self.actions[long]
 
533
            return ":%s:%s" % (self.getActionDescr(long), action)
 
534
        if long in self.optParams_d:
 
535
            return ':%s:_files' % self.getActionDescr(long)
 
536
        return ''
 
537
 
 
538
    def getActionDescr(self, long):
 
539
        """
 
540
        Return the description to be used when this argument is completed
 
541
        @return: C{str}
 
542
        """
 
543
        if long in self.actionDescr:
 
544
            return self.actionDescr[long]
 
545
        else:
 
546
            return long
 
547
 
 
548
    def getDescription(self, long):
 
549
        """
 
550
        Return the description to be used for this argument
 
551
        @return: C{str}
 
552
        """
 
553
        #check if we have an alternate descr for this arg, and if so use it
 
554
        if long in self.altArgDescr:
 
555
            return self.altArgDescr[long]
 
556
 
 
557
        #otherwise we have to get it from the optFlags or optParams
 
558
        try:
 
559
            descr = self.optFlags_d[long][1]
 
560
        except KeyError:
 
561
            try:
 
562
                descr = self.optParams_d[long][2]
 
563
            except KeyError:
 
564
                descr = None
 
565
 
 
566
        if descr is not None:
 
567
            return descr
 
568
 
 
569
        # lets try to get it from the opt_foo method doc string if there is one
 
570
        longMangled = long.replace('-', '_') # this is what t.p.usage does
 
571
        obj = getattr(self.options, 'opt_%s' % longMangled, None)
 
572
        if obj:
 
573
            descr = descrFromDoc(obj)
 
574
            if descr is not None:
 
575
                return descr
 
576
 
 
577
        return long # we really ought to have a good description to use
 
578
 
 
579
    def getShortOption(self, long):
 
580
        """
 
581
        Return the short option letter or None
 
582
        @return: C{str} or C{None}
 
583
        """
 
584
        optList = self.optAll_d[long]
 
585
        try:
 
586
            return optList[0] or None
 
587
        except IndexError:
 
588
            pass
 
589
 
 
590
    def addAdditionalOptions(self):
 
591
        """
 
592
        Add additional options to the optFlags and optParams lists.
 
593
        These will be defined by 'opt_foo' methods of the Options subclass
 
594
        @return: C{None}
 
595
        """
 
596
        methodsDict = {}
 
597
        reflect.accumulateMethods(self.options, methodsDict, 'opt_')
 
598
        methodToShort = {}
 
599
        for name in methodsDict.copy():
 
600
            if len(name) == 1:
 
601
                methodToShort[methodsDict[name]] = name
 
602
                del methodsDict[name]
 
603
 
 
604
        for methodName, methodObj in methodsDict.items():
 
605
            long = methodName.replace('_', '-') # t.p.usage does this
 
606
            # if this option is already defined by the optFlags or
 
607
            # optParameters then we don't want to override that data
 
608
            if long in self.optAll_d:
 
609
                continue
 
610
 
 
611
            descr = self.getDescription(long)
 
612
 
 
613
            short = None
 
614
            if methodObj in methodToShort:
 
615
                short = methodToShort[methodObj]
 
616
 
 
617
            reqArgs = methodObj.im_func.func_code.co_argcount
 
618
            if reqArgs == 2:
 
619
                self.optParams.append([long, short, None, descr])
 
620
                self.optParams_d[long] = [short, None, descr]
 
621
                self.optAll_d[long] = [short, None, descr]
 
622
            elif reqArgs == 1:
 
623
                self.optFlags.append([long, short, descr])
 
624
                self.optFlags_d[long] = [short, descr]
 
625
                self.optAll_d[long] = [short, None, descr]
 
626
            else:
 
627
                raise TypeError, '%r has wrong number ' \
 
628
                                 'of arguments' % (methodObj,)
 
629
 
 
630
def descrFromDoc(obj):
 
631
    """
 
632
    Generate an appropriate description from docstring of the given object
 
633
    """
 
634
    if obj.__doc__ is None:
 
635
        return None
 
636
 
 
637
    lines = obj.__doc__.split("\n")
 
638
    descr = None
 
639
    try:
 
640
        if lines[0] != "" and not lines[0].isspace():
 
641
            descr = lines[0].lstrip()
 
642
        # skip first line if it's blank
 
643
        elif lines[1] != "" and not lines[1].isspace():
 
644
            descr = lines[1].lstrip()
 
645
    except IndexError:
 
646
        pass
 
647
    return descr
 
648
 
 
649
def firstLine(s):
 
650
    """
 
651
    Return the first line of the given string
 
652
    """
 
653
    try:
 
654
        i = s.index('\n')
 
655
        return s[:i]
 
656
    except ValueError:
 
657
        return s
 
658
 
 
659
def escape(str):
 
660
    """
 
661
    Shell escape the given string
 
662
    """
 
663
    return commands.mkarg(str)[1:]
 
664
 
 
665
def siteFunctionsPath():
 
666
    """
 
667
    Return the path to the system-wide site-functions directory or
 
668
    C{None} if it cannot be determined
 
669
    """
 
670
    try:
 
671
        cmd = "zsh -f -c 'echo ${(M)fpath:#/*/site-functions}'"
 
672
        output = commands.getoutput(cmd)
 
673
        if os.path.isdir(output):
 
674
            return output
 
675
    except:
 
676
        pass
 
677
 
 
678
generateFor = [('conch', 'twisted.conch.scripts.conch', 'ClientOptions'),
 
679
               ('mktap', 'twisted.scripts.mktap', 'FirstPassOptions'),
 
680
               ('trial', 'twisted.scripts.trial', 'Options'),
 
681
               ('cftp', 'twisted.conch.scripts.cftp', 'ClientOptions'),
 
682
               ('tapconvert', 'twisted.scripts.tapconvert', 'ConvertOptions'),
 
683
               ('twistd', 'twisted.scripts.twistd', 'ServerOptions'),
 
684
               ('ckeygen', 'twisted.conch.scripts.ckeygen', 'GeneralOptions'),
 
685
               ('lore', 'twisted.lore.scripts.lore', 'Options'),
 
686
               ('pyhtmlizer', 'twisted.scripts.htmlizer', 'Options'),
 
687
               ('tap2deb', 'twisted.scripts.tap2deb', 'MyOptions'),
 
688
               ('tkconch', 'twisted.conch.scripts.tkconch', 'GeneralOptions'),
 
689
               ('manhole', 'twisted.scripts.manhole', 'MyOptions'),
 
690
               ('tap2rpm', 'twisted.scripts.tap2rpm', 'MyOptions'),
 
691
               ('websetroot', None, None),
 
692
               ('tkmktap', None, None),
 
693
               ]
 
694
# NOTE: the commands using None above are no longer included in Twisted.
 
695
# However due to limitations in zsh's completion system the version of
 
696
# _twisted_zsh_stub shipped with zsh contains a static list of Twisted's
 
697
# commands. It will display errors if completion functions for these missing
 
698
# commands are not found :( So we just include dummy (empty) completion
 
699
# function files
 
700
 
 
701
specialBuilders = {'mktap'  : MktapBuilder,
 
702
                   'twistd' : TwistdBuilder}
 
703
 
 
704
def makeCompFunctionFiles(out_path, generateFor=generateFor,
 
705
                          specialBuilders=specialBuilders):
 
706
    """
 
707
    Generate completion function files in the given directory for all
 
708
    twisted commands
 
709
 
 
710
    @type out_path: C{str}
 
711
    @param out_path: The path to the directory to generate completion function
 
712
                     fils in
 
713
 
 
714
    @param generateFor: Sequence in the form of the 'generateFor' top-level
 
715
                        variable as defined in this module. Indicates what
 
716
                        commands to build completion files for.
 
717
 
 
718
    @param specialBuilders: Sequence in the form of the 'specialBuilders'
 
719
                            top-level variable as defined in this module.
 
720
                            Indicates what commands require a special
 
721
                            Builder class.
 
722
 
 
723
    @return: C{list} of 2-tuples of the form (cmd_name, error) indicating
 
724
             commands that we skipped building completions for. cmd_name
 
725
             is the name of the skipped command, and error is the Exception
 
726
             that was raised when trying to import the script module.
 
727
             Commands are usually skipped due to a missing dependency,
 
728
             e.g. Tkinter.
 
729
    """
 
730
    skips = []
 
731
    for cmd_name, module_name, class_name in generateFor:
 
732
        if module_name is None:
 
733
            # create empty file
 
734
            f = _openCmdFile(out_path, cmd_name)
 
735
            f.close()
 
736
            continue
 
737
        try:
 
738
            m = __import__('%s' % (module_name,), None, None, (class_name))
 
739
            f = _openCmdFile(out_path, cmd_name)
 
740
            o = getattr(m, class_name)() # instantiate Options class
 
741
 
 
742
            if cmd_name in specialBuilders:
 
743
                b = specialBuilders[cmd_name](cmd_name, o, f)
 
744
                b.write()
 
745
            else:
 
746
                b = Builder(cmd_name, o, f)
 
747
                b.write()
 
748
        except Exception, e:
 
749
            skips.append( (cmd_name, e) )
 
750
            continue
 
751
    return skips
 
752
 
 
753
def _openCmdFile(out_path, cmd_name):
 
754
    return file(os.path.join(out_path, '_'+cmd_name), 'w')
 
755
 
 
756
def run():
 
757
    options = MyOptions()
 
758
    try:
 
759
        options.parseOptions(sys.argv[1:])
 
760
    except usage.UsageError, e:
 
761
        print e
 
762
        print options.getUsage()
 
763
        sys.exit(2)
 
764
 
 
765
    if options['install']:
 
766
        import twisted
 
767
        dir = os.path.join(os.path.dirname(twisted.__file__), "python", "zsh")
 
768
        skips = makeCompFunctionFiles(dir)
 
769
    else:
 
770
        skips = makeCompFunctionFiles(options['directory'])
 
771
 
 
772
    for cmd_name, error in skips:
 
773
        sys.stderr.write("zshcomp: Skipped building for %s. Script module " \
 
774
                         "could not be imported:\n" % (cmd_name,))
 
775
        sys.stderr.write(str(error)+'\n')
 
776
    if skips:
 
777
        sys.exit(3)
 
778
 
 
779
if __name__ == '__main__':
 
780
    run()