Mesa 3.0.0__py3-none-any.whl → 3.0.0a0__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.

Potentially problematic release.


This version of Mesa might be problematic. Click here for more details.

Files changed (104) hide show
  1. mesa/__init__.py +3 -3
  2. mesa/agent.py +114 -406
  3. mesa/batchrunner.py +27 -54
  4. mesa/cookiecutter-mesa/cookiecutter.json +8 -0
  5. mesa/cookiecutter-mesa/hooks/post_gen_project.py +11 -0
  6. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +4 -0
  7. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +27 -0
  8. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +11 -0
  9. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +60 -0
  10. mesa/datacollection.py +29 -140
  11. mesa/experimental/__init__.py +1 -11
  12. mesa/experimental/cell_space/__init__.py +1 -16
  13. mesa/experimental/cell_space/cell.py +23 -93
  14. mesa/experimental/cell_space/cell_agent.py +21 -117
  15. mesa/experimental/cell_space/cell_collection.py +17 -54
  16. mesa/experimental/cell_space/discrete_space.py +8 -92
  17. mesa/experimental/cell_space/grid.py +8 -32
  18. mesa/experimental/cell_space/network.py +7 -12
  19. mesa/experimental/devs/__init__.py +0 -2
  20. mesa/experimental/devs/eventlist.py +14 -52
  21. mesa/experimental/devs/examples/epstein_civil_violence.py +39 -71
  22. mesa/experimental/devs/examples/wolf_sheep.py +45 -45
  23. mesa/experimental/devs/simulator.py +15 -55
  24. mesa/main.py +63 -0
  25. mesa/model.py +83 -211
  26. mesa/space.py +149 -215
  27. mesa/time.py +77 -62
  28. mesa/{experimental → visualization}/UserParam.py +6 -17
  29. mesa/visualization/__init__.py +2 -25
  30. mesa/{experimental → visualization}/components/altair.py +0 -10
  31. mesa/visualization/components/matplotlib.py +134 -0
  32. mesa/{experimental/solara_viz.py → visualization/jupyter_viz.py} +110 -65
  33. {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/METADATA +13 -65
  34. mesa-3.0.0a0.dist-info/RECORD +38 -0
  35. mesa-3.0.0.dist-info/licenses/NOTICE → mesa-3.0.0a0.dist-info/licenses/LICENSE +2 -2
  36. mesa/examples/README.md +0 -37
  37. mesa/examples/__init__.py +0 -21
  38. mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +0 -116
  39. mesa/examples/advanced/epstein_civil_violence/Readme.md +0 -34
  40. mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
  41. mesa/examples/advanced/epstein_civil_violence/agents.py +0 -164
  42. mesa/examples/advanced/epstein_civil_violence/app.py +0 -73
  43. mesa/examples/advanced/epstein_civil_violence/model.py +0 -114
  44. mesa/examples/advanced/pd_grid/Readme.md +0 -43
  45. mesa/examples/advanced/pd_grid/__init__.py +0 -0
  46. mesa/examples/advanced/pd_grid/agents.py +0 -50
  47. mesa/examples/advanced/pd_grid/analysis.ipynb +0 -228
  48. mesa/examples/advanced/pd_grid/app.py +0 -54
  49. mesa/examples/advanced/pd_grid/model.py +0 -71
  50. mesa/examples/advanced/sugarscape_g1mt/Readme.md +0 -64
  51. mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
  52. mesa/examples/advanced/sugarscape_g1mt/agents.py +0 -344
  53. mesa/examples/advanced/sugarscape_g1mt/app.py +0 -62
  54. mesa/examples/advanced/sugarscape_g1mt/model.py +0 -180
  55. mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +0 -50
  56. mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
  57. mesa/examples/advanced/wolf_sheep/Readme.md +0 -57
  58. mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
  59. mesa/examples/advanced/wolf_sheep/agents.py +0 -102
  60. mesa/examples/advanced/wolf_sheep/app.py +0 -84
  61. mesa/examples/advanced/wolf_sheep/model.py +0 -137
  62. mesa/examples/basic/__init__.py +0 -0
  63. mesa/examples/basic/boid_flockers/Readme.md +0 -22
  64. mesa/examples/basic/boid_flockers/__init__.py +0 -0
  65. mesa/examples/basic/boid_flockers/agents.py +0 -71
  66. mesa/examples/basic/boid_flockers/app.py +0 -58
  67. mesa/examples/basic/boid_flockers/model.py +0 -69
  68. mesa/examples/basic/boltzmann_wealth_model/Readme.md +0 -56
  69. mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
  70. mesa/examples/basic/boltzmann_wealth_model/agents.py +0 -31
  71. mesa/examples/basic/boltzmann_wealth_model/app.py +0 -74
  72. mesa/examples/basic/boltzmann_wealth_model/model.py +0 -43
  73. mesa/examples/basic/boltzmann_wealth_model/st_app.py +0 -115
  74. mesa/examples/basic/conways_game_of_life/Readme.md +0 -39
  75. mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
  76. mesa/examples/basic/conways_game_of_life/agents.py +0 -47
  77. mesa/examples/basic/conways_game_of_life/app.py +0 -51
  78. mesa/examples/basic/conways_game_of_life/model.py +0 -31
  79. mesa/examples/basic/conways_game_of_life/st_app.py +0 -72
  80. mesa/examples/basic/schelling/Readme.md +0 -40
  81. mesa/examples/basic/schelling/__init__.py +0 -0
  82. mesa/examples/basic/schelling/agents.py +0 -26
  83. mesa/examples/basic/schelling/analysis.ipynb +0 -205
  84. mesa/examples/basic/schelling/app.py +0 -42
  85. mesa/examples/basic/schelling/model.py +0 -59
  86. mesa/examples/basic/virus_on_network/Readme.md +0 -61
  87. mesa/examples/basic/virus_on_network/__init__.py +0 -0
  88. mesa/examples/basic/virus_on_network/agents.py +0 -69
  89. mesa/examples/basic/virus_on_network/app.py +0 -114
  90. mesa/examples/basic/virus_on_network/model.py +0 -96
  91. mesa/experimental/cell_space/voronoi.py +0 -257
  92. mesa/experimental/components/matplotlib.py +0 -242
  93. mesa/visualization/components/__init__.py +0 -83
  94. mesa/visualization/components/altair_components.py +0 -188
  95. mesa/visualization/components/matplotlib_components.py +0 -175
  96. mesa/visualization/mpl_space_drawing.py +0 -593
  97. mesa/visualization/solara_viz.py +0 -458
  98. mesa/visualization/user_param.py +0 -69
  99. mesa/visualization/utils.py +0 -9
  100. mesa-3.0.0.dist-info/RECORD +0 -95
  101. mesa-3.0.0.dist-info/licenses/LICENSE +0 -202
  102. /mesa/{examples/advanced → cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}}/__init__.py +0 -0
  103. {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/WHEEL +0 -0
  104. {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,40 +1,36 @@
1
- """Example of using ABM simulator for Wolf-Sheep Predation Model."""
1
+ """
2
+ Wolf-Sheep Predation Model
3
+ ================================
4
+
5
+ Replication of the model found in NetLogo:
6
+ Wilensky, U. (1997). NetLogo Wolf Sheep Predation model.
7
+ http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation.
8
+ Center for Connected Learning and Computer-Based Modeling,
9
+ Northwestern University, Evanston, IL.
10
+ """
2
11
 
3
12
  import mesa
4
- from mesa.experimental.cell_space import FixedAgent
5
13
  from mesa.experimental.devs.simulator import ABMSimulator
6
14
 
7
15
 
8
16
  class Animal(mesa.Agent):
9
- """Base Animal class."""
10
-
11
- def __init__(self, model, moore, energy, p_reproduce, energy_from_food):
12
- """Initialize Animal instance.
13
-
14
- Args:
15
- model: a model instance
16
- moore: using moore grid or not
17
- energy: initial energy
18
- p_reproduce: probability of reproduction
19
- energy_from_food: energy gained from 1 unit of food
20
- """
21
- super().__init__(model)
17
+ def __init__(self, unique_id, model, moore, energy, p_reproduce, energy_from_food):
18
+ super().__init__(unique_id, model)
22
19
  self.energy = energy
23
20
  self.p_reproduce = p_reproduce
24
21
  self.energy_from_food = energy_from_food
25
22
  self.moore = moore
26
23
 
27
24
  def random_move(self):
28
- """Move to random neighboring cell."""
29
25
  next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True)
30
26
  next_move = self.random.choice(next_moves)
31
27
  # Now move:
32
28
  self.model.grid.move_agent(self, next_move)
33
29
 
34
30
  def spawn_offspring(self):
35
- """Create offspring."""
36
31
  self.energy /= 2
37
32
  offspring = self.__class__(
33
+ self.model.next_id(),
38
34
  self.model,
39
35
  self.moore,
40
36
  self.energy,
@@ -43,15 +39,13 @@ class Animal(mesa.Agent):
43
39
  )
44
40
  self.model.grid.place_agent(offspring, self.pos)
45
41
 
46
- def feed(self): ... # noqa: D102
42
+ def feed(self): ...
47
43
 
48
44
  def die(self):
49
- """Die."""
50
45
  self.model.grid.remove_agent(self)
51
46
  self.remove()
52
47
 
53
48
  def step(self):
54
- """Execute one step of the agent."""
55
49
  self.random_move()
56
50
  self.energy -= 1
57
51
 
@@ -64,10 +58,13 @@ class Animal(mesa.Agent):
64
58
 
65
59
 
66
60
  class Sheep(Animal):
67
- """A sheep that walks around, reproduces (asexually) and gets eaten."""
61
+ """
62
+ A sheep that walks around, reproduces (asexually) and gets eaten.
63
+
64
+ The init is the same as the RandomWalker.
65
+ """
68
66
 
69
67
  def feed(self):
70
- """Eat grass and gain energy."""
71
68
  # If there is grass available, eat it
72
69
  agents = self.model.grid.get_cell_list_contents(self.pos)
73
70
  grass_patch = next(obj for obj in agents if isinstance(obj, GrassPatch))
@@ -77,10 +74,11 @@ class Sheep(Animal):
77
74
 
78
75
 
79
76
  class Wolf(Animal):
80
- """A wolf that walks around, reproduces (asexually) and eats sheep."""
77
+ """
78
+ A wolf that walks around, reproduces (asexually) and eats sheep.
79
+ """
81
80
 
82
81
  def feed(self):
83
- """Eat wolf and gain energy."""
84
82
  agents = self.model.grid.get_cell_list_contents(self.pos)
85
83
  sheep = [obj for obj in agents if isinstance(obj, Sheep)]
86
84
  if len(sheep) > 0:
@@ -91,11 +89,13 @@ class Wolf(Animal):
91
89
  sheep_to_eat.die()
92
90
 
93
91
 
94
- class GrassPatch(FixedAgent):
95
- """A patch of grass that grows at a fixed rate and it is eaten by sheep."""
92
+ class GrassPatch(mesa.Agent):
93
+ """
94
+ A patch of grass that grows at a fixed rate and it is eaten by sheep
95
+ """
96
96
 
97
97
  @property
98
- def fully_grown(self) -> bool: # noqa: D102
98
+ def fully_grown(self) -> bool:
99
99
  return self._fully_grown
100
100
 
101
101
  @fully_grown.setter
@@ -109,16 +109,15 @@ class GrassPatch(FixedAgent):
109
109
  function_args=[self, "fully_grown", True],
110
110
  )
111
111
 
112
- def __init__(self, model, fully_grown, countdown, grass_regrowth_time):
113
- """Creates a new patch of grass.
112
+ def __init__(self, unique_id, model, fully_grown, countdown, grass_regrowth_time):
113
+ """
114
+ Creates a new patch of grass
114
115
 
115
116
  Args:
116
- model: a model instance
117
- fully_grown: (boolean) Whether the patch of grass is fully grown or not
117
+ grown: (boolean) Whether the patch of grass is fully grown or not
118
118
  countdown: Time for the patch of grass to be fully grown again
119
- grass_regrowth_time: regrowth time for the grass
120
119
  """
121
- super().__init__(model)
120
+ super().__init__(unique_id, model)
122
121
  self._fully_grown = fully_grown
123
122
  self.grass_regrowth_time = grass_regrowth_time
124
123
 
@@ -127,12 +126,13 @@ class GrassPatch(FixedAgent):
127
126
  setattr, countdown, function_args=[self, "fully_grown", True]
128
127
  )
129
128
 
130
- def set_fully_grown(self): # noqa
129
+ def set_fully_grown(self):
131
130
  self.fully_grown = True
132
131
 
133
132
 
134
133
  class WolfSheep(mesa.Model):
135
- """Wolf-Sheep Predation Model.
134
+ """
135
+ Wolf-Sheep Predation Model
136
136
 
137
137
  A model for simulating wolf and sheep (predator-prey) ecosystem modelling.
138
138
  """
