ASPPY is a Python-based runtime that executes Classic ASP (VBScript) pages. It provides compatibility with most VBScript built-in functions, the Classic ASP object model (Request, Response, Session, Application, Server), and various COM components commonly used in legacy ASP applications.

Platform Support

ASPPY works on:

Dependencies

The following Python libraries are required:

LibraryPurpose
Python 3.8+Runtime environment
fpdf2PDF generation (pip install fpdf2)
bcryptPassword hashing (pip install bcrypt)
PillowImage processing (pip install pillow)
pyodbcAccess/Excel/ODBC database providers (pip install pyodbc)
certifi (optional)TLS CA bundle for MSXML HTTP (pip install certifi)
zipfile (built-in)ZIP file handling

VBScript Built-in Functions

Compatibility notes for built-ins:

String Functions

FunctionDescription
Len(string)Returns the length of a string
LenB(expression)Returns the length of a string in bytes
UCase(string)Converts a string to uppercase
LCase(string)Converts a string to lowercase
Trim(string)Removes leading and trailing spaces
LTrim(string)Removes leading spaces
RTrim(string)Removes trailing spaces
StrReverse(string)Reverses a string
StrComp(string1, string2[, compare])Compares two strings
Left(string, length)Returns leftmost characters
Right(string, length)Returns rightmost characters
Mid(string, start[, length])Returns characters from a string
LeftB(string, length)Returns leftmost bytes
RightB(string, length)Returns rightmost bytes
MidB(expr, start[, length])Returns bytes from a string
InStr([start, ]string1, string2[, compare])Finds one string within another
InStrB([start, ]string1, string2)Finds one string within another (byte)
Replace(expression, find, replace[, start[, count[, compare]]])Replaces text in a string
Split(expression[, delimiter[, count[, compare]]])Splits a string into an array
Join(list[, delimiter])Joins an array into a string
Filter(inputstrings, value[, include[, compare]])Returns a filtered array
Space(number)Returns a string of spaces
String(number, character)Returns a repeating character string
Asc(string)Returns the ANSI code of the first character
AscW(string)Returns the Unicode code of the first character
AscB(string)Returns the first byte of a string
Chr(charcode)Returns the character associated with an ANSI code
ChrW(charcode)Returns the character associated with a Unicode code
ChrB(charcode)Returns a single-byte character
Hex(number)Returns the hexadecimal value
Oct(number)Returns the octal value

Array Functions

FunctionDescription
Array(arglist)Creates an array
IsArray(varname)Returns True if variable is an array
LBound(arrayname[, dimension])Returns the lowest subscript
UBound(arrayname[, dimension])Returns the highest subscript

Type Conversion Functions

FunctionDescription
CBool(expression)Converts to Boolean
CByte(expression)Converts to Byte
CCur(expression)Converts to Currency
CDbl(expression)Converts to Double
CInt(expression)Converts to Integer
CLng(expression)Converts to Long
CSng(expression)Converts to Single
CStr(expression)Converts to String
CDate(date)Converts to Date

Date/Time Functions

FunctionDescription
Now()Returns current date and time
Date()Returns current system date
Time()Returns current system time
Timer()Returns seconds since midnight
Year(date)Returns the year
Month(date)Returns the month (1-12)
Day(date)Returns the day (1-31)
Hour(time)Returns the hour (0-23)
Minute(time)Returns the minute (0-59)
Second(time)Returns the second (0-59)
DateSerial(year, month, day)Returns a date
TimeSerial(hour, minute, second)Returns a time
DateAdd(interval, number, date)Adds a time interval
DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]])Returns the difference
DatePart(interval, date[, firstdayofweek[, firstweekofyear]])Returns a part of a date
Weekday(date[, firstdayofweek])Returns the weekday (1-7)
WeekdayName(weekday[, abbreviate[, firstdayofweek]])Returns the weekday name
MonthName(month[, abbreviate])Returns the month name
DateValue(string)Returns a date from a string
TimeValue(string)Returns a time from a string
CDate(string)Converts a string to a date
IsDate(expression)Returns True if expression is a date
FormatDateTime(date[, namedformat])Formats a date/time

Date Constants

ConstantValueDescription
vbSunday1Sunday
vbMonday2Monday
vbTuesday3Tuesday
vbWednesday4Wednesday
vbThursday5Thursday
vbFriday6Friday
vbSaturday7Saturday
vbUseSystemDayOfWeek0Use system day of week
vbFirstJan11First week with Jan 1
vbFirstFourDays2First week with 4 days
vbFirstFullWeek3First full week
vbGeneralDate0General date format
vbLongDate1Long date format
vbShortDate2Short date format
vbLongTime3Long time format
vbShortTime4Short time format
vbBinaryCompare0Binary comparison
vbTextCompare1Text comparison

Math Functions

FunctionDescription
Abs(number)Returns absolute value
Atn(number)Returns arctangent
Cos(number)Returns cosine
Exp(number)Returns e raised to a power
Fix(number)Returns integer portion
Int(number)Returns integer portion (floor)
Log(number)Returns natural logarithm
Rnd([number])Returns random number
Round(expression[, numdecimalplaces])Rounds a number
Sqr(number)Returns square root
Sgn(number)Returns sign of a number

Format Functions

Note: formatting functions are compatibility-focused and may differ from IIS under some locale/regional settings.

FunctionDescription
FormatNumber(expression[, numdigitsafterdecimal[, includeleadingdigit[, useparensfornegativenumbers[, groupdigits]]]])Formats a number
FormatCurrency(expression[, numdigitsafterdecimal[, includeleadingdigit[, useparensfornegativenumbers[, groupdigits]]]])Formats as currency
FormatPercent(expression[, numdigitsafterdecimal[, includeleadingdigit[, useparensfornegativenumbers[, groupdigits]]]])Formats as percentage

