DEV Community

John Wick
John Wick

Posted on

A Deep Dive on StayPresent's ping() and cron() Keep-Warm API

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

  1. ping(): The Synchronous Building Block
  2. Return Value in Detail
  3. Host Resolution Rules
  4. cron(): Scheduling Repeated Pings
  5. Callbacks: on_success and on_failure
  6. The CronHandle Object
  7. Discovering Handles with active_cron_handles()
  8. Interaction with Graceful Shutdown
  9. Full Example
  10. Best Practices
  11. Common Mistakes
  12. FAQs
  13. 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")
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode
  • ok is True for any 2xx or 3xx response — not just a bare 200.
  • elapsed is in seconds, rounded to 3 decimal places.
  • error is None on 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"])
Enter fullscreen mode Exit fullscreen mode

Host Resolution Rules

host is flexible by design — a bare hostname/IP, a "host:port"-style string, or a full URL:

  • If host is a full URL, port/path/https are 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.
  • https defaults to True for anything that isn't a local address, and False for "127.0.0.1"/"localhost"/"::1" — matching what staypresent.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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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']}"),
)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)),
)
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Use on_failure to 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), and cron() only for actual ongoing keep-warm behavior.
  • If you need to stop pinging dynamically (e.g. during a maintenance window), keep the returned CronHandle around 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 CronHandle and 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]
Enter fullscreen mode Exit fullscreen mode

Top comments (0)