Lease an instance for your own SITL
open_sitl leases its own instance. You need this page when something else launches SITL: a script of yours, sim_vehicle.py, a ground station test. Holding the lease is what keeps every mcardupilot session and every leasing script off that instance while yours runs.
From an agent: lease_instance
Section titled “From an agent: lease_instance”lease_instance(owner="sim_vehicle.py quadplane, mission test"){ "instance": 41, "mavlink_port": 6170, "json_port": 9412, "owner": "sim_vehicle.py quadplane, mission test" }The server took the first pool instance whose lock was free and whose ports nothing holds, and keeps the lock until release_instance(41) or until the server exits. Start SITL on it:
sim_vehicle.py -v ArduPlane -f quadplane -I 41-I 41 puts MAVLink on 5760 + 10 × 41 = 6170. Every other caller now sees the holder by name:
list_instances()[ { "instance": 41, "in_pool": true, "mavlink_port": 6170, "holder": "sim_vehicle.py quadplane, mission test pid 728433", "session_id": null, "leased_here": true, "busy_ports": [] } ]When you are done, stop your SITL first, then give the number back:
release_instance(41)Ask for a specific number with lease_instance(owner, instance=45). If someone holds it, the error names them; if no one holds it but its ports are taken, the error says by what.
From Python: Sitl does it for you
Section titled “From Python: Sitl does it for you”If your script launches SITL itself, let the library launch it. Sitl leases an instance, probes its ports, launches SITL in its own process group, and on close() (or leaving the with) kills the group, waits for the ports to free and releases the lease:
from mcardupilot.sitl import Sitl
with Sitl(frame="quadplane", defaults=["/path/to/quadplane.parm"], owner="mission test") as s: print(s.instance, s.port) # 40 6160, say s.arm_on_pad("QLOITER") ...instance=42 asks for one and raises InstanceBusy naming the holder. Use the library from a test harness goes further.
From Python: a bare LeasePool
Section titled “From Python: a bare LeasePool”For a SITL you must launch some other way, hold a lease around it:
import subprocessfrom mcardupilot import procsfrom mcardupilot.config import Settings, mavlink_portfrom mcardupilot.leases import LeasePool
settings = Settings() # the same pool and lock directory as the serverpool = LeasePool(settings.leases_dir, settings.instance_pool)
def ports_taken(n: int) -> str | None: busy = procs.busy_ports(n) return f"its ports are taken: {', '.join(busy)}" if busy else None
with pool.lease("nightly sweep", check=ports_taken) as lease: n = lease.instance subprocess.run(["sim_vehicle.py", "-v", "ArduPlane", "-f", "quadplane", "-I", str(n)])check runs while the lock is held. The pool scan skips an instance it rejects; a requested one raises with the reason.
From the shell
Section titled “From the shell”uv run python -m mcardupilot.leases statuspool 40-59, locks in ~/.local/share/mcardupilot/leases 41 tcp:6170 sim_vehicle.py quadplane, mission test pid 728433 44 tcp:6200 mcardupilot session f391b1 (tools-hop-test) pid 7284362 of 20 heldInstances leased outside the pool, by a script with a wider pool, are listed too and marked (outside the pool).
What a lease does not cover
Section titled “What a lease does not cover”A lease only excludes callers that take leases. autotest.py (always instance 0) and make fly take none, which is why every launcher also probes the instance’s ports under the lock, and why instance 0 cannot be pooled at all. If you launch SITL by hand on a pool instance without a lease, mcardupilot skips that instance because its ports are busy, but your SITL is unprotected against the next unleased launch.