Changeset 2635
- Timestamp:
- 01/31/06 12:32:34 (19 years ago)
- Location:
- pykota/trunk
- Files:
-
- 7 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/cupspykota
r2632 r2635 380 380 """Computes the job size with a software method.""" 381 381 self.logdebug("Precomputing job's size...") 382 jobsize = 0 383 if self.JobSizeBytes : 384 try : 385 from pkpgpdls import analyzer, pdlparser 386 except ImportError : 387 self.printInfo("pkpgcounter is now distributed separately, please grab it from http://www.librelogiciel.com/software/pkpgcounter/action_Download", "error") 388 self.printInfo("Precomputed job size will be forced to 0 pages.", "error") 389 else : 390 infile = open(self.DataFile, "rb") 391 try : 392 parser = analyzer.PDLAnalyzer(infile) 393 jobsize = parser.getJobSize() 394 except pdlparser.PDLParserError, msg : 395 # Here we just log the failure, but 396 # we finally ignore it and return 0 since this 397 # computation is just an indication of what the 398 # job's size MAY be. 399 self.printInfo(_("Unable to precompute the job's size with the generic PDL analyzer : %s") % msg, "warn") 400 else : 401 if self.InputFile is not None : 402 # when a filename is passed as an argument, the backend 403 # must generate the correct number of copies. 404 jobsize *= self.Copies 405 infile.close() 406 self.softwareJobSize = jobsize 382 self.preaccounter.beginJob(None) 383 self.preaccounter.endJob(None) 384 self.softwareJobSize = self.preaccounter.getJobSize(None) 407 385 self.logdebug("Precomputed job's size is %s pages." % self.softwareJobSize) 408 386 … … 1178 1156 wrapper.initBackendParameters() 1179 1157 wrapper.saveDatasAndCheckSum() 1158 wrapper.preaccounter = openAccounter(wrapper, ispreaccounter=1) 1180 1159 wrapper.accounter = openAccounter(wrapper) 1181 1160 wrapper.precomputeJobSize() -
pykota/trunk/conf/pykota.conf.sample
r2631 r2635 563 563 # and uses pkpgcounter's code internally. 564 564 accounter: software() 565 566 # What is the "pre"-accounter used for precomputing the job's size. 567 # 568 # Supported values are : 569 # 570 # preaccounter: software() 571 # preaccounter: software(/path/to/your/script) 572 # 573 # NB : the preaccounter directive doesn't support hardware() for obvious reasons. 574 # If unset, "software()" is assumed. If you use your own script, ensure that it 575 # only prints the job's number of pages (or an estimation of it) on its standard output. 576 # 577 # This value can be set either globally or on a per printer basis 578 # If both are defined, the printer option has priority. 579 # 580 preaccounter: software() 581 565 582 566 583 # What should we do if the accounter's subprocess doesn't return -
pykota/trunk/NEWS
r2631 r2635 24 24 - 1.24alpha8 : 25 25 26 - The 'preaccounter' configuration directive was introduced to control 27 which parser to use for precomputation of the job's size. When unset, 28 the internal parser is used just like it was previously. See 29 conf/pykota.conf.sample for more details. 30 26 31 - Now subprocesses launched through the overwrite_jobticket directive 27 32 can also output 'CANCEL', to represent the user's own choice to -
pykota/trunk/pykota/accounter.py
r2622 r2635 36 36 class AccounterBase : 37 37 """A class to account print usage by querying printers.""" 38 def __init__(self, kotafilter, arguments ) :38 def __init__(self, kotafilter, arguments, ispreaccounter=0) : 39 39 """Sets instance vars depending on the current printer.""" 40 40 self.filter = kotafilter … … 42 42 self.onerror = self.filter.config.getPrinterOnAccounterError(self.filter.PrinterName) 43 43 self.isSoftware = 1 # by default software accounting 44 self.isPreAccounter = ispreaccounter 44 45 45 46 def getLastPageCounter(self) : … … 58 59 59 60 # get last job information for this printer 60 if not printer.LastJob.Exists : 61 # The printer hasn't been used yet, from PyKota's point of view 62 self.LastPageCounter = 0 63 else : 64 # get last job size and page counter from Quota Storage 65 # Last lifetime page counter before actual job is 66 # last page counter + last job size 67 self.LastPageCounter = int(printer.LastJob.PrinterPageCounter or 0) + int(printer.LastJob.JobSize or 0) 61 if not self.isPreAccounter : 62 # TODO : check if this code is still needed 63 if not printer.LastJob.Exists : 64 # The printer hasn't been used yet, from PyKota's point of view 65 self.LastPageCounter = 0 66 else : 67 # get last job size and page counter from Quota Storage 68 # Last lifetime page counter before actual job is 69 # last page counter + last job size 70 self.LastPageCounter = int(printer.LastJob.PrinterPageCounter or 0) + int(printer.LastJob.JobSize or 0) 68 71 69 72 def fakeBeginJob(self) : … … 86 89 raise RuntimeError, "AccounterBase.computeJobSize() must be overriden !" 87 90 88 def openAccounter(kotafilter ) :91 def openAccounter(kotafilter, ispreaccounter=0) : 89 92 """Returns a connection handle to the appropriate accounter.""" 90 (backend, args) = kotafilter.config.getAccounterBackend(kotafilter.PrinterName) 93 if ispreaccounter : 94 (backend, args) = kotafilter.config.getPreAccounterBackend(kotafilter.PrinterName) 95 else : 96 (backend, args) = kotafilter.config.getAccounterBackend(kotafilter.PrinterName) 91 97 try : 92 98 exec "from pykota.accounters import %s as accounterbackend" % backend.lower() … … 94 100 raise PyKotaAccounterError, _("Unsupported accounter backend %s") % backend 95 101 else : 96 return accounterbackend.Accounter(kotafilter, args )102 return accounterbackend.Accounter(kotafilter, args, ispreaccounter) -
pykota/trunk/pykota/accounters/hardware.py
r2622 r2635 31 31 32 32 class Accounter(AccounterBase) : 33 def __init__(self, kotabackend, arguments ) :33 def __init__(self, kotabackend, arguments, ispreaccounter=0) : 34 34 """Initializes querying accounter.""" 35 35 AccounterBase.__init__(self, kotabackend, arguments) -
pykota/trunk/pykota/accounters/software.py
r2622 r2635 30 30 def computeJobSize(self) : 31 31 """Feeds an external command with our datas to let it compute the job size, and return its value.""" 32 if (not self.isPreAccounter) and \ 33 (self.filter.accounter.arguments == self.filter.preaccounter.arguments) : 34 # if precomputing has been done and both accounter and preaccounter are 35 # configured the same, no need to launch a second pass since we already 36 # know the result. 37 self.filter.logdebug("Precomputing pass told us that job is %s pages long." % self.filter.softwareJobSize) 38 return self.filter.softwareJobSize # Optimize : already computed ! 39 40 if self.arguments : 41 self.filter.logdebug("Using external script %s to compute job's size." % self.arguments) 42 return self.withExternalScript() 43 else : 44 self.filter.logdebug("Using internal parser to compute job's size.") 45 return self.withInternalParser() 46 47 def withInternalParser(self) : 48 """Does software accounting through an external script.""" 49 jobsize = 0 50 if self.filter.JobSizeBytes : 51 try : 52 from pkpgpdls import analyzer, pdlparser 53 except ImportError : 54 self.filter.printInfo("pkpgcounter is now distributed separately, please grab it from http://www.librelogiciel.com/software/pkpgcounter/action_Download", "error") 55 self.filter.printInfo("Precomputed job size will be forced to 0 pages.", "error") 56 else : 57 infile = open(self.filter.DataFile, "rb") 58 try : 59 parser = analyzer.PDLAnalyzer(infile) 60 jobsize = parser.getJobSize() 61 except pdlparser.PDLParserError, msg : 62 # Here we just log the failure, but 63 # we finally ignore it and return 0 since this 64 # computation is just an indication of what the 65 # job's size MAY be. 66 self.filter.printInfo(_("Unable to precompute the job's size with the generic PDL analyzer : %s") % msg, "warn") 67 else : 68 if self.filter.InputFile is not None : 69 # when a filename is passed as an argument, the backend 70 # must generate the correct number of copies. 71 jobsize *= self.filter.Copies 72 infile.close() 73 return jobsize 74 75 def withExternalScript(self) : 76 """Does software accounting through an external script.""" 32 77 self.filter.printInfo(_("Launching SOFTWARE(%s)...") % self.arguments) 33 if not self.arguments : 34 pagecounter = self.filter.softwareJobSize # Optimize : already computed ! 35 self.filter.logdebug("Internal software accounter said job is %s pages long." % repr(pagecounter)) 36 else : 37 MEGABYTE = 1024*1024 38 infile = open(self.filter.DataFile, "rb") 39 child = popen2.Popen4(self.arguments) 40 try : 41 data = infile.read(MEGABYTE) 42 while data : 43 child.tochild.write(data) 44 data = infile.read(MEGABYTE) 45 child.tochild.flush() 46 child.tochild.close() 47 except (IOError, OSError), msg : 48 msg = "%s : %s" % (self.arguments, msg) 49 self.filter.printInfo(_("Unable to compute job size with accounter %s") % msg) 50 infile.close() 51 pagecounter = None 52 try : 53 answer = child.fromchild.read() 54 except (IOError, OSError), msg : 55 msg = "%s : %s" % (self.arguments, msg) 56 self.filter.printInfo(_("Unable to compute job size with accounter %s") % msg) 57 else : 58 lines = [l.strip() for l in answer.split("\n")] 59 for i in range(len(lines)) : 60 try : 61 pagecounter = int(lines[i]) 62 except (AttributeError, ValueError) : 63 self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i]) 64 else : 65 break 66 child.fromchild.close() 78 MEGABYTE = 1024*1024 79 infile = open(self.filter.DataFile, "rb") 80 child = popen2.Popen4(self.arguments) 81 try : 82 data = infile.read(MEGABYTE) 83 while data : 84 child.tochild.write(data) 85 data = infile.read(MEGABYTE) 86 child.tochild.flush() 87 child.tochild.close() 88 except (IOError, OSError), msg : 89 msg = "%s : %s" % (self.arguments, msg) 90 self.filter.printInfo(_("Unable to compute job size with accounter %s") % msg) 91 infile.close() 92 pagecounter = None 93 try : 94 answer = child.fromchild.read() 95 except (IOError, OSError), msg : 96 msg = "%s : %s" % (self.arguments, msg) 97 self.filter.printInfo(_("Unable to compute job size with accounter %s") % msg) 98 else : 99 lines = [l.strip() for l in answer.split("\n")] 100 for i in range(len(lines)) : 101 try : 102 pagecounter = int(lines[i]) 103 except (AttributeError, ValueError) : 104 self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i]) 105 else : 106 break 107 child.fromchild.close() 108 109 try : 110 status = child.wait() 111 except OSError, msg : 112 self.filter.printInfo(_("Problem while waiting for software accounter pid %s to exit : %s") % (child.pid, msg)) 113 else : 114 if os.WIFEXITED(status) : 115 status = os.WEXITSTATUS(status) 116 self.filter.printInfo(_("Software accounter %s exit code is %s") % (self.arguments, str(status))) 67 117 68 try : 69 status = child.wait() 70 except OSError, msg : 71 self.filter.printInfo(_("Problem while waiting for software accounter pid %s to exit : %s") % (child.pid, msg)) 72 else : 73 if os.WIFEXITED(status) : 74 status = os.WEXITSTATUS(status) 75 self.filter.printInfo(_("Software accounter %s exit code is %s") % (self.arguments, str(status))) 76 77 if pagecounter is None : 78 message = _("Unable to compute job size with accounter %s") % self.arguments 79 if self.onerror == "CONTINUE" : 80 self.filter.printInfo(message, "error") 81 else : 82 raise PyKotaAccounterError, message 83 self.filter.logdebug("Software accounter %s said job is %s pages long." % (self.arguments, repr(pagecounter))) 118 if pagecounter is None : 119 message = _("Unable to compute job size with accounter %s") % self.arguments 120 if self.onerror == "CONTINUE" : 121 self.filter.printInfo(message, "error") 122 else : 123 raise PyKotaAccounterError, message 124 self.filter.logdebug("Software accounter %s said job is %s pages long." % (self.arguments, repr(pagecounter))) 84 125 85 126 return pagecounter or 0 -
pykota/trunk/pykota/config.py
r2622 r2635 190 190 return url.strip() 191 191 192 def getPreAccounterBackend(self, printername) : 193 """Returns the preaccounter backend to use for a given printer.""" 194 validaccounters = [ "software" ] 195 try : 196 fullaccounter = self.getPrinterOption(printername, "preaccounter").strip() 197 except PyKotaConfigError : 198 return ("software", "") 199 else : 200 flower = fullaccounter.lower() 201 if flower.startswith("software") : 202 try : 203 (accounter, args) = [x.strip() for x in fullaccounter.split('(', 1)] 204 except ValueError : 205 raise PyKotaConfigError, _("Invalid preaccounter %s for printer %s") % (fullaccounter, printername) 206 if args.endswith(')') : 207 args = args[:-1].strip() 208 return ("software", args) 209 else : 210 raise PyKotaConfigError, _("Option preaccounter in section %s only supports values in %s") % (printername, str(validaccounters)) 211 192 212 def getAccounterBackend(self, printername) : 193 """Returns the accounter backend to use for a given printer. 194 195 if it is not set, it defaults to 'hardware' which means ask printer 196 for its internal lifetime page counter. 197 """ 213 """Returns the accounter backend to use for a given printer.""" 198 214 validaccounters = [ "hardware", "software" ] 199 215 fullaccounter = self.getPrinterOption(printername, "accounter").strip()