Skip to content

Commit

Permalink
Drop support for Python 3.9, require Python >= 3.10 (#2132)
Browse files Browse the repository at this point in the history
* Drop support for Python 3.9, require Python >= 3.10

Starting development for Mesa 3.0 and following SPEC 0 we can drop Python 3.9 support and require Python 3.10 or higher for future development. This allows adopting more modern Python features and simplifies the testing and CI configurations.

See the Python 3.10 release notes: https://docs.python.org/3/whatsnew/3.10.html

The 2.3.x release series will keep supporting Python 3.9.

* Update type hinting for Python 3.10

`X | Y` can now be used for type annotations, including making a variable optional with `X | None`

* intro_tutorial.ipynb: Require Python 3.10

* typing: Replace Union with pipe (|)

We can do this now in Python 3.10!
  • Loading branch information
EwoutH authored May 8, 2024
1 parent 1c0b8d5 commit f4ae953
Show file tree
Hide file tree
Showing 17 changed files with 34 additions and 40 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/build_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ jobs:
python-version: "3.11"
- os: ubuntu
python-version: "3.10"
- os: ubuntu
python-version: "3.9"
# Disabled for now. See https://github.com/projectmesa/mesa/issues/1253
#- os: ubuntu
# python-version: 'pypy-3.8'
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
rev: v3.15.2
hooks:
- id: pyupgrade
args: [--py38-plus]
args: [--py310-plus]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 # Use the ref you want to point at
hooks:
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/intro_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"source": [
"### Tutorial Setup\n",
"\n",
"Create and activate a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). *Python version 3.9 or higher is required*.\n",
"Create and activate a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). *Python version 3.10 or higher is required*.\n",
"\n",
"Install Mesa:\n",
"\n",
Expand Down
4 changes: 2 additions & 2 deletions mesa/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
import warnings
import weakref
from collections import defaultdict
from collections.abc import Iterable, Iterator, MutableSet, Sequence
from collections.abc import Callable, Iterable, Iterator, MutableSet, Sequence
from random import Random

# mypy
from typing import TYPE_CHECKING, Any, Callable
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
# We ensure that these are not imported during runtime to prevent cyclic
Expand Down
8 changes: 4 additions & 4 deletions mesa/batchrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from collections.abc import Iterable, Mapping
from functools import partial
from multiprocessing import Pool
from typing import Any, Optional, Union
from typing import Any

from tqdm.auto import tqdm

Expand All @@ -11,9 +11,9 @@

def batch_run(
model_cls: type[Model],
parameters: Mapping[str, Union[Any, Iterable[Any]]],
parameters: Mapping[str, Any | Iterable[Any]],
# We still retain the Optional[int] because users may set it to None (i.e. use all CPUs)
number_processes: Optional[int] = 1,
number_processes: int | None = 1,
iterations: int = 1,
data_collection_period: int = -1,
max_steps: int = 1000,
Expand Down Expand Up @@ -76,7 +76,7 @@ def batch_run(


def _make_model_kwargs(
parameters: Mapping[str, Union[Any, Iterable[Any]]],
parameters: Mapping[str, Any | Iterable[Any]],
) -> list[dict[str, Any]]:
"""Create model kwargs from parameters dictionary.
Expand Down
2 changes: 1 addition & 1 deletion mesa/datacollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def collect(self, model):
if self.model_reporters:
for var, reporter in self.model_reporters.items():
# Check if lambda or partial function
if isinstance(reporter, (types.LambdaType, partial)):
if isinstance(reporter, types.LambdaType | partial):
self.model_vars[var].append(reporter(model))
# Check if model attribute
elif isinstance(reporter, str):
Expand Down
4 changes: 2 additions & 2 deletions mesa/experimental/cell_space/cell_collection.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from __future__ import annotations

import itertools
from collections.abc import Iterable, Mapping
from collections.abc import Callable, Iterable, Mapping
from functools import cached_property
from random import Random
from typing import TYPE_CHECKING, Callable, Generic, TypeVar
from typing import TYPE_CHECKING, Generic, TypeVar

if TYPE_CHECKING:
from mesa.experimental.cell_space.cell import Cell
Expand Down
2 changes: 1 addition & 1 deletion mesa/experimental/cell_space/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def _validate_parameters(self):
raise ValueError("Dimensions must be a list of positive integers.")
if not isinstance(self.torus, bool):
raise ValueError("Torus must be a boolean.")
if self.capacity is not None and not isinstance(self.capacity, (float, int)):
if self.capacity is not None and not isinstance(self.capacity, float | int):
raise ValueError("Capacity must be a number or None.")

def select_random_empty_cell(self) -> T:
Expand Down
6 changes: 3 additions & 3 deletions mesa/experimental/cell_space/network.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from random import Random
from typing import Any, Optional
from typing import Any

from mesa.experimental.cell_space.cell import Cell
from mesa.experimental.cell_space.discrete_space import DiscreteSpace
Expand All @@ -11,8 +11,8 @@ class Network(DiscreteSpace):
def __init__(
self,
G: Any, # noqa: N803
capacity: Optional[int] = None,
random: Optional[Random] = None,
capacity: int | None = None,
random: Random | None = None,
cell_klass: type[Cell] = Cell,
) -> None:
"""A Networked grid
Expand Down
3 changes: 1 addition & 2 deletions mesa/experimental/components/altair.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import contextlib
from typing import Optional

import solara

Expand All @@ -8,7 +7,7 @@


@solara.component
def SpaceAltair(model, agent_portrayal, dependencies: Optional[list[any]] = None):
def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
space = getattr(model, "grid", None)
if space is None:
# Sometimes the space is defined as model.space instead of model.grid
Expand Down
8 changes: 3 additions & 5 deletions mesa/experimental/components/matplotlib.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import Optional

import networkx as nx
import solara
from matplotlib.figure import Figure
Expand All @@ -9,7 +7,7 @@


@solara.component
def SpaceMatplotlib(model, agent_portrayal, dependencies: Optional[list[any]] = None):
def SpaceMatplotlib(model, agent_portrayal, dependencies: list[any] | None = None):
space_fig = Figure()
space_ax = space_fig.subplots()
space = getattr(model, "grid", None)
Expand Down Expand Up @@ -116,7 +114,7 @@ def portray(space):


@solara.component
def PlotMatplotlib(model, measure, dependencies: Optional[list[any]] = None):
def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
fig = Figure()
ax = fig.subplots()
df = model.datacollector.get_model_vars_dataframe()
Expand All @@ -127,7 +125,7 @@ def PlotMatplotlib(model, measure, dependencies: Optional[list[any]] = None):
for m, color in measure.items():
ax.plot(df.loc[:, m], label=m, color=color)
fig.legend()
elif isinstance(measure, (list, tuple)):
elif isinstance(measure, list | tuple):
for m in measure:
ax.plot(df.loc[:, m], label=m)
fig.legend()
Expand Down
3 changes: 2 additions & 1 deletion mesa/experimental/devs/eventlist.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import annotations

import itertools
from collections.abc import Callable
from enum import IntEnum
from heapq import heapify, heappop, heappush
from types import MethodType
from typing import Any, Callable
from typing import Any
from weakref import WeakMethod, ref


Expand Down
3 changes: 2 additions & 1 deletion mesa/experimental/devs/simulator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations

import numbers
from typing import Any, Callable
from collections.abc import Callable
from typing import Any

from mesa import Model

Expand Down
4 changes: 2 additions & 2 deletions mesa/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
from collections import defaultdict

# mypy
from typing import Any, Union
from typing import Any

from mesa.agent import Agent, AgentSet
from mesa.datacollection import DataCollector

TimeT = Union[float, int]
TimeT = float | int


class Model:
Expand Down
10 changes: 5 additions & 5 deletions mesa/space.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
import itertools
import math
import warnings
from collections.abc import Iterable, Iterator, Sequence
from collections.abc import Callable, Iterable, Iterator, Sequence
from numbers import Real
from typing import Any, Callable, TypeVar, Union, cast, overload
from typing import Any, TypeVar, cast, overload
from warnings import warn

with contextlib.suppress(ImportError):
Expand All @@ -42,12 +42,12 @@

Coordinate = tuple[int, int]
# used in ContinuousSpace
FloatCoordinate = Union[tuple[float, float], npt.NDArray[float]]
FloatCoordinate = tuple[float, float] | npt.NDArray[float]
NetworkCoordinate = int

Position = Union[Coordinate, FloatCoordinate, NetworkCoordinate]
Position = Coordinate | FloatCoordinate | NetworkCoordinate

GridContent = Union[Agent, None]
GridContent = Agent | None
MultiGridContent = list[Agent]

F = TypeVar("F", bound=Callable[..., Any])
Expand Down
4 changes: 1 addition & 3 deletions mesa/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@
from collections.abc import Iterable

# mypy
from typing import Union

from mesa.agent import Agent, AgentSet
from mesa.model import Model

# BaseScheduler has a self.time of int, while
# StagedActivation has a self.time of float
TimeT = Union[float, int]
TimeT = float | int


class BaseScheduler:
Expand Down
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build-backend = "hatchling.build"
name = "Mesa"
description = "Agent-based modeling (ABM) in Python"
license = { text = "Apache 2.0" }
requires-python = ">=3.9"
requires-python = ">=3.10"
authors = [
{ name = "Project Mesa Team", email = "[email protected]" },
]
Expand All @@ -25,7 +25,6 @@ classifiers = [
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Intended Audience :: Science/Research",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -81,9 +80,9 @@ path = "mesa/__init__.py"

[tool.ruff]
# See https://github.com/charliermarsh/ruff#rules for error code definitions.
# Hardcode to Python 3.9.
# Hardcode to Python 3.10.
# Reminder to update mesa-examples if the value below is changed.
target-version = "py39"
target-version = "py310"
extend-exclude = ["docs", "build"]

[tool.ruff.lint]
Expand Down

0 comments on commit f4ae953

Please sign in to comment.