WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s
ci / test (push) Successful in 6m14s
History-free snapshot of the private development repository at 77a82eb. Code, docs and the public catalogue only: no plant or job knowledge layers, no client, job or producer names (tools/scrub.py clean against the widened term list; a second word-level grep clean). The intro deck is left out because its slide images cannot be checked for job drawings.
This commit is contained in:
+93
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Hot Reload -- stop the WillTools routes bridge cleanly, THEN reload pyRevit.
|
||||
#
|
||||
# Why the stop must come first: the routes HTTP dispatch is wired to a module-
|
||||
# global external event handler created once, on the host main thread, when
|
||||
# `pyrevit/routes/server/server.py` is first imported (server.py:35-36), and it
|
||||
# is NOT recreated across a reload -- pyRevit re-imports extension scripts, not
|
||||
# its own lib. A request in flight when the engine is torn down runs its handler
|
||||
# against a dead engine -> unhandled exception on a non-main thread -> Revit dies
|
||||
# uncatchably (measured 2026-08-14; see startup.py and CLAUDE.md "Never reload").
|
||||
# Stopping the HTTP server first closes that crash window. On reload, startup.py
|
||||
# re-registers the routes and a fresh server comes back up automatically.
|
||||
#
|
||||
# If the server will NOT stop cleanly, this ABORTS rather than reloads -- a
|
||||
# reload with a live bridge is the exact crash, so a bad stop must never fall
|
||||
# through to it.
|
||||
#
|
||||
# IronPython 2.7: no f-strings. No module docstring / tooltip / author
|
||||
# (test_button_tooltips.py).
|
||||
|
||||
# pylint: disable=import-error,invalid-name,broad-except
|
||||
from pyrevit import EXEC_PARAMS
|
||||
from pyrevit import routes
|
||||
from pyrevit import forms
|
||||
from pyrevit import script
|
||||
from pyrevit.loader import sessionmgr
|
||||
from pyrevit.loader import sessioninfo
|
||||
|
||||
logger = script.get_logger()
|
||||
|
||||
go = True
|
||||
if EXEC_PARAMS.executed_from_ui:
|
||||
go = forms.alert(
|
||||
'Hot Reload stops the WillTools bridge, then reloads pyRevit.\n\n'
|
||||
'Run this only when no bridge request is in flight -- no sweep, no '
|
||||
'agent mid-exec. A request raised the instant before this click cannot '
|
||||
'be cancelled and can still take Revit down.\n\nReload now?',
|
||||
ok=False, yes=True, no=True)
|
||||
|
||||
if go:
|
||||
stopped_clean = True
|
||||
try:
|
||||
routes.deactivate_server()
|
||||
except Exception as ex:
|
||||
stopped_clean = False
|
||||
logger.error('deactivate_server raised: {0}'.format(ex))
|
||||
|
||||
# Verify the socket is actually gone before tearing the engine down. A
|
||||
# deactivate that "succeeded" but left the server up is still unsafe.
|
||||
try:
|
||||
if routes.get_active_server() is not None:
|
||||
stopped_clean = False
|
||||
except Exception as ex:
|
||||
stopped_clean = False
|
||||
logger.error('get_active_server raised: {0}'.format(ex))
|
||||
|
||||
if not stopped_clean:
|
||||
forms.alert(
|
||||
'Could not stop the WillTools bridge cleanly.\n\n'
|
||||
'Do NOT reload with a live bridge -- restart Revit instead of '
|
||||
'reloading. (A reload while the routes handler is live is what '
|
||||
'crashes Revit.)', ok=True)
|
||||
else:
|
||||
# Vouch to tools/rvt.py that THIS reload is a clean stop-first one, so
|
||||
# the reloaded bridge is not refused as a raw-reload landmine. startup.py
|
||||
# consumes this marker on the very next load and stamps that session
|
||||
# 'hotreload'. Keyed by pid: a reload is in-process, so the same Revit
|
||||
# picks it up and no other session can. Best-effort -- if it cannot be
|
||||
# written the only cost is rvt.py refusing the reloaded bridge (the safe
|
||||
# default), never a failed reload.
|
||||
try:
|
||||
import os as _os
|
||||
from System.Diagnostics import Process as _Process
|
||||
_here = _os.path.abspath(__file__).replace('\\', '/')
|
||||
_mark = _here.find('/WillTools.extension/')
|
||||
if _mark != -1:
|
||||
_root = _here[:_mark]
|
||||
else:
|
||||
_root = _os.path.join(_os.path.expanduser('~'), 'pyrevit-dev')
|
||||
_pid = _Process.GetCurrentProcess().Id
|
||||
_flagpath = _os.path.join(
|
||||
_root, '.bridge-hotreload-{0}.flag'.format(_pid))
|
||||
_fh = open(_flagpath, 'w')
|
||||
_fh.write('hotreload\n')
|
||||
_fh.close()
|
||||
except Exception as ex:
|
||||
logger.warning('could not write hot-reload marker: {0}'.format(ex))
|
||||
|
||||
logger.info('WillTools bridge stopped. Reloading pyRevit...')
|
||||
sessionmgr.reload_pyrevit()
|
||||
# Mirrors the shipped Reload button: record the new session id.
|
||||
script.get_results().newsession = sessioninfo.get_session_uuid()
|
||||
Reference in New Issue
Block a user