@@ -152,11 +152,10 @@ class WolfSheep(mesa.Model):
152
152
  simulator=None,
153
153
  seed=None,
154
154
  ):
155
- """Create a new Wolf-Sheep model with the given parameters.
155
+ """
156
+ Create a new Wolf-Sheep model with the given parameters.
156
157
 
157
158
  Args:
158
- height: height of the grid
159
- width: width of the grid
160
159
  initial_sheep: Number of sheep to start with
161
160
  initial_wolves: Number of wolves to start with
162
161
  sheep_reproduce: Probability of each sheep reproducing each step
@@ -166,9 +165,7 @@ class WolfSheep(mesa.Model):
166
165
  grass_regrowth_time: How long it takes for a grass patch to regrow
167
166
  once it is eaten
168
167
  sheep_gain_from_food: Energy sheep gain from grass, if enabled.
169
- moore: whether to use moore or von Neumann grid
170
- simulator: Simulator to use for simulating wolf and sheep
171
- seed: Random seed
168
+ moore:
172
169
  """
173
170
  super().__init__(seed=seed)
174
171
  # Set parameters
@@ -194,6 +191,7 @@ class WolfSheep(mesa.Model):
194
191
  )
195
192
  energy = self.random.randrange(2 * sheep_gain_from_food)
196
193
  sheep = Sheep(
194
+ self.next_id(),
197
195
  self,
198
196
  moore,
199
197
  energy,
@@ -210,6 +208,7 @@ class WolfSheep(mesa.Model):
210
208
  )
211
209
  energy = self.random.randrange(2 * wolf_gain_from_food)
212
210
  wolf = Wolf(
211
+ self.next_id(),
213
212
  self,
214
213
  moore,
215
214
  energy,
@@ -226,13 +225,14 @@ class WolfSheep(mesa.Model):
226
225
  countdown = grass_regrowth_time
227
226
  else:
228
227
  countdown = self.random.randrange(grass_regrowth_time)
229
- patch = GrassPatch(self, fully_grown, countdown, grass_regrowth_time)
228
+ patch = GrassPatch(
229
+ self.next_id(), self, fully_grown, countdown, grass_regrowth_time
230
+ )
230
231
  self.grid.place_agent(patch, pos)
231
232
 
232
233
  def step(self):
233
- """Perform one step of the model."""
234
- self.agents_by_type[Sheep].shuffle_do("step")
235
- self.agents_by_type[Wolf].shuffle_do("step")
234
+ self.get_agents_of_type(Sheep).shuffle(inplace=True).do("step")
235
+ self.get_agents_of_type(Wolf).shuffle(inplace=True).do("step")
236
236
 
237
237
 
238
238
  if __name__ == "__main__":
@@ -1,10 +1,3 @@
1
- """Provides several simulator classes.
2
-
3
- A Simulator is responsible for executing a simulation model. It controls time advancement and enables event scheduling.
4
-
5
-
6
- """
7
-
8
1
  from __future__ import annotations
9
2
 
10
3
  import numbers
@@ -34,12 +27,6 @@ class Simulator:
34
27
  # TODO: add experimentation support
35
28
 
36
29
  def __init__(self, time_unit: type, start_time: int | float):
37
- """Initialize a Simulator instance.
38
-
39
- Args:
40
- time_unit: type of the smulaiton time
41
- start_time: the starttime of the simulator
42
- """
43
30
  # should model run in a separate thread,
44
31
  # and we can then interact with start, stop, run_until, and step?
45
32
  self.event_list = EventList()
@@ -49,10 +36,10 @@ class Simulator:
49
36
  self.time = self.start_time
50
37
  self.model = None
51
38
 
52
- def check_time_unit(self, time: int | float) -> bool: ... # noqa: D102
39
+ def check_time_unit(self, time: int | float) -> bool: ...
53
40
 
54
41
  def setup(self, model: Model) -> None:
55
- """Set up the simulator with the model to simulate.
42
+ """Set up the simulator with the model to simulate
56
43
 
57
44
  Args:
58
45
  model (Model): The model to simulate
@@ -62,13 +49,12 @@ class Simulator:
62
49
  self.model = model
63
50
 
64
51
  def reset(self):
65
- """Reset the simulator by clearing the event list and removing the model to simulate."""
52
+ """Reset the simulator by clearing the event list and removing the model to simulate"""
66
53
  self.event_list.clear()
67
54
  self.model = None
68
55
  self.time = self.start_time
69
56
 
70
57
  def run_until(self, end_time: int | float) -> None:
71
- """Run the simulator until the end time."""
72
58
  while True:
73
59
  try:
74
60
  event = self.event_list.pop_event()
@@ -85,7 +71,7 @@ class Simulator:
85
71
  break
86
72
 
87
73
  def run_for(self, time_delta: int | float):
88
- """Run the simulator for the specified time delta.
74
+ """run the simulator for the specified time delta
89
75
 
90
76
  Args:
91
77
  time_delta (float| int): The time delta. The simulator is run from the current time to the current time
@@ -102,7 +88,7 @@ class Simulator:
102
88
  function_args: list[Any] | None = None,
103
89
  function_kwargs: dict[str, Any] | None = None,
104
90
  ) -> SimulationEvent:
105
- """Schedule event for the current time instant.
91
+ """Schedule event for the current time instant
106
92
 
107
93
  Args:
108
94
  function (Callable): The callable to execute for this event
@@ -130,7 +116,7 @@ class Simulator:
130
116
  function_args: list[Any] | None = None,
131
117
  function_kwargs: dict[str, Any] | None = None,
132
118
  ) -> SimulationEvent:
133
- """Schedule event for the specified time instant.
119
+ """Schedule event for the specified time instant
134
120
 
135
121
  Args:
136
122
  function (Callable): The callable to execute for this event
@@ -164,7 +150,7 @@ class Simulator:
164
150
  function_args: list[Any] | None = None,
165
151
  function_kwargs: dict[str, Any] | None = None,
166
152
  ) -> SimulationEvent:
167
- """Schedule event for the current time plus the time delta.
153
+ """Schedule event for the current time plus the time delta
168
154
 
169
155
  Args:
170
156
  function (Callable): The callable to execute for this event
@@ -188,12 +174,13 @@ class Simulator:
188
174
  return event
189
175
 
190
176
  def cancel_event(self, event: SimulationEvent) -> None:
191
- """Remove the event from the event list.
177
+ """remove the event from the event list
192
178
 
193
179
  Args:
194
180
  event (SimulationEvent): The simulation event to remove
195
181
 
196
182
  """