Information Functions

Note: type/coercion semantics generally follow VBScript, but may differ in edge cases involving Empty/Null/Nothing and host objects.

FunctionDescription
IsArray(varname)Returns True if variable is an array
IsDate(expression)Returns True if expression is a date
IsEmpty(expression)Returns True if variable is Empty
IsNull(expression)Returns True if expression is Null
IsNumeric(expression)Returns True if expression is numeric
IsObject(expression)Returns True if expression is an object
TypeName(varname)Returns the type name
VarType(varname)Returns the variant type

VarType Constants

ConstantValueDescription
vbEmpty0Empty (uninitialized)
vbNull1Null (no valid data)
vbInteger2Integer
vbLong3Long integer
vbSingle4Single-precision
vbDouble8Double-precision
vbCurrency6Currency
vbDate7Date
vbString8String
vbObject9Object
vbBoolean11Boolean
vbArray8192Array

Color Functions

FunctionDescription
RGB(red, green, blue)Returns an RGB color value

Classic ASP Objects

Server-Side Includes

<!--#include file="..." --> and <!--#include virtual="..." --> are resolved before the page runs. The two forms differ only in what the path is relative to:

FormResolved relative to
file="x.inc"The folder of the file containing the directive. Inside a nested include this is the include's own folder, not the requesting page's, so an .inc can pull in its siblings without knowing who included it.
virtual="/x.inc"The application root, at every nesting depth. A bare virtual="x.inc" (no leading slash) is also accepted and is treated as application-root relative.

Matching IIS, the scanner accepts backslashes as separators, ./ and ../ segments, single or double quotes, and loose whitespace around #include and =. Paths that climb above the application root are refused.

A file included twice is expanded twice. The directive is a textual splice, not an idempotent import, so including a snippet at two points in a page emits it at both — there is no #pragma once behaviour and no de-duplication. That also applies when the same file is reached by two different spellings (file= in one place, virtual= in another), or directly in one place and transitively through another include. Because definitions are hoisted before execution, a duplicated include that declares a Sub or Function does not raise a redefinition error; only its top-level statements and markup repeat.

A genuine cyclea.inc including b.inc including a.inc — is an error, not a silently truncated expansion, and fails the request as IIS does. Detection walks the current include chain, which is what keeps legitimate repeats working; nesting deeper than 32 levels is also refused.

Text that merely looks like a directive is left alone: #include inside a VBScript string literal or inside an ASP comment is inert.

Server Object

Methods

MethodDescription
CreateObject(progid)Creates an instance of a COM object
HTMLEncode(string)Encodes HTML characters
URLEncode(string)URL-encodes a string
MapPath(path)Maps a virtual path to a physical path. Never appends a trailing separator, even when path has one — MapPath("data/x/") and MapPath("data/x") both return ...\data\x, matching IIS, which cannot know whether the target is a file or a folder because it never touches the filesystem. So Server.MapPath("data/x/") & "f.txt" silently yields ...\data\xf.txt; put the filename inside the call instead: Server.MapPath("data/x/f.txt"). An empty path raises (see Runtime Error Parity).
Execute(path)Executes an ASP file
Transfer(path)Transfers execution to another ASP file
GetLastError()Returns the last error object

Properties

PropertyDescription
ScriptTimeoutGets/sets script timeout in seconds

Extension Methods (ASPPY-specific)

MethodDescription
ASPPYListAspPages()Lists all .asp pages under docroot
ASPPYRun(virtual_path)Runs another ASP page and captures output

Supported Server.CreateObject ProgIDs


Request Object

Properties

PropertyDescription
QueryStringNameValueCollection of query string parameters
FormNameValueCollection of form data parsed from POST only
CookiesNameValueCollection of cookies
ServerVariablesNameValueCollection of server variables
TotalBytesTotal bytes in request body
Method / HttpMethodHTTP method (GET, POST, PUT, DELETE, etc.)
FilesCollection of uploaded files
Request (default)Access to QueryString, Form, Cookies

Methods

MethodDescription
BinaryRead(count)Reads raw bytes from request body

HTTP methods and body parsing

Request.Files Collection

Access uploaded files from multipart form submissions.

' Iterate over all uploaded files
For Each file In Request.Files
    Response.Write(file.FileName)
    Response.Write(file.Size)
    Response.Write(file.ContentType)
    ' Save to disk
    file.SaveAs Server.MapPath("/uploads/" & file.FileName)
Next

' Access specific file
Set f = Request.Files("myfile")
If Not f Is Nothing Then
    Response.Write f.Name
    Response.Write f.FileName
    Response.Write f.Size
    Response.Write f.ContentType
End If

' Check if file exists
If Request.Files.Exists("myfile") Then
    ' File was uploaded
End If

' Get count
Response.Write Request.Files.Count

UploadedFile Properties

PropertyDescription
NameForm field name
FileNameOriginal filename
ContentTypeMIME content type
SizeFile size in bytes

UploadedFile Methods

MethodDescription
SaveAs(path)Saves the file to disk

Files Collection Methods

MethodDescription
CountNumber of uploaded files
Exists(name)Checks if file with given name exists
Item(name)Gets uploaded file by name
Keys()Returns array of field names
Items()Returns array of UploadedFile objects

Response Object

Properties

PropertyDescription
BufferEnables/disables response buffering
CookiesCollection of cookies to send
LCIDLocale identifier
ContentTypeMIME type of the response (default text/html)
CharsetCharacter set of the response body. Defaults to utf-8 - see Character Encoding
CodePageCodepage for this response. Defaults to 65001 (UTF-8), not the host ANSI codepage as under IIS

