This file is indexed.

/usr/lib/python3/dist-packages/SimPy/Simulation.py is in python3-simpy 2.3.1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# coding=utf-8
"""
Simulation implements SimPy Processes, Resources, Buffers, and the backbone simulation
scheduling by coroutine calls. Provides data collection through classes
Monitor and Tally.
Based on generators

"""
import random
import sys
import types
from heapq import heappush, heappop

from SimPy.Lister import Lister
from SimPy.Recording import Monitor, Tally
from SimPy.Lib import Process, SimEvent, PriorityQ, Resource, Level, \
                      Store, Simerror, FatalSimerror, FIFO

# Required for backward compatibility
import SimPy
import SimPy.Globals as Globals
from SimPy.Globals import initialize, simulate, now, stopSimulation, \
        allEventNotices, allEventTimes, startCollection,\
        _startWUStepping, _stopWUStepping, activate, reactivate


# yield keywords
hold = 1
passivate = 2
request = 3
release = 4
waitevent = 5
queueevent = 6
waituntil = 7
get = 8
put = 9


class Infinity(object):
    def __cmp__(self, other):
        return 1

infinity = Infinity()

def holdfunc(a):
    a[0][1]._hold(a)

def requestfunc(a):
    """Handles 'yield request, self, res' and
    'yield (request, self, res),(<code>,self, par)'.
    <code > can be 'hold' or 'waitevent'.
    """
    if type(a[0][0]) == tuple:
        ## Compound yield request statement
        ## first tuple in ((request, self, res),(xx, self, yy))
        b = a[0][0]
        ## b[2] == res (the resource requested)
        ##process the first part of the compound yield statement
        ##a[1] is the Process instance
        b[2]._request(arg = (b, a[1]))
        ##deal with add - on condition to command
        ##Trigger processes for reneging
        class _Holder(Process):
            """Provides timeout process"""
            def __init__(self,name,sim=None):
                Process.__init__(self,name=name,sim=sim)
            def trigger(self, delay):
                yield hold, self, delay
                if not proc in b[2].activeQ:
                    proc.sim.reactivate(proc)

        class _EventWait(Process):
            """Provides event waiting process"""
            def __init__(self,name,sim=None):
                Process.__init__(self,name=name,sim=sim)
            def trigger(self, event):
                yield waitevent, self, event
                if not proc in b[2].activeQ:
                    proc.eventsFired = self.eventsFired
                    proc.sim.reactivate(proc)

        #activate it
        proc = a[0][0][1] # the process to be woken up
        actCode = a[0][1][0]
        if actCode == hold:
            proc._holder = _Holder(name = 'RENEGE - hold for %s'%proc.name,
                                   sim=proc.sim)
            ##                                          the timeout delay
            proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == waituntil:
            raise FatalSimerror('Illegal code for reneging: waituntil')
        elif actCode == waitevent:
            proc._holder = _EventWait(name = 'RENEGE - waitevent for %s'\
                                      %proc.name,sim=proc.sim)
            ##                                          the event
            proc.sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == queueevent:
            raise FatalSimerror('Illegal code for reneging: queueevent')
        else:
            raise FatalSimerror('Illegal code for reneging %s'%actCode)
    else:
        ## Simple yield request command
        a[0][2]._request(a)

def releasefunc(a):
    a[0][2]._release(a)

def passivatefunc(a):
    a[0][1]._passivate(a)

def waitevfunc(a):
    #if waiting for one event only (not a tuple or list)
    evtpar = a[0][2]
    if isinstance(evtpar, SimEvent):
        a[0][2]._wait(a)
    # else, if waiting for an OR of events (list / tuple):
    else: #it should be a list / tuple of events
        # call _waitOR for first event
        evtpar[0]._waitOR(a)

def queueevfunc(a):
    #if queueing for one event only (not a tuple or list)
    evtpar = a[0][2]
    if isinstance(evtpar, SimEvent):
        a[0][2]._queue(a)
    #else, if queueing for an OR of events (list / tuple):
    else: #it should be a list / tuple of events
        # call _queueOR for first event
        evtpar[0]._queueOR(a)

def waituntilfunc(par):
    par[0][1].sim._waitUntilFunc(par[0][1], par[0][2])

