• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / ZODB / 8679957491

30 Mar 2024 11:42AM CUT coverage: 83.744%. Remained the same
8679957491

push

github

dataflake
- vb [ci skip]

2878 of 4051 branches covered (71.04%)

13348 of 15939 relevant lines covered (83.74%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/ZODB/scripts/zodbload.py
1
#!/usr/bin/env python
2
##############################################################################
3
#
4
# Copyright (c) 2003 Zope Foundation and Contributors.
5
# All Rights Reserved.
6
#
7
# This software is subject to the provisions of the Zope Public License,
8
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
9
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
10
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
11
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
12
# FOR A PARTICULAR PURPOSE.
13
#
14
##############################################################################
15
"""Test script for testing ZODB under a heavy zope-like load.
×
16

17
Note that, to be as realistic as possible with ZEO, you should run this
18
script multiple times, to simulate multiple clients.
19

20
Here's how this works.
21

22
The script starts some number of threads.  Each thread, sequentially
23
executes jobs.  There is a job producer that produces jobs.
24

25
Input data are provided by a mail producer that hands out message from
26
a mailbox.
27

28
Execution continues until there is an error, which will normally occur
29
when the mailbox is exhausted.
30

31
Command-line options are used to provide job definitions. Job
32
definitions have perameters of the form name=value.  Jobs have 2
33
standard parameters:
34

35
  frequency=integer
36

37
     The frequency of the job. The default is 1.
38

39
  sleep=float
40

41
     The number os seconds to sleep before performing the job. The
42
     default is 0.
43

44
Usage: loadmail2 [options]
45

46
  Options:
47

48
    -edit [frequency=integer] [sleep=float]
49

50
       Define an edit job. An edit job edits a random already-saved
51
       email message, deleting and inserting a random number of words.
52

53
       After editing the message, the message is (re)cataloged.
54

55
    -insert [number=int] [frequency=integer] [sleep=float]
56

57
       Insert some number of email messages.
58

59
    -index [number=int] [frequency=integer] [sleep=float]
60

61
       Insert and index (catalog) some number of email messages.
62

63
    -search [terms='word1 word2 ...'] [frequency=integer] [sleep=float]
64

65
       Search the catalog. A query is givem with one or more terms as
66
       would be entered into a typical seach box.  If no query is
67
       given, then queries will be randomly selected based on a set of
68
       built-in word list.
69

70
    -setup
71

72
       Set up the database. This will delete any existing Data.fs
73
       file.  (Of course, this may have no effect, if there is a
74
       custom_zodb that defined a different storage.) It also adds a
75
       mail folder and a catalog.
76

77
    -options file
78

79
       Read options from the given file. Th efile should be a python
80
       source file that defines a sequence of options named 'options'.
81

82
    -threads n
83

84
       Specify the number of threads to execute. If not specified (< 2),
85
       then jobs are run in a single (main) thread.
86

87
    -mbox filename
88

89
       Specify the mailbox for getting input data.
90

91
       There is a (lame) syntax for providing options within the
92
       filename. The filename may be followed by up to 3 integers,
93
       min, max, and start:
94

95
         -mbox 'foo.mbox 0 100 10000'
96

97
       The messages from min to max will be read from the mailbox.
98
       They will be assigned message numbers starting with start.
99
       So, in the example above, we read the first hundred messages
100
       and assign thgem message numbers starting with 10001.
101

102
       The maxmum can be given as a negative number, in which case, it
103
       specifies the number of messages to read.
104

105
       The start defaults to the minimum. The following two options:
106

107
         -mbox 'foo.mbox 300 400 300'
108

109
       and
110

111
         -mbox 'foo.mbox 300 -100'
112

113
       are equivalent
114
"""
115

116
import mailbox
×
117
import math
×
118
import os
×
119
import random
×
120
import re
×
121
import sys
×
122
import threading
×
123
import time
×
124

125
import transaction
×
126

127

128
class JobProducer:
×
129

130
    def __init__(self):
×
131
        self.jobs = []
×
132

133
    def add(self, callable, frequency, sleep, repeatp=0):
×
134
        self.jobs.extend([(callable, sleep, repeatp)] * int(frequency))
×
135
        random.shuffle(self.jobs)
×
136

137
    def next(self):
×
138
        factory, sleep, repeatp = random.choice(self.jobs)
×
139
        time.sleep(sleep)
×
140
        callable, args = factory.create()
×
141
        return factory, callable, args, repeatp
×
142

143
    def __nonzero__(self):
×
144
        return not not self.jobs
×
145

146

147
class MBox:
×
148

149
    def __init__(self, filename):
×
150
        if ' ' in filename:
×
151
            filename = filename.split()
×
152
            if len(filename) < 4:
×
153
                filename += [0, 0, -1][-(4-len(filename)):]
×
154
            filename, min, max, start = filename
×
155
            min = int(min)
×
156
            max = int(max)
×
157
            start = int(start)
×
158

159
            if start < 0:
×
160
                start = min
×
161

162
            if max < 0:
×
163
                # negative max is treated as a count
164
                self._max = start - max
×
165
            elif max > 0:
×
166
                self._max = start + max - min
×
167
            else:
168
                self._max = 0
×
169

170
        else:
171
            self._max = 0
×
172
            min = start = 0
×
173

174
        if filename.endswith('.bz2'):
×
175
            f = os.popen("bunzip2 <"+filename, 'r')
×
176
            filename = filename[-4:]
×
177
        else:
178
            f = open(filename)
×
179

180
        self._mbox = mb = mailbox.UnixMailbox(f)
×
181

182
        self.number = start
×
183
        while min:
×
184
            next(mb)
×
185
            min -= 1
×
186

187
        self._lock = threading.Lock()
×
188
        self.__name__ = os.path.splitext(os.path.split(filename)[1])[0]
×
189
        self._max = max
×
190

191
    def next(self):
×
192
        with self.lock:
×
193
            if self._max > 0 and self.number >= self._max:
×
194
                raise IndexError(self.number + 1)
×
195
            message = next(self._mbox)
×
196
            message.body = message.fp.read()
×
197
            message.headers = list(message.headers)
×
198
            self.number += 1
×
199
            message.number = self.number
×
200
            message.mbox = self.__name__
×
201
            return message
×
202

203

204
bins = 9973
×
205
# bins = 11
206

207

208
def mailfolder(app, mboxname, number):
×
209
    mail = getattr(app, mboxname, None)
×
210
    if mail is None:
×
211
        app.manage_addFolder(mboxname)
×
212
        mail = getattr(app, mboxname)
×
213
        from BTrees.Length import Length
×
214
        mail.length = Length()
×
215
        for i in range(bins):
×
216
            mail.manage_addFolder('b'+str(i))
×
217
    bin = hash(str(number)) % bins
×
218
    return getattr(mail, 'b'+str(bin))
×
219

220

221
def VmSize():
×
222

223
    try:
×
224
        with open('/proc/%s/status' % os.getpid()) as f:
×
225
            lines = f.readlines()
×
226
    except:  # noqa: E722 do not use bare 'except'
×
227
        return 0
×
228
    else:
229
        l_ = list(filter(lambda l: l[:7] == 'VmSize:', lines))  # noqa: E741
×
230
        if l_:
×
231
            l_ = l_[0][7:].strip().split()[0]
×
232
            return int(l_)
×
233
    return 0
×
234

235

236
def setup(lib_python):
×
237
    try:
×
238
        os.remove(os.path.join(lib_python, '..', '..', 'var', 'Data.fs'))
×
239
    except:  # noqa: E722 do not use bare 'except'
×
240
        pass
×
241
    import AccessControl.SecurityManagement
×
242
    import Products
×
243
    import Zope2
×
244
    app = Zope2.app()
×
245

246
    Products.ZCatalog.ZCatalog.manage_addZCatalog(app, 'cat', '')
×
247

248
    from Products.ZCTextIndex.Lexicon import CaseNormalizer
×
249
    from Products.ZCTextIndex.Lexicon import Splitter
×
250
    from Products.ZCTextIndex.ZCTextIndex import PLexicon
×
251

252
    app.cat._setObject('lex',
×
253
                       PLexicon('lex', '', Splitter(), CaseNormalizer())
254
                       )
255

256
    class extra:
×
257
        doc_attr = 'PrincipiaSearchSource'
×
258
        lexicon_id = 'lex'
×
259
        index_type = 'Okapi BM25 Rank'
×
260

261
    app.cat.addIndex('PrincipiaSearchSource', 'ZCTextIndex', extra)
×
262

263
    transaction.commit()
×
264

265
    system = AccessControl.SpecialUsers.system
×
266
    AccessControl.SecurityManagement.newSecurityManager(None, system)
×
267

268
    app._p_jar.close()
×
269

270

271
def do(db, f, args):
×
272
    """Do something in a transaction, retrying of necessary
273

274
    Measure the speed of both the compurartion and the commit
275
    """
276
    from ZODB.POSException import ConflictError
×
277
    wcomp = ccomp = wcommit = ccommit = 0.0
×
278
    rconflicts = wconflicts = 0
×
279
    start = time.time()
×
280

281
    while 1:
282
        connection = db.open()
×
283
        try:
×
284
            transaction.begin()
×
285
            t = time.time()
×
286
            c = time.clock()
×
287
            try:
×
288
                try:
×
289
                    r = f(connection, *args)
×
290
                except ConflictError:
×
291
                    rconflicts += 1
×
292
                    transaction.abort()
×
293
                    continue
×
294
            finally:
295
                wcomp += time.time() - t
×
296
                ccomp += time.clock() - c
×
297

298
            t = time.time()
×
299
            c = time.clock()
×
300
            try:
×
301
                try:
×
302
                    transaction.commit()
×
303
                    break
×
304
                except ConflictError:
×
305
                    wconflicts += 1
×
306
                    transaction.abort()
×
307
                    continue
×
308
            finally:
309
                wcommit += time.time() - t
×
310
                ccommit += time.clock() - c
×
311
        finally:
312
            connection.close()
×
313

314
    return start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r
×
315

316

317
def run1(tid, db, factory, job, args):
×
318
    (start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r
×
319
     ) = do(db, job, args)
320
    start = "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % time.localtime(start)[:6]
×
321
    print("{} {} {:8.3g} {:8.3g} {} {}\t{:8.3g} {:8.3g} {} {!r}".format(
×
322
        start, tid, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit,
323
        factory.__name__, r))
324

325

326
def run(jobs, tid=b''):
×
327
    import Zope2
×
328
    while 1:
329
        factory, job, args, repeatp = next(jobs)
×
330
        run1(tid, Zope2.DB, factory, job, args)
×
331
        if repeatp:
×
332
            while 1:
333
                i = random.randint(0, 100)
×
334
                if i > repeatp:
×
335
                    break
×
336
                run1(tid, Zope2.DB, factory, job, args)
×
337

338

339
def index(connection, messages, catalog, max):
×
340
    app = connection.root()['Application']
×
341
    for message in messages:
×
342
        mail = mailfolder(app, message.mbox, message.number)
×
343

344
        if max:
×
345
            # Cheat and use folder implementation secrets
346
            # to avoid having to read the old data
347
            _objects = mail._objects
×
348
            if len(_objects) >= max:
×
349
                for d in _objects[:len(_objects)-max+1]:
×
350
                    del mail.__dict__[d['id']]
×
351
                mail._objects = _objects[len(_objects)-max+1:]
×
352

353
        docid = 'm'+str(message.number)
×
354
        mail.manage_addDTMLDocument(docid, file=message.body)
×
355

356
        # increment counted
357
        getattr(app, message.mbox).length.change(1)
×
358

359
        doc = mail[docid]
×
360
        for h in message.headers:
×
361
            h = h.strip()
×
362
            l_ = h.find(':')
×
363
            if l_ <= 0:
×
364
                continue
×
365
            name = h[:l_].lower()
×
366
            if name == 'subject':
×
367
                name = 'title'
×
368
            v = h[l_ + 1:].strip()
×
369
            type = 'string'
×
370

371
            if name == 'title':
×
372
                doc.manage_changeProperties(title=h)
×
373
            else:
374
                try:
×
375
                    doc.manage_addProperty(name, v, type)
×
376
                except:  # noqa: E722 do not use bare 'except'
×
377
                    pass
×
378
        if catalog:
×
379
            app.cat.catalog_object(doc)
×
380

381
    return message.number
×
382

383

384
class IndexJob:
×
385
    needs_mbox = 1
×
386
    catalog = 1
×
387
    prefix = 'index'
×
388

389
    def __init__(self, mbox, number=1, max=0):
×
390
        self.__name__ = "{}{}_{}".format(self.prefix, number, mbox.__name__)
×
391
        self.mbox, self.number, self.max = mbox, int(number), int(max)
×
392

393
    def create(self):
×
394
        messages = [next(self.mbox) for i in range(self.number)]
×
395
        return index, (messages, self.catalog, self.max)
×
396

397

398
class InsertJob(IndexJob):
×
399
    catalog = 0
×
400
    prefix = 'insert'
×
401

402

403
wordre = re.compile(r'(\w{3,20})')
×
404
stop = 'and', 'not'
×
405

406

407
def edit(connection, mbox, catalog=1):
×
408
    app = connection.root()['Application']
×
409
    mail = getattr(app, mbox.__name__, None)
×
410
    if mail is None:
×
411
        time.sleep(1)
×
412
        return "No mailbox %s" % mbox.__name__
×
413

414
    nmessages = mail.length()
×
415
    if nmessages < 2:
×
416
        time.sleep(1)
×
417
        return "No messages to edit in %s" % mbox.__name__
×
418

419
    # find a message to edit:
420
    while 1:
421
        number = random.randint(1, nmessages-1)
×
422
        did = 'm' + str(number)
×
423

424
        mail = mailfolder(app, mbox.__name__, number)
×
425
        doc = getattr(mail, did, None)
×
426
        if doc is not None:
×
427
            break
×
428

429
    text = doc.raw.split()
×
430
    norig = len(text)
×
431
    if norig > 10:
×
432
        ndel = int(math.exp(random.randint(0, int(math.log(norig)))))
×
433
        nins = int(math.exp(random.randint(0, int(math.log(norig)))))
×
434
    else:
435
        ndel = 0
×
436
        nins = 10
×
437

438
    for j in range(ndel):
×
439
        j = random.randint(0, len(text)-1)
×
440
        word = text[j]
×
441
        m = wordre.search(word)
×
442
        if m:
×
443
            word = m.group(1).lower()
×
444
            if (word not in wordsd) and word not in stop:
×
445
                words.append(word)
×
446
                wordsd[word] = 1
×
447
        del text[j]
×
448

449
    for j in range(nins):
×
450
        word = random.choice(words)
×
451
        text.append(word)
×
452

453
    doc.raw = ' '.join(text)
×
454

455
    if catalog:
×
456
        app.cat.catalog_object(doc)
×
457

458
    return norig, ndel, nins
×
459

460

461
class EditJob:
×
462
    needs_mbox = 1
×
463
    prefix = 'edit'
×
464
    catalog = 1
×
465

466
    def __init__(self, mbox):
×
467
        self.__name__ = "{}_{}".format(self.prefix, mbox.__name__)
×
468
        self.mbox = mbox
×
469

470
    def create(self):
×
471
        return edit, (self.mbox, self.catalog)
×
472

473

474
class ModifyJob(EditJob):
×
475
    prefix = 'modify'
×
476
    catalog = 0
×
477

478

479
def search(connection, terms, number):
×
480
    app = connection.root()['Application']
×
481
    cat = app.cat
×
482
    n = 0
×
483

484
    for i in number:
×
485
        term = random.choice(terms)
×
486

487
        results = cat(PrincipiaSearchSource=term)
×
488
        n += len(results)
×
489
        for result in results:
×
490
            obj = result.getObject()
×
491
            # Apparently, there is a bug in Zope that leads obj to be None
492
            # on occasion.
493
            if obj is not None:
×
494
                obj.getId()
×
495

496
    return n
×
497

498

499
class SearchJob:
×
500

501
    def __init__(self, terms='', number=10):
×
502

503
        if terms:
×
504
            terms = terms.split()
×
505
            self.__name__ = "search_" + '_'.join(terms)
×
506
            self.terms = terms
×
507
        else:
508
            self.__name__ = 'search'
×
509
            self.terms = words
×
510

511
        number = min(int(number), len(self.terms))
×
512
        self.number = list(range(number))
×
513

514
    def create(self):
×
515
        return search, (self.terms, self.number)
×
516

517

518
words = ['banishment', 'indirectly', 'imprecise', 'peeks',
×
519
         'opportunely', 'bribe', 'sufficiently', 'Occidentalized', 'elapsing',
520
         'fermenting', 'listen', 'orphanage', 'younger', 'draperies', 'Ida',
521
         'cuttlefish', 'mastermind', 'Michaels', 'populations', 'lent',
522
         'cater', 'attentional', 'hastiness', 'dragnet', 'mangling',
523
         'scabbards', 'princely', 'star', 'repeat', 'deviation', 'agers',
524
         'fix', 'digital', 'ambitious', 'transit', 'jeeps', 'lighted',
525
         'Prussianizations', 'Kickapoo', 'virtual', 'Andrew', 'generally',
526
         'boatsman', 'amounts', 'promulgation', 'Malay', 'savaging',
527
         'courtesan', 'nursed', 'hungered', 'shiningly', 'ship', 'presides',
528
         'Parke', 'moderns', 'Jonas', 'unenlightening', 'dearth', 'deer',
529
         'domesticates', 'recognize', 'gong', 'penetrating', 'dependents',
530
         'unusually', 'complications', 'Dennis', 'imbalances', 'nightgown',
531
         'attached', 'testaments', 'congresswoman', 'circuits', 'bumpers',
532
         'braver', 'Boreas', 'hauled', 'Howe', 'seethed', 'cult', 'numismatic',
533
         'vitality', 'differences', 'collapsed', 'Sandburg', 'inches', 'head',
534
         'rhythmic', 'opponent', 'blanketer', 'attorneys', 'hen', 'spies',
535
         'indispensably', 'clinical', 'redirection', 'submit', 'catalysts',
536
         'councilwoman', 'kills', 'topologies', 'noxious', 'exactions',
537
         'dashers', 'balanced', 'slider', 'cancerous', 'bathtubs', 'legged',
538
         'respectably', 'crochets', 'absenteeism', 'arcsine', 'facility',
539
         'cleaners', 'bobwhite', 'Hawkins', 'stockade', 'provisional',
540
         'tenants', 'forearms', 'Knowlton', 'commit', 'scornful',
541
         'pediatrician', 'greets', 'clenches', 'trowels', 'accepts',
542
         'Carboloy', 'Glenn', 'Leigh', 'enroll', 'Madison', 'Macon', 'oiling',
543
         'entertainingly', 'super', 'propositional', 'pliers', 'beneficiary',
544
         'hospitable', 'emigration', 'sift', 'sensor', 'reserved',
545
         'colonization', 'shrilled', 'momentously', 'stevedore', 'Shanghaiing',
546
         'schoolmasters', 'shaken', 'biology', 'inclination', 'immoderate',
547
         'stem', 'allegory', 'economical', 'daytime', 'Newell', 'Moscow',
548
         'archeology', 'ported', 'scandals', 'Blackfoot', 'leery', 'kilobit',
549
         'empire', 'obliviousness', 'productions', 'sacrificed', 'ideals',
550
         'enrolling', 'certainties', 'Capsicum', 'Brookdale', 'Markism',
551
         'unkind', 'dyers', 'legislates', 'grotesquely', 'megawords',
552
         'arbitrary', 'laughing', 'wildcats', 'thrower', 'sex', 'devils',
553
         'Wehr', 'ablates', 'consume', 'gossips', 'doorways', 'Shari',
554
         'advanced', 'enumerable', 'existentially', 'stunt', 'auctioneers',
555
         'scheduler', 'blanching', 'petulance', 'perceptibly', 'vapors',
556
         'progressed', 'rains', 'intercom', 'emergency', 'increased',
557
         'fluctuating', 'Krishna', 'silken', 'reformed', 'transformation',
558
         'easter', 'fares', 'comprehensible', 'trespasses', 'hallmark',
559
         'tormenter', 'breastworks', 'brassiere', 'bladders', 'civet', 'death',
560
         'transformer', 'tolerably', 'bugle', 'clergy', 'mantels', 'satin',
561
         'Boswellizes', 'Bloomington', 'notifier', 'Filippo', 'circling',
562
         'unassigned', 'dumbness', 'sentries', 'representativeness', 'souped',
563
         'Klux', 'Kingstown', 'gerund', 'Russell', 'splices', 'bellow',
564
         'bandies', 'beefers', 'cameramen', 'appalled', 'Ionian', 'butterball',
565
         'Portland', 'pleaded', 'admiringly', 'pricks', 'hearty', 'corer',
566
         'deliverable', 'accountably', 'mentors', 'accorded',
567
         'acknowledgement', 'Lawrenceville', 'morphology', 'eucalyptus',
568
         'Rena', 'enchanting', 'tighter', 'scholars', 'graduations', 'edges',
569
         'Latinization', 'proficiency', 'monolithic', 'parenthesizing', 'defy',
570
         'shames', 'enjoyment', 'Purdue', 'disagrees', 'barefoot', 'maims',
571
         'flabbergast', 'dishonorable', 'interpolation', 'fanatics', 'dickens',
572
         'abysses', 'adverse', 'components', 'bowl', 'belong', 'Pipestone',
573
         'trainees', 'paw', 'pigtail', 'feed', 'whore', 'conditioner',
574
         'Volstead', 'voices', 'strain', 'inhabits', 'Edwin', 'discourses',
575
         'deigns', 'cruiser', 'biconvex', 'biking', 'depreciation', 'Harrison',
576
         'Persian', 'stunning', 'agar', 'rope', 'wagoner', 'elections',
577
         'reticulately', 'Cruz', 'pulpits', 'wilt', 'peels', 'plants',
578
         'administerings', 'deepen', 'rubs', 'hence', 'dissension', 'implored',
579
         'bereavement', 'abyss', 'Pennsylvania', 'benevolent', 'corresponding',
580
         'Poseidon', 'inactive', 'butchers', 'Mach', 'woke', 'loading',
581
         'utilizing', 'Hoosier', 'undo', 'Semitization', 'trigger', 'Mouthe',
582
         'mark', 'disgracefully', 'copier', 'futility', 'gondola', 'algebraic',
583
         'lecturers', 'sponged', 'instigators', 'looted', 'ether', 'trust',
584
         'feeblest', 'sequencer', 'disjointness', 'congresses', 'Vicksburg',
585
         'incompatibilities', 'commend', 'Luxembourg', 'reticulation',
586
         'instructively', 'reconstructs', 'bricks', 'attache', 'Englishman',
587
         'provocation', 'roughen', 'cynic', 'plugged', 'scrawls', 'antipode',
588
         'injected', 'Daedalus', 'Burnsides', 'asker', 'confronter',
589
         'merriment', 'disdain', 'thicket', 'stinker', 'great', 'tiers',
590
         'oust', 'antipodes', 'Macintosh', 'tented', 'packages',
591
         'Mediterraneanize', 'hurts', 'orthodontist', 'seeder', 'readying',
592
         'babying', 'Florida', 'Sri', 'buckets', 'complementary',
593
         'cartographer', 'chateaus', 'shaves', 'thinkable', 'Tehran',
594
         'Gordian', 'Angles', 'arguable', 'bureau', 'smallest', 'fans',
595
         'navigated', 'dipole', 'bootleg', 'distinctive', 'minimization',
596
         'absorbed', 'surmised', 'Malawi', 'absorbent', 'close', 'conciseness',
597
         'hopefully', 'declares', 'descent', 'trick', 'portend', 'unable',
598
         'mildly', 'Morse', 'reference', 'scours', 'Caribbean', 'battlers',
599
         'astringency', 'likelier', 'Byronizes', 'econometric', 'grad',
600
         'steak', 'Austrian', 'ban', 'voting', 'Darlington', 'bison', 'Cetus',
601
         'proclaim', 'Gilbertson', 'evictions', 'submittal', 'bearings',
602
         'Gothicizer', 'settings', 'McMahon', 'densities', 'determinants',
603
         'period', 'DeKastere', 'swindle', 'promptness', 'enablers', 'wordy',
604
         'during', 'tables', 'responder', 'baffle', 'phosgene', 'muttering',
605
         'limiters', 'custodian', 'prevented', 'Stouffer', 'waltz', 'Videotex',
606
         'brainstorms', 'alcoholism', 'jab', 'shouldering', 'screening',
607
         'explicitly', 'earner', 'commandment', 'French', 'scrutinizing',
608
         'Gemma', 'capacitive', 'sheriff', 'herbivore', 'Betsey', 'Formosa',
609
         'scorcher', 'font', 'damming', 'soldiers', 'flack', 'Marks',
610
         'unlinking', 'serenely', 'rotating', 'converge', 'celebrities',
611
         'unassailable', 'bawling', 'wording', 'silencing', 'scotch',
612
         'coincided', 'masochists', 'graphs', 'pernicious', 'disease',
613
         'depreciates', 'later', 'torus', 'interject', 'mutated', 'causer',
614
         'messy', 'Bechtel', 'redundantly', 'profoundest', 'autopsy',
615
         'philosophic', 'iterate', 'Poisson', 'horridly', 'silversmith',
616
         'millennium', 'plunder', 'salmon', 'missioner', 'advances', 'provers',
617
         'earthliness', 'manor', 'resurrectors', 'Dahl', 'canto', 'gangrene',
618
         'gabler', 'ashore', 'frictionless', 'expansionism', 'emphasis',
619
         'preservations', 'Duane', 'descend', 'isolated', 'firmware',
620
         'dynamites', 'scrawled', 'cavemen', 'ponder', 'prosperity', 'squaw',
621
         'vulnerable', 'opthalmic', 'Simms', 'unite', 'totallers', 'Waring',
622
         'enforced', 'bridge', 'collecting', 'sublime', 'Moore', 'gobble',
623
         'criticizes', 'daydreams', 'sedate', 'apples', 'Concordia',
624
         'subsequence', 'distill', 'Allan', 'seizure', 'Isadore', 'Lancashire',
625
         'spacings', 'corresponded', 'hobble', 'Boonton', 'genuineness',
626
         'artifact', 'gratuities', 'interviewee', 'Vladimir', 'mailable',
627
         'Bini', 'Kowalewski', 'interprets', 'bereave', 'evacuated', 'friend',
628
         'tourists', 'crunched', 'soothsayer', 'fleetly', 'Romanizations',
629
         'Medicaid', 'persevering', 'flimsy', 'doomsday', 'trillion',
630
         'carcasses', 'guess', 'seersucker', 'ripping', 'affliction',
631
         'wildest', 'spokes', 'sheaths', 'procreate', 'rusticates', 'Schapiro',
632
         'thereafter', 'mistakenly', 'shelf', 'ruination', 'bushel',
633
         'assuredly', 'corrupting', 'federation', 'portmanteau', 'wading',
634
         'incendiary', 'thing', 'wanderers', 'messages', 'Paso', 'reexamined',
635
         'freeings', 'denture', 'potting', 'disturber', 'laborer', 'comrade',
636
         'intercommunicating', 'Pelham', 'reproach', 'Fenton', 'Alva', 'oasis',
637
         'attending', 'cockpit', 'scout', 'Jude', 'gagging', 'jailed',
638
         'crustaceans', 'dirt', 'exquisitely', 'Internet', 'blocker', 'smock',
639
         'Troutman', 'neighboring', 'surprise', 'midscale', 'impart',
640
         'badgering', 'fountain', 'Essen', 'societies', 'redresses',
641
         'afterwards', 'puckering', 'silks', 'Blakey', 'sequel', 'greet',
642
         'basements', 'Aubrey', 'helmsman', 'album', 'wheelers', 'easternmost',
643
         'flock', 'ambassadors', 'astatine', 'supplant', 'gird', 'clockwork',
644
         'foxes', 'rerouting', 'divisional', 'bends', 'spacer',
645
         'physiologically', 'exquisite', 'concerts', 'unbridled', 'crossing',
646
         'rock', 'leatherneck', 'Fortescue', 'reloading', 'Laramie', 'Tim',
647
         'forlorn', 'revert', 'scarcer', 'spigot', 'equality', 'paranormal',
648
         'aggrieves', 'pegs', 'committeewomen', 'documented', 'interrupt',
649
         'emerald', 'Battelle', 'reconverted', 'anticipated', 'prejudices',
650
         'drowsiness', 'trivialities', 'food', 'blackberries', 'Cyclades',
651
         'tourist', 'branching', 'nugget', 'Asilomar', 'repairmen', 'Cowan',
652
         'receptacles', 'nobler', 'Nebraskan', 'territorial', 'chickadee',
653
         'bedbug', 'darted', 'vigilance', 'Octavia', 'summands', 'policemen',
654
         'twirls', 'style', 'outlawing', 'specifiable', 'pang', 'Orpheus',
655
         'epigram', 'Babel', 'butyrate', 'wishing', 'fiendish', 'accentuate',
656
         'much', 'pulsed', 'adorned', 'arbiters', 'counted', 'Afrikaner',
657
         'parameterizes', 'agenda', 'Americanism', 'referenda', 'derived',
658
         'liquidity', 'trembling', 'lordly', 'Agway', 'Dillon', 'propellers',
659
         'statement', 'stickiest', 'thankfully', 'autograph', 'parallel',
660
         'impulse', 'Hamey', 'stylistic', 'disproved', 'inquirer', 'hoisting',
661
         'residues', 'variant', 'colonials', 'dequeued', 'especial', 'Samoa',
662
         'Polaris', 'dismisses', 'surpasses', 'prognosis', 'urinates',
663
         'leaguers', 'ostriches', 'calculative', 'digested', 'divided',
664
         'reconfigurer', 'Lakewood', 'illegalities', 'redundancy',
665
         'approachability', 'masterly', 'cookery', 'crystallized', 'Dunham',
666
         'exclaims', 'mainline', 'Australianizes', 'nationhood', 'pusher',
667
         'ushers', 'paranoia', 'workstations', 'radiance', 'impedes',
668
         'Minotaur', 'cataloging', 'bites', 'fashioning', 'Alsop', 'servants',
669
         'Onondaga', 'paragraph', 'leadings', 'clients', 'Latrobe',
670
         'Cornwallis', 'excitingly', 'calorimetric', 'savior', 'tandem',
671
         'antibiotics', 'excuse', 'brushy', 'selfish', 'naive', 'becomes',
672
         'towers', 'popularizes', 'engender', 'introducing', 'possession',
673
         'slaughtered', 'marginally', 'Packards', 'parabola', 'utopia',
674
         'automata', 'deterrent', 'chocolates', 'objectives', 'clannish',
675
         'aspirin', 'ferociousness', 'primarily', 'armpit', 'handfuls',
676
         'dangle', 'Manila', 'enlivened', 'decrease', 'phylum', 'hardy',
677
         'objectively', 'baskets', 'chaired', 'Sepoy', 'deputy', 'blizzard',
678
         'shootings', 'breathtaking', 'sticking', 'initials', 'epitomized',
679
         'Forrest', 'cellular', 'amatory', 'radioed', 'horrified', 'Neva',
680
         'simultaneous', 'delimiter', 'expulsion', 'Himmler', 'contradiction',
681
         'Remus', 'Franklinizations', 'luggage', 'moisture', 'Jews',
682
         'comptroller', 'brevity', 'contradictions', 'Ohio', 'active',
683
         'babysit', 'China', 'youngest', 'superstition', 'clawing', 'raccoons',
684
         'chose', 'shoreline', 'helmets', 'Jeffersonian', 'papered',
685
         'kindergarten', 'reply', 'succinct', 'split', 'wriggle', 'suitcases',
686
         'nonce', 'grinders', 'anthem', 'showcase', 'maimed', 'blue', 'obeys',
687
         'unreported', 'perusing', 'recalculate', 'rancher', 'demonic',
688
         'Lilliputianize', 'approximation', 'repents', 'yellowness',
689
         'irritates', 'Ferber', 'flashlights', 'booty', 'Neanderthal',
690
         'someday', 'foregoes', 'lingering', 'cloudiness', 'guy', 'consumer',
691
         'Berkowitz', 'relics', 'interpolating', 'reappearing', 'advisements',
692
         'Nolan', 'turrets', 'skeletal', 'skills', 'mammas', 'Winsett',
693
         'wheelings', 'stiffen', 'monkeys', 'plainness', 'braziers', 'Leary',
694
         'advisee', 'jack', 'verb', 'reinterpret', 'geometrical', 'trolleys',
695
         'arboreal', 'overpowered', 'Cuzco', 'poetical', 'admirations',
696
         'Hobbes', 'phonemes', 'Newsweek', 'agitator', 'finally', 'prophets',
697
         'environment', 'easterners', 'precomputed', 'faults', 'rankly',
698
         'swallowing', 'crawl', 'trolley', 'spreading', 'resourceful', 'go',
699
         'demandingly', 'broader', 'spiders', 'Marsha', 'debris', 'operates',
700
         'Dundee', 'alleles', 'crunchier', 'quizzical', 'hanging', 'Fisk']
701

702
wordsd = {}
×
703
for word in words:
×
704
    wordsd[word] = 1
×
705

706

707
def collect_options(args, jobs, options):
×
708

709
    while args:
×
710
        arg = args.pop(0)
×
711
        if arg.startswith('-'):
×
712
            name = arg[1:]
×
713
            if name == 'options':
×
714
                fname = args.pop(0)
×
715
                d = {}
×
716
                with open(fname) as fp:
×
717
                    exec(compile(fp.read(), fname, 'exec'), d)
×
718
                collect_options(list(d['options']), jobs, options)
×
719
            elif name in options:
×
720
                v = args.pop(0)
×
721
                if options[name] is not None:
×
722
                    raise ValueError(
×
723
                        "Duplicate values for %s, %s and %s"
724
                        % (name, v, options[name])
725
                    )
726
                options[name] = v
×
727
            elif name == 'setup':
×
728
                options['setup'] = 1
×
729
            elif name.capitalize()+'Job' in globals():
×
730
                job = name
×
731
                kw = {}
×
732
                while args and args[0].find("=") > 0:
×
733
                    arg = args.pop(0).split('=')
×
734
                    name, v = arg[0], '='.join(arg[1:])
×
735
                    if name in kw:
×
736
                        raise ValueError(
×
737
                            "Duplicate parameter %s for job %s"
738
                            % (name, job)
739
                        )
740
                    kw[name] = v
×
741
                if 'frequency' in kw:
×
742
                    frequency = kw['frequency']
×
743
                    del kw['frequency']
×
744
                else:
745
                    frequency = 1
×
746

747
                if 'sleep' in kw:
×
748
                    sleep = float(kw['sleep'])
×
749
                    del kw['sleep']
×
750
                else:
751
                    sleep = 0.0001
×
752

753
                if 'repeat' in kw:
×
754
                    repeatp = float(kw['repeat'])
×
755
                    del kw['repeat']
×
756
                else:
757
                    repeatp = 0
×
758

759
                jobs.append((job, kw, frequency, sleep, repeatp))
×
760
            else:
761
                raise ValueError("not an option or job", name)
×
762
        else:
763
            raise ValueError("Expected an option", arg)
×
764

765

766
def find_lib_python():
×
767
    for b in os.getcwd(), os.path.split(sys.argv[0])[0]:
×
768
        for i in range(6):
×
769
            d = ['..']*i + ['lib', 'python']
×
770
            p = os.path.join(b, *d)
×
771
            if os.path.isdir(p):
×
772
                return p
×
773
    raise ValueError("Couldn't find lib/python")
×
774

775

776
def main(args=None):
×
777
    lib_python = find_lib_python()
×
778
    sys.path.insert(0, lib_python)
×
779

780
    if args is None:
×
781
        args = sys.argv[1:]
×
782
    if not args:
×
783
        print(__doc__)
×
784
        sys.exit(0)
×
785

786
    print(args)
×
787
    random.seed(hash(tuple(args)))  # always use the same for the given args
×
788

789
    options = {"mbox": None, "threads": None}
×
790
    jobdefs = []
×
791
    collect_options(args, jobdefs, options)
×
792

793
    mboxes = {}
×
794
    if options["mbox"]:
×
795
        mboxes[options["mbox"]] = MBox(options["mbox"])
×
796

797
    # Perform a ZConfig-based Zope initialization:
798
    zetup(os.path.join(lib_python, '..', '..', 'etc', 'zope.conf'))
×
799

800
    if 'setup' in options:
×
801
        setup(lib_python)
×
802
    else:
803
        import Zope2
×
804
        Zope2.startup()
×
805

806
    jobs = JobProducer()
×
807
    for job, kw, frequency, sleep, repeatp in jobdefs:
×
808
        Job = globals()[job.capitalize()+'Job']
×
809
        if getattr(Job, 'needs_mbox', 0):
×
810
            if "mbox" not in kw:
×
811
                if not options["mbox"]:
×
812
                    raise ValueError(
×
813
                        "no mailbox (mbox option) file  specified")
814
                kw['mbox'] = mboxes[options["mbox"]]
×
815
            else:
816
                if not mboxes.has_key[kw["mbox"]]:
×
817
                    mboxes[kw['mbox']] = MBox[kw['mbox']]
×
818
                kw["mbox"] = mboxes[kw['mbox']]
×
819
        jobs.add(Job(**kw), frequency, sleep, repeatp)
×
820

821
    if not jobs:
×
822
        print("No jobs to execute")
×
823
        return
×
824

825
    threads = int(options['threads'] or '0')
×
826
    if threads > 1:
×
827
        threads = [threading.Thread(target=run, args=(jobs, i), name=str(i))
×
828
                   for i in range(threads)]
829
        for thread in threads:
×
830
            thread.start()
×
831
        for thread in threads:
×
832
            thread.join()
×
833
    else:
834
        run(jobs)
×
835

836

837
def zetup(configfile_name):
×
838
    from App import config
×
839
    from Zope.Startup import handlers as h
×
840
    from Zope.Startup.options import ZopeOptions
×
841
    opts = ZopeOptions()
×
842
    opts.configfile = configfile_name
×
843
    opts.realize(args=[])
×
844
    h.handleConfig(opts.configroot, opts.confighandlers)
×
845
    config.setConfiguration(opts.configroot)
×
846
    from Zope.Startup import dropPrivileges
×
847
    dropPrivileges(opts.configroot)
×
848

849

850
if __name__ == '__main__':
851
    main()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc