salabim 25.0.3__py3-none-any.whl → 25.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
salabim/salabim.py CHANGED
@@ -1,13 +1,13 @@
1
- # _ _ _ ____ ____ ___ _____
2
- # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | ___| / _ \ |___ /
3
- # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) ||___ \ | | | | |_ \
4
- # \__ \| (_| || || (_| || |_) || || | | | | | / __/ ___) | _ | |_| | _ ___) |
5
- # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____||____/ (_) \___/ (_)|____/
1
+ # _ _ _ ____ ____ ___ __
2
+ # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | ___| / _ \ / /_
3
+ # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) ||___ \ | | | | | '_ \
4
+ # \__ \| (_| || || (_| || |_) || || | | | | | / __/ ___) | _ | |_| | _ | (_) |
5
+ # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____||____/ (_) \___/ (_) \___/
6
6
  # discrete event simulation
7
7
  #
8
8
  # see www.salabim.org for more information, the documentation and license information
9
9
 
10
- __version__ = "25.0.3"
10
+ __version__ = "25.0.6"
11
11
  import heapq
12
12
  import random
13
13
  import time
@@ -42,7 +42,7 @@ import urllib.error
42
42
  import base64
43
43
  import zipfile
44
44
  from pathlib import Path
45
-
45
+
46
46
 
47
47
  from typing import Any, Union, Iterable, Tuple, List, Callable, TextIO, Dict, Set, Type, Hashable, Optional
48
48
 
@@ -99,7 +99,8 @@ _ANSI_to_rgb = {
99
99
  "\033[0m": (),
100
100
  }
101
101
 
102
- ANSI=types.SimpleNamespace(**_color_name_to_ANSI)
102
+ ANSI = types.SimpleNamespace(**_color_name_to_ANSI)
103
+
103
104
 
104
105
  def a_log(*args):
105
106
  if not hasattr(a_log, "a_logfile_name"):
@@ -484,10 +485,10 @@ class Monitor:
484
485
  fill: Iterable = None,
485
486
  stats_only: bool = False,
486
487
  env: "Environment" = None,
487
- *args,
488
488
  **kwargs,
489
489
  ):
490
490
  self.env = _set_env(env)
491
+ _check_overlapping_parameters(self, "__init__", "setup")
491
492
 
492
493
  if isinstance(self.env, Environment):
493
494
  _set_name(name, self.env._nameserializeMonitor, self)
@@ -532,7 +533,7 @@ class Monitor:
532
533
  self._x.extend(fill)
533
534
  self._t.extend(len(fill) * [self.env._now])
534
535
 
535
- self.setup(*args, **kwargs)
536
+ self.setup(**kwargs)
536
537
 
537
538
  def __eq__(self, other):
538
539
  if isinstance(other, Monitor):
@@ -4356,8 +4357,9 @@ class Queue:
4356
4357
  if omitted, default_env will be used
4357
4358
  """
4358
4359
 
4359
- def __init__(self, name: str = None, monitor: Any = True, fill: Iterable = None, capacity: float = inf, env: "Environment" = None, *args, **kwargs) -> None:
4360
+ def __init__(self, name: str = None, monitor: Any = True, fill: Iterable = None, capacity: float = inf, env: "Environment" = None, **kwargs) -> None:
4360
4361
  self.env = _set_env(env)
4362
+ _check_overlapping_parameters(self, "__init__", "setup")
4361
4363
 
4362
4364
  _set_name(name, self.env._nameserializeQueue, self)
4363
4365
  self._head = Qmember()
@@ -4390,7 +4392,7 @@ class Queue:
4390
4392
  c.enter(self)
4391
4393
  if self.env._trace:
4392
4394
  self.env.print_trace("", "", self.name() + " create")
4393
- self.setup(*args, **kwargs)
4395
+ self.setup(**kwargs)
4394
4396
 
4395
4397
  def setup(self) -> None:
4396
4398
  """
@@ -5896,6 +5898,7 @@ class Animate3dBase(DynamicClass):
5896
5898
  ) -> None:
5897
5899
  super().__init__()
5898
5900
  self.env = _set_env(env)
5901
+ _check_overlapping_parameters(self, "__init__", "setup")
5899
5902
 
5900
5903
  self.visible = visible
5901
5904
  self.keep = keep
@@ -7142,6 +7145,8 @@ class Component:
7142
7145
  ):
