44r'''
55learning.py
66
7- A Python 3 tutorial from http ://www.liaoxuefeng.com
7+ A Python 3 tutorial from https ://www.liaoxuefeng.com
88
99Usage:
1010
1111python3 learning.py
1212'''
1313
14+ # check #######################################################################
15+
1416import sys
17+ from datetime import datetime
18+
19+ CERT_EXPIRES = '2018-07-01'
1520
1621def check_version ():
1722 v = sys .version_info
18- if v .major == 3 and v .minor >= 4 :
19- return True
20- print ('Your current python is %d.%d. Please use Python 3.4.' % (v .major , v .minor ))
21- return False
22-
23- if not check_version ():
23+ if v .major == 3 and v .minor >= 5 :
24+ return
25+ print ('Your current python is %d.%d. Please use Python 3.6.' % (v .major , v .minor ))
2426 exit (1 )
2527
26- import os , io , json , subprocess , tempfile
28+ def check_cert ():
29+ today = datetime .now ().strftime ('%Y-%m-%d' )
30+ if today >= CERT_EXPIRES :
31+ print ('This learning.py is expired. Please download a newer version.' )
32+ exit (1 )
33+
34+ check_version ()
35+ check_cert ()
36+
37+ # start server ################################################################
38+
39+ import os , io , json , subprocess , tempfile , ssl
2740from urllib import parse
28- from wsgiref . simple_server import make_server
41+ from http . server import HTTPServer , BaseHTTPRequestHandler , SimpleHTTPRequestHandler
2942
3043EXEC = sys .executable
3144PORT = 39093
32- HOST = 'local.liaoxuefeng.com:%d' % PORT
3345TEMP = tempfile .mkdtemp (suffix = '_py' , prefix = 'learn_python_' )
34- INDEX = 0
46+
47+ HTML_INDEX = r'''
48+ <html>
49+ <head><title>Learning Python</title></head>
50+ <body>
51+ <form method="post" action="/run">
52+ <textarea name="code" style="width:90%;height: 600px"></textarea>
53+ <p><button type="submit">Run</button></p>
54+ </form>
55+ </body>
56+ </html>
57+ '''
58+
59+ class LearningHTTPRequestHandler (BaseHTTPRequestHandler ):
60+
61+ def do_GET (self ):
62+ if self .path != '/' :
63+ return self .send_error (404 )
64+ self ._sendHttpHeader ('text/html' )
65+ self ._sendHttpBody (HTML_INDEX )
66+
67+ def do_POST (self ):
68+ if self .path != '/run' :
69+ return self .send_error (400 )
70+ print ('Prepare code...' )
71+ body = self .rfile .read (int (self .headers ['Content-length' ]))
72+ qs = parse .parse_qs (body .decode ('utf-8' ))
73+ if not 'code' in qs :
74+ return self .send_error (400 )
75+ code = qs ['code' ][0 ]
76+ r = dict ()
77+ try :
78+ fpath = write_py (get_name (), code )
79+ print ('Execute: %s %s' % (EXEC , fpath ))
80+ r ['output' ] = decode (subprocess .check_output ([EXEC , fpath ], stderr = subprocess .STDOUT , timeout = 5 ))
81+ except subprocess .CalledProcessError as e :
82+ r = dict (error = 'Exception' , output = decode (e .output ))
83+ except subprocess .TimeoutExpired as e :
84+ r = dict (error = 'Timeout' , output = '执行超时' )
85+ except subprocess .CalledProcessError as e :
86+ r = dict (error = 'Error' , output = '执行错误' )
87+ print ('Execute done.' )
88+ self ._sendHttpHeader ()
89+ self ._sendHttpBody (r )
90+
91+ def _sendHttpHeader (self , contentType = 'application/json' ):
92+ origin = self .headers ['Origin' ] or 'https://www.liaoxuefeng.com'
93+ self .send_response (200 )
94+ self .send_header ('Content-Type' , contentType )
95+ self .send_header ('Access-Control-Allow-Origin' , origin )
96+ self .send_header ('Access-Control-Allow-Methods' , 'GET,POST' )
97+ self .send_header ('Access-Control-Max-Age' , '86400' )
98+ self .end_headers ()
99+
100+ def _sendHttpBody (self , data ):
101+ body = b''
102+ if isinstance (data , bytes ):
103+ body = data
104+ elif isinstance (data , str ):
105+ body = data .encode ('utf-8' , errors = 'ignore' )
106+ else :
107+ body = json .dumps (data ).encode ('utf-8' , errors = 'ignore' )
108+ self .wfile .write (body )
35109
36110def main ():
37- httpd = make_server ('127.0.0.1' , PORT , application )
111+ certfile = write_cert ()
112+ httpd = HTTPServer (('127.0.0.1' , PORT ), LearningHTTPRequestHandler )
113+ httpd .socket = ssl .wrap_socket (httpd .socket , certfile = certfile , server_side = True )
38114 print ('Ready for Python code on port %d...' % PORT )
115+ print ('Press Ctrl + C to exit...' )
39116 httpd .serve_forever ()
40117
118+ # functions ###################################################################
119+
120+ INDEX = 0
121+
41122def get_name ():
42123 global INDEX
43124 INDEX = INDEX + 1
@@ -56,51 +137,112 @@ def decode(s):
56137 except UnicodeDecodeError :
57138 return s .decode ('gbk' )
58139
59- def application (environ , start_response ):
60- host = environ .get ('HTTP_HOST' )
61- method = environ .get ('REQUEST_METHOD' )
62- path = environ .get ('PATH_INFO' )
63- if method == 'GET' and path == '/' :
64- start_response ('200 OK' , [('Content-Type' , 'text/html' )])
65- return [b'<html><head><title>Learning Python</title></head><body><form method="post" action="/run"><textarea name="code" style="width:90%;height: 600px"></textarea><p><button type="submit">Run</button></p></form></body></html>' ]
66- if method == 'GET' and path == '/env' :
67- start_response ('200 OK' , [('Content-Type' , 'text/html' )])
68- L = [b'<html><head><title>ENV</title></head><body>' ]
69- for k , v in environ .items ():
70- p = '<p>%s = %s' % (k , str (v ))
71- L .append (p .encode ('utf-8' ))
72- L .append (b'</html>' )
73- return L
74- if host != HOST or method != 'POST' or path != '/run' or not environ .get ('CONTENT_TYPE' , '' ).lower ().startswith ('application/x-www-form-urlencoded' ):
75- start_response ('400 Bad Request' , [('Content-Type' , 'application/json' )])
76- return [b'{"error":"bad_request"}' ]
77- s = environ ['wsgi.input' ].read (int (environ ['CONTENT_LENGTH' ]))
78- qs = parse .parse_qs (s .decode ('utf-8' ))
79- if not 'code' in qs :
80- start_response ('400 Bad Request' , [('Content-Type' , 'application/json' )])
81- return [b'{"error":"invalid_params"}' ]
82- name = qs ['name' ][0 ] if 'name' in qs else get_name ()
83- code = qs ['code' ][0 ]
84- headers = [('Content-Type' , 'application/json' )]
85- origin = environ .get ('HTTP_ORIGIN' , '' )
86- if origin .find ('.liaoxuefeng.com' ) == - 1 :
87- start_response ('400 Bad Request' , [('Content-Type' , 'application/json' )])
88- return [b'{"error":"invalid_origin"}' ]
89- headers .append (('Access-Control-Allow-Origin' , origin ))
90- start_response ('200 OK' , headers )
91- r = dict ()
92- try :
93- fpath = write_py (name , code )
94- print ('Execute: %s %s' % (EXEC , fpath ))
95- r ['output' ] = decode (subprocess .check_output ([EXEC , fpath ], stderr = subprocess .STDOUT , timeout = 5 ))
96- except subprocess .CalledProcessError as e :
97- r = dict (error = 'Exception' , output = decode (e .output ))
98- except subprocess .TimeoutExpired as e :
99- r = dict (error = 'Timeout' , output = '执行超时' )
100- except subprocess .CalledProcessError as e :
101- r = dict (error = 'Error' , output = '执行错误' )
102- print ('Execute done.' )
103- return [json .dumps (r ).encode ('utf-8' )]
140+ # certificate #################################################################
141+
142+ def write_cert ():
143+ fpath = os .path .join (TEMP , 'local.liaoxuefeng.com.pem' )
144+ with open (fpath , 'w' , encoding = 'utf-8' ) as f :
145+ f .write (CERT_DATA )
146+ return fpath
147+
148+ CERT_DATA = r'''
149+ -----BEGIN RSA PRIVATE KEY-----
150+ MIIEpAIBAAKCAQEA5b3zv77bzN8JShoXkVjXFG9NK342wnfgye+NmuSoRfCMPrwL
151+ GToJZiwxokXGyVY8obmQA69WUgonpuotp0AhDK8bOvykKuX+7MyE36sh9TUVOiIw
152+ 6D3ODG2E1K5IIEytfGhuPmeJe3OapnL/lJytlLhonu63VEf+jpYh6I/gXD4hZu1N
153+ tx4rOeJLl3qSFgRgX4oBZtO0d4aHop/XW5XcdaeuJxIW5qw7PxXqlch4h9TUs0Og
154+ udko380DrO9pmTuVXr2rtk6Jy96wDAVwsAiSX85FF59Q3QmjDzM+fx8a/vyWkxOw
155+ 9dT0DzfZBJ9g/tPk13ir14U1vPpDMO1lIXjKZQIDAQABAoIBAEp+FwT7W8XII/j1
156+ EOM+DS9BD6KkoBjaSfbwR9gLgEx9PNwymN6rJNUOS2G0gkpSPgKqannnZnPfew/y
157+ Kq9qacz1Ej1EIe8O+GPLxOHJWY9qkOFyqK0FLUR2VnWntRdUBYSrT+PIKpnu2BxU
158+ eW60hswMJ1AxUxxu46lUINaJoFQH7ANHzpCxXFiWDiVK8VCCFS3MaJfqz945zLdg
159+ orHMty+gCcaWRX9wanZ5k9RmX2qzqLAAhlnKIfmItpel8HTppOk1taVDopUgPGd+
160+ 8IG7IANbRlykkeBvlA5soSk7RNM2+crHcdCz5ehoSjMHsY3e99EPoPjKQmb3Ak0p
161+ zgtjPpkCgYEA8p+eULq0OM2anLQeOqQe2QNxkTuriOI2qyobOnCYAJRFlxCLJ8K8
162+ OIf+srIJUYzJsGmPSqo8iizM5V9bmBRihYnPwePUJS0af8G2vdeJARoAFBo4LZNi
163+ YBN3Gi6hKo8hDgOPQ6D2a25NXYua4z5HlOb8PU6JhX1/6O6yv4ScZh8CgYEA8miG
164+ QxHbx3a7Zfy3/QCQrEwmbp9k49ex/DZvh50N5MqG9h6lb6+ct+qUT7HkvcXUrBxL
165+ 6HP7aLVUTTvo9z10sBh1Kt1UQP/57JbKQNMCYKUbMxZH59XdFPzVwkbyDE7Z29tf
166+ 8QEMnk9CM6HbvbQDSvOZ04IsfSEstMEPKqVmFvsCgYEAsOSborRdTcTp4zKXj521
167+ N/gAxyjAKe70eNscOwF4cYOpMTjInFaosHbGxjZ0ANcq/coYxRFVTlDXmqxptXm3
168+ UzFlHjIjrG80EM2FlOgeZYU1ZXKwXtpEMVQ/1AEHVGZCbVs/CsnCoBUtpvRwGxp/
169+ ShsW8QPf1EnqBkRyYpwnA3UCgYA5nrLbWnFddlGRKoMpdmrtKaSxAt5ecjTyeJYG
170+ LETTL3jpI9u7MokUBoR+dRCkM1QcHRXGCVunRgLl4Om9azRDb2zaZYXTdYUYwbcN
171+ tZqJEnXmrNMmvmUwyfCdn3OFjXCnm/uwM8mmD7zyvPSYoSNvO3xDFFwy2iHgTUun
172+ nW0o5QKBgQCF/mCu/z1+CsddcHu1RYjnG08uVk0ErjTLB5Qy0YI2NWVE2zdV9fi9
173+ MWn7S+oEcmjaMdXdF0W/MDyB8TLUSdsXGHPl/vRizK3vhDQGxQ+y3ru8GEVq8BjX
174+ YePMDifbgHDXrCeBeb7TypefD/ScxdVJdI9sPSno20AehzDkXcAssQ==
175+ -----END RSA PRIVATE KEY-----
176+
177+ -----BEGIN CERTIFICATE-----
178+ MIIFzTCCBLWgAwIBAgIQB594sjWBo6qup5zlKa2F7zANBgkqhkiG9w0BAQsFADCB
179+ lzELMAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMs
180+ IEluYy4xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxHTAbBgNVBAsT
181+ FERvbWFpbiBWYWxpZGF0ZWQgU1NMMSEwHwYDVQQDExhUcnVzdEFzaWEgRFYgU1NM
182+ IENBIC0gRzUwHhcNMTcwNzA1MDAwMDAwWhcNMTgwNzA1MjM1OTU5WjAgMR4wHAYD
183+ VQQDDBVsb2NhbC5saWFveHVlZmVuZy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
184+ DwAwggEKAoIBAQDlvfO/vtvM3wlKGheRWNcUb00rfjbCd+DJ742a5KhF8Iw+vAsZ
185+ OglmLDGiRcbJVjyhuZADr1ZSCiem6i2nQCEMrxs6/KQq5f7szITfqyH1NRU6IjDo
186+ Pc4MbYTUrkggTK18aG4+Z4l7c5qmcv+UnK2UuGie7rdUR/6OliHoj+BcPiFm7U23
187+ His54kuXepIWBGBfigFm07R3hoein9dbldx1p64nEhbmrDs/FeqVyHiH1NSzQ6C5
188+ 2SjfzQOs72mZO5Vevau2TonL3rAMBXCwCJJfzkUXn1DdCaMPMz5/Hxr+/JaTE7D1
189+ 1PQPN9kEn2D+0+TXeKvXhTW8+kMw7WUheMplAgMBAAGjggKJMIIChTAgBgNVHREE
190+ GTAXghVsb2NhbC5saWFveHVlZmVuZy5jb20wCQYDVR0TBAIwADBhBgNVHSAEWjBY
191+ MFYGBmeBDAECATBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2Nw
192+ czAlBggrBgEFBQcCAjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTAfBgNVHSME
193+ GDAWgBRtWMd/GufhPy6mjJc1Qrv00zisPzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0l
194+ BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGbBggrBgEFBQcBAQSBjjCBizA8Bggr
195+ BgEFBQcwAYYwaHR0cDovL3RydXN0YXNpYTItb2NzcC5kaWdpdGFsY2VydHZhbGlk
196+ YXRpb24uY29tMEsGCCsGAQUFBzAChj9odHRwOi8vdHJ1c3Rhc2lhMi1haWEuZGln
197+ aXRhbGNlcnR2YWxpZGF0aW9uLmNvbS90cnVzdGFzaWFnNS5jcnQwggEDBgorBgEE
198+ AdZ5AgQCBIH0BIHxAO8AdQDd6x0reg1PpiCLga2BaHB+Lo6dAdVciI09EcTNtuy+
199+ zAAAAV0QuhtTAAAEAwBGMEQCIERpJLcXX4eFEW02eZMh6EYFW236foQrCsakOgQQ
200+ NW96AiBCE6/7vrUxWKf964i4D9z2NA0TCuXm9giVgQaFju0XXQB2AKS5CZC0GFgU
201+ h7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABXRC6G5MAAAQDAEcwRQIhAK4GeiTU
202+ OQcjGFvLAugOMIEKCEuiJsEu0t+f1ql5Edn2AiAUqqLMw87IkjMnJsbEUGsHkng0
203+ KL6MLDC3BaHVcH6HsjANBgkqhkiG9w0BAQsFAAOCAQEAYDU2DSBh/63brAW/VWlQ
204+ PQIZzWhji6209MG5hg/RN3Zo9uoz2GodNWwOSkvcFVUz9oBExLfcfhZAsBz26dgs
205+ abWstAje63oduhXU9MR1LDFfG6GLit0Pou0yiS0hfg3jpxpYCIo97QAe8bkuMdRQ
206+ 7V09yKKo44M+iXbkIUivnM1ckYJHU9xQ3y8/q/DQajUmVIEPRzmyz6B3tP4WA11T
207+ X5T89OK6osvLcYSJXvxOeR3J8Ohxdwi+PRX4BCgXgTseOj+biwJuCo9z7uwvCoXG
208+ fdilj1tXNa5eDtSRplqbFB+kPGkP/NZ5b1+huarDqE/aeNpmREqONhxi49KB1/9u
209+ 1w==
210+ -----END CERTIFICATE-----
211+
212+ -----BEGIN CERTIFICATE-----
213+ MIIFZTCCBE2gAwIBAgIQOhAOfxCeGsWcxf/2QNXkQjANBgkqhkiG9w0BAQsFADCB
214+ yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
215+ ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
216+ U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
217+ ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
218+ aG9yaXR5IC0gRzUwHhcNMTYwODExMDAwMDAwWhcNMjYwODEwMjM1OTU5WjCBlzEL
219+ MAkGA1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIElu
220+ Yy4xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5ldHdvcmsxHTAbBgNVBAsTFERv
221+ bWFpbiBWYWxpZGF0ZWQgU1NMMSEwHwYDVQQDExhUcnVzdEFzaWEgRFYgU1NMIENB
222+ IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39aSJZG/97x3a
223+ 6Qmuc9+MubagegRAVUmFYHTYTs8IKB2pM7wXN7W8mekdZaEgUjDFxvRBK/DhTb7U
224+ 8ONLsKKdT86aOhzbz2noCTn9wPWnGwkg+/4YKg/dPQQdV9tMsSu0cwqInWHxSAkm
225+ AI1hYFC9D7Sf7Hp/5cRcD+dK454YMRzNOGLQnCVI8JEqrz6o9SOvQNTqTcfqt6DC
226+ 0UlXG+MPD1eNPjlzf1Vwaab+VSTgySoC+Ikbq2VsdykeOiGXW/OIiASH7+2LcR05
227+ PmQ7GEOlM8yzoVojFpM8sHz+WxI05ZOPri5+vX3HhHHjWr5432G0dVmgohnZvlVZ
228+ oy8XrlbpAgMBAAGjggF2MIIBcjASBgNVHRMBAf8ECDAGAQH/AgEAMC8GA1UdHwQo
229+ MCYwJKAioCCGHmh0dHA6Ly9zLnN5bWNiLmNvbS9wY2EzLWc1LmNybDAOBgNVHQ8B
230+ Af8EBAMCAQYwLgYIKwYBBQUHAQEEIjAgMB4GCCsGAQUFBzABhhJodHRwOi8vcy5z
231+ eW1jZC5jb20wYQYDVR0gBFowWDBWBgZngQwBAgEwTDAjBggrBgEFBQcCARYXaHR0
232+ cHM6Ly9kLnN5bWNiLmNvbS9jcHMwJQYIKwYBBQUHAgIwGRoXaHR0cHM6Ly9kLnN5
233+ bWNiLmNvbS9ycGEwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCkGA1Ud
234+ EQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0yLTYwMTAdBgNVHQ4EFgQU
235+ bVjHfxrn4T8upoyXNUK79NM4rD8wHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6
236+ Aq8zMTMwDQYJKoZIhvcNAQELBQADggEBABUphhBbeG7scE3EveIN0dOjXPgwgQi8
237+ I2ZAKYm6DawoGz1lEJVdvFmkyMbP973X80b7mKmn0nNbe1kjA4M0O0hHaMM1ZaEv
238+ 7e9vHEAoGyysMO6HzPWYMkyNxcCV7Nos2Uv4RvLDpQHh7P4Kt6fUU13ipcynrtQD
239+ 1lFUM0yoTzwwFsPu3Pk+94hL58ErqwqJQwxoHMgLIQeMVHeNKcWFy1bddSbIbCWU
240+ Zs6cMxhrra062ZCpDCbxyEaFNGAtYQMqNz55Z/14XgSUONZ/cJTns6QKhpcgTOwB
241+ fnNzRnk+aWreP7osKhXlz4zs+llP7goBDKFOMMtoEXx3YjJCKgpqmBU=
242+ -----END CERTIFICATE-----
243+ '''
244+
245+ # start main at last ##########################################################
104246
105247if __name__ == '__main__' :
106248 main ()
0 commit comments