#!/usr/bin/python2.4 ############################################################################## # # Copyright (c) 2003 Zope Corporation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test script for testing ZODB under a heavy zope-like load. Note that, to be as realistic as possible with ZEO, you should run this script multiple times, to simulate multiple clients. Here's how this works. The script starts some number of threads. Each thread, sequentially executes jobs. There is a job producer that produces jobs. Input data are provided by a mail producer that hands out message from a mailbox. Execution continues until there is an error, which will normally occur when the mailbox is exhausted. Command-line options are used to provide job definitions. Job definitions have perameters of the form name=value. Jobs have 2 standard parameters: frequency=integer The frequency of the job. The default is 1. sleep=float The number os seconds to sleep before performing the job. The default is 0. Usage: loadmail2 [options] Options: -edit [frequency=integer] [sleep=float] Define an edit job. An edit job edits a random already-saved email message, deleting and inserting a random number of words. After editing the message, the message is (re)cataloged. -insert [number=int] [frequency=integer] [sleep=float] Insert some number of email messages. -index [number=int] [frequency=integer] [sleep=float] Insert and index (catalog) some number of email messages. -search [terms='word1 word2 ...'] [frequency=integer] [sleep=float] Search the catalog. A query is givem with one or more terms as would be entered into a typical seach box. If no query is given, then queries will be randomly selected based on a set of built-in word list. -setup Set up the database. This will delete any existing Data.fs file. (Of course, this may have no effect, if there is a custom_zodb that defined a different storage.) It also adds a mail folder and a catalog. -options file Read options from the given file. Th efile should be a python source file that defines a sequence of options named 'options'. -threads n Specify the number of threads to execute. If not specified (< 2), then jobs are run in a single (main) thread. -mbox filename Specify the mailbox for getting input data. There is a (lame) syntax for providing options within the filename. The filename may be followed by up to 3 integers, min, max, and start: -mbox 'foo.mbox 0 100 10000' The messages from min to max will be read from the mailbox. They will be assigned message numbers starting with start. So, in the example above, we read the first hundred messages and assign thgem message numbers starting with 10001. The maxmum can be given as a negative number, in which case, it specifies the number of messages to read. The start defaults to the minimum. The following two options: -mbox 'foo.mbox 300 400 300' and -mbox 'foo.mbox 300 -100' are equivalent $Id: zodbload.py 29533 2005-03-17 19:11:11Z tim_one $ """ import mailbox import math import os import random import re import sys import threading import time import transaction class JobProducer: def __init__(self): self.jobs = [] def add(self, callable, frequency, sleep, repeatp=0): self.jobs.extend([(callable, sleep, repeatp)] * int(frequency)) random.shuffle(self.jobs) def next(self): factory, sleep, repeatp = random.choice(self.jobs) time.sleep(sleep) callable, args = factory.create() return factory, callable, args, repeatp def __nonzero__(self): return not not self.jobs class MBox: def __init__(self, filename): if ' ' in filename: filename = filename.split() if len(filename) < 4: filename += [0, 0, -1][-(4-len(filename)):] filename, min, max, start = filename min = int(min) max = int(max) start = int(start) if start < 0: start = min if max < 0: # negative max is treated as a count self._max = start - max elif max > 0: self._max = start + max - min else: self._max = 0 else: self._max = 0 min = start = 0 if filename.endswith('.bz2'): f = os.popen("bunzip2 <"+filename, 'r') filename = filename[-4:] else: f = open(filename) self._mbox = mb = mailbox.UnixMailbox(f) self.number = start while min: mb.next() min -= 1 self._lock = threading.Lock() self.__name__ = os.path.splitext(os.path.split(filename)[1])[0] self._max = max def next(self): self._lock.acquire() try: if self._max > 0 and self.number >= self._max: raise IndexError(self.number + 1) message = self._mbox.next() message.body = message.fp.read() message.headers = list(message.headers) self.number += 1 message.number = self.number message.mbox = self.__name__ return message finally: self._lock.release() bins = 9973 #bins = 11 def mailfolder(app, mboxname, number): mail = getattr(app, mboxname, None) if mail is None: app.manage_addFolder(mboxname) mail = getattr(app, mboxname) from BTrees.Length import Length mail.length = Length() for i in range(bins): mail.manage_addFolder('b'+str(i)) bin = hash(str(number))%bins return getattr(mail, 'b'+str(bin)) def VmSize(): try: f = open('/proc/%s/status' % os.getpid()) except: return 0 else: l = filter(lambda l: l[:7] == 'VmSize:', f.readlines()) if l: l = l[0][7:].strip().split()[0] return int(l) return 0 def setup(lib_python): try: os.remove(os.path.join(lib_python, '..', '..', 'var', 'Data.fs')) except: pass import Zope2 import Products import AccessControl.SecurityManagement app=Zope2.app() Products.ZCatalog.ZCatalog.manage_addZCatalog(app, 'cat', '') from Products.ZCTextIndex.ZCTextIndex import PLexicon from Products.ZCTextIndex.Lexicon import Splitter, CaseNormalizer app.cat._setObject('lex', PLexicon('lex', '', Splitter(), CaseNormalizer()) ) class extra: doc_attr = 'PrincipiaSearchSource' lexicon_id = 'lex' index_type = 'Okapi BM25 Rank' app.cat.addIndex('PrincipiaSearchSource', 'ZCTextIndex', extra) transaction.commit() system = AccessControl.SpecialUsers.system AccessControl.SecurityManagement.newSecurityManager(None, system) app._p_jar.close() def do(db, f, args): """Do something in a transaction, retrying of necessary Measure the speed of both the compurartion and the commit """ from ZODB.POSException import ConflictError wcomp = ccomp = wcommit = ccommit = 0.0 rconflicts = wconflicts = 0 start = time.time() while 1: connection = db.open() try: transaction.begin() t=time.time() c=time.clock() try: try: r = f(connection, *args) except ConflictError: rconflicts += 1 transaction.abort() continue finally: wcomp += time.time() - t ccomp += time.clock() - c t=time.time() c=time.clock() try: try: transaction.commit() break except ConflictError: wconflicts += 1 transaction.abort() continue finally: wcommit += time.time() - t ccommit += time.clock() - c finally: connection.close() return start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r def run1(tid, db, factory, job, args): (start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r ) = do(db, job, args) start = "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % time.localtime(start)[:6] print "%s %s %8.3g %8.3g %s %s\t%8.3g %8.3g %s %r" % ( start, tid, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, factory.__name__, r) def run(jobs, tid=''): import Zope2 while 1: factory, job, args, repeatp = jobs.next() run1(tid, Zope2.DB, factory, job, args) if repeatp: while 1: i = random.randint(0,100) if i > repeatp: break run1(tid, Zope2.DB, factory, job, args) def index(connection, messages, catalog, max): app = connection.root()['Application'] for message in messages: mail = mailfolder(app, message.mbox, message.number) if max: # Cheat and use folder implementation secrets # to avoid having to read the old data _objects = mail._objects if len(_objects) >= max: for d in _objects[:len(_objects)-max+1]: del mail.__dict__[d['id']] mail._objects = _objects[len(_objects)-max+1:] docid = 'm'+str(message.number) mail.manage_addDTMLDocument(docid, file=message.body) # increment counted getattr(app, message.mbox).length.change(1) doc = mail[docid] for h in message.headers: h = h.strip() l = h.find(':') if l <= 0: continue name = h[:l].lower() if name=='subject': name='title' v = h[l+1:].strip() type='string' if name=='title': doc.manage_changeProperties(title=h) else: try: doc.manage_addProperty(name, v, type) except: pass if catalog: app.cat.catalog_object(doc) return message.number class IndexJob: needs_mbox = 1 catalog = 1 prefix = 'index' def __init__(self, mbox, number=1, max=0): self.__name__ = "%s%s_%s" % (self.prefix, number, mbox.__name__) self.mbox, self.number, self.max = mbox, int(number), int(max) def create(self): messages = [self.mbox.next() for i in range(self.number)] return index, (messages, self.catalog, self.max) class InsertJob(IndexJob): catalog = 0 prefix = 'insert' wordre = re.compile(r'(\w{3,20})') stop = 'and', 'not' def edit(connection, mbox, catalog=1): app = connection.root()['Application'] mail = getattr(app, mbox.__name__, None) if mail is None: time.sleep(1) return "No mailbox %s" % mbox.__name__ nmessages = mail.length() if nmessages < 2: time.sleep(1) return "No messages to edit in %s" % mbox.__name__ # find a message to edit: while 1: number = random.randint(1, nmessages-1) did = 'm' + str(number) mail = mailfolder(app, mbox.__name__, number) doc = getattr(mail, did, None) if doc is not None: break text = doc.raw.split() norig = len(text) if norig > 10: ndel = int(math.exp(random.randint(0, int(math.log(norig))))) nins = int(math.exp(random.randint(0, int(math.log(norig))))) else: ndel = 0 nins = 10 for j in range(ndel): j = random.randint(0,len(text)-1) word = text[j] m = wordre.search(word) if m: word = m.group(1).lower() if (not wordsd.has_key(word)) and word not in stop: words.append(word) wordsd[word] = 1 del text[j] for j in range(nins): word = random.choice(words) text.append(word) doc.raw = ' '.join(text) if catalog: app.cat.catalog_object(doc) return norig, ndel, nins class EditJob: needs_mbox = 1 prefix = 'edit' catalog = 1 def __init__(self, mbox): self.__name__ = "%s_%s" % (self.prefix, mbox.__name__) self.mbox = mbox def create(self): return edit, (self.mbox, self.catalog) class ModifyJob(EditJob): prefix = 'modify' catalog = 0 def search(connection, terms, number): app = connection.root()['Application'] cat = app.cat n = 0 for i in number: term = random.choice(terms) results = cat(PrincipiaSearchSource=term) n += len(results) for result in results: obj = result.getObject() # Apparently, there is a bug in Zope that leads obj to be None # on occasion. if obj is not None: obj.getId() return n class SearchJob: def __init__(self, terms='', number=10): if terms: terms = terms.split() self.__name__ = "search_" + '_'.join(terms) self.terms = terms else: self.__name__ = 'search' self.terms = words number = min(int(number), len(self.terms)) self.number = range(number) def create(self): return search, (self.terms, self.number) words=['banishment', 'indirectly', 'imprecise', 'peeks', 'opportunely', 'bribe', 'sufficiently', 'Occidentalized', 'elapsing', 'fermenting', 'listen', 'orphanage', 'younger', 'draperies', 'Ida', 'cuttlefish', 'mastermind', 'Michaels', 'populations', 'lent', 'cater', 'attentional', 'hastiness', 'dragnet', 'mangling', 'scabbards', 'princely', 'star', 'repeat', 'deviation', 'agers', 'fix', 'digital', 'ambitious', 'transit', 'jeeps', 'lighted', 'Prussianizations', 'Kickapoo', 'virtual', 'Andrew', 'generally', 'boatsman', 'amounts', 'promulgation', 'Malay', 'savaging', 'courtesan', 'nursed', 'hungered', 'shiningly', 'ship', 'presides', 'Parke', 'moderns', 'Jonas', 'unenlightening', 'dearth', 'deer', 'domesticates', 'recognize', 'gong', 'penetrating', 'dependents', 'unusually', 'complications', 'Dennis', 'imbalances', 'nightgown', 'attached', 'testaments', 'congresswoman', 'circuits', 'bumpers', 'braver', 'Boreas', 'hauled', 'Howe', 'seethed', 'cult', 'numismatic', 'vitality', 'differences', 'collapsed', 'Sandburg', 'inches', 'head', 'rhythmic', 'opponent', 'blanketer', 'attorneys', 'hen', 'spies', 'indispensably', 'clinical', 'redirection', 'submit', 'catalysts', 'councilwoman', 'kills', 'topologies', 'noxious', 'exactions', 'dashers', 'balanced', 'slider', 'cancerous', 'bathtubs', 'legged', 'respectably', 'crochets', 'absenteeism', 'arcsine', 'facility', 'cleaners', 'bobwhite', 'Hawkins', 'stockade', 'provisional', 'tenants', 'forearms', 'Knowlton', 'commit', 'scornful', 'pediatrician', 'greets', 'clenches', 'trowels', 'accepts', 'Carboloy', 'Glenn', 'Leigh', 'enroll', 'Madison', 'Macon', 'oiling', 'entertainingly', 'super', 'propositional', 'pliers', 'beneficiary', 'hospitable', 'emigration', 'sift', 'sensor', 'reserved', 'colonization', 'shrilled', 'momentously', 'stevedore', 'Shanghaiing', 'schoolmasters', 'shaken', 'biology', 'inclination', 'immoderate', 'stem', 'allegory', 'economical', 'daytime', 'Newell', 'Moscow', 'archeology', 'ported', 'scandals', 'Blackfoot', 'leery', 'kilobit', 'empire', 'obliviousness', 'productions', 'sacrificed', 'ideals', 'enrolling', 'certainties', 'Capsicum', 'Brookdale', 'Markism', 'unkind', 'dyers', 'legislates', 'grotesquely', 'megawords', 'arbitrary', 'laughing', 'wildcats', 'thrower', 'sex', 'devils', 'Wehr', 'ablates', 'consume', 'gossips', 'doorways', 'Shari', 'advanced', 'enumerable', 'existentially', 'stunt', 'auctioneers', 'scheduler', 'blanching', 'petulance', 'perceptibly', 'vapors', 'progressed', 'rains', 'intercom', 'emergency', 'increased', 'fluctuating', 'Krishna', 'silken', 'reformed', 'transformation', 'easter', 'fares', 'comprehensible', 'trespasses', 'hallmark', 'tormenter', 'breastworks', 'brassiere', 'bladders', 'civet', 'death', 'transformer', 'tolerably', 'bugle', 'clergy', 'mantels', 'satin', 'Boswellizes', 'Bloomington', 'notifier', 'Filippo', 'circling', 'unassigned', 'dumbness', 'sentries', 'representativeness', 'souped', 'Klux', 'Kingstown', 'gerund', 'Russell', 'splices', 'bellow', 'bandies', 'beefers', 'cameramen', 'appalled', 'Ionian', 'butterball', 'Portland', 'pleaded', 'admiringly', 'pricks', 'hearty', 'corer', 'deliverable', 'accountably', 'mentors', 'accorded', 'acknowledgement', 'Lawrenceville', 'morphology', 'eucalyptus', 'Rena', 'enchanting', 'tighter', 'scholars', 'graduations', 'edges', 'Latinization', 'proficiency', 'monolithic', 'parenthesizing', 'defy', 'shames', 'enjoyment', 'Purdue', 'disagrees', 'barefoot', 'maims', 'flabbergast', 'dishonorable', 'interpolation', 'fanatics', 'dickens', 'abysses', 'adverse', 'components', 'bowl', 'belong', 'Pipestone', 'trainees', 'paw', 'pigtail', 'feed', 'whore', 'conditioner', 'Volstead', 'voices', 'strain', 'inhabits', 'Edwin', 'discourses', 'deigns', 'cruiser', 'biconvex', 'biking', 'depreciation', 'Harrison', 'Persian', 'stunning', 'agar', 'rope', 'wagoner', 'elections', 'reticulately', 'Cruz', 'pulpits', 'wilt', 'peels', 'plants', 'administerings', 'deepen', 'rubs', 'hence', 'dissension', 'implored', 'bereavement', 'abyss', 'Pennsylvania', 'benevolent', 'corresponding', 'Poseidon', 'inactive', 'butchers', 'Mach', 'woke', 'loading', 'utilizing', 'Hoosier', 'undo', 'Semitization', 'trigger', 'Mouthe', 'mark', 'disgracefully', 'copier', 'futility', 'gondola', 'algebraic', 'lecturers', 'sponged', 'instigators', 'looted', 'ether', 'trust', 'feeblest', 'sequencer', 'disjointness', 'congresses', 'Vicksburg', 'incompatibilities', 'commend', 'Luxembourg', 'reticulation', 'instructively', 'reconstructs', 'bricks', 'attache', 'Englishman', 'provocation', 'roughen', 'cynic', 'plugged', 'scrawls', 'antipode', 'injected', 'Daedalus', 'Burnsides', 'asker', 'confronter', 'merriment', 'disdain', 'thicket', 'stinker', 'great', 'tiers', 'oust', 'antipodes', 'Macintosh', 'tented', 'packages', 'Mediterraneanize', 'hurts', 'orthodontist', 'seeder', 'readying', 'babying', 'Florida', 'Sri', 'buckets', 'complementary', 'cartographer', 'chateaus', 'shaves', 'thinkable', 'Tehran', 'Gordian', 'Angles', 'arguable', 'bureau', 'smallest', 'fans', 'navigated', 'dipole', 'bootleg', 'distinctive', 'minimization', 'absorbed', 'surmised', 'Malawi', 'absorbent', 'close', 'conciseness', 'hopefully', 'declares', 'descent', 'trick', 'portend', 'unable', 'mildly', 'Morse', 'reference', 'scours', 'Caribbean', 'battlers', 'astringency', 'likelier', 'Byronizes', 'econometric', 'grad', 'steak', 'Austrian', 'ban', 'voting', 'Darlington', 'bison', 'Cetus', 'proclaim', 'Gilbertson', 'evictions', 'submittal', 'bearings', 'Gothicizer', 'settings', 'McMahon', 'densities', 'determinants', 'period', 'DeKastere', 'swindle', 'promptness', 'enablers', 'wordy', 'during', 'tables', 'responder', 'baffle', 'phosgene', 'muttering', 'limiters', 'custodian', 'prevented', 'Stouffer', 'waltz', 'Videotex', 'brainstorms', 'alcoholism', 'jab', 'shouldering', 'screening', 'explicitly', 'earner', 'commandment', 'French', 'scrutinizing', 'Gemma', 'capacitive', 'sheriff', 'herbivore', 'Betsey', 'Formosa', 'scorcher', 'font', 'damming', 'soldiers', 'flack', 'Marks', 'unlinking', 'serenely', 'rotating', 'converge', 'celebrities', 'unassailable', 'bawling', 'wording', 'silencing', 'scotch', 'coincided', 'masochists', 'graphs', 'pernicious', 'disease', 'depreciates', 'later', 'torus', 'interject', 'mutated', 'causer', 'messy', 'Bechtel', 'redundantly', 'profoundest', 'autopsy', 'philosophic', 'iterate', 'Poisson', 'horridly', 'silversmith', 'millennium', 'plunder', 'salmon', 'missioner', 'advances', 'provers', 'earthliness', 'manor', 'resurrectors', 'Dahl', 'canto', 'gangrene', 'gabler', 'ashore', 'frictionless', 'expansionism', 'emphasis', 'preservations', 'Duane', 'descend', 'isolated', 'firmware', 'dynamites', 'scrawled', 'cavemen', 'ponder', 'prosperity', 'squaw', 'vulnerable', 'opthalmic', 'Simms', 'unite', 'totallers', 'Waring', 'enforced', 'bridge', 'collecting', 'sublime', 'Moore', 'gobble', 'criticizes', 'daydreams', 'sedate', 'apples', 'Concordia', 'subsequence', 'distill', 'Allan', 'seizure', 'Isadore', 'Lancashire', 'spacings', 'corresponded', 'hobble', 'Boonton', 'genuineness', 'artifact', 'gratuities', 'interviewee', 'Vladimir', 'mailable', 'Bini', 'Kowalewski', 'interprets', 'bereave', 'evacuated', 'friend', 'tourists', 'crunched', 'soothsayer', 'fleetly', 'Romanizations', 'Medicaid', 'persevering', 'flimsy', 'doomsday', 'trillion', 'carcasses', 'guess', 'seersucker', 'ripping', 'affliction', 'wildest', 'spokes', 'sheaths', 'procreate', 'rusticates', 'Schapiro', 'thereafter', 'mistakenly', 'shelf', 'ruination', 'bushel', 'assuredly', 'corrupting', 'federation', 'portmanteau', 'wading', 'incendiary', 'thing', 'wanderers', 'messages', 'Paso', 'reexamined', 'freeings', 'denture', 'potting', 'disturber', 'laborer', 'comrade', 'intercommunicating', 'Pelham', 'reproach', 'Fenton', 'Alva', 'oasis', 'attending', 'cockpit', 'scout', 'Jude', 'gagging', 'jailed', 'crustaceans', 'dirt', 'exquisitely', 'Internet', 'blocker', 'smock', 'Troutman', 'neighboring', 'surprise', 'midscale', 'impart', 'badgering', 'fountain', 'Essen', 'societies', 'redresses', 'afterwards', 'puckering', 'silks', 'Blakey', 'sequel', 'greet', 'basements', 'Aubrey', 'helmsman', 'album', 'wheelers', 'easternmost', 'flock', 'ambassadors', 'astatine', 'supplant', 'gird', 'clockwork', 'foxes', 'rerouting', 'divisional', 'bends', 'spacer', 'physiologically', 'exquisite', 'concerts', 'unbridled', 'crossing', 'rock', 'leatherneck', 'Fortescue', 'reloading', 'Laramie', 'Tim', 'forlorn', 'revert', 'scarcer', 'spigot', 'equality', 'paranormal', 'aggrieves', 'pegs', 'committeewomen', 'documented', 'interrupt', 'emerald', 'Battelle', 'reconverted', 'anticipated', 'prejudices', 'drowsiness', 'trivialities', 'food', 'blackberries', 'Cyclades', 'tourist', 'branching', 'nugget', 'Asilomar', 'repairmen', 'Cowan', 'receptacles', 'nobler', 'Nebraskan', 'territorial', 'chickadee', 'bedbug', 'darted', 'vigilance', 'Octavia', 'summands', 'policemen', 'twirls', 'style', 'outlawing', 'specifiable', 'pang', 'Orpheus', 'epigram', 'Babel', 'butyrate', 'wishing', 'fiendish', 'accentuate', 'much', 'pulsed', 'adorned', 'arbiters', 'counted', 'Afrikaner', 'parameterizes', 'agenda', 'Americanism', 'referenda', 'derived', 'liquidity', 'trembling', 'lordly', 'Agway', 'Dillon', 'propellers', 'statement', 'stickiest', 'thankfully', 'autograph', 'parallel', 'impulse', 'Hamey', 'stylistic', 'disproved', 'inquirer', 'hoisting', 'residues', 'variant', 'colonials', 'dequeued', 'especial', 'Samoa', 'Polaris', 'dismisses', 'surpasses', 'prognosis', 'urinates', 'leaguers', 'ostriches', 'calculative', 'digested', 'divided', 'reconfigurer', 'Lakewood', 'illegalities', 'redundancy', 'approachability', 'masterly', 'cookery', 'crystallized', 'Dunham', 'exclaims', 'mainline', 'Australianizes', 'nationhood', 'pusher', 'ushers', 'paranoia', 'workstations', 'radiance', 'impedes', 'Minotaur', 'cataloging', 'bites', 'fashioning', 'Alsop', 'servants', 'Onondaga', 'paragraph', 'leadings', 'clients', 'Latrobe', 'Cornwallis', 'excitingly', 'calorimetric', 'savior', 'tandem', 'antibiotics', 'excuse', 'brushy', 'selfish', 'naive', 'becomes', 'towers', 'popularizes', 'engender', 'introducing', 'possession', 'slaughtered', 'marginally', 'Packards', 'parabola', 'utopia', 'automata', 'deterrent', 'chocolates', 'objectives', 'clannish', 'aspirin', 'ferociousness', 'primarily', 'armpit', 'handfuls', 'dangle', 'Manila', 'enlivened', 'decrease', 'phylum', 'hardy', 'objectively', 'baskets', 'chaired', 'Sepoy', 'deputy', 'blizzard', 'shootings', 'breathtaking', 'sticking', 'initials', 'epitomized', 'Forrest', 'cellular', 'amatory', 'radioed', 'horrified', 'Neva', 'simultaneous', 'delimiter', 'expulsion', 'Himmler', 'contradiction', 'Remus', 'Franklinizations', 'luggage', 'moisture', 'Jews', 'comptroller', 'brevity', 'contradictions', 'Ohio', 'active', 'babysit', 'China', 'youngest', 'superstition', 'clawing', 'raccoons', 'chose', 'shoreline', 'helmets', 'Jeffersonian', 'papered', 'kindergarten', 'reply', 'succinct', 'split', 'wriggle', 'suitcases', 'nonce', 'grinders', 'anthem', 'showcase', 'maimed', 'blue', 'obeys', 'unreported', 'perusing', 'recalculate', 'rancher', 'demonic', 'Lilliputianize', 'approximation', 'repents', 'yellowness', 'irritates', 'Ferber', 'flashlights', 'booty', 'Neanderthal', 'someday', 'foregoes', 'lingering', 'cloudiness', 'guy', 'consumer', 'Berkowitz', 'relics', 'interpolating', 'reappearing', 'advisements', 'Nolan', 'turrets', 'skeletal', 'skills', 'mammas', 'Winsett', 'wheelings', 'stiffen', 'monkeys', 'plainness', 'braziers', 'Leary', 'advisee', 'jack', 'verb', 'reinterpret', 'geometrical', 'trolleys', 'arboreal', 'overpowered', 'Cuzco', 'poetical', 'admirations', 'Hobbes', 'phonemes', 'Newsweek', 'agitator', 'finally', 'prophets', 'environment', 'easterners', 'precomputed', 'faults', 'rankly', 'swallowing', 'crawl', 'trolley', 'spreading', 'resourceful', 'go', 'demandingly', 'broader', 'spiders', 'Marsha', 'debris', 'operates', 'Dundee', 'alleles', 'crunchier', 'quizzical', 'hanging', 'Fisk'] wordsd = {} for word in words: wordsd[word] = 1 def collect_options(args, jobs, options): while args: arg = args.pop(0) if arg.startswith('-'): name = arg[1:] if name == 'options': fname = args.pop(0) d = {} execfile(fname, d) collect_options(list(d['options']), jobs, options) elif options.has_key(name): v = args.pop(0) if options[name] != None: raise ValueError( "Duplicate values for %s, %s and %s" % (name, v, options[name]) ) options[name] = v elif name == 'setup': options['setup'] = 1 elif globals().has_key(name.capitalize()+'Job'): job = name kw = {} while args and args[0].find("=") > 0: arg = args.pop(0).split('=') name, v = arg[0], '='.join(arg[1:]) if kw.has_key(name): raise ValueError( "Duplicate parameter %s for job %s" % (name, job) ) kw[name]=v if kw.has_key('frequency'): frequency = kw['frequency'] del kw['frequency'] else: frequency = 1 if kw.has_key('sleep'): sleep = float(kw['sleep']) del kw['sleep'] else: sleep = 0.0001 if kw.has_key('repeat'): repeatp = float(kw['repeat']) del kw['repeat'] else: repeatp = 0 jobs.append((job, kw, frequency, sleep, repeatp)) else: raise ValueError("not an option or job", name) else: raise ValueError("Expected an option", arg) def find_lib_python(): for b in os.getcwd(), os.path.split(sys.argv[0])[0]: for i in range(6): d = ['..']*i + ['lib', 'python'] p = os.path.join(b, *d) if os.path.isdir(p): return p raise ValueError("Couldn't find lib/python") def main(args=None): lib_python = find_lib_python() sys.path.insert(0, lib_python) if args is None: args = sys.argv[1:] if not args: print __doc__ sys.exit(0) print args random.seed(hash(tuple(args))) # always use the same for the given args options = {"mbox": None, "threads": None} jobdefs = [] collect_options(args, jobdefs, options) mboxes = {} if options["mbox"]: mboxes[options["mbox"]] = MBox(options["mbox"]) # Perform a ZConfig-based Zope initialization: zetup(os.path.join(lib_python, '..', '..', 'etc', 'zope.conf')) if options.has_key('setup'): setup(lib_python) else: import Zope2 Zope2.startup() #from ThreadedAsync.LoopCallback import loop #threading.Thread(target=loop, args=(), name='asyncore').start() jobs = JobProducer() for job, kw, frequency, sleep, repeatp in jobdefs: Job = globals()[job.capitalize()+'Job'] if getattr(Job, 'needs_mbox', 0): if not kw.has_key("mbox"): if not options["mbox"]: raise ValueError( "no mailbox (mbox option) file specified") kw['mbox'] = mboxes[options["mbox"]] else: if not mboxes.has_key[kw["mbox"]]: mboxes[kw['mbox']] = MBox[kw['mbox']] kw["mbox"] = mboxes[kw['mbox']] jobs.add(Job(**kw), frequency, sleep, repeatp) if not jobs: print "No jobs to execute" return threads = int(options['threads'] or '0') if threads > 1: threads = [threading.Thread(target=run, args=(jobs, i), name=str(i)) for i in range(threads)] for thread in threads: thread.start() for thread in threads: thread.join() else: run(jobs) def zetup(configfile_name): from Zope.Startup.options import ZopeOptions from Zope.Startup import handlers as h from App import config opts = ZopeOptions() opts.configfile = configfile_name opts.realize(args=[]) h.handleConfig(opts.configroot, opts.confighandlers) config.setConfiguration(opts.configroot) from Zope.Startup import dropPrivileges dropPrivileges(opts.configroot) if __name__ == '__main__': main()