Methods

MethodDescription
Write(string)Writes output to the response
BinaryWrite(data)Writes binary data
Clear()Clears the buffered output
Flush()Flushes buffered output
End()Stops script execution
AddHeader(name, value)Adds a custom header
AppendToLog(message)Appends to server log
Redirect(url)Redirects to another URL
File(path[, inline])Serves a file (inline=True for display, False for download)

Session Object

Properties

PropertyDescription
SessionIDUnique session identifier
TimeoutSession timeout in minutes
ContentsCollection of session variables
StaticObjectsCollection of session-scoped objects
LCIDLocale identifier for the session
CodePageCodepage for the session. Defaults to 65001 (UTF-8), not the host ANSI codepage as under IIS - see Character Encoding

Methods

MethodDescription
Abandon()Ends the session

Application Object

Properties

PropertyDescription
ContentsCollection of application variables
StaticObjectsCollection of static objects

Methods

MethodDescription
Lock()Locks application variables
Unlock()Unlocks application variables

ASPPY Extended Objects

ASPPY Object

The ASPPY object provides additional functionality beyond classic ASP.

It is available as global ASPPY in script scope.

How members are named

Member access is always case-insensitive, so casing never matters. The shape of a name follows one rule:

Objects that exist on IIS use PascalCase. Wrappers around a third-party library keep that library's own names, so its documentation transfers unchanged.

SurfaceConventionExample
Response, Request, Server, Session, Application, ADODB, FSO, CDO, Scripting.Dictionary, VBScript.RegExpPascalCase, exactly as IISResponse.BinaryWrite
MSXML2.*camelCase — the real MSXML COM API is camelCasedoc.selectSingleNode
ASPPY.Imagelowercase — mirrors Pillowimg.thumbnail
ASPPY.Pdfsnake_case — mirrors fpdf2 (PascalCase aliases also provided, see PDF)pdf.set_margins / pdf.SetMargins
ASPPY.JSON, .Zip, .Crypto, ExecutePythonPascalCase — no upstream API to mirrorASPPY.JSON.Encode

If a member name is wrong, error 438 names the closest match rather than leaving you to guess: Unknown member: SETMARGINZ on PdfDoc (did you mean 'SetMargins'?).

Members beginning with _ are host-side internals. VBScript identifiers cannot start with an underscore, so they are unreachable from script and, like on IIS, raise 438.

JSON

ASPPY.JSON.Encode(value[, pretty])  ' Returns JSON string
ASPPY.JSON.Decode(json_string)      ' Returns VBScript value

Note: member access is case-insensitive in VBScript; runtime members are exposed as json, zip, image, crypto, pdf.

ZIP

ASPPY.Zip.Zip(path[, out_path])     ' Creates a ZIP file
ASPPY.Zip.Unzip(zip_path, dest_folder[, overwrite])  ' Extracts a ZIP file

Image (Pillow)

ASPPY.Image.open(path)               ' Opens an image file
ASPPY.Image.new(mode, size, color)    ' Creates a new image
ASPPY.Image.merge(mode, bands)       ' Merges image bands
ASPPY.Image.blend(img1, img2, alpha) ' Blends two images
ASPPY.Image.composite(img1, img2, mask) ' Creates composite

ASPPY.ImageDraw.Draw(img)            ' Creates a draw object
ASPPY.ImageFilter.BLUR               ' Blur filter constant
ASPPY.ImageFilter.CONTOUR            ' Contour filter
ASPPY.ImageFilter.EDGE_ENHANCE       ' Edge enhancement
ASPPY.ImageFilter.SHARPEN            ' Sharpen filter
ASPPY.ImageFilter.GaussianBlur(radius) ' Gaussian blur

ASPPY.ImageEnhance.Brightness(img)   ' Brightness enhancer
ASPPY.ImageEnhance.Contrast(img)     ' Contrast enhancer

ImageInstance Properties/Methods

Property/MethodDescription
sizeImage dimensions (width, height)
widthImage width
heightImage height
modeImage color mode
formatImage format
save(path)Saves the image
resize(size)Resizes the image
thumbnail(size)Creates thumbnail
crop(box)Crops the image
rotate(angle)Rotates the image
convert(mode)Converts color mode
split()Splits into bands
getpixel(xy)Gets pixel value
putpixel(xy, value)Sets pixel value
filter(filter_obj)Applies filter
paste(other_img, box[, mask])Pastes another image

PDF (FPDF)

Set pdf = ASPPY.Pdf.New([orientation[, unit[, format]]])

PdfDoc Methods

MethodDescription
add_page([orientation])Adds a new page
set_margins(left, top[, right])Sets page margins
set_auto_page_break(auto[, margin])Sets auto page break
set_font(family[, style[, size]])Sets the font
set_text_color(r[, g[, b]])Sets text color
set_draw_color(r[, g[, b]])Sets drawing color
set_fill_color(r[, g[, b]])Sets fill color
set_line_width(width)Sets line width
fill_page(r[, g[, b]])Fills the page with color
text(x, y, text)Writes text at position
cell(w[, h[, text[, border[, ln[, align[, fill[, link]]]]]]])Writes a cell
multi_cell(w, h, text[, border[, align[, fill]]])Writes multi-cell
set_xy(x, y)Sets current position
ln([h])Moves to next line
image(path[, x[, y[, w[, h]]]])Adds an image
output(path)Saves PDF to file

Crypto (bcrypt)

ASPPY.Crypto.Hash(password[, rounds])    ' Hashes a password (rounds 4-31, default 12)
ASPPY.Crypto.Verify(password, hashed)     ' Verifies password against hash

ExecutePython

