diff --git a/src/python/pyrte.c b/src/python/pyrte.c
index 5ad3e90b39d15b74301ca31dd1456409327b6e8c_c3JjL3B5dGhvbi9weXJ0ZS5j..94b50ed5112d3ddd883fdb94aed679a9b447d403_c3JjL3B5dGhvbi9weXJ0ZS5j 100644
--- a/src/python/pyrte.c
+++ b/src/python/pyrte.c
@@ -353,7 +353,7 @@
 
 VERSION HISTORY (update SOFTWAREVN as well!)
 ---------------
-08-SEP-2020  MGD  v3.0.0,  Python 3 (via JFP Python 3.10.0a0)
+10-SEP-2020  MGD  v3.0.0,  Python 3 (via JFP Python 3.10.0a0)
                   v2.0.0,  Python 2 equivalent
                            Python 2.7.18 build kludge for
                              VSI C V7.4-001 on OpenVMS IA64 V8.4-2L1
@@ -2246,8 +2246,6 @@
 PyObject *args
 )
 {
-   int  DataLength;
-#if PY_MAJOR_VERSION >= 3
-   const char  *DataPtr;
-#else
+   int  isuni,
+        DataLength;
    char  *DataPtr;
@@ -2253,5 +2251,4 @@
    char  *DataPtr;
-#endif
    PyObject  *pValue;
 
    /*********/
@@ -2274,4 +2271,5 @@
       return (Py_None);
    }
 
+   DEBUGPYOBJ("pValue",pValue)
 #if PY_MAJOR_VERSION >= 3
@@ -2277,7 +2275,7 @@
 #if PY_MAJOR_VERSION >= 3
-   if (PyUnicode_Check (pValue))
+   if ((isuni = PyUnicode_Check (pValue)) || PyBytes_Check (pValue))
 #else
    if (PyString_Check (pValue))
 #endif
    {
 #if PY_MAJOR_VERSION >= 3
@@ -2279,9 +2277,12 @@
 #else
    if (PyString_Check (pValue))
 #endif
    {
 #if PY_MAJOR_VERSION >= 3
-      DataPtr = PyUnicode_AsUTF8AndSize (pValue, &DataLength);
+      if (isuni)
+         DataPtr = (char*)PyUnicode_AsUTF8AndSize (pValue, &DataLength);
+      else
+         PyBytes_AsStringAndSize (pValue, &DataPtr, &DataLength);
 #else
       PyString_AsStringAndSize (pValue, &DataPtr, &DataLength);
 #endif
@@ -2300,4 +2301,7 @@
       return (Py_None);
    }
 
+#if PY_MAJOR_VERSION >= 3
+   PyErr_SetString (PyExc_TypeError, "type must be Unicode, Bytes or None");
+#else
    PyErr_SetString (PyExc_TypeError, "type must be String or None");
@@ -2303,4 +2307,5 @@
    PyErr_SetString (PyExc_TypeError, "type must be String or None");
+#endif
    return (NULL);
 }
 
@@ -3016,7 +3021,7 @@
       return (Py_None);
    }
 