183
+
197
184
  self.event_list.remove(event)
198
185
 
199
186
  def _schedule_event(self, event: SimulationEvent):
@@ -217,29 +204,13 @@ class ABMSimulator(Simulator):
217
204
  """
218
205
 
219
206
  def __init__(self):
220
- """Initialize a ABM simulator."""
221
207
  super().__init__(int, 0)
222
208
 
223
209
  def setup(self, model):
224
- """Set up the simulator with the model to simulate.
225
-
226
- Args:
227
- model (Model): The model to simulate
228
-
229
- """
230
210
  super().setup(model)
231
211
  self.schedule_event_now(self.model.step, priority=Priority.HIGH)
232
212
 
233
213
  def check_time_unit(self, time) -> bool:
234
- """Check whether the time is of the correct unit.
235
-
236
- Args:
237
- time (int | float): the time
238
-
239
- Returns:
240
- bool: whether the time is of the correct unit
241
-
242
- """
243
214
  if isinstance(time, int):
244
215
  return True
245
216
  if isinstance(time, float):
@@ -254,9 +225,9 @@ class ABMSimulator(Simulator):
254
225
  function_args: list[Any] | None = None,
255
226
  function_kwargs: dict[str, Any] | None = None,
256
227
  ) -> SimulationEvent:
257
- """Schedule a SimulationEvent for the next tick.
228
+ """Schedule a SimulationEvent for the next tick
258
229
 
259
- Args:
230
+ Args
260
231
  function (Callable): the callable to execute
261
232
  priority (Priority): the priority of the event
262
233
  function_args (List[Any]): List of arguments to pass to the callable
@@ -272,7 +243,7 @@ class ABMSimulator(Simulator):
272
243
  )
273
244
 
274
245
  def run_until(self, end_time: int) -> None:
275
- """Run the simulator up to and included the specified end time.
246
+ """run the simulator up to and included the specified end time
276
247
 
277
248
  Args:
278
249
  end_time (float| int): The end_time delta. The simulator is until the specified end time
@@ -299,7 +270,7 @@ class ABMSimulator(Simulator):
299
270
  break
300
271
 
301
272
  def run_for(self, time_delta: int):
302
- """Run the simulator for the specified time delta.
273
+ """run the simulator for the specified time delta
303
274
 
304
275
  Args:
305
276
  time_delta (float| int): The time delta. The simulator is run from the current time to the current time
@@ -311,24 +282,13 @@ class ABMSimulator(Simulator):
311
282
 
312
283
 
313
284
  class DEVSimulator(Simulator):
314
- """A simulator where the unit of time is a float.
315
-
316
- Can be used for full-blown discrete event simulating using event scheduling.
285
+ """A simulator where the unit of time is a float. Can be used for full-blown discrete event simulating using
286
+ event scheduling.
317
287
 
318
288
  """