ASPPY.ExecutePython(code [, args] [, timeout])       ' Executes inline Python source, returns ASPPY_RETURN value as string
ASPPY.ExecutePythonFile(path [, args] [, timeout])   ' Executes a .py file (relative to docroot), returns ASPPY_RETURN value

Enable with environment variable ASP_PY_ALLOW_PYTHON=1. Each call spawns an isolated Python subprocess. The Python snippet uses ASPPY_RETURN(value) to return a string to VBScript.

Both optional arguments work the same way on either method. args takes any JSON-encodable value (string, number, boolean, Array, Scripting.Dictionary, or a nesting of those) and the Python side reads it as the built-in ASPPY_ARGS, already decoded — it is None when the argument is omitted. timeout is a per-call limit in seconds that overrides ASP_PY_PYTHON_TIMEOUT. See the ASPPY.ExecutePython page for full documentation, samples, and environment variables.


COM Objects

Scripting.Dictionary

Set dict = Server.CreateObject("Scripting.Dictionary")
Property/MethodDescription
CountNumber of items
CompareModeComparison mode (0=Binary, 1=Text, 2=Database)
Item(key)Gets/sets item value
KeysReturns array of keys
ItemsReturns array of items
Add(key, item)Adds a key/item pair
Exists(key)Returns True if key exists
Remove(key)Removes a key
RemoveAll()Removes all items

Scripting.FileSystemObject

Set fso = Server.CreateObject("Scripting.FileSystemObject")
Property/MethodDescription
BuildPath(path, name)Builds a path
CreateTextFile(filename[, overwrite[, unicode]])Creates text file
OpenTextFile(filename[, iomode[, create[, format]]])Opens text file
GetFile(filepath)Gets File object
GetFolder(folderpath)Gets Folder object
GetDrive(drivespec)Gets Drive object
DriveExists(drivespec)Checks if drive exists
FileExists(filepath)Checks if file exists
FolderExists(folderpath)Checks if folder exists

File Object

Property/MethodDescription
PathFull path
NameFile name
SizeFile size
TypeFile type
DateCreatedCreation date
DateLastAccessedLast access date
DateLastModifiedLast modified date
DriveThe Drive OBJECT. Its default property is Path, so "" & f.Drive gives C: (no trailing separator); f.Drive.RootFolder.Path gives C:\
ParentFolderParent folder
ShortName8.3 NAME only, no path (contrast ShortPath)
ShortPath8.3 path
AttributesFile attributes
Copy(destination[, overwrite])Copies file
Move(destination)Moves file
Delete([force])Deletes file
OpenAsTextStream([iomode[, format]])Opens as text stream

Folder Object

Property/MethodDescription
PathFull path
NameFolder name
SizeTotal size of folder
DateCreatedCreation date
DateLastAccessedLast access date
DateLastModifiedLast modified date
DriveThe Drive OBJECT. Its default property is Path, so "" & f.Drive gives C: (no trailing separator); f.Drive.RootFolder.Path gives C:\
IsRootFolderTrue if root
FilesFiles collection
SubFoldersSubfolders collection
AttributesFolder attributes
Copy(destination[, overwrite])Copies folder
Move(destination)Moves folder
Delete([force])Deletes folder

TextStream Object

Property/MethodDescription
AtEndOfStreamTrue at end of file
Read(n)Reads n characters
ReadLine()Reads a line
ReadAll()Reads entire file
Write(string)Writes string
WriteLine([string])Writes line
WriteBlankLines(n)Writes blank lines
Close()Closes the stream
Skip(n)Skips n characters
SkipLine()Skips a line

VBScript.RegExp

Set regex = Server.CreateObject("VBScript.RegExp")
Property/MethodDescription
PatternRegular expression pattern
IgnoreCaseCase-insensitive matching
GlobalMatch all occurrences
MultiLineMulti-line matching
Test(string)Tests for a match
Replace(string, replace_with)Replaces matches
Execute(string)Returns match collection

ADODB.Connection

Set conn = Server.CreateObject("ADODB.Connection")
Property/MethodDescription
ConnectionStringConnection string
StateConnection state
CommandTimeoutCommand timeout
CursorLocationCursor location
VersionADO library version, "10.0" (what IIS on Windows 10 reports)
IsolationLeveladXactReadCommitted (4096)
AttributesConnectOptionEnum bitmask, 0
PropertiesProvider property collection — present so scripts run, but always empty (see the note below)
Open([connection_string])Opens connection
Close()Closes connection
Execute(sql[, records_affected[, options]])Executes SQL
OpenSchema(queryType[, criteria])Schema rowsets — see below
BeginTrans()Begins transaction
CommitTrans()Commits transaction
RollbackTrans()Rolls back transaction

Connection string notes:

ADODB.Recordset

Set rs = Server.CreateObject("ADODB.Recordset")
Property/MethodDescription
StateRecordset state
EOFEnd of file
BOFBeginning of file
RecordCountNumber of records
FieldsFields collection
Open([source[, active_conn[, cursor_type[, lock_type[, options]]]]])Opens recordset
Close()Closes recordset
MoveFirst()Moves to first record
MoveLast()Moves to last record
MoveNext()Moves to next record
MovePrevious()Moves to previous record
Move(n)Moves n records
AddNew()Starts insert mode for a new record
Update()Commits pending field changes
Delete()Deletes current record
ResyncResyncs with database
GetRows([rows[, start[, fields]]])Rows as a 2-D array, field-major
GetString([format[, rows[, colDelim[, rowDelim[, nullExpr]]]]])Rows as a delimited string — see below
BookmarkMarker for the current row; read it, move away, assign it back
CompareBookmarks(a, b)CompareEnum: 0 less, 1 equal, 2 greater, 4 not equal
AbsolutePosition, PageSize, PageCount, AbsolutePagePositioning and paging
Filter, Sort, Find, Clone, Requery, SupportsClient-side cursor operations