-   if (!PyArg_ParseTuple (args, "SO|O:start_response",
+   if (!PyArg_ParseTuple (args, "OO|O:start_response",
                           &pStatus, &pHeaders, &pWsgiExcInfo))
       return (NULL);
 
@@ -3079,4 +3084,7 @@
 {
    int  cnt,
         DataLength;
+#if PY_MAJOR_VERSION >= 3
+   const char  *DataPtr;
+#else
    char  *DataPtr;
@@ -3082,4 +3090,5 @@
    char  *DataPtr;
+#endif
    PyObject  *pItem,
              *pIter;
 
@@ -3094,9 +3103,9 @@
    DEBUGPYOBJ("pResponse",pResponse)
 
 #if PY_MAJOR_VERSION >= 3
-   if (PyBytes_Check (pResponse))
+   if (PyUnicode_Check (pResponse))
 #else
    if (PyString_Check (pResponse))
 #endif
    {
 #if PY_MAJOR_VERSION >= 3
@@ -3098,9 +3107,9 @@
 #else
    if (PyString_Check (pResponse))
 #endif
    {
 #if PY_MAJOR_VERSION >= 3
-      PyBytes_AsStringAndSize (pResponse, &DataPtr, &DataLength);
+      DataPtr = PyUnicode_AsUTF8AndSize (pResponse, &DataLength);
 #else
       PyString_AsStringAndSize (pResponse, &DataPtr, &DataLength);
 #endif
@@ -3117,8 +3126,8 @@
    {
       if (!WsgiHeadersSent) WsgiSendHeaders ();
 #if PY_MAJOR_VERSION >= 3
-      if (PyBytes_Check (pItem))
+      if (PyUnicode_Check (pItem))
 #else
       if (PyString_Check (pItem))
 #endif
 #if PY_MAJOR_VERSION >= 3
@@ -3121,8 +3130,8 @@
 #else
       if (PyString_Check (pItem))
 #endif
 #if PY_MAJOR_VERSION >= 3
-         if (!PyBytes_AsStringAndSize (pItem, &DataPtr, &DataLength))
+         if (DataPtr = PyUnicode_AsUTF8AndSize (pItem, &DataLength))
 #else
          if (!PyString_AsStringAndSize (pItem, &DataPtr, &DataLength))
 #endif
@@ -3152,5 +3161,6 @@
 int WsgiSendHeaders ()
 
 {
-   int  StatusCode;
+   int  StatusCode,
+        StatusLength;
    char  *NamePtr,
@@ -3156,3 +3166,2 @@
    char  *NamePtr,
-         *StatusPtr,
          *ValuePtr;
@@ -3158,4 +3167,9 @@
          *ValuePtr;
+#if PY_MAJOR_VERSION >= 3
+   const char  *StatusPtr;
+#else
+   char  *StatusPtr;
+#endif
    PyObject  *pItem,
              *pIter;
 
@@ -3170,7 +3184,7 @@
    if (!pWsgiHeaders || !pWsgiStatus) return (0);
 
 #if PY_MAJOR_VERSION >= 3
-   if (!PyBytes_Check (pWsgiStatus))
+   if (!PyUnicode_Check (pWsgiStatus))
 #else
    if (!PyString_Check (pWsgiStatus))
 #endif
@@ -3181,7 +3195,7 @@
 
    WsgiHeadersSent = 1;
 #if PY_MAJOR_VERSION >= 3
-   StatusPtr = PyBytes_AsString (pWsgiStatus);
+   StatusPtr = PyUnicode_AsUTF8AndSize (pWsgiStatus, &StatusLength);
 #else
    StatusPtr = PyString_AsString (pWsgiStatus);
 #endif
diff --git a/src/python/readmore.html b/src/python/readmore.html
index 5ad3e90b39d15b74301ca31dd1456409327b6e8c_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s..94b50ed5112d3ddd883fdb94aed679a9b447d403_c3JjL3B5dGhvbi9yZWFkbW9yZS5odG1s 100644
--- a/src/python/readmore.html
+++ b/src/python/readmore.html
@@ -60,6 +60,12 @@
   padding-left:1em;
 }
 
+.indent
+{
+  margin-left:2em;
+  margin-right:5em;
+}
+
 .quote {
   border-style:dotted; border-color:#888888; border-width:1px;
   font-size:90%;
@@ -89,8 +95,8 @@
 
 <h1>Python Run-Time Environment</h1>
 <h3>~~~ PRE-RELEASE ~~~</h3>
-<h3>Version 2.0.0, 8th September 2020
-<br>Version 3.0.0, 8th September 2020</h3>
+<h3>Version 2.0.0, 10th September 2020
+<br>Version 3.0.0, 10th September 2020</h3>
 
 <p><b>Copyright &copy; 2007-2020 Mark G. Daniel</b>
 <br>This program, comes with ABSOLUTELY NO WARRANTY.
@@ -335,6 +341,70 @@
 
 after changing mapping rules.
 
+<h3>Supporting Multiple Python Versions</h3>
+
+<p> It is sometimes useful, sometimes necessary, to maintain multiple versions
+of Python on a system, and also support multiple versions to web applications
+(scripts).  It is not difficult to have PyRTE activate a specific version by
+wrapping the executable in a procedure that sets up the appropriate Python
+environment in a process-local context.
+
+<pre class="code">
+$! CGI_BIN:PYRTE.COM
+$! activate JFP Python 2.7
+$ @disk$jfplib0020i:[000000]lib_logicals.com "/process"
+$ @disk$jfppy1400i:[000000]python_logicals.com "/process"
+$ @python_root:[vms]setup
+$ pyrte == "$cgi_exe:pyrte.exe"
+$ pyrte
+</pre>
+
+<pre class="code">
+$! CGI_BIN:PYRTE3.COM
+$! activate JFP Python 3.10
+$ @disk$jfplib0020i:[000000]lib_logicals.com "/process"
+$ @disk$jfppy3100i:[python3100.vms]logicals.com "/process"
+$ @python3_root:[vms]setup
+$ pyrte3 == "$cgi_exe:pyrte3.exe"
+$ pyrte3
+</pre>
+
+<p> Each of these is mapped for independent activation based on the request
+path.
+
+<pre class="code">
+# WASD_CONFIG_MAP
+exec /py-bin/* (@cgi_bin:pyrte.com)/wasd_root/src/python/scripts/* \
+     script=syntax=unix map=once script=query=none ods=5
+exec /py3-bin/* (@cgi_bin:pyrte3.com)/<i>volume<sup>**</sup></i>/wasd_root/src/python/scripts/* \
+     script=syntax=unix map=once script=query=none ods=5
+</pre>
+
+<table style="margin-left:2em;font-size:95%;">
+<td style="vertical-align:top;"><sup>**</sup></td><td>
+<b>Note</b> that the scripts activated are the same on-disk files, located in
+the PyRTE source directory.
+<br><b>Also note</b> that <tt>/py3-bin/</tt> mapping result is different to
+that of the <tt>/py-bin/</tt> mapping.  There is a /<i>volume</i>/ present.
+<br> This is only required where the same on-disk files are being scripted. 
+<b> This is a kludge.</b>  Due to the way WASD caches script information (on
+the disk file path), <b>including any associated RTE</b>, it was necessary to
+differentiate the two activations and the chosen solution was to include the
+physical volume (on which <tt>[WASD_ROOT]</tt> resides) in the mapping. 
+Another solution might be to use an alternate concealed logical device name.
+</td>
+</table>
+
+<p> This is one approach.  A little creativity would yield others.
+
+<h3>wasd.vsm.com.au</h3> 
+
+<p> At the time of writing the demonstration site uses this approach to
+default the examples below to (JFP) Python 2.7, while optionally allowing them
+to be activated under 3.10 &ndash; just modify the browser location field to
+contain <tt>/py3-bin/</tt> rather than the default <tt>/py-bin/</tt>.  Several
+of the scripts display the underlying Python version confirming the activations.
+
 <a name="example">
 <h2>Example Scripts</h2>
 </a>
@@ -573,7 +643,7 @@
 
 <dl>
 
-<dt>v3.0.0&nbsp; 08-SEP-2020 ~~~ PRE-RELEASE ~~~</dt>
+<dt>v3.0.0&nbsp; 10-SEP-2020 ~~~ PRE-RELEASE ~~~</dt>
 <dd>&bull;&nbsp; Python version 3.10.0a0</dd>
 </dd>
 
@@ -577,7 +647,7 @@
 <dd>&bull;&nbsp; Python version 3.10.0a0</dd>
 </dd>
 
-<dt>v2.0.0&nbsp; 08-SEP-2020 ~~~ PRE-RELEASE ~~~</dt>
+<dt>v2.0.0&nbsp; 10-SEP-2020 ~~~ PRE-RELEASE ~~~</dt>
 <dd>&bull;&nbsp; Python version 2.7.18</dd>
 </dd>
 
diff --git a/src/python/scripts/wsgi_test2.py b/src/python/scripts/wsgi_test2.py
old mode 100755
new mode 100644
index 5ad3e90b39d15b74301ca31dd1456409327b6e8c_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDIucHk=..94b50ed5112d3ddd883fdb94aed679a9b447d403_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDIucHk=
--- a/src/python/scripts/wsgi_test2.py
+++ b/src/python/scripts/wsgi_test2.py
@@ -1,8 +1,10 @@
 def application(environ, start_response):
     import cgi
+    import html
+    import platform
     write_fn = start_response('200 OK', [('Content-Type', 'text/html')])
     yield '<html><head><title>os.environment</title></head>\n' \
           '<body bgcolor="#ffffff" color="#000000">\n' \
           '<p><b><u>os.environment</b></u></p>\n' \
           '<table cellspacing="0" cellpadding="2" border="1">'
     names = environ.keys()
@@ -3,9 +5,9 @@
     write_fn = start_response('200 OK', [('Content-Type', 'text/html')])
     yield '<html><head><title>os.environment</title></head>\n' \
           '<body bgcolor="#ffffff" color="#000000">\n' \
           '<p><b><u>os.environment</b></u></p>\n' \
           '<table cellspacing="0" cellpadding="2" border="1">'
     names = environ.keys()
-    names.sort()
+    sorted(names)
     for name in names:
         yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
@@ -10,6 +12,6 @@
     for name in names:
         yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-            name, cgi.escape(`environ[name]`))
+            name, escape(str(environ[name])))
 
     form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
                             keep_blank_values=1)
@@ -20,8 +22,9 @@
         yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
             field.name, field.value)
 
-    write_fn('<tr><td>This has been write()n</td><td>:-)</td></tr>\n');
+    write_fn('<tr><td>This has been write()n :-)</td><td>Python ' +
+             platform.sys.version + '</td></tr>\n')
 
     yield '</table>\n' \
           '</body></html>\n'
 
@@ -24,6 +27,17 @@
 
     yield '</table>\n' \
           '</body></html>\n'
 
+def escape(s, quote=None):
+    '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
+    If the optional flag quote is true, the quotation mark character (")
+is also translated. (Python 2 and 3 neutral.)'''
+    s = s.replace("&", "&amp;") # Must be done first!
+    s = s.replace("<", "&lt;")
+    s = s.replace(">", "&gt;")
+    if quote:
+        s = s.replace('"', "&quot;")
+    return s
+
 import wasd
 wasd.wsgi_run(application)
diff --git a/src/python/scripts/wsgi_test3.py b/src/python/scripts/wsgi_test3.py
old mode 100755
new mode 100644
index 5ad3e90b39d15b74301ca31dd1456409327b6e8c_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDMucHk=..94b50ed5112d3ddd883fdb94aed679a9b447d403_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDMucHk=
--- a/src/python/scripts/wsgi_test3.py
+++ b/src/python/scripts/wsgi_test3.py
@@ -1,7 +1,7 @@
 def show_environ(environ, start_response):
     start_response('200 OK',[('Content-type','text/html')])
     sorted_keys = environ.keys()
-    sorted_keys.sort()
+    sorted(sorted_keys)
     return [
         '<html><body><h1>Keys in <tt>environ</tt></h1><p>',
         '<br />'.join(sorted_keys),
diff --git a/src/python/scripts/wsgi_test4.py b/src/python/scripts/wsgi_test4.py
old mode 100755
new mode 100644
index 5ad3e90b39d15b74301ca31dd1456409327b6e8c_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDQucHk=..94b50ed5112d3ddd883fdb94aed679a9b447d403_c3JjL3B5dGhvbi9zY3JpcHRzL3dzZ2lfdGVzdDQucHk=
--- a/src/python/scripts/wsgi_test4.py
+++ b/src/python/scripts/wsgi_test4.py
@@ -3,4 +3,9 @@
     It can operate as a standard CGI or as a CGIplus script.
     It accepts a GIF, JPEG or PNG file and displays it in the browser.
     """
+    # caters for Python 2 and 3
+    if hasattr(importlib,'reload'):
+       importlib.reload(cgi)
+    else:
+       reload(cgi)
     if os.environ["REQUEST_METHOD"] == "POST":
@@ -6,5 +11,5 @@
     if os.environ["REQUEST_METHOD"] == "POST":
-       display_uploaded_file (start_response, "image_file")
+       display_uploaded_file (start_response)
     else:
        print_html_form (start_response)
     return ('')
@@ -29,8 +34,8 @@
     wsgi_write = start_response('200 OK',[('Content-type','text/html')])
     wsgi_write (HTML_TEMPLATE % {'SCRIPT_NAME':os.environ['SCRIPT_NAME']})
 
-def display_uploaded_file (start_response, form_field):
+def display_uploaded_file (start_response):
     """This display an image file uploaded by an HTML form.
     """
 
     form = cgi.FieldStorage()
@@ -33,9 +38,9 @@
     """This display an image file uploaded by an HTML form.
     """
 
     form = cgi.FieldStorage()
-    if not form.has_key(form_field): return
-    fileitem = form[form_field]
+    if not 'image_file' in form: return
+    fileitem = form['image_file']
     if not fileitem.file: return
     if not fileitem.filename: return
     filename = fileitem.filename.lower()
@@ -47,7 +52,7 @@
     elif filename.rfind(".png") != -1:
        response_headers = [('Content-type','image/png')]
     else:
-       wsgi_write = start_response('200 OK',[('Content-type','text/html')])
+       wsgi_write = start_response(status,[('Content-type','text/html')])
        wsgi_write ("Only GIFs, JPEGs and PNGs handled by this test.")
        return
 
@@ -61,6 +66,7 @@
     del fileitem
 
 import wasd;
+import importlib
 import cgi
 import cgitb; cgitb.enable()
 import os, sys