319
289
 
320
290
  def __init__(self):
321
- """Initialize a DEVS simulator."""
322
291
  super().__init__(float, 0.0)
323
292
 
324
293
  def check_time_unit(self, time) -> bool:
325
- """Check whether the time is of the correct unit.
326
-
327
- Args:
328
- time (float): the time
329
-
330
- Returns:
331
- bool: whether the time is of the correct unit
332
-
333
- """
334
294
  return isinstance(time, numbers.Number)
mesa/main.py ADDED
@@ -0,0 +1,63 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+ from subprocess import call
5
+
6
+ import click
7
+
8
+ from mesa import __version__
9
+
10
+ PROJECT_PATH = click.Path(
11
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True
12
+ )
13
+ COOKIECUTTER_DIR = "mesa/cookiecutter-mesa"
14
+ SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
15
+ COOKIECUTTER_PATH = os.path.join(os.path.dirname(SCRIPTS_DIR), COOKIECUTTER_DIR)
16
+
17
+ CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
18
+
19
+
20
+ @click.group(context_settings=CONTEXT_SETTINGS)
21
+ def cli():
22
+ "Manage Mesa projects"
23
+
24
+
25
+ @cli.command()
26
+ @click.argument("project", type=PROJECT_PATH, default=".")
27
+ def runserver(project):
28
+ """Run mesa project PROJECT
29
+
30
+ PROJECT is the path to the directory containing `run.py`, or the current
31
+ directory if not specified.
32
+ """
33
+ run_files = ["run.py", "server.py"]
34
+ for run_file in run_files:
35
+ run_path = Path(project) / run_file
36
+ if not run_path.exists():
37
+ continue
38
+ args = [sys.executable, str(run_path)]
39
+ call(args)
40
+ sys.exit(f"ERROR: file run.py or server.py (in {Path(project)}) does not exist")
41
+
42
+
43
+ @click.command()
44
+ @click.option(
45
+ "--no-input", is_flag=True, help="Do not prompt user for custom mesa model input."
46
+ )
47
+ def startproject(no_input):
48
+ """Create a new mesa project"""
49
+ args = ["cookiecutter", COOKIECUTTER_PATH]
50
+ if no_input:
51
+ args.append("--no-input")
52
+ call(args)
53
+
54
+
55
+ @click.command()
56
+ def version():
57
+ """Show the version of mesa"""
58
+ print(f"mesa {__version__}")
59
+
60
+
61
+ cli.add_command(runserver)
62
+ cli.add_command(startproject)
63
+ cli.add_command(version)