7143
7146
  self.env = _set_env(env)
7144
7147
 
7148
+ _check_overlapping_parameters(self, "__init__", "setup")
7149
+
7145
7150
  _set_name(name, self.env._nameserializeComponent, self)
7146
7151
  self._qmembers = {}
7147
7152
  self._process = None
@@ -7196,9 +7201,13 @@ class Component:
7196
7201
  else:
7197
7202
  self.env.print_trace("", "", self.name() + " create data component", self._modetxt())
7198
7203
  else:
7204
+ _check_overlapping_parameters(self, "__init__", process_name,process=p)
7205
+ _check_overlapping_parameters(self, "setup", process_name,process=p)
7206
+
7199
7207
  self.env.print_trace("", "", self.name() + " create", self._modetxt())
7200
7208
 
7201
7209
  kwargs_p = {}
7210
+
7202
7211
  if kwargs:
7203
7212
  parameters = inspect.signature(p).parameters
7204
7213
 
@@ -7254,6 +7263,7 @@ by adding at the end:
7254
7263
  self.status._value = scheduled
7255
7264
  if self.env._yieldless:
7256
7265
  self._glet = greenlet.greenlet(lambda: self._process(**kwargs_p), parent=self.env._glet)
7266
+ self.env.glets.append(self._glet)
7257
7267
  self._reschedule(scheduled_time, priority, urgent, "activate", cap_now, extra=extra)
7258
7268
  self.setup(**kwargs)
7259
7269
 
@@ -7493,14 +7503,13 @@ by adding at the end:
7493
7503
  return return_or_print(result, as_str, file)
7494
7504
 
7495
7505
  def _push(self, t, priority, urgent, return_value=None, switch=True):
7496
- if t != inf:
7497
- self.env._seq += 1
7498
- if urgent:
7499
- seq = -self.env._seq
7500
- else:
7501
- seq = self.env._seq
7502
- self._on_event_list = True
7503
- heapq.heappush(self.env._event_list, (t, priority, seq, self, return_value))
7506
+ self.env._seq += 1
7507
+ if urgent:
7508
+ seq = -self.env._seq
7509
+ else:
7510
+ seq = self.env._seq
7511
+ self._on_event_list = True
7512
+ heapq.heappush(self.env._event_list, (t, priority, seq, self, return_value))
7504
7513
  if self.env._yieldless:
7505
7514
  if self is self.env._current_component:
7506
7515
  self.env._glet.switch()
@@ -7928,6 +7937,7 @@ by adding:
7928
7937
  lineno = self.lineno_txt(add_at=True)
7929
7938
  self.env.print_trace("", "", self.name() + " passivate", merge_blanks(lineno, self._modetxt()))
7930
7939
  self.status._value = passive
7940
+ self._push(inf, 0, False, None)
7931
7941
 
7932
7942
  if self.env._yieldless:
7933
7943
  if self is self.env._current_component:
@@ -8069,6 +8079,8 @@ by adding:
8069
8079
  self.env.print_trace("", "", "cancel " + self.name() + " " + self._modetxt())
8070
8080
  self.status._value = data
8071
8081
  if self.env._yieldless:
8082
+ self._glet.throw()
8083
+ self._glet = None
8072
8084
  if self is self.env._current_component:
8073
8085
  self.env._glet.switch()
8074
8086
 
@@ -8618,7 +8630,7 @@ by adding:
8618
8630
  scheduled_time = fail_at + self.env._offset
8619
8631
  else:
8620
8632
  raise ValueError("both fail_at and fail_delay specified")
8621
- schedule_priority=priority
8633
+ schedule_priority = priority
8622
8634
  self.set_mode(mode)
8623
8635
 
8624
8636
  self._failed = False
@@ -9019,7 +9031,7 @@ by adding:
9019
9031
  raise ValueError("both fail_at and fail_delay specified")
9020
9032
 
9021
9033
  self.set_mode(mode)
9022
- schedule_priority=priority
9034
+ schedule_priority = priority
9023
9035
 
9024
9036
  self._cond = cond # add test ***
9025
9037
  for state in states:
@@ -9222,11 +9234,10 @@ by adding:
9222
9234
  scheduled_time = fail_at + self.env._offset
9223
9235
  else:
9224
9236
  raise ValueError("both fail_at and fail_delay specified")