GetString

Reads forward from the current row and leaves the recordset at EOF (or just past the last row taken), as ADO does. Omitted arguments take the ADO defaults: TAB between columns, CR between rows, "" for Null. Every row is terminated, including the last.

' Pipe-separated, CRLF-terminated, empty string for NULL
gs = rs.GetString(, , "|", vbCrLf, "")

StringFormat only has one legal value, adClipString (2); anything else raises.

Bookmark

ASPPY recordsets are client-side cursors over an in-memory row list, so bookmarks are always available and are the row's ordinal. Assigning a bookmark outside the recordset raises rather than moving anywhere.

Divergence worth knowing: on IIS, bookmark support depends on the provider and cursor — a Jet OpenSchema recordset reports Supports(adBookmark) = False. ASPPY supports them everywhere, so code guarded by If rs.Supports(adBookmark) takes the enabled branch here and the disabled one there.

OpenSchema

Returns schema information as a Recordset, with the OLE DB rowset column names, so rs("TABLE_NAME") works as it does on IIS. Supported SchemaEnum values:

ConstantValueColumns
adSchemaTables20TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, …
adSchemaColumns4TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, IS_NULLABLE, DATA_TYPE, …
adSchemaIndexes12INDEX_NAME, TABLE_NAME, UNIQUE, PRIMARY_KEY, COLUMN_NAME (SQLite only)
adSchemaPrimaryKeys28PK_TABLE_NAME, COLUMN_NAME, ORDINAL (SQLite only)
Set rs = conn.OpenSchema(20)                              ' every table
Set rs = conn.OpenSchema(20, Array(Empty, Empty, "widget"))  ' one table
Set rs = conn.OpenSchema(4,  Array(Empty, Empty, "widget"))  ' its columns

criteria is an array of restriction values positionally matched to the rowset's restriction columns; Empty entries mean "no restriction". Any other queryType raises rather than returning an empty recordset. System tables are excluded.

ADODB.Field

Reached through rs.Fields(n), rs.Fields("name") or rs("name"). Its default property is Value, so a Field reads as its value in any expression.

PropertyDescription
Name, ValueColumn name and current value
TypeDataTypeEnum. SQLite reports no type in its cursor metadata, so it is inferred from the first non-NULL value in the column — SELECT 1 AS F1 gives adInteger (3), matching IIS
DefinedSizeDeclared size. For a fixed-width type this is the type's BYTE width (an adInteger column gives 4, as on IIS), otherwise the declared maximum
ActualSizeLength in bytes of the stored value: the type width for fixed types, the character or byte count for variable ones, 0 for Null
AttributesFieldAttributeEnum. 114 for a nullable fixed-width column (adFldMayDefer | adFldFixed | adFldIsNullable | adFldMayBeNull), 98 for a variable-length one
StatusFieldStatusEnum; adFieldOK (0) for a field read from a resultset
OriginalValue, UnderlyingValueThe bound row's value. No separate pre-edit copy is kept, and there is no re-fetch, so both report the current row value
Precision, NumericScaleReported as 0; IIS gets these from the provider (10 and 255 for a Jet integer)
PropertiesEmpty collection — see the note under ADODB.Command
AppendChunk(data)Appends to a long binary/text value

ADODB.Command

Set cmd = Server.CreateObject("ADODB.Command")
Property/MethodDescription
ActiveConnectionConnection used for execution
CommandTextSQL command text
CommandTypeCommand type (text)
ParametersParameters collection
CreateParameter(...)Creates a parameter object
Execute(...)Executes command and returns a recordset
StateObjectStateEnum: adStateClosed (0) / adStateOpen (1)
PropertiesProvider property collection (empty, see below)

Parameter objects returned by CreateParameter expose Name, Type, Direction, Size, Value, Precision, NumericScale, Attributes and Properties.

The Properties collections

Connection, Command, Recordset, Field and Parameter all expose a Properties collection so that obj.Properties.Count runs instead of raising error 438. It is always empty: those entries are OLE DB provider properties, and ASPPY talks to SQLite and ODBC directly rather than through a provider. IIS reports 14 on an unopened Connection, around 94 once it is opened against Jet, and 88 on a connected Command. Anything that enumerates them, or reads a specific provider property by name, will not find it.

ADODB.Parameter / Parameters

Property/MethodDescription
NameParameter name
TypeADO type constant
DirectionDirection (adParamInput, etc.)
SizeDeclared size
ValueParameter value
Parameters.Append(param)Adds parameter
Parameters.Item(name_or_index)Gets parameter
Parameters.CountNumber of parameters

ADODB.Stream

Set stream = Server.CreateObject("ADODB.Stream")
Property/MethodDescription
TypeStream type (1=Binary, 2=Text)
CharsetCharacter set
PositionCurrent position
SizeStream size
EOSEnd of stream
StateStream state
LineSeparatorLine separator
ModeOpen mode
Open()Opens stream
Close()Closes stream
LoadFromFile(filename)Loads from file
SaveToFile(filename[, options])Saves to file
Read([count])Reads bytes
ReadText([count])Reads text
Write(data)Writes bytes (needs a Byte() array, not a string)
WriteText(string[, options])Writes text. options is a StreamWriteEnum: adWriteChar (0, default) or adWriteLine (1), which appends LineSeparator
CopyTo(dest_stream[, count])Copies to another stream
SetEOS()Makes the current position the end of the stream, discarding everything after it
SkipLine()Skips line
Flush()Flushes buffer

Byte order marks, Size and Position

A text stream is measured in bytes, and ADO writes a byte order mark for the Unicode encodings. Verified against IIS by writing 16 characters plus one LF and reading the buffer back as binary:

CharsetSizeFirst bytes
utf-820EF BB BF 48 — 3-byte BOM
Unicode (UTF-16LE, the default)36FF FE 48 00 — 2-byte BOM
windows-1252, iso-8859-11748 65 6C 6C — no BOM

The BOM appears only once there is content: an open but empty stream has Size 0. Size, Position, Read, CopyTo, SaveToFile and switching Type to binary all share the same byte view, so at end of stream Position equals Size, and the file written by SaveToFile is byte-for-byte what Read returns.

This matters in practice: ADODB.Stream plus SaveToFile is the usual way Classic ASP produces UTF-8 CSV and XML, and consumers — Excel above all — rely on the BOM being there.

Known limitation. ReadText and SkipLine still index the buffer by character internally. For single-byte charsets and for ASCII text that is indistinguishable from the byte offset; it can differ for multi-byte content in the middle of a stream.

MSXML2 Objects

ServerXMLHTTP

Set http = Server.CreateObject("MSXML2.ServerXMLHTTP")
Property/MethodDescription
ReadyStateRequest state
StatusHTTP status code
StatusTextHTTP status text
ResponseTextResponse as text
ResponseXMLResponse as XML DOM
ResponseBodyResponse as a Byte() SafeArray — see below
Open(method, url[, async[, user[, password]]])Opens request
SetRequestHeader(header, value)Sets request header
Send([body])Sends request
SetTimeouts(resolve, connect, send, receive)Receive timeout is used as the overall timeout
SetProxy(setting[, server[, bypass]]) / SetProxyCredentials(user, pass)Recorded; requests always go direct
SetOption(option, value) / GetOption(option)Recorded and readable. Option 2 (ignore SSL cert errors) is deliberately not honoured
WaitForResponse([timeout])Waits for response (sends are synchronous, so always already complete)
GetResponseHeader(name) / GetAllResponseHeaders()Response headers
Abort()Aborts request

Binary payloads are a Byte() SafeArray

ResponseBody, Request.BinaryRead and ADODB.Stream.Read return a byte array, matching IIS exactly:

ExpressionResult
IsArray(b)True
TypeName(b)"Byte()"
VarType(b)8209 (vbArray + vbByte)
IsObject(b)False
LBound(b) / UBound(b)0 / LenB(b) - 1
b(0)the first byte, as an Integer

objStream.Write http.ResponseBody and Response.BinaryWrite accept it unchanged.

XMLHTTP

Set http = Server.CreateObject("MSXML2.XMLHTTP")

Same interface as ServerXMLHTTP.

DOMDocument

Set xml = Server.CreateObject("MSXML2.DOMDocument")
Property/MethodDescription
asyncAsync loading
readyStateDocument state
xmlXML content
textText content
load(url)Loads from URL
loadXML(xml_string)Loads from string
save(destination)Saves document
selectNodes(xpath)Selects nodes by XPath
selectSingleNode(xpath)Selects the first matching node, else Nothing
getElementsByTagName(tagname)Gets elements
transformNode(xsl) / transformNodeToObject(xsl, out)XSLT 1.0 transform (needs lxml)
createElement, createTextNode, createCDATASection, createComment, createProcessingInstruction, createDocumentFragment, createEntityReference, createAttribute, createNodeNode factories, each reporting the MSXML nodeType (1, 3, 4, 8, 7, 11, 5, 2)
baseName, prefix, namespaceURI, parsed, dataType, definition, specifiedPresent on the document and on element, attribute and text nodes
getAttributeNode(name), setAttribute, removeAttributeAttribute access on element nodes

Node model notes

XPath, XSLT and the optional lxml package

Python's bundled XML parser has no XSLT engine and only a small XPath subset, so those features come from lxml, which is optional in the same way fpdf2, Pillow and bcrypt are:

pip install lxml
FeatureWith lxmlWithout
XPath: //a/b, [@x='y'], [n]worksworks
XPath: namespace prefixes (//ns:Item), axes, text(), contains(), unions, absolute pathsworksraises, naming lxml
transformNode / transformNodeToObjectworksraises, naming lxml
CDATA, comments and PIs survive load → saveyesdiscarded on parse
Original namespace prefixes preserved on outputyesrewritten as ns0:

An unsupported XPath raises rather than returning an empty node list. Silently returning zero matches — which is what the stdlib parser does for anything outside its subset, including every namespaced document — is far harder to diagnose than an error.

Namespace prefixes in an XPath resolve from the document's own declarations, and from setProperty "SelectionNamespaces", "xmlns:a='urn:x'".

Deliberate divergences

WScript.Shell

Set wsh = Server.CreateObject("WScript.Shell")
Property/MethodDescription
ExpandEnvironmentStrings(s)Expands %NAME%. Unknown placeholders are preserved, as in WSH
Environment([scope])SYSTEM / USER / VOLATILE / PROCESS collection, with Count, Length and Item
CurrentDirectoryRead/write
SpecialFoldersCollection; sf("Windows"), "System", "Fonts", "Temp", "Desktop", … An unknown or unavailable name yields "" rather than raising
CreateShortcut(path).lnkIWshShortcut, .urlIWshURLShortcut. Any other extension raises
RegRead(name)Reads a registry value (Windows only). HKLM/HKCU/HKCR/HKU/HKCC and their long forms; a trailing \ reads the key's default value. REG_MULTI_SZ comes back as an array, REG_BINARY as a Byte()
Run, ExecNot implemented — raise a catchable 429, see below

IWshShortcut exposes TargetPath, Arguments, WorkingDirectory, Description, IconLocation, WindowStyle, Hotkey, FullName and Save. IWshURLShortcut exposes TargetPath, FullName and Save, and re-opening an existing .url reads its target back.

The .lnk writer builds a real [MS-SHLLINK] file — header, LinkTargetIDList, LinkInfo and the string blocks — rather than calling the Shell COM object, so the same code path runs on a non-Windows host. Shortcuts written by ASPPY were verified by reading them back through Windows' own WScript.Shell: every property round-trips and Explorer resolves the target. On a non-Windows host the LinkTargetIDList is omitted (it is produced by the Windows shell), which leaves a link Windows can parse but not resolve.

Deliberate divergences

CDO.Message

Set msg = Server.CreateObject("CDO.Message")
Property/MethodDescription
FromSender address
ToRecipient address(es)
CCCC recipients
BCCBCC recipients
SubjectMessage subject
HTMLBodyHTML body
TextBodyPlain text body
BodyPartBody part object
ConfigurationConfiguration object
DisableSendIf True, Send() is no-op success
AddAttachment(url)Adds attachment
Send()Sends the message

ASPPY.POP3

Set pop = Server.CreateObject("ASPPY.POP3")
MethodDescription
Connect/Open(host[, port[, use_ssl[, timeout]]])Connects to POP3 server
Login(user, pass)Authenticates
Stat()Returns message count and mailbox size
List()Returns message listing
UIDL([msg_num])UID listing or UID for one message
Retr/GetMessage(msg_num)Fetches a message object
Delete/Dele(msg_num)Marks message for deletion
DeleteAll()Marks all for deletion
Quit/Close()Closes connection

ASPPY.IMAP

Set imap = Server.CreateObject("ASPPY.IMAP")
MethodDescription
Connect/Open(host[, port[, use_ssl[, timeout]]])Connects to IMAP server
Login(user, pass)Authenticates
Select([folder[, readonly]])Selects mailbox
Search([criteria])Finds messages by sequence number
SearchUID([criteria])Finds messages by UID
Fetch/GetMessage(msg_num)Fetches message by sequence number
GetMessageByUID(uid)Fetches message by UID
Delete/Dele(msg_num)Marks message deleted
DeleteAll()Marks all selected messages deleted
Expunge()Permanently removes deleted messages
Logout/Close()Closes connection

Runtime Environment Variables

VariableDescription
ASP_PY_LOGEnables request log output from built-in server
ASP_PY_TRACE_REQUESTEnables verbose request/body trace logging
ASP_PY_REQ_MEM_MAXMax in-memory request body bytes before temp-file buffering
ASP_PY_ADO_ROOTSandbox root for ADODB.Stream filesystem operations
ASP_PY_FSO_ROOTSandbox root for Scripting.FileSystemObject
ASP_PY_HTTP_MAX_BYTESMax response bytes for MSXML HTTP requests
ASP_PY_HTTP_ALLOW_HOSTSComma-separated HTTP host allowlist for MSXML
ASP_PY_ALLOW_LOCALHOSTAllows MSXML HTTP access to localhost
ASP_PY_ALLOW_PRIVATE_NETSAllows MSXML HTTP access to private subnets
ASP_PY_XML_ALLOW_LOCALAllows DOMDocument.load() from a local file, and save() outside the application root. Saving inside the root needs no flag.
ASP_PY_CDO_DISABLE_SENDDisables SMTP send in CDO.Message
ASP_PY_CDO_ALLOW_OUTSIDE_DOCROOTAllows CDO attachments outside docroot
ASP_PY_ALLOW_PYTHONEnables ASPPY.ExecutePython (set to 1)
ASP_PY_PYTHON_TIMEOUTMax seconds for ExecutePython snippets (default 30)
ASP_PY_PYTHON_ROOTSandbox root for ExecutePythonFile

Runtime Error Parity

Err.Number is the contract ASPPY holds itself to: it is checked case by case against a live IIS 10 / VBScript 10.8 installation, because it is locale-independent and it is what real code branches on (If Err.Number = 9 Then). Behaviours verified against IIS include:

ExpressionErr.NumberMeaning
ReDim a(-1)0Legal. Creates an EMPTY dynamic array with UBound = -1, the same object Array() and Split("") return. This is the idiomatic empty growable list, and ReDim Preserve a(UBound(a) + 1) grows it.
ReDim a(-2)7Out of memory. Only -1 is a valid empty upper bound; anything lower asks for a negative element count.
Dim a(4) then ReDim a(9)10This array is fixed or temporarily locked. An array declared with explicit bounds cannot be resized - use Dim a().
Dim a() then UBound(a)9Subscript out of range. An un-ReDim'd dynamic array has no dimensions yet. (Contrast with ReDim a(-1) above.)
obj + 1, obj & "", obj = 1450Wrong number of arguments. An operator reads the object's DEFAULT property first; a Scripting.Dictionary's default is Item(key), which needs an argument.
rs.Fields("id") + 10Works: ADODB.Field's default property is Value. Likewise Request.QueryString used without an index yields the raw query string, and Request.Cookies("x") yields the cookie value.
Operator on an object with no default property438Object doesn't support this property or method.
CBool("xyz"), CBool("")13Type mismatch. CBool accepts only True/False or a number. Implicit truthiness (If "abc" Then) is separate and does not raise.
Server.MapPath("")-2147467259An empty path is rejected; it does not resolve to the application root.

Err.Number is a signed 32-bit Long, so an HRESULT such as 0x80004005 reports as -2147467259 - never as 2147500037. This holds for Err.Raise &H80004005 too.

Deliberate divergences

VBScript Compatibility Notes

Localization

ASPPY implements the Classic ASP locale model for 60 locales, driven by a generated table (ASPPY/locale_data.json) extracted from the Windows NLS data and validated field-by-field against live IIS output.

What is locale-aware

AreaBehaviour
FormatNumber, FormatCurrency, FormatPercentDecimal/group separators, group size, currency symbol and its position, per-currency decimal digits (ja-JP renders ¥1,234,568, no decimals), and the locale negative/parenthesised patterns
FormatDateTimeAll five named formats from the locale's own patterns. vbShortTime is always 24-hour but uses the locale time separator
MonthName, WeekdayNameLocalised names, including the genitive month forms Slavic/Baltic/Finnic locales use in long dates (Polish 5 marca, not 5 marzec)
Weekday, DatePart("w")vbUseSystemDayOfWeek resolves to the locale first day - Monday across most of Europe, Saturday for ar-DZ
CDbl, CCur, CInt, CLng, IsNumericLocale number parsing. CDbl("1,5") is 15 under en-US (comma = group separator) and 1.5 under nl-BE; fr-FR rejects "1.5" with a type mismatch, exactly as IIS does
CDate, IsDateLocale field order, so "3/5/2024" is March 5th under en-US and 5 March under nl-BE. A leading four-digit component is always read as ISO
StrComp(a, b, vbTextCompare)Locale collation rather than a lowercase compare: "straße" equals "strasse", and accents act as a tiebreak
GetLocale / SetLocaleNumeric LCIDs and short names ("nl-be", "de") across all 60 locales
Session.LCID, Response.LCID, <%@ LCID %>All three seed the script engine locale. Session.LCID is re-applied at the start of every request in the session, as under IIS

Deliberate divergences

Supported locales

en-US, en-GB, en-AU, en-CA · fr-FR, fr-BE, fr-CA, fr-CH · de-DE, de-CH, de-AT · nl-NL, nl-BE · es-ES (both sort orders), es-MX · it-IT, it-CH · pt-PT, pt-BR · ca-ES, gl-ES, eu-ES · sv-SE, sv-FI, da-DK, fi-FI, nb-NO, nn-NO, is-IS · pl-PL, cs-CZ, sk-SK, hu-HU, sl-SI, hr-HR, ro-RO, bg-BG, ru-RU, uk-UA, el-GR, tr-TR, et-EE, lv-LV, lt-LT · ja-JP, ko-KR, zh-CN, zh-TW, zh-HK · ar-SA, ar-DZ, he-IL, hi-IN, th-TH, id-ID, ms-MY, vi-VN, kk-KZ, uz-Latn-UZ.


Character Encoding

ASPPY defaults to UTF-8 (codepage 65001) everywhere. This is an intentional break from IIS.

Classic ASP under IIS inherits the system ANSI codepage of the host machine - typically 1252 on a Western Windows server, 932 on a Japanese one. ASPPY does not, for three reasons:

SurfaceASPPY defaultIIS default
Session.CodePage / Response.CodePage65001system ANSI (e.g. 1252)
Response.Charsetutf-8unset - header omits charset
.asp source filesUTF-8, BOM-tolerant, falls back to cp1252BOM, else @CODEPAGE, else metabase
Request.Form / Request.QueryStringdecoded as UTF-8decoded per Session.CodePage
Server.URLEncodepercent-encoded UTF-8percent-encoded per current codepage

Migration impact

Pages served as windows-1252 under IIS are served as UTF-8 by ASPPY and labelled as such in the Content-Type header, so browsers render them correctly with no source change. Legacy .asp files saved in windows-1252 need no conversion - ASPPY detects and decodes them automatically.

Intervention is only required where a non-browser consumer expects legacy bytes: CSV exports opened in Excel, fixed-format files for banking or EDI partners, or older integration endpoints that assume iso-8859-1. Set the encoding explicitly on those responses:

Response.Charset = "windows-1252"

Compatibility Matrix

SubsystemLevelNotes
Character encoding / codepagesIntentional divergenceUTF-8 (65001) by default instead of the host ANSI codepage - see Character Encoding
Core VBScript built-insNearBroad coverage for common Classic ASP apps; edge coercion behaviors can differ
Date/time and numeric formattingFullVerified against live IIS across 60 locales; only the ar-SA/th-TH calendars differ - see Localization
Localization (LCID model)Full60 locales; Session.LCID, Response.LCID, <%@ LCID %>, Get/SetLocale, locale parsing and collation
Request/Response/Server/Application/SessionNearClassic object model implemented; some operational semantics differ from IIS hosting internals
VBScript.RegExpNearCommon API and VB-style replacement tokens supported
Scripting.DictionaryNearCore collection behavior supported; COM-level quirks may differ
Scripting.FileSystemObject / ADODB.StreamPartialSupported in sandboxed model (docroot/root-constrained)
ADODB.Connection/Recordset/CommandPartialProvider support is runtime-dependent; ADO surface is compatibility-oriented
Database providersPartialSQLite, Access, Excel(read-only), generic ODBC, PostgreSQL via ODBC
MSXML HTTP/DOM shimsPartialSecurity-guarded and stdlib-focused; not full MSXML COM parity
CDO.MessagePartialPractical SMTP/pickup subset; not complete CDOSYS feature parity
POP3/IMAP shimsPartialLegacy-friendly subset for mailbox workflows
ASPPY.ExecutePythonFullSubprocess-based; full CPython access with ASPPY_RETURN convention

Global.asa Support

ASPPY supports the Global.asa file with the following events:

Example Global.asa:

<script language="vbscript" runat="server">
Sub Application_OnStart
    Application("StartTime") = Now()
End Sub

Sub Session_OnStart
    Session("UserID") = ""
End Sub
</script>

Running ASPPY

Start the built-in server:

python -m ASPPY.server [host] [port] [docroot]

Example:

python -m ASPPY.server 0.0.0.0 8080 www

The server will serve both static files and .asp pages from the specified document root.