def getfunc(a):
    """Handles 'yield get, self, buffer, what, priority' and
    'yield (get, self, buffer, what, priority),(<code>,self, par)'.
    <code > can be 'hold' or 'waitevent'.
    """
    if type(a[0][0]) == tuple:
        ## Compound yield request statement
        ## first tuple in ((request, self, res),(xx, self, yy))
        b = a[0][0]
        ## b[2] == res (the resource requested)
        ##process the first part of the compound yield statement
        ##a[1] is the Process instance
        b[2]._get(arg = (b, a[1]))
        ##deal with add - on condition to command
        ##Trigger processes for reneging
        class _Holder(Process):
            """Provides timeout process"""
            def __init__(self,**par):
                Process.__init__(self,**par)
            def trigger(self, delay):
                yield hold, self, delay
                #if not proc in b[2].activeQ:
                if proc in b[2].getQ:
                    a[1].sim.reactivate(proc)

        class _EventWait(Process):
            """Provides event waiting process"""
            def __init__(self,**par):
                Process.__init__(self,**par)
            def trigger(self, event):
                yield waitevent, self, event
                if proc in b[2].getQ:
                    a[1].eventsFired = self.eventsFired
                    a[1].sim.reactivate(proc)

        #activate it
        proc = a[0][0][1] # the process to be woken up
        actCode = a[0][1][0]
        if actCode == hold:
            proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name,
                                   sim=proc.sim)
            ##                                          the timeout delay
            a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == waituntil:
            raise FatalSimerror('waituntil: Illegal code for reneging: waituntil')
        elif actCode == waitevent:
            proc._holder = _EventWait(name="RENEGE - waitevent for%s"\
                                      %proc.name,sim=proc.sim)
            ##                                          the event
            a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == queueevent:
            raise FatalSimerror('Illegal code for reneging: queueevent')
        else:
            raise FatalSimerror('Illegal code for reneging %s'%actCode)
    else:
        ## Simple yield request command
        a[0][2]._get(a)


def putfunc(a):
    """Handles 'yield put' (simple and compound hold / waitevent)
    """
    if type(a[0][0]) == tuple:
        ## Compound yield request statement
        ## first tuple in ((request, self, res),(xx, self, yy))
        b = a[0][0]
        ## b[2] == res (the resource requested)
        ##process the first part of the compound yield statement
        ##a[1] is the Process instance
        b[2]._put(arg = (b, a[1]))
        ##deal with add - on condition to command
        ##Trigger processes for reneging
        class _Holder(Process):
            """Provides timeout process"""
            def __init__(self,**par):
                Process.__init__(self,**par)
            def trigger(self, delay):
                yield hold, self, delay
                #if not proc in b[2].activeQ:
                if proc in b[2].putQ:
                    a[1].sim.reactivate(proc)

        class _EventWait(Process):
            """Provides event waiting process"""
            def __init__(self,**par):
                Process.__init__(self,**par)
            def trigger(self, event):
                yield waitevent, self, event
                if proc in b[2].putQ:
                    a[1].eventsFired = self.eventsFired
                    a[1].sim.reactivate(proc)

        #activate it
        proc = a[0][0][1] # the process to be woken up
        actCode = a[0][1][0]
        if actCode == hold:
            proc._holder = _Holder(name='RENEGE - hold for %s'%proc.name,
                                   sim=proc.sim)
            ##                                          the timeout delay
            a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == waituntil:
            raise FatalSimerror('Illegal code for reneging: waituntil')
        elif actCode == waitevent:
            proc._holder = _EventWait(name='RENEGE - waitevent for %s'\
                                      %proc.name,sim=proc.sim)
            ##                                          the event
            a[1].sim.activate(proc._holder, proc._holder.trigger(a[0][1][2]))
        elif actCode == queueevent:
            raise FatalSimerror('Illegal code for reneging: queueevent')
        else:
            raise FatalSimerror('Illegal code for reneging %s'%actCode)
    else:
        ## Simple yield request command
        a[0][2]._put(a)


