Status: canonical. Updated 2026-09-04.
bbsengine6.util is the catch-all utility module: display formatting,
date/time operations, logging, input helpers, file/directory verification,
range operations, SQL resource loading, password hashing, and ANSI
stripping. This document merges the canonical spec with the
handbook/util.md function index.
| Function | Notes |
|---|---|
logentry() |
Handler registration guarded by threading.Lock (_log_lock). Default SysLogHandler lazily initialized on first call. |
diceroll() |
Uses random.SystemRandom() (_dice_rng); thread-safe. |
hr(), heading(), oxfordcomma(), pluralize() |
Pure functions. |
expandrange(), collapserange(), rangestr(), printr() |
Pure functions. |
checksum() |
Pure function. |
ltree_to_path(), chop_last_element(), strip_ansi(), path_to_ltree() |
Pure string functions. |
serialize_datetimes(), tobool() |
Pure functions. |
getremoteaddr(), getcurrentloginid() |
Read from process environment; no shared state. |
verify_dir_exists_writable(), verify_file_exists_readable(), verify_file_exists_readable_writable() |
Stateless filesystem checks. |
timedeltastr() |
Pure function. |
encryptpassword() |
Pure (no I/O; bcrypt is internally thread-safe). |
| Function | Notes |
|---|---|
init() |
Calls locale.setlocale() and time.tzset(), which mutate global process state. Call once at application startup from the main thread. |
init() calls locale.setlocale(locale.LC_ALL, ""), which reads the
user's locale from LC_ALL / LC_CTYPE / LANG. This drives:
strftime() codes like %x and %X.Set the locale before any date/time formatting that relies on locale-specific codes.
On Debian/Ubuntu, install locale packages with:
apt install locales
dpkg-reconfigure locales
hr(acs=True, width=None, end="\n", color="{boxcolor}") -> bool
heading(title: str, **kwargs) -> None
pluralize(amount: int, singular: str, plural: str, quantity=True, emoji="", determiner="a", **kw) -> str
oxfordcomma(seq, conjunction="and") -> Optional[str]
filedisplay(res, **kw) -> None
datestamp(t=None, format="%Y-%m-%d %I:%M%P %Z (%a)") -> str
timedeltastr(delta) -> str
timedeltastr (formerly timedelta_) converts a datetime.timedelta
to a compact duration string with only the non-zero units, zero-padded
to two digits:
>>> from datetime import timedelta
>>> timedeltastr(timedelta(days=2, hours=3, minutes=15, seconds=30))
'02d03h15m30s'
logentry(message, *, level=logging.INFO, handler=None, formatter=None, logger_name="bbsengine6") -> None
The default handler is a lazily-initialized SysLogHandler. The first
call from a given thread may pay the setup cost; subsequent calls are
fast. String level aliases ("debug", "info", "warning",
"error") are accepted for convenience.
inputpassword(prompt="password: ", mask="X", **kwargs) -> str
collapserange(lst: list) -> list
expandrange(txt: str) -> list
rangestr(ranges) -> str
printr(ranges) -> None
expandrange("1-3,5,7-9") returns [1, 2, 3, 5, 7, 8, 9]. The
collapserange / rangestr round-trip is the canonical wire format
for page ranges and similar.
verify_dir_exists_writable(dirname, **kw) -> bool
verify_file_exists_readable(filename, **kw) -> bool
verify_file_exists_readable_writable(filename, **kw) -> bool
The legacy camelCase aliases (verifyDirExistsWritable,
verifyFileExistsReadable, verifyFileExistsReadableWritable) remain
as DeprecationWarning-emitting shims that call the snake_case forms.
encryptpassword(plaintextpassword: str) -> str
Compute a bcrypt hash locally. Thin wrapper around
bbsengine6.password.hash_password. Returns a $2b$06$... string of
length 60. This is the single source of truth for new password hashes;
both member.setpassword and the legacy alias route through it.
getencryptedpassword(args, plaintextpassword) -> Optional[str]
Compatibility wrapper kept as a thin shim over encryptpassword. The
args parameter is accepted but ignored; new code should call
encryptpassword directly.
diceroll(sides=6, count=1, mode="single")
Backed by random.SystemRandom(); cryptographically strong.
init(args=None, **kw) -> None
Set locale from environment. Call once at application startup, before any locale-dependent formatting.
checksum(data: bytes) -> str
tobool(value) -> bool
strip_ansi(s: str) -> str
serialize_datetimes(data: dict) -> dict
ltree_to_path(ltree: str) -> str
path_to_ltree(path: str) -> str
chop_last_element(ltree: str) -> str
pathToLtree(path: str) -> str
pathToLtree is a legacy camelCase alias for path_to_ltree and is
scheduled for removal; new code should use path_to_ltree.
getremoteaddr() -> Optional[str]
getcurrentloginid(args, **kwargs) -> str | None
get_safe_path(args, *components, **kwargs) -> str
load_sql(args, resource_name, *, package: Optional[str] = None) -> str
load_sql reads a .sql resource from bbsengine6.sql (default) or
the package named in package=.
get_encryption_key() -> bytes
encrypt_password(plaintext: str) -> str
decrypt_password(ciphertext_b64: str) -> str
These are the at-rest encryption helpers used by the message queue and similar subsystems; they are distinct from the bcrypt password hashing above.
sanitize_args(argv)
safe_main(...)
safe_main wraps main() with the canonical argv sanitization pass
that strips DB credentials from --psql-* and similar arguments before
they appear in process listings.
The names exported from bbsengine6.util, mirroring the short index in
handbook/util.md (with current names; legacy aliases noted where they
appear in the public surface).
| Function | Purpose |
|---|---|
hr(), heading(), pluralize(), filedisplay() |
Display formatting |
datestamp(), timedeltastr() |
Date/time |
inputpassword() |
Masked password prompt |
oxfordcomma() |
List-to-prose join |
logentry() |
Syslog logging |
collapserange(), expandrange(), rangestr(), printr() |
Range operations |
diceroll() |
Cryptographically strong dice rolls |
timedeltastr() |
Human-readable timedelta rendering |
encryptpassword() |
bcrypt hash (canonical) |
init() |
Locale init |
checksum() |
Byte-level hash |
ltree_to_path(), path_to_ltree(), chop_last_element() |
ltree conversions |
tobool() |
Coerce truthy strings to bool |
getremoteaddr(), getcurrentloginid() |
Environment reads |
get_safe_path(), load_sql() |
Path / SQL resource loading |
serialize_datetimes() |
JSON-friendly datetime flattening |
strip_ansi() |
ANSI escape stripping |
getencryptedpassword() |
Legacy alias of encryptpassword |
verify_dir_exists_writable(), verify_file_exists_readable(), verify_file_exists_readable_writable() |
Filesystem checks |
get_encryption_key(), encrypt_password(), decrypt_password() |
At-rest encryption |
logging, logging.handlers -- Syslog loggingos, re, random, threading -- Standard library utilitiesdatetime -- Date/time handlingdateutil -- Timezone-aware datetime parsingbcrypt -- Password hashing (via bbsengine6.password).io, .database, .input, .password -- Internal BBS engine modulesdatestamp() calls time.tzset() on every invocation, which is
redundant and modifies global state. The init path should set the
timezone once.logentry() string level aliases ("debug", "info", etc.) are
accepted but an enum would be more type-safe.