CLI Runner (asppycli.py)

asppycli.py (in the repository root) renders a single .asp page from the command line and writes the result to stdout — without starting the HTTP server. It uses the exact same VM render pipeline as ASPPY/server.py, including global.asa, Application/Session state, #include resolution and Server.Execute, so the output matches the HTTP server byte-for-byte.

Built for AI agents and automation. If you are an AI model (or a CI script) building an ASPPY app, this is the fastest way to verify every page you create or edit: no server to start, no port, no browser. Run the page, check the exit code, and read the rendered HTML (or the IIS-style error page) straight from stdout.

Quick Start

# Render a demo/test page to stdout (sample pages live in www_test)
python asppycli.py www_test/index.asp

# Save output to a file (for diffing against a known-good baseline)
python asppycli.py www_test/01-basics.asp -o out.html

Working Example

Run one of the demo pages that ships with ASPPY (in the www_test folder) and show the response headers:

python asppycli.py www_test/01-basics.asp --show-headers -o out.html

Output (headers go to stderr, the rendered body goes to out.html):

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache
Expires: 0
Set-Cookie: ASP_PY_SESSIONID=4ba3049be4de42228f842e494f41e5ce; Path=/

The process exits with code 0, and out.html contains the fully rendered page — identical to what the HTTP server would send to a browser.

Command-Line Options

Option Description
file Path to the .asp file to render (required, positional).
-o, --output FILE Write the rendered body to FILE instead of stdout. Preserves bytes exactly (binary-safe).
--docroot DIR Document root. Defaults to the directory containing the .asp file. Pass it explicitly for MVC apps (e.g. --docroot www) so includes and Server.MapPath() resolve exactly as under the HTTP server.
--method VERB HTTP method to simulate (default GET).
--query QS Query string, e.g. --query "id=42&mode=edit". Available via Request.QueryString.
--body DATA Request body. Implies POST and Content-Type: application/x-www-form-urlencoded, so values arrive in Request.Form.
--header "Name: Value" Extra request header (repeatable).
--path URL Virtual request path seen by Request.Path — use this to test MVC front-controller routes, e.g. --path /contacts/1/edit.
--show-headers Print the HTTP status line and response headers to stderr (keeps stdout clean for the body).
--session NAME=VALUE Preload a Session variable before the page runs (repeatable). Lets you render a page behind a login without replaying the login.

Testing pages behind a login

Each run gets a fresh session, so a login POST cannot carry over to the next invocation. Instead, set the variable your auth check reads:

python asppycli.py www/servicedeck/api.asp --docroot www \
    --query "action=services" --session authed=True

Values are typed the way VBScript would type the literal:

ArgumentStored as
--session authed=TrueBoolean True
--session uid=42Integer 42
--session rate=1.5Double 1.5
--session name=PieterString "Pieter"
--session e=Empty / --session n=NullEmpty / Null
--session flag:s=TrueString "True" — the :s suffix forces a string

Variables are applied after Session_OnStart, so they override anything global.asa sets. Only scalars can be injected; a Session entry holding an object reference has to be built by the page itself.

Exit Codes

Designed for scripts: check the exit code before parsing any output.

Exit Code Meaning
0 Page rendered with HTTP status < 400.
1 Page rendered with HTTP status >= 400 (e.g. 500 ASP runtime error, 404 unknown route). The error page is still written to stdout.
2 Usage error or .asp file not found.

Simulating Requests

Form POST

python asppycli.py www_test/07-request-form.asp --method POST --body "username=Ann&color=blue"

The page sees Request.Form("username") = "Ann" and Request.ServerVariables("REQUEST_METHOD") = "POST", exactly as if a browser had submitted the form.

Query strings

python asppycli.py www/report.asp --query "id=42&mode=edit"

MVC front-controller routes

For MVC apps where default.asp routes on Request.Path, point the runner at the front controller and pass the route with --path:

# Renders the starter app home page (route "/")
python asppycli.py www_starter/default.asp --docroot www_starter --path /

# Unknown route: the router's 404 is returned, and the exit code is 1
python asppycli.py www_starter/default.asp --docroot www_starter --path /contacts/99

Error Pages

A VBScript runtime error produces the same IIS-style error page the HTTP server returns — with file, line number and a source caret — plus status 500 and exit code 1:

HTTP/1.1 500 Internal Server Error

<p>ASPPY runtime error '8000ffff'</p>
<p>Variable is undefined: 'UNDEFINEDVAR'</p>
<p>/err.asp, line 2</p>
<p>Response.Write undefinedVar<br>
-^</p>

Batch Checking a Whole Folder (asppycheck.py)

To validate every .asp page in a folder recursively with one command, use the companion script asppycheck.py (also in the repository root). It renders each page through the same VM pipeline and reports every failure with the file, line number and error description:

# Check all sample/test pages
python asppycheck.py www_test

# Check your own app
python asppycheck.py www

# Also list passing pages / stop at the first failure
python asppycheck.py www --verbose
python asppycheck.py www --fail-fast

Example output with one broken page:

FAIL  zz-broken.asp  [500] Variable is undefined: 'MISSINGVAR' (/zz-broken.asp, line 2)

checked 28 page(s): 27 ok, 0 warning(s), 1 failure(s)

failures:
  zz-broken.asp: Variable is undefined: 'MISSINGVAR' (/zz-broken.asp, line 2)
Exit Code Meaning
0 Every page rendered with HTTP status < 500.
1 At least one page failed (status >= 500 or engine exception).
2 Folder not found / no .asp files.
AI agents: after any change that touches multiple files (shared includes, models, layouts), run python asppycheck.py www once instead of re-testing pages one by one — it catches regressions across the whole app in a single command.

Automation and CI

Because output is deterministic and exit codes are meaningful, the runner slots directly into shell loops and CI pipelines. For the common case — "does every page still render?" — prefer asppycheck.py above; the loops below show the building blocks for custom assertions:

# bash: fail the build if any demo page errors
for f in www_test/*.asp; do
    python asppycli.py "$f" --docroot www_test -o /dev/null || { echo "FAIL: $f"; exit 1; }
done
# PowerShell: render every page and report failures
Get-ChildItem www_test -Filter *.asp | ForEach-Object {
    python asppycli.py $_.FullName --docroot www_test > $null 2>&1
    if ($LASTEXITCODE -ne 0) { Write-Host "FAIL: $($_.Name)" }
}

Save a known-good baseline with -o baseline.html, then diff after parser/VM or app changes:

python asppycli.py www_test/01-basics.asp -o new.html
diff baseline.html new.html

Recommended Workflow for AI Agents

  1. Create or edit a page under www/.
  2. Run python asppycli.py www/<page>.asp --docroot www.
  3. Check the exit code: 0 means the page rendered without a runtime error.
  4. Inspect stdout for the expected HTML; on exit code 1, read the error page — it names the file, line and source of the failure.
  5. For MVC routes, verify each real route: --path /contacts/1, --path /contacts/1/edit, and confirm unknown routes exit 1 with a 404.
  6. After changes that touch shared files (includes, models, layouts), run python asppycheck.py www to batch-verify every page at once.

Notes

Credits

The CLI runner was contributed by Jeffrey (@jeffreyheping) — proposed in issue #10.