class Simulation(object):
    _dispatch = {
            hold: holdfunc, request: requestfunc, release: releasefunc,
            passivate: passivatefunc, waitevent: waitevfunc,
            queueevent: queueevfunc, waituntil: waituntilfunc, get: getfunc,
            put: putfunc,
    }
    _commandcodes = list(_dispatch.keys())
    _commandwords = {
            hold: 'hold', request: 'request', release: 'release',
            passivate: 'passivate', waitevent: 'waitevent',
            queueevent: 'queueevent', waituntil: 'waituntil', get: 'get',
            put: 'put'
    }

    def __init__(self):
        self.initialize()

    def initialize(self):
        self._t = 0
        self.next_time = 0

        # Eventqueue stuff.
        self._timestamps = []
        self._sortpr = 0

        self._start = False
        self._stop = False
        self.condQ = []
        self.allMonitors = []
        self.allTallies = []

    def now(self):
        return self._t

    def stopSimulation(self):
        """Application function to stop simulation run"""
        self._stop = True

    def _post(self, what, at, prior = False):
        """Post an event notice for process what for time at"""
        # event notices are Process instances
        if at < self._t:
            raise FatalSimerror('Attempt to schedule event in the past')
        what._nextTime = at
        self._sortpr -= 1
        if prior:
            # before all other event notices at this time
            # heappush with highest priority value so far (negative of
            # monotonely decreasing number)
            # store event notice in process instance
            what._rec = [at, self._sortpr, what, False]
            # make event list refer to it
            heappush(self._timestamps, what._rec)
        else:
            # heappush with lowest priority
            # store event notice in process instance
            what._rec = [at,-self._sortpr, what, False]
            # make event list refer to it
            heappush(self._timestamps, what._rec)

    def _unpost(self, whom):
        """
        Mark event notice for whom as cancelled if whom is a suspended process
        """
        if whom._nextTime is not None:  # check if whom was actually active
            whom._rec[3] = True ## Mark as cancelled
            whom._nextTime = None

    def allEventNotices(self):
        """Returns string with eventlist as;
                t1: processname, processname2
                t2: processname4, processname5, . . .
                . . .  .
        """
        ret = ''
        tempList = []
        tempList[:] = self._timestamps
        tempList.sort()
        # return only event notices which are not cancelled
        tempList = [[x[0],x[2].name] for x in tempList if not x[3]]
        tprev = -1
        for t in tempList:
            # if new time, new line
            if t[0] == tprev:
                # continue line
                ret += ', %s'%t[1]
            else:
                # new time
                if tprev == -1:
                    ret = '%s: %s' % (t[0],t[1])
                else:
                    ret += '\n%s: %s' % (t[0],t[1])
                tprev = t[0]
        return ret + '\n'

    def allEventTimes(self):
        """Returns list of all times for which events are scheduled.
        """
        r = []
        r[:] = self._timestamps
        r.sort()
        # return only event times of not cancelled event notices
        r1 = [x[0] for x in r if not x[3]]
        tprev = -1
        ret = []
        for t in r1:
            if t == tprev:
                #skip time, already in list
                pass
            else:
                ret.append(t)
                tprev = t
        return ret

    def activate(self, obj, process, at = 'undefined', delay = 'undefined',
                 prior = False):
        """Application function to activate passive process."""
        if __debug__:
            if not (obj.sim == self):
                raise FatalSimerror('activate: Process %s not in activating '
                        'Simulation instance' % obj.name)
            if not (type(process) == types.GeneratorType):
                raise FatalSimerror('Activating function which'+
                    ' is not a generator (contains no \'yield\')')
        if not obj._terminated and not obj._nextTime:
            #store generator reference in object; needed for reactivation
            obj._nextpoint = process
            if at == 'undefined':
                at = self._t
            if delay == 'undefined':
                zeit = max(self._t, at)
            else:
                zeit = max(self._t, self._t + delay)
            self._post(obj, at = zeit, prior = prior)

    def reactivate(self, obj, at = 'undefined', delay = 'undefined',
                   prior = False):
        """Application function to reactivate a process which is active,
        suspended or passive."""
        # Object may be active, suspended or passive
        if not obj._terminated:
            a = Process('SimPysystem',sim=self)
            a.cancel(obj)
            # object now passive
            if at == 'undefined':
                at = self._t
            if delay == 'undefined':
                zeit = max(self._t, at)
            else:
                zeit = max(self._t, self._t + delay)
            self._post(obj, at = zeit, prior = prior)

    def startCollection(self, when = 0.0, monitors = None, tallies = None):
        """Starts data collection of all designated Monitor and Tally objects
        (default = all) at time 'when'.
        """
        class Starter(Process):
            def collect(self, monitors, tallies):
                for m in monitors:
                    m.reset()
                for t in tallies:
                    t.reset()
                yield hold, self
        if monitors is None:
            monitors = self.allMonitors
        if tallies is None:
            tallies = self.allTallies
        if when == 0.0:
            for m in monitors:
                try:
                    ylast = m[-1][1]
                    empty = False
                except IndexError:
                    empty = True
                m.reset()
                if not empty:
                    m.observe(t = now(), y = ylast)
            for t in tallies:
                t.reset()
        else:
            s = Starter(sim = self)
            self.activate(s, s.collect(monitors = monitors, tallies = tallies),\
                      at = when, prior = True)


    def _waitUntilFunc(self, proc, cond):
        """
        Puts a process 'proc' waiting for a condition into a waiting queue.
        'cond' is a predicate function which returns True if the condition is
        satisfied.
        """
        if not cond():
            self.condQ.append(proc)
            proc.cond = cond
            # passivate calling process
            proc._nextTime = None
        else:
            #schedule continuation of calling process
            self._post(proc, at = self._t, prior = 1)

    def _terminate(self, process):
        """Marks a process as terminated."""
        process._nextpoint = None
        process._terminated = True
        process._nextTime = None

    def has_events(self):
        """
        Checks if there are events which can be processed. Returns ``True`` if
        there are events and the simulation has not been stopped.
        """
        return not self._stop and self._timestamps

    def peek(self):
        """
        Returns the time of the next event or infinity, if no
        more events are scheduled.
        """
        if not self._timestamps:
            return infinity
        else:
            return self._timestamps[0][0]

    def step(self):
        """
        Executes the next uncancelled event in the eventqueue.
        """

        # Fetch next process and advance its process execution method.
        noActiveNotice = True
        # Get an uncancelled event
        while noActiveNotice:
            if self._timestamps:
                _tnotice, p, proc, cancelled = heappop(self._timestamps)
                noActiveNotice = cancelled
            else:
                return None

        # Advance simulation time.
        proc._rec = None
        self._t = _tnotice

        # Execute the event. This will advance the process execution method.
        try:
            resultTuple = next(proc._nextpoint)

            # Process the command function which has been yielded by the
            # process.
            if type(resultTuple[0]) == tuple:
                # allowing for reneges, e.g.:
                # >>> yield (request, self, res),(waituntil, self, cond)
                command = resultTuple[0][0]
            else:
                command = resultTuple[0]
            if __debug__:
                if not command in self._commandcodes:
                    raise FatalSimerror('Illegal command: yield %s'%command)
            self._dispatch[command]((resultTuple, proc))
        except StopIteration:
            # Process execution method has terminated.
            self._terminate(proc)

        # Test the conditions for all waiting processes if there are any at
        # all. Where condition are satisfied, reactivate that process
        # immediately and remove it from queue.
        # Always test the wait conditions. They might be triggered by on a
        # terminating process execution method (e.g. the above next() call
        # raises the StopIteration exception)
        if self.condQ:
            i = 0
            while i < len(self.condQ):
                proc = self.condQ[i]
                if proc.cond():
                    self.condQ.pop(i)
                    self.reactivate(proc)
                else:
                    i += 1

        # Return time of the next scheduled event.
        #return self._timestamps[0][0] if self._timestamps else None
        if self._timestamps:
            return self._timestamps[0][0]
        else:
            return None

    def simulate(self, until=0):
        """
        Start the simulation and run its loop until the timeout ``until`` is
        reached, stopSimulation is called, or no more events are scheduled.
        """
        try:
            if not self._timestamps:
                return 'SimPy: No activities scheduled'

            # Some speedups. Storing these values in local variables prevents
            # the self-lookup. Note that this can't be done for _stop because
            # this variable will get overwritten, bools are immutable.
            step = self.step
            timestamps = self._timestamps
            while not self._stop and timestamps and timestamps[0][0] <= until:
                step()

            if not self._stop and timestamps:
                # Timestamps left, simulation not stopped
                self._t = until
                return 'SimPy: Normal exit at time %s' % self._t
            elif not timestamps:
                # No more timestamps
                return 'SimPy: No more events at time %s' % self._t
            else:
                # Stopped by call of stopSimulation
                return 'SimPy: Run stopped at time %s' % self._t
        # Delete the excepts?
        except FatalSimerror as error:
            raise FatalSimerror('SimPy: ' + error.value)
        except Simerror as error:
            return 'SimPy: ' + error.value
        finally:
            self._stop = True

# For backward compatibility
Globals.sim = Simulation()

peek = Globals.sim.peek

step = Globals.sim.step

allMonitors = Globals.sim.allMonitors

allTallies = Globals.sim.allTallies
# End backward compatibility