root / pykota / trunk / bin / edpykota @ 1105

Revision 1105, 25.0 kB (checked in by jalet, 21 years ago)

PyKota now tries to add its attributes intelligently in existing LDAP
directories.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[696]1#! /usr/bin/env python
2
3# PyKota Print Quota Editor
4#
[952]5# PyKota - Print Quotas for CUPS and LPRng
[696]6#
7# (c) 2003 Jerome Alet <alet@librelogiciel.com>
[873]8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
[696]12#
[873]13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
[696]21#
22# $Id$
23#
24# $Log$
[1105]25# Revision 1.55  2003/07/28 09:11:12  jalet
26# PyKota now tries to add its attributes intelligently in existing LDAP
27# directories.
28#
[1089]29# Revision 1.54  2003/07/21 06:32:42  jalet
30# Prevents email messages to be sent at modification/creation time for
31# a user/group quota
32#
[1078]33# Revision 1.53  2003/07/09 06:03:41  jalet
34# Fixed typo when using edpykota --prototype
35#
[1070]36# Revision 1.52  2003/07/07 12:11:13  jalet
37# Small fix
38#
[1069]39# Revision 1.51  2003/07/07 11:55:50  jalet
40# Small fix
41#
[1064]42# Revision 1.50  2003/07/05 12:33:53  jalet
43# More on previous fix.
44#
[1063]45# Revision 1.49  2003/07/05 12:32:07  jalet
46# Ensure that the user don't pass more than two prices for a printer.
47#
[1043]48# Revision 1.48  2003/06/25 19:52:30  jalet
49# Should be ready for testing :-)
50#
[1041]51# Revision 1.47  2003/06/25 14:10:01  jalet
52# Hey, it may work (edpykota --reset excepted) !
53#
[1031]54# Revision 1.46  2003/06/16 11:59:09  jalet
55# More work on LDAP
56#
[1023]57# Revision 1.45  2003/06/11 19:32:00  jalet
58# Severe bug wrt account balance setting should be corrected.
59#
[975]60# Revision 1.44  2003/04/29 22:03:38  jalet
61# Better error handling.
62#
[952]63# Revision 1.43  2003/04/23 22:13:56  jalet
64# Preliminary support for LPRng added BUT STILL UNTESTED.
65#
[937]66# Revision 1.42  2003/04/17 13:38:47  jalet
67# Docstring corrected for better manual page
68#
[927]69# Revision 1.41  2003/04/16 12:35:49  jalet
70# Groups quota work now !
71#
[924]72# Revision 1.40  2003/04/16 08:22:09  jalet
73# More strict error detection.
74# Minor code rewrite to avoid some repetitive tests.
75#
[923]76# Revision 1.39  2003/04/16 08:01:53  jalet
77# edpykota --charge command line option works now.
78#
[922]79# Revision 1.38  2003/04/15 22:02:43  jalet
80# More complete docstring
81#
[921]82# Revision 1.37  2003/04/15 21:58:33  jalet
83# edpykota now accepts a --delete option.
84# Preparation to allow edpykota to accept much more command line options
85# (WARNING : docstring is OK, but code isn't !)
86#
[917]87# Revision 1.36  2003/04/15 13:55:28  jalet
88# Options --limitby and --balance added to edpykota
89#
[916]90# Revision 1.35  2003/04/15 13:06:39  jalet
91# Allow to add a printer without any user
92#
[907]93# Revision 1.34  2003/04/11 16:51:11  jalet
94# Bug fix for edpykota --add with users who already had a quota on the printer.
95#
[900]96# Revision 1.33  2003/04/10 21:47:20  jalet
97# Job history added. Upgrade script neutralized for now !
98#
[895]99# Revision 1.32  2003/04/08 21:31:39  jalet
100# (anything or 0) = anything !!! Go back to school Jerome !
101#
[891]102# Revision 1.31  2003/04/08 21:13:44  jalet
103# Prepare --groups option to work.
104#
[890]105# Revision 1.30  2003/04/08 21:10:18  jalet
106# Checks --groups option presence instead of --users because --users is the default.
107#
[886]108# Revision 1.29  2003/04/05 09:28:56  jalet
109# Unnecessary message was logged
110#
[873]111# Revision 1.28  2003/03/29 13:45:26  jalet
112# GPL paragraphs were incorrectly (from memory) copied into the sources.
113# Two README files were added.
114# Upgrade script for PostgreSQL pre 1.01 schema was added.
115#
[848]116# Revision 1.27  2003/03/10 00:23:04  jalet
117# Bad english
118#
[847]119# Revision 1.26  2003/03/10 00:11:27  jalet
120# Cleaner example.
121#
[843]122# Revision 1.25  2003/03/09 23:56:21  jalet
123# Option noquota added to do accounting only.
124#
[825]125# Revision 1.24  2003/02/27 23:48:41  jalet
126# Correctly maps PyKota's log levels to syslog log levels
127#
[824]128# Revision 1.23  2003/02/27 22:55:20  jalet
129# WARN log priority doesn't exist.
130#
[820]131# Revision 1.22  2003/02/27 09:37:02  jalet
132# Wildcards seem to work now
133#
[815]134# Revision 1.21  2003/02/27 09:04:46  jalet
135# user and group names can be passed as wildcards if the --add option
136# is not set. The default is to act on all users or groups.
137#
[791]138# Revision 1.20  2003/02/10 12:07:30  jalet
139# Now repykota should output the recorded total page number for each printer too.
140#
[775]141# Revision 1.19  2003/02/09 13:40:29  jalet
142# typo
143#
[772]144# Revision 1.18  2003/02/09 12:56:53  jalet
145# Internationalization begins...
146#
[769]147# Revision 1.17  2003/02/08 22:47:23  jalet
148# Option --reset can now be used without having to use soft and hard limits
149# on the command line.
150#
[768]151# Revision 1.16  2003/02/08 22:39:46  jalet
152# --reset command line option added
153#
[767]154# Revision 1.15  2003/02/08 22:20:01  jalet
155# Clarification on why we don't check with /etc/passwd to see if the user
156# name is valid or not.
157#
[766]158# Revision 1.14  2003/02/08 22:18:15  jalet
159# Now checks user and group names for validity before adding them
160#
[763]161# Revision 1.13  2003/02/08 22:09:02  jalet
162# Only printer was added the first time.
163#
[762]164# Revision 1.12  2003/02/08 21:44:49  jalet
165# Python 2.1 string module doesn't define ascii_letters
166#
[758]167# Revision 1.11  2003/02/08 09:42:44  jalet
168# Better handle wrong or bad command line arguments
169#
[757]170# Revision 1.10  2003/02/08 09:39:20  jalet
171# typos
172#
[756]173# Revision 1.9  2003/02/08 09:38:06  jalet
174# Badly placed test
175#
[750]176# Revision 1.8  2003/02/07 22:53:57  jalet
177# Checks if printer name is valid before adding it
178#
[749]179# Revision 1.7  2003/02/07 22:17:58  jalet
180# Incomplete test
181#
[748]182# Revision 1.6  2003/02/07 22:13:13  jalet
183# Perhaps edpykota is now able to add printers !!! Oh, stupid me !
184#
[720]185# Revision 1.5  2003/02/06 14:49:04  jalet
186# edpykota should be ok now
187#
[719]188# Revision 1.4  2003/02/06 14:28:59  jalet
189# edpykota should be ok, minus some typos
190#
[717]191# Revision 1.3  2003/02/06 10:47:21  jalet
192# Documentation string and command line options didn't match.
193#
[715]194# Revision 1.2  2003/02/06 10:39:23  jalet
195# Preliminary edpykota work.
196#
[696]197# Revision 1.1  2003/02/05 21:41:09  jalet
198# Skeletons added for all command line tools
199#
200#
201#
202
[715]203import sys
204
205from pykota import version
206from pykota.tool import PyKotaTool, PyKotaToolError
[975]207from pykota.config import PyKotaConfigError
208from pykota.storage import PyKotaStorageError
[715]209
210__doc__ = """edpykota v%s (C) 2003 C@LL - Conseil Internet & Logiciels Libres
211A Print Quota editor for PyKota.
212
213command line usage :
214
[717]215  edpykota [options] user1 user2 ... userN
[937]216 
[717]217  edpykota [options] group1 group2 ... groupN
[715]218
219options :
220
221  -v | --version       Prints edpykota's version number then exits.
222  -h | --help          Prints this message then exits.
223 
224  -a | --add           Adds users and/or printers if they don't
225                       exist on the Quota Storage Server.
226                       
[921]227  -d | --delete        Deletes users/groups from the quota storage.
228                       Printers are never deleted.
229                       
230  -c | --charge p[,j]  Sets the price per page and per job to charge
231                       for a particular printer. Job price is optional.
232                       If both are to be set, separate them with a comma.
233                       Floating point values are allowed.
234 
235  -i | --ingroups g1[,g2...]  Puts the users into each of the groups
236                              listed, separated by commas. The groups
237                              must already exist in the Quota Storage.
238 
[715]239  -u | --users         Edit users print quotas, this is the default.
240 
[719]241  -P | --printer p     Edit quotas on printer p only. Actually p can
242                       use wildcards characters to select only
243                       some printers. The default value is *, meaning
244                       all printers.
[715]245 
246  -g | --groups        Edit groups print quotas instead of users.
247                         
248  -p | --prototype u|g Uses user u or group g as a prototype to set
249                       print quotas
250                       
[843]251  -n | --noquota       Doesn't set a quota but only does accounting.
252 
[768]253  -r | --reset         Resets the printed page counter for the user
254                       or group to zero. The life time page counter
255                       is kept unchanged.
256                       
[917]257  -l | --limitby l     Choose if the user/group is limited in printing                     
258                       by its account balance or by its page quota.
259                       The default value is 'quota'. Allowed values
260                       are 'quota' and 'balance'.
261                       
262  -b | --balance b     Sets the user's account balance to b.                     
263                       Account balance may be increase or decreased
264                       if b is prefixed with + or -.
265                       WARNING : when decreasing account balance,
266                       the total paid so far by the user is decreased
267                       too.
268                       Groups don't have a real balance, but the
269                       sum of their users' account balance.
270                       
[715]271  -S | --softlimit sl  Sets the quota soft limit to sl pages.                       
272 
273  -H | --hardlimit hl  Sets the quota hard limit to hl pages.
274 
[815]275  user1 through userN and group1 through groupN can use wildcards
276  if the --add option is not set.
277 
[715]278examples :                             
279
280  $ edpykota -p jerome john paul george ringo
281 
282  This will set print quotas for the users john, paul, george and ringo
283  to the same values than user jerome. User jerome must exist.
284 
285  $ edpykota --printer lp -S 50 -H 60 jerome
286 
287  This will set jerome's print quota on the lp printer to a soft limit
288  of 50 pages, and a hard limit of 60 pages. If either user jerome or
289  printer lp doesn't exist on the Quota Storage Server then nothing is done.
290
[921]291  $ edpykota --add --printer lp --ingroups coders,it -S 50 -H 60 jerome
[715]292 
293  Same as above, but if either user jerome or printer lp doesn't exist
[921]294  on the Quota Storage Server they are automatically added. Also
295  user jerome is put into the groups "coders" and "it" which must
296  already exist in the Quota Storage.
[952]297  WARNING : the CUPS PPD file for this printer or the /etc/printcap
298            file (depending on your printing backend)  must still be
299            modified manually, as well as pykota's configuration file
300            for a new printer to be managed successfully.
[715]301           
302  $ edpykota -g -S 500 -H 550 financial support           
303 
304  This will set print quota soft limit to 500 pages and hard limit
305  to 550 pages for groups financial and support on all printers.
[769]306 
[815]307  $ edpykota --reset jerome "jo*"
[769]308 
[815]309  This will reset jerome's page counter to zero on all printers, as
310  well as every user whose name begins with 'jo'.
311  Their life time page counter on each printer will be kept unchanged.
[843]312 
[847]313  $ edpykota --printer hpcolor --noquota jerome
[843]314 
[847]315  This will tell PyKota to not limit jerome when printing on the
316  hpcolor printer. All his jobs will be allowed on this printer, but
317  accounting of the pages he prints will still be kept.
[848]318  Print Quotas for jerome on other printers are unchanged.
[917]319 
320  $ edpykota --limitby balance jerome
321 
322  This will tell PyKota to limit jerome by his account's balance
323  when printing.
324 
325  $ edpykota --balance +10.0 jerome
326 
327  This will increase jerome's account balance by 10.0 (in your
328  own currency). You can decrease the account balance with a
329  dash prefix, and set it to a fixed amount with no prefix.
[921]330 
331  $ edpykota --delete jerome rachel
332 
333  This will completely delete jerome and rachel from the Quota Storage
334  database. All their quotas and jobs will be deleted too.
[922]335 
336  $ edpykota --printer lp --charge 0.1
337 
338  This will set the page price for printer lp to 0.1. Job price
339  will not be changed.
[715]340
341This program is free software; you can redistribute it and/or modify
342it under the terms of the GNU General Public License as published by
343the Free Software Foundation; either version 2 of the License, or
344(at your option) any later version.
345
346This program is distributed in the hope that it will be useful,
347but WITHOUT ANY WARRANTY; without even the implied warranty of
348MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
349GNU General Public License for more details.
350
351You should have received a copy of the GNU General Public License
352along with this program; if not, write to the Free Software
353Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
354
355Please e-mail bugs to: %s""" % (version.__version__, version.__author__)
356       
[719]357class EdPyKota(PyKotaTool) :       
358    """A class for edpykota."""
359    def main(self, names, options) :
360        """Edit user or group quotas."""
[1041]361       
362        suffix = (options["groups"] and "Group") or "User"       
363       
[719]364        softlimit = hardlimit = None   
[1041]365        if not options["noquota"] :
366            if options["softlimit"] :
367                try :
368                    softlimit = int(options["softlimit"].strip())
369                except ValueError :   
370                    raise PyKotaToolError, _("Invalid softlimit value %s.") % options["softlimit"]
371            if options["hardlimit"] :
372                try :
373                    hardlimit = int(options["hardlimit"].strip())
374                except ValueError :   
375                    raise PyKotaToolError, _("Invalid hardlimit value %s.") % options["hardlimit"]
376            if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) :       
377                # error, exchange them
378                self.logger.log_message(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit))
379                (softlimit, hardlimit) = (hardlimit, softlimit)
[924]380           
381        balance = options["balance"]
382        if balance :
383            balance = balance.strip()
384            try :
385                balancevalue = float(balance)
386            except ValueError :   
387                raise PyKotaToolError, _("Invalid balance value %s" % options["balance"])
388           
[923]389        if options["charge"] :
390            try :
391                charges = [float(part) for part in options["charge"].split(',', 1)]
392            except ValueError :   
393                raise PyKotaToolError, _("Invalid charge amount value %s" % options["charge"])
394            else :   
[1064]395                if len(charges) > 2 :
396                    charges = charges[:2]
[1063]397                if len(charges) != 2 :
398                    charges = [charges[0], None]
[924]399                   
400        limitby = options["limitby"]
401        if limitby :
402            limitby = limitby.strip().lower()
403        if limitby and (limitby not in ('quota', 'balance')) :   
404            raise PyKotaToolError, _("Invalid limitby value %s" % options["limitby"])
405           
[927]406        if options["ingroups"] :   
407            groupnames = [gname.strip() for gname in options["ingroups"].split(',')]
408        else :   
409            groupnames = []
410           
[1041]411        if options["prototype"] :   
412            protoentry = getattr(self.storage, "get%s" % suffix)(options["prototype"])
413           
414        printeradded = 0
415        printers = self.storage.getMatchingPrinters(options["printer"])
416        if not printers :
417            pname = options["printer"]
418            if options["add"] and pname :
419                if self.isValidName(pname) :
420                    printers = [ self.storage.addPrinter(pname) ]
421                    if printers[0].Exists :
422                        printeradded = 1
423                    else :   
424                        raise PyKotaToolError, _("Impossible to add printer %s") % pname
425                else :   
426                    raise PyKotaToolError, _("Invalid printer name %s") % pname
427            else :
428                raise PyKotaToolError, _("There's no printer matching %s") % pname
429        if not names :   
430            if options["add"] and not printeradded :
431                raise PyKotaToolError, _("You have to pass user or group names on the command line")
432            else :   
433                names = [ "*" ] # all users
434               
435        changed = {} # tracks changes made at the user/group level
436        for printer in printers :
[923]437            if options["charge"] :
438                (perpage, perjob) = charges
[1041]439                printer.setPrices(perpage, perjob)   
440               
[719]441            if options["prototype"] :
[1041]442                if protoentry.Exists :
[1078]443                    protoquota = getattr(self.storage, "get%sPQuota" % suffix)(protoentry, printer)
[1041]444                    if not protoquota.Exists :
445                        self.logger.log_message(_("Prototype %s not found in Quota Storage for printer %s.") % (protoentry.Name, printer.Name))
446                        continue    # skip this printer
447                    else :   
448                        (softlimit, hardlimit) = (protoquota.SoftLimit, protoquota.HardLimit)
449                else :       
450                    self.logger.log_message(_("Prototype object %s not found in Quota Storage.") % protoentry.Name)
[1105]451                   
[1041]452            if not options["noquota"] :   
453                if hardlimit is None :   
454                    hardlimit = softlimit
455                    if hardlimit is not None :
456                        self.logger.log_message(_("Undefined hard limit set to soft limit (%s) on printer %s.") % (str(hardlimit), printer.Name))
457                if softlimit is None :   
458                    softlimit = hardlimit
459                    if softlimit is not None :
460                        self.logger.log_message(_("Undefined soft limit set to hard limit (%s) on printer %s.") % (str(softlimit), printer.Name))
461                       
[820]462            if options["add"] :   
[1041]463                allentries = []   
464                for name in names :
465                    entry = getattr(self.storage, "get%s" % suffix)(name)
466                    entrypquota = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
467                    allentries.append((entry, entrypquota))
[820]468            else :   
[1041]469                allentries = getattr(self.storage, "getPrinter%ssAndQuotas" % suffix)(printer)
470               
471            for (entry, entrypquota) in [(e, q) for (e, q) in allentries if self.matchString(e.Name, names)] :
472                if not changed.has_key(entry.Name) :
473                    changed[entry.Name] = {}
[1070]474                    if not options["groups"] :
475                        changed[entry.Name]["ingroups"] = []
[1105]476                       
477                if not entry.Exists :       
[719]478                    # not found
479                    if options["add"] :
[767]480                        # In case we want to add something, it is crucial
481                        # that we DON'T check with the system accounts files
482                        # like /etc/passwd because users may be defined
483                        # only remotely
[1041]484                        if self.isValidName(entry.Name) :
[1105]485                            entry = getattr(self.storage, "add%s" % suffix)(entry)
[1041]486                        else :   
487                            if options["groups"] :
488                                self.logger.log_message(_("Invalid group name %s") % entry.Name)
[890]489                            else :   
[1041]490                                self.logger.log_message(_("Invalid user name %s") % entry.Name)
[1105]491                               
492                if not entrypquota.Exists :
493                    # not found
494                    if options["add"] :
495                        entrypquota = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer)
496                       
[1041]497                if not entrypquota.Exists :     
498                    self.logger.log_message(_("Quota not found for object %s on printer %s.") % (entry.Name, printer.Name))
[719]499                else :   
[921]500                    if options["delete"] :
[1041]501                        entry.delete()
[719]502                    else :
[1041]503                        if options["noquota"] or options["prototype"] or ((softlimit is not None) and (hardlimit is not None)) :
504                            entrypquota.setLimits(softlimit, hardlimit)
505                        if limitby :
506                            if changed[entry.Name].get("limitby") is None :
507                                entry.setLimitBy(limitby)
508                                changed[entry.Name]["limitby"] = limitby
509                       
510                        if not options["groups"] :
[921]511                            if options["reset"] :
[1041]512                                entrypquota.reset()
513                               
[921]514                            if balance :
[1041]515                                if changed[entry.Name].get("balance") is None :
[1023]516                                    if balance.startswith("+") or balance.startswith("-") :
[1043]517                                        newbalance = float(entry.AccountBalance or 0.0) + balancevalue
518                                        newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
519                                        entry.setAccountBalance(newbalance, newlifetimepaid)
[1023]520                                    else :
[1043]521                                        diff = balancevalue - float(entry.AccountBalance or 0.0)
522                                        newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
523                                        entry.setAccountBalance(balancevalue, newlifetimepaid)
[1041]524                                    changed[entry.Name]["balance"] = balance
[927]525                            for groupname in groupnames :       
[1069]526                                if groupname not in changed[entry.Name]["ingroups"] :
[1041]527                                    group = self.storage.getGroup(groupname)
528                                    if group.Exists :
529                                        self.storage.addUserToGroup(entry, group)
530                                        changed[entry.Name]["ingroups"].append(groupname)
[1023]531                                    else :
532                                        self.logger.log_message(_("Group %s not found in the PyKota Storage.") % groupname)
[1041]533                                       
[1089]534                        # This line disabled to prevent sending of unwanted email                 
535                        # messages if quota is reached at creation/modification time.
536                        # The check will be done at print time anyway.
537                        # getattr(self, "warn%sPQuota" % suffix)(entrypquota)   
[719]538                     
[715]539if __name__ == "__main__" : 
540    try :
541        defaults = { \
[719]542                     "printer" : "*", \
[715]543                   }
[921]544        short_options = "vhdc:l:b:i:naugrp:P:S:H:"
545        long_options = ["help", "version", "charge=", "delete", "limitby=", "balance=", "ingroups=", "noquota", "add", "users", "groups", "reset", "prototype=", "printer=", "softlimit=", "hardlimit="]
[715]546       
547        # Initializes the command line tool
[720]548        editor = EdPyKota(doc=__doc__)
[715]549       
550        # parse and checks the command line
[720]551        (options, args) = editor.parseCommandline(sys.argv[1:], short_options, long_options)
[715]552       
553        # sets long options
554        options["help"] = options["h"] or options["help"]
555        options["version"] = options["v"] or options["version"]
556        options["add"] = options["a"] or options["add"]
[895]557        options["users"] = options["u"] or options["users"]
558        options["groups"] = options["g"] or options["groups"]
[715]559        options["prototype"] = options["p"] or options["prototype"]
[720]560        options["printer"] = options["P"] or options["printer"] or defaults["printer"]
[715]561        options["softlimit"] = options["S"] or options["softlimit"]
562        options["hardlimit"] = options["H"] or options["hardlimit"] 
[768]563        options["reset"] = options["r"] or options["reset"] 
[843]564        options["noquota"] = options["n"] or options["noquota"]
[917]565        options["limitby"] = options["l"] or options["limitby"]
566        options["balance"] = options["b"] or options["balance"] 
[921]567        options["delete"] = options["d"] or options["delete"] 
568        options["ingroups"] = options["i"] or options["ingroups"]
569        options["charge"] = options["c"] or options["charge"]
[715]570       
571        if options["help"] :
[720]572            editor.display_usage_and_quit()
[715]573        elif options["version"] :
[720]574            editor.display_version_and_quit()
[715]575        elif options["users"] and options["groups"] :   
[843]576            raise PyKotaToolError, _("incompatible options, see help.")
[921]577        elif (options["add"] or options["prototype"]) and options["delete"] :   
578            raise PyKotaToolError, _("incompatible options, see help.")
[720]579        elif (options["softlimit"] or options["hardlimit"]) and options["prototype"] :   
[843]580            raise PyKotaToolError, _("incompatible options, see help.")
581        elif options["noquota"] and (options["prototype"] or options["hardlimit"] or options["softlimit"]) :
582            raise PyKotaToolError, _("incompatible options, see help.")
[921]583        elif options["groups"] and (options["balance"] or options["ingroups"]) :
[917]584            raise PyKotaToolError, _("incompatible options, see help.")
[715]585        else :
[720]586            sys.exit(editor.main(args, options))
[975]587    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError), msg :           
[715]588        sys.stderr.write("%s\n" % msg)
589        sys.stderr.flush()
590        sys.exit(-1)
Note: See TracBrowser for help on using the browser.