Everything about the python self ping scheduler in StayPresent — ping(), cron(), CronHandle, callbacks, and active_cron_handles().
A Deep Dive on StayPresent's ping() and cron() Keep-Warm API
Most guides on keep-warm pinging stop at "call cron() every few minutes." That covers the basic case, but StayPresent's python self ping scheduler has more nuance worth understanding — return values, callback safety, CronHandle lifecycle management, and how it interacts with graceful shutdown. This is the deep-dive reference.
Table of Contents
-
ping(): The Synchronous Building Block - Return Value in Detail
- Host Resolution Rules
-
cron(): Scheduling Repeated Pings - Callbacks:
on_successandon_failure - The
CronHandleObject - Discovering Handles with
active_cron_handles() - Interaction with Graceful Shutdown
- Full Example
- Best Practices
- Common Mistakes
- FAQs
- Conclusion
ping(): The Synchronous Building Block
cron() is really just ping() called on a schedule, so understanding ping() first makes everything else straightforward:
import staypresent
result = staypresent.ping("https://my-bot.onrender.com")
Signature: staypresent.ping(host: str, port: int = None, path: str = "/", timeout: float = 10.0, https: bool = None) -> dict
It sends a single, blocking HTTP GET and returns immediately with the outcome — there's no background thread involved at all for a bare ping() call.
Return Value in Detail
{
"url": "https://my-bot.onrender.com/",
"ok": True,
"status_code": 200,
"elapsed": 0.42,
"error": None,
}
-
okisTruefor any 2xx or 3xx response — not just a bare 200. -
elapsedis in seconds, rounded to 3 decimal places. -
errorisNoneon success, or a short description on failure (an HTTP error status, a timeout, a DNS failure, connection refused, etc.).
result = staypresent.ping("https://my-bot.onrender.com")
if not result["ok"]:
print("Something's wrong:", result["error"])
Host Resolution Rules
host is flexible by design — a bare hostname/IP, a "host:port"-style string, or a full URL:
- If
hostis a full URL,port/path/httpsare ignored — and if you passed non-default values for any of them anyway, a warning is logged so the mismatch doesn't go unnoticed. -
"0.0.0.0"and"::"are treated as"127.0.0.1", since they're bind addresses, not something you can actually send an outgoing request to. -
httpsdefaults toTruefor anything that isn't a local address, andFalsefor"127.0.0.1"/"localhost"/"::1"— matching whatstaypresent.run()itself serves locally, so a quick local sanity check (staypresent.ping("127.0.0.1", port=8080)) works with zero extra configuration.
cron(): Scheduling Repeated Pings
import staypresent
handle = staypresent.cron("https://my-bot.onrender.com", interval=300)
Signature:
staypresent.cron(
host: str,
port: int = None,
path: str = "/",
interval: float = 300.0,
repeat: bool = True,
timeout: float = 10.0,
https: bool = None,
on_success=None,
on_failure=None,
) -> CronHandle
host/port/path/https are validated immediately, at call time — a bad value raises right away rather than only failing silently on the very first scheduled tick, which would otherwise be much harder to notice. interval must be > 0. repeat=False pings exactly once instead of forever, useful for a one-shot delayed warm-up rather than continuous keep-alive.
Callbacks: on_success and on_failure
staypresent.cron(
"https://my-bot.onrender.com",
interval=240,
on_success=lambda r: print(f"warm ping ok ({r['elapsed']}s)"),
on_failure=lambda r: print(f"warm ping failed: {r['error']}"),
)
Both callbacks receive the same result dict ping() itself returns. Critically, an exception raised inside either callback is caught, logged, and swallowed — it can never kill the background pinger thread. This means a buggy on_success handler degrades to "this particular callback silently didn't do its job" rather than "keep-warm pinging stopped entirely because of an unrelated bug in a logging callback."
The CronHandle Object
cron() returns a CronHandle you can use to manage that specific pinger:
handle = staypresent.cron("https://my-bot.onrender.com", interval=240)
handle.is_running # True/False
handle.url # the URL this cron job pings
handle.stop(wait=False, timeout=None) # stop pinging; safe to call more than once
stop() is idempotent — calling it multiple times, or on a handle that's already stopped, doesn't raise.
Discovering Handles with active_cron_handles()
Plenty of real code discards cron()'s return value entirely for a pure fire-and-forget keep-warm pinger:
staypresent.cron("https://my-bot.onrender.com") # no assignment at all
That's completely fine — active_cron_handles() still finds it later, since StayPresent's own registry holds a reference to every handle regardless of whether you kept one:
for handle in staypresent.active_cron_handles():
print(handle.url, handle.is_running)
A handle stays in the registry until its .stop() is called (or its thread otherwise exits) — a discarded return value never causes a keep-warm job to silently vanish from introspection.
Interaction with Graceful Shutdown
Cron pingers run on daemon background threads, so they're never registered with staypresent.run()'s own shutdown handling the way bot processes are. In practice this is fine: daemon threads are torn down automatically the moment the process exits, with no cleanup needed. staypresent.run() does call active_cron_handles() during its own Ctrl+C/SIGTERM shutdown sequence and logs any pingers still active at that point — purely for visibility, since it neither stops nor waits on them.
Full Example
import os
import staypresent
def log_failure(result):
print(f"keep-warm ping failed: {result['error']}")
staypresent.web.json({"status": "running"})
handle = staypresent.cron(
"https://my-bot.onrender.com",
interval=240,
on_failure=log_failure,
)
staypresent.run(
"bot.py",
port=int(os.getenv("PORT", 8080)),
)
Best Practices
- Use
on_failureto surface keep-warm ping failures somewhere visible (logs, a monitoring channel) — a silently failing keep-warm pinger looks identical to a healthy one until your service unexpectedly goes to sleep. - Prefer a bare
ping()call for a one-off manual health check (e.g. in a debugging script), andcron()only for actual ongoing keep-warm behavior. - If you need to stop pinging dynamically (e.g. during a maintenance window), keep the returned
CronHandlearound and call.stop()rather than trying to kill the thread yourself.
Common Mistakes
- Assuming a callback exception stops the pinger. It doesn't — it's caught and logged, which is good for resilience but means a silently broken callback might go unnoticed if you're not checking logs.
-
Pinging
"0.0.0.0"expecting it to reach the public internet. It's treated as"127.0.0.1"— a purely local loopback check, not an external ping. -
Losing track of a
CronHandleand assuming there's no way to stop it later.active_cron_handles()recovers it even if you never stored the return value yourself.
FAQs
Does cron() guarantee pings happen exactly every interval seconds?
Pings run one at a time — if a ping takes a while to time out, the gap before the next one grows accordingly rather than piling up parallel requests, so under failure conditions the effective interval can run longer than configured.
Can I run several cron() pingers at once?
Yes — each call returns its own independent CronHandle, and active_cron_handles() returns all of them.
Does ping() block my whole application?
Only the calling thread, for up to timeout seconds. Use cron() if you need pinging to happen in the background without blocking your main script.
Conclusion
StayPresent's python self ping scheduler is more than "hit a URL every few minutes" — ping()'s detailed return value, cron()'s exception-safe callbacks, and CronHandle/active_cron_handles() together give you real visibility and control over keep-warm behavior, instead of a black-box background thread you just have to trust.
pip install staypresent[prod]
Top comments (0)