9225
- schedule_priority=priority
9237
+ schedule_priority = priority
9226
9238
  self.set_mode(mode)
9227
9239
 
9228
9240
  for arg in args:
9229
-
9230
9241
  value = True
9231
9242
  priority = request_priority
9232
9243
  if isinstance(arg, State):
@@ -9262,7 +9273,6 @@ by adding:
9262
9273
  if not self._waits:
9263
9274
  raise TypeError("no states specified")
9264
9275
 
9265
-
9266
9276
  self._remaining_duration = scheduled_time - self.env._now
9267
9277
 
9268
9278
  self._trywait()
@@ -10546,9 +10556,11 @@ class Environment:
10546
10556
  do_reset: bool = None,
10547
10557
  blind_animation: bool = False,
10548
10558
  yieldless: bool = None,
10549
- *args,
10550
10559
  **kwargs,
10551
10560
  ):
10561
+ _check_overlapping_parameters(self, "__init__", "setup")
10562
+
10563
+ self.glets = []
10552
10564
  if name is None:
10553
10565
  if isdefault_env:
10554
10566
  name = "default environment"
@@ -10713,7 +10725,7 @@ class Environment:
10713
10725
  if ap_kwargs:
10714
10726
  self.animation_parameters(**ap_kwargs)
10715
10727
 
10716
- self.setup(*args, **kwargs)
10728
+ self.setup(**kwargs)
10717
10729
 
10718
10730
  # ENVIRONMENT ANNOTATION START
10719
10731
  # ENVIRONMENT ANNOTATION END
@@ -10731,6 +10743,10 @@ class Environment:
10731
10743
  """
10732
10744
  pass
10733
10745
 
10746
+ def cancel_all(self):
10747
+ for t, priority, sequence, comp, return_value in self._event_list:
10748
+ comp.cancel()
10749
+
10734
10750
  def serialize(self) -> int:
10735
10751
  self.serial += 1
10736
10752
  return self.serial
@@ -11203,6 +11219,8 @@ class Environment:
11203
11219
  if self._event_list:
11204
11220
  (t, priority, seq, c, return_value) = heapq.heappop(self._event_list)
11205
11221
  else:
11222
+ t = inf # only events with t==inf left, so signal that we have ended
11223
+ if t == inf:
11206
11224
  c = self._main
11207
11225
  if self.end_on_empty_eventlist:
11208
11226
  t = self.env._now
@@ -23495,8 +23513,10 @@ class State:
23495
23513
  if omitted, default_env is used
23496
23514
  """
23497
23515
 
23498
- def __init__(self, name: str = None, value: Any = False, type: str = "any", monitor: bool = True, env: "Environment" = None, *args, **kwargs):
23516
+ def __init__(self, name: str = None, value: Any = False, type: str = "any", monitor: bool = True, env: "Environment" = None, **kwargs):
23499
23517
  self.env = _set_env(env)
23518
+ _check_overlapping_parameters(self, "__init__", "setup")
23519
+
23500
23520
  _set_name(name, self.env._nameserializeState, self)
23501
23521
  self._value = value
23502
23522
  with self.env.suppress_trace():
@@ -23505,7 +23525,7 @@ class State:
23505
23525
  self.value = _StateMonitor(parent=self, name="Value of " + self.name(), level=True, initial_tally=value, monitor=monitor, type=type, env=self.env)
23506
23526
  if self.env._trace:
23507
23527
  self.env.print_trace("", "", self.name() + " create", "value = " + repr(self._value))
23508
- self.setup(*args, **kwargs)
23528
+ self.setup(**kwargs)
23509
23529
 
23510
23530
  def setup(self) -> None:
23511
23531
  """
@@ -23968,10 +23988,11 @@ class Resource:
23968
23988
  honor_only_highest_priority: bool = False,
23969
23989
  monitor: bool = True,
23970
23990
  env: "Environment" = None,
23971
- *args,
23972
23991
  **kwargs,
23973
23992
  ):
23974
23993
  self.env = _set_env(env)
23994
+ _check_overlapping_parameters(self, "__init__", "setup")
23995
+
23975
23996
  if initial_claimed_quantity != 0:
23976
23997
  if not anonymous:
23977
23998
  raise ValueError("initial_claimed_quantity != 0 only allowed for anonymous resources")
@@ -24006,7 +24027,7 @@ class Resource:
24006
24027
  self.occupancy = _SystemMonitor("Occupancy of " + self.name(), level=True, initial_tally=0, monitor=monitor, type="float", env=self.env)
24007
24028
  if self.env._trace:
24008
24029
  self.env.print_trace("", "", self.name() + " create", "capacity=" + str(self._capacity) + (" anonymous" if self._anonymous else ""))
24009
- self.setup(*args, **kwargs)
24030
+ self.setup(**kwargs)
24010
24031
 
24011
24032
  def ispreemptive(self) -> bool:
24012
24033
  """
@@ -25090,6 +25111,38 @@ def _set_name(name, _nameserialize, object):
25090
25111
  object._name = name
25091
25112
 
25092
25113
 
25114
+ def _check_overlapping_parameters(obj, method_name0, method_name1,process=None):
25115
+ """
25116
+ this function is a helper to see whether __init__, setup and process parameters overlap
25117
+
25118
+ Parameters
25119
+ ----------
25120
+ obj : object
25121
+ object to be checked (usually called with self)
25122
+
25123
+ method_name0 : str
25124
+ name of the first method to be tested
25125
+
25126
+ method_name1 : str
25127
+ name of the second method to be tested
25128
+
25129
+ process: method
25130
+ used for process check: should be the process methoc
25131
+ """
25132
+ method0=getattr(obj, method_name0)
25133
+
25134
+ if process is None:
25135
+ method1=getattr(obj, method_name1)
25136
+ else:
25137
+ method1=process
25138
+
25139
+ overlapping_parameters = set(inspect.signature(method0).parameters) & set(inspect.signature(method1).parameters)
25140
+ if overlapping_parameters:
25141
+ raise TypeError(
25142
+ f"{obj.__class__.__name__}.{method_name1}() error: parameter '{list(overlapping_parameters)[0]}' not allowed, because it is also a parameter of {obj.__class__.__name__}.{method_name0}()"
25143
+ )
25144
+
25145
+
25093
25146
  @functools.lru_cache()
25094
25147
  def _screen_dimensions():
25095
25148
  if PythonInExcel or AnacondaCode:
@@ -27821,7 +27874,12 @@ def set_environment_aliases():
27821
27874
  return # do not set when using Sphinx build!
27822
27875
 
27823
27876
  for name, obj in list(globals().items()):
27824
- if (not name.startswith("_") or name in ("_Trajectory", "_Distribution")) and name != "yieldless" and name != "Environment" and not hasattr(Environment,name):
27877
+ if (
27878
+ (not name.startswith("_") or name in ("_Trajectory", "_Distribution"))
27879
+ and name != "yieldless"
27880
+ and name != "Environment"
27881
+ and not hasattr(Environment, name)
27882
+ ):
27825
27883
  if inspect.isclass(obj) and obj.__module__ == Environment.__module__:
27826
27884
  if issubclass(obj, Exception):
27827
27885
  exec(f"Environment.{name}={name}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: salabim
3
- Version: 25.0.3
3
+ Version: 25.0.6
4
4
  Summary: salabim - discrete event simulation in Python
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://salabim.org
@@ -3,8 +3,8 @@ salabim/LICENSE.txt,sha256=eTPlcDJz4G0096Qv-wfMjm1Wxbd4ilDlsYg5rN4HjWQ,1106
3
3
  salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
4
4
  salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
5
5
  salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
6
- salabim/salabim.py,sha256=LySbJixC2l-o0QrALCRfqoF5nb7H5GStA3-58Y7PNg8,1123720
7
- salabim-25.0.3.dist-info/METADATA,sha256=eKOnH5RzXA3WLs_HXoX_DhwAKYLyapIC8K9DqMSQ2O0,3457
8
- salabim-25.0.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
- salabim-25.0.3.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
10
- salabim-25.0.3.dist-info/RECORD,,
6
+ salabim/salabim.py,sha256=XWNVgxbGhU4tOM46WlvF2WQbsBcGwFThJIWZSdiCefA,1125722
7
+ salabim-25.0.6.dist-info/METADATA,sha256=IPb6uB8LBViARvDmZmULWzieHXyBAWkK_pNCLAkdQuY,3457
8
+ salabim-25.0.6.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
9
+ salabim-25.0.6.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
10
+ salabim-25.0.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5