aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Scragly <[email protected]>2019-01-20 20:42:34 +1000
committerGravatar Scragly <[email protected]>2019-01-20 20:42:34 +1000
commit2657852ee3e97ee2dc233c932bd7c88bceec94b1 (patch)
tree9f43cc705fe2b217b8fea1cababb97b795389757
parentUpgrades Pipfile.lock to use requests 2.20.1, fixes CVE-2018-18074 (#2) (diff)
Remove RMQ, Add API POST request method.
-rw-r--r--Pipfile19
-rw-r--r--Pipfile.lock337
-rw-r--r--README.md59
-rw-r--r--config.py35
-rw-r--r--docker-compose.yml40
-rw-r--r--docker/Dockerfile3
-rw-r--r--docker/Dockerfile.webapp25
-rw-r--r--rmq.py132
-rw-r--r--snekbox.py65
-rw-r--r--snekweb.py73
-rw-r--r--templates/index.html114
-rw-r--r--templates/result.html9
-rw-r--r--tests/test_snekbox.py20
13 files changed, 243 insertions, 688 deletions
diff --git a/Pipfile b/Pipfile
index b2ec729..15254a2 100644
--- a/Pipfile
+++ b/Pipfile
@@ -4,15 +4,12 @@ verify_ssl = true
name = "pypi"
[packages]
-pika = "*"
docker = "*"
-
-[dev-packages]
flask = "*"
-flask-sockets = "*"
-gevent = "==1.2.2"
-gevent-websocket = "*"
+gevent = "*"
gunicorn = "*"
+
+[dev-packages]
"flake8" = "*"
pytest = "*"
pytest-cov = "*"
@@ -25,18 +22,10 @@ python_version = "3.6"
lint = "flake8"
test = "py.test tests --cov . --cov-report term-missing -v"
report = "py.test tests --cov . --cov-report=html"
-
-snekbox = "python snekbox.py"
-snekweb = "gunicorn -w 2 -b 0.0.0.0:5000 --log-level debug -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker snekweb:app"
-
+snekbox = "gunicorn -w 2 -b 0.0.0.0:8060 snekbox:app"
buildbox = "docker build -t pythondiscord/snekbox:latest -f docker/Dockerfile ."
pushbox = "docker push pythondiscord/snekbox:latest"
-
buildboxbase = "docker build -t pythondiscord/snekbox-base:latest -f docker/base.Dockerfile ."
pushboxbase = "docker push pythondiscord/snekbox-base:latest"
-
buildci = "docker build -t pythondiscord/snekbox-ci:latest -f docker/ci.Dockerfile ."
pushci = "docker push pythondiscord/snekbox-ci:latest"
-
-buildweb = "docker build -t pythondiscord/snekboxweb:latest -f docker/Dockerfile.webapp ."
-pushweb = "docker push pythondiscord/snekboxweb:latest"
diff --git a/Pipfile.lock b/Pipfile.lock
index d2f8198..ae63b8c 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "db284676e7f7232ab8e2f8dbda2319fcb62fe648209c2ff8db78c6db058b7d1e"
+ "sha256": "7b1debb4f15f17babadb882a0adc77f19a2e674100cf08d2675e2e5e8a9c8de3"
},
"pipfile-spec": 6,
"requires": {
@@ -18,10 +18,10 @@
"default": {
"certifi": {
"hashes": [
- "sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c",
- "sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a"
+ "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
+ "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
],
- "version": "==2018.10.15"
+ "version": "==2018.11.29"
},
"chardet": {
"hashes": [
@@ -30,80 +30,6 @@
],
"version": "==3.0.4"
},
- "docker": {
- "hashes": [
- "sha256:145c673f531df772a957bd1ebc49fc5a366bcd55efa0e64bbd029f5cc7a1fd8e",
- "sha256:666611862edded75f6049893f779bff629fdcd4cd21ccf01d648626e709adb13"
- ],
- "index": "pypi",
- "version": "==3.6.0"
- },
- "docker-pycreds": {
- "hashes": [
- "sha256:0a941b290764ea7286bd77f54c0ace43b86a8acd6eb9ead3de9840af52384079",
- "sha256:8b0e956c8d206f832b06aa93a710ba2c3bcbacb5a314449c040b0b814355bbff"
- ],
- "version": "==0.3.0"
- },
- "idna": {
- "hashes": [
- "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
- "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
- ],
- "version": "==2.7"
- },
- "pika": {
- "hashes": [
- "sha256:035e4e46069a81d1135eed27cf74ef0fedf9a0a32285966717233529e9f69bae",
- "sha256:306145b8683e016d81aea996bcaefee648483fc5a9eb4694bb488f54df54a751"
- ],
- "index": "pypi",
- "version": "==0.12.0"
- },
- "requests": {
- "hashes": [
- "sha256:65b3a120e4329e33c9889db89c80976c5272f56ea92d3e74da8a463992e3ff54",
- "sha256:ea881206e59f41dbd0bd445437d792e43906703fff75ca8ff43ccdb11f33f263"
- ],
- "version": "==2.20.1"
- },
- "six": {
- "hashes": [
- "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
- "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
- ],
- "version": "==1.11.0"
- },
- "urllib3": {
- "hashes": [
- "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
- "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
- ],
- "version": "==1.24.1"
- },
- "websocket-client": {
- "hashes": [
- "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786",
- "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849"
- ],
- "version": "==0.54.0"
- }
- },
- "develop": {
- "atomicwrites": {
- "hashes": [
- "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
- "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
- ],
- "version": "==1.2.1"
- },
- "attrs": {
- "hashes": [
- "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
- "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
- ],
- "version": "==18.2.0"
- },
"click": {
"hashes": [
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
@@ -111,49 +37,20 @@
],
"version": "==7.0"
},
- "coverage": {
+ "docker": {
"hashes": [
- "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f",
- "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe",
- "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d",
- "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0",
- "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607",
- "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d",
- "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b",
- "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3",
- "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e",
- "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815",
- "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36",
- "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1",
- "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14",
- "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c",
- "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794",
- "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b",
- "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840",
- "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd",
- "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82",
- "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952",
- "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389",
- "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f",
- "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4",
- "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da",
- "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647",
- "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d",
- "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42",
- "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478",
- "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b",
- "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb",
- "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"
+ "sha256:2840ffb9dc3ef6d00876bde476690278ab13fa1f8ba9127ef855ac33d00c3152",
+ "sha256:5831256da3477723362bc71a8df07b8cd8493e4a4a60cebd45580483edbe48ae"
],
- "version": "==4.5.2"
+ "index": "pypi",
+ "version": "==3.7.0"
},
- "flake8": {
+ "docker-pycreds": {
"hashes": [
- "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670",
- "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2"
+ "sha256:6ce3270bcaf404cc4c3e27e4b6c70d3521deae82fb508767870fdbf772d584d4",
+ "sha256:7266112468627868005106ec19cd0d722702d2b7d5912a28e19b826c3d37af49"
],
- "index": "pypi",
- "version": "==3.6.0"
+ "version": "==0.4.0"
},
"flask": {
"hashes": [
@@ -163,48 +60,34 @@
"index": "pypi",
"version": "==1.0.2"
},
- "flask-sockets": {
- "hashes": [
- "sha256:072927da8edca0e81e024f5787e643c87d80b351b714de95d723becb30e0643b",
- "sha256:350a76d55f5889f64afd2ca9b32f262680b7960965f0830365576307d30cfe1e"
- ],
- "index": "pypi",
- "version": "==0.2.1"
- },
"gevent": {
"hashes": [
- "sha256:0901975628790e8a57fc92bb7062e5b856edea48c8de9caf36cfda14eae07329",
- "sha256:1af93825db5753550fa8ff5ab2f2132e8733170b3f8d38347b34fa4a984cb624",
- "sha256:2ff045a91509c35664c27a849c8cbf742a227f587b7cdbc88301e9c85dcaedff",
- "sha256:35790f1a3c8e431ada3471b70bb2105050009ea4beb15cbe41b86bc716a7ffa9",
- "sha256:4791c8ae9c57d6f153354736e1ccab1e2baf6c8d9ae5a77a9ac90f41e2966b2d",
- "sha256:552719cec4721673b8c7d2f9de666e3f7591b9b182f801ecaef1c76e638052aa",
- "sha256:59e9237af027f8db85e5d78a9da2e328ae96f01d67a0d62abcecad3db7876908",
- "sha256:60109741377367eef8ded9283a1bf629621b73acaf3e1e8aac9d1a0f50fa0f05",
- "sha256:70558dd45c7a1f8046ba45792e489dd0f409bd8a3b7a0635ca9d3055223b3dff",
- "sha256:81cb24e0f7bd9888596364e8d8ed0d65c2547c84884c67bb46d956faeed67396",
- "sha256:833bebdc36bfeeedefc200ca9aee9b8eddd80f56b63ca1e886e18b97b1240edd",
- "sha256:8a710eddb3e9e5f22bdbd458b5f211b94f59409ecd6896f15b9fee2cba266a59",
- "sha256:9b492bb1a043540abb6e54fdb5537531e24962ca49c09f3b47dc4f9c37f6297c",
- "sha256:a16db4f56699ef07f0249b953ff949aae641e50b2bdc4710f11c0d8d9089b296",
- "sha256:a66cf99f08da65c501826a19e30f5a6e7ba942fdd79baba5ce2d51eebaa13444",
- "sha256:b67a10799923f9fed546ca5f8b93a2819c71a60132d7a97b4a13fbdab66b278a",
- "sha256:b7e0e6400c2f3ce78a9ae1cdd55b53166feedd003d60c033863881227129a4d3",
- "sha256:c9dd6534c46ed782e2d7236767cd07115cb29ce8670c2fc0794f264de9024fe0",
- "sha256:de13a8e378103af84a8bf6015ad1d2761d46f29b8393e8dd6d9bb7cb51bbb713",
- "sha256:deafd70d04ab62428d4e291e8e2c0fb22f38690e6a9f23a67ee6c304087634da",
- "sha256:df52e06a2754c2d905aad75a7dc06a732c804d9edbc87f06f47c8f483ba98bca"
+ "sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64",
+ "sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea",
+ "sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c",
+ "sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51",
+ "sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e",
+ "sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917",
+ "sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1",
+ "sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c",
+ "sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909",
+ "sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12",
+ "sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8",
+ "sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942",
+ "sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950",
+ "sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8",
+ "sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee",
+ "sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922",
+ "sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e",
+ "sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0",
+ "sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad",
+ "sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51",
+ "sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1",
+ "sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05",
+ "sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1"
],
"index": "pypi",
- "version": "==1.2.2"
- },
- "gevent-websocket": {
- "hashes": [
- "sha256:17b67d91282f8f4c973eba0551183fc84f56f1c90c8f6b6b30256f31f66f5242",
- "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"
- ],
- "index": "pypi",
- "version": "==0.10.1"
+ "version": "==1.4.0"
},
"greenlet": {
"hashes": [
@@ -228,6 +111,7 @@
"sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720",
"sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656"
],
+ "markers": "platform_python_implementation == 'CPython'",
"version": "==0.4.15"
},
"gunicorn": {
@@ -238,6 +122,13 @@
"index": "pypi",
"version": "==19.9.0"
},
+ "idna": {
+ "hashes": [
+ "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
+ "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ ],
+ "version": "==2.8"
+ },
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
@@ -285,6 +176,101 @@
],
"version": "==1.1.0"
},
+ "requests": {
+ "hashes": [
+ "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
+ "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
+ ],
+ "version": "==2.21.0"
+ },
+ "six": {
+ "hashes": [
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
+ ],
+ "version": "==1.12.0"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
+ "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
+ ],
+ "version": "==1.24.1"
+ },
+ "websocket-client": {
+ "hashes": [
+ "sha256:8c8bf2d4f800c3ed952df206b18c28f7070d9e3dcbd6ca6291127574f57ee786",
+ "sha256:e51562c91ddb8148e791f0155fdb01325d99bb52c4cdbb291aee7a3563fd0849"
+ ],
+ "version": "==0.54.0"
+ },
+ "werkzeug": {
+ "hashes": [
+ "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
+ "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
+ ],
+ "version": "==0.14.1"
+ }
+ },
+ "develop": {
+ "atomicwrites": {
+ "hashes": [
+ "sha256:0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0",
+ "sha256:ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"
+ ],
+ "version": "==1.2.1"
+ },
+ "attrs": {
+ "hashes": [
+ "sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
+ "sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
+ ],
+ "version": "==18.2.0"
+ },
+ "coverage": {
+ "hashes": [
+ "sha256:09e47c529ff77bf042ecfe858fb55c3e3eb97aac2c87f0349ab5a7efd6b3939f",
+ "sha256:0a1f9b0eb3aa15c990c328535655847b3420231af299386cfe5efc98f9c250fe",
+ "sha256:0cc941b37b8c2ececfed341444a456912e740ecf515d560de58b9a76562d966d",
+ "sha256:10e8af18d1315de936d67775d3a814cc81d0747a1a0312d84e27ae5610e313b0",
+ "sha256:1b4276550b86caa60606bd3572b52769860a81a70754a54acc8ba789ce74d607",
+ "sha256:1e8a2627c48266c7b813975335cfdea58c706fe36f607c97d9392e61502dc79d",
+ "sha256:2b224052bfd801beb7478b03e8a66f3f25ea56ea488922e98903914ac9ac930b",
+ "sha256:447c450a093766744ab53bf1e7063ec82866f27bcb4f4c907da25ad293bba7e3",
+ "sha256:46101fc20c6f6568561cdd15a54018bb42980954b79aa46da8ae6f008066a30e",
+ "sha256:4710dc676bb4b779c4361b54eb308bc84d64a2fa3d78e5f7228921eccce5d815",
+ "sha256:510986f9a280cd05189b42eee2b69fecdf5bf9651d4cd315ea21d24a964a3c36",
+ "sha256:5535dda5739257effef56e49a1c51c71f1d37a6e5607bb25a5eee507c59580d1",
+ "sha256:5a7524042014642b39b1fcae85fb37556c200e64ec90824ae9ecf7b667ccfc14",
+ "sha256:5f55028169ef85e1fa8e4b8b1b91c0b3b0fa3297c4fb22990d46ff01d22c2d6c",
+ "sha256:6694d5573e7790a0e8d3d177d7a416ca5f5c150742ee703f3c18df76260de794",
+ "sha256:6831e1ac20ac52634da606b658b0b2712d26984999c9d93f0c6e59fe62ca741b",
+ "sha256:77f0d9fa5e10d03aa4528436e33423bfa3718b86c646615f04616294c935f840",
+ "sha256:828ad813c7cdc2e71dcf141912c685bfe4b548c0e6d9540db6418b807c345ddd",
+ "sha256:85a06c61598b14b015d4df233d249cd5abfa61084ef5b9f64a48e997fd829a82",
+ "sha256:8cb4febad0f0b26c6f62e1628f2053954ad2c555d67660f28dfb1b0496711952",
+ "sha256:a5c58664b23b248b16b96253880b2868fb34358911400a7ba39d7f6399935389",
+ "sha256:aaa0f296e503cda4bc07566f592cd7a28779d433f3a23c48082af425d6d5a78f",
+ "sha256:ab235d9fe64833f12d1334d29b558aacedfbca2356dfb9691f2d0d38a8a7bfb4",
+ "sha256:b3b0c8f660fae65eac74fbf003f3103769b90012ae7a460863010539bb7a80da",
+ "sha256:bab8e6d510d2ea0f1d14f12642e3f35cefa47a9b2e4c7cea1852b52bc9c49647",
+ "sha256:c45297bbdbc8bb79b02cf41417d63352b70bcb76f1bbb1ee7d47b3e89e42f95d",
+ "sha256:d19bca47c8a01b92640c614a9147b081a1974f69168ecd494687c827109e8f42",
+ "sha256:d64b4340a0c488a9e79b66ec9f9d77d02b99b772c8b8afd46c1294c1d39ca478",
+ "sha256:da969da069a82bbb5300b59161d8d7c8d423bc4ccd3b410a9b4d8932aeefc14b",
+ "sha256:ed02c7539705696ecb7dc9d476d861f3904a8d2b7e894bd418994920935d36bb",
+ "sha256:ee5b8abc35b549012e03a7b1e86c09491457dba6c94112a2482b18589cc2bdb9"
+ ],
+ "version": "==4.5.2"
+ },
+ "flake8": {
+ "hashes": [
+ "sha256:6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670",
+ "sha256:c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2"
+ ],
+ "index": "pypi",
+ "version": "==3.6.0"
+ },
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
@@ -294,18 +280,18 @@
},
"more-itertools": {
"hashes": [
- "sha256:c187a73da93e7a8acc0001572aebc7e3c69daf7bf6881a2cea10650bd4420092",
- "sha256:c476b5d3a34e12d40130bc2f935028b5f636df8f372dc2c1c01dc19681b2039e",
- "sha256:fcbfeaea0be121980e15bc97b3817b5202ca73d0eae185b4550cbfce2a3ebb3d"
+ "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4",
+ "sha256:c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc",
+ "sha256:fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"
],
- "version": "==4.3.0"
+ "version": "==5.0.0"
},
"pluggy": {
"hashes": [
- "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095",
- "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f"
+ "sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
+ "sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"
],
- "version": "==0.8.0"
+ "version": "==0.8.1"
},
"py": {
"hashes": [
@@ -330,40 +316,33 @@
},
"pytest": {
"hashes": [
- "sha256:1d131cc532be0023ef8ae265e2a779938d0619bb6c2510f52987ffcba7fa1ee4",
- "sha256:ca4761407f1acc85ffd1609f464ca20bb71a767803505bd4127d0e45c5a50e23"
+ "sha256:41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2",
+ "sha256:c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be"
],
"index": "pypi",
- "version": "==4.0.1"
+ "version": "==4.1.1"
},
"pytest-cov": {
"hashes": [
- "sha256:513c425e931a0344944f84ea47f3956be0e416d95acbd897a44970c8d926d5d7",
- "sha256:e360f048b7dae3f2f2a9a4d067b2dd6b6a015d384d1577c994a43f3f7cbad762"
+ "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
+ "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
],
"index": "pypi",
- "version": "==2.6.0"
+ "version": "==2.6.1"
},
"pytest-dependency": {
"hashes": [
- "sha256:895e5b9444fc57a84ff0d5e3fcb7ad8cb7081e6049eaad5c3b9c3419dd0c91d3"
+ "sha256:bda0ef48e6a44c091399b12ab4a7e580d2dd8294c222b301f88d7d57f47ba142"
],
"index": "pypi",
- "version": "==0.3.2"
+ "version": "==0.4.0"
},
"six": {
"hashes": [
- "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
- "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
- ],
- "version": "==1.11.0"
- },
- "werkzeug": {
- "hashes": [
- "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c",
- "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"
+ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
+ "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
- "version": "==0.14.1"
+ "version": "==1.12.0"
}
}
}
diff --git a/README.md b/README.md
index 346815f..d8dd6cb 100644
--- a/README.md
+++ b/README.md
@@ -7,21 +7,18 @@ Python sandbox runners for executing code in isolation aka snekbox
The user sends a piece of python code to a snekbox, the snekbox executes the code and sends the result back to the users.
```
- +-------------+ +------------+ +-----------+
- input -> | |---------->| |-------->| | >----------+
- | WEBSERVER | | RABBITMQ | | SNEKBOX | execution |
-result <- | |<----------| |<--------| | <----------+
- +-------------+ +------------+ +-----------+
- ^ ^ ^
- | | |- Executes python code
- | | |- Returns result
- | | +-----------------------
- | |
- | |- Message queues opens on demand and closes automatically
- | +---------------------------------------------------------
+ +-------------+ +-----------+
+ input -> | |---------->| | >----------+
+ | HTTP POST | | SNEKBOX | execution |
+result <- | |<----------| | <----------+
+ +-------------+ +-----------+
+ ^ ^
+ | |- Executes python code
+ | |- Returns result
+ | +-----------------------
|
- |- Uses websockets for asynchronous connection between webui and webserver
- +-------------------------------------------------------------------------
+ |- HTTP POST Endpoint receives request and returns result
+ +---------------------------------------------------------
```
@@ -36,6 +33,9 @@ result <- | |<----------| |<--------| | <------
| docker | 18.03.1-ce |
| docker-compose | 1.21.2 |
| nsjail | 2.5 |
+| flask | 1.0.2 |
+| gevent | 1.4 |
+| gunicorn | 19.9 |
_________________________________________
## Setup local test
@@ -83,38 +83,20 @@ python3.6 -ISq -c "print('test')"
## Development environment
-Start a rabbitmq instance and get the container IP
+Start the webserver with docker:
```bash
-docker-compose up -d pdrmq
-docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' rmq
-# expected output with default setting: 172.17.0.2
-# If not, change the config.py file to match
+docker-compose up -d
```
-rabbitmq webinterface: `http://localhost:15672`
-
-start the webserver
-
-```bash
-docker-compose up -d pdsnkweb
-netstat -plnt
-# tcp 0.0.0.0:5000 LISTEN
-```
-
-`http://localhost:5000`
-
+Run locally with pipenv:
```bash
pipenv run snekbox # for debugging
-# or
-docker-compose up pdsnk # for running the container
```
-
+Visit: `http://localhost:8060`
________________________________________
## Unit testing and lint
-Make sure rabbitmq is running before running tests
-
```bash
pipenv run lint
pipenv run test
@@ -126,11 +108,6 @@ ________________________________________
```bash
# Build
pipenv run buildbox
-pipenv run buildweb
-
# Push
pipenv run pushbox
-pipenv run pushweb
```
-
-
diff --git a/config.py b/config.py
deleted file mode 100644
index 455c79e..0000000
--- a/config.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import os
-import docker
-from docker.errors import NotFound
-# import traceback
-
-
-def autodiscover():
- container_names = ["rmq", "pdrmq", "snekbox_pdrmq_1"]
-
- client = docker.from_env()
- for name in container_names:
- try:
- container = client.containers.get(name)
- if container.status == "running":
- host = list(container.attrs.get('NetworkSettings').get('Networks').values())[0]['IPAddress']
- return host
-
- except NotFound:
- continue
-
- except Exception:
- pass
- # print(traceback.format_exc())
-
- return '127.0.0.1'
-
-
-USERNAME = os.environ.get('RMQ_USERNAME', 'guest')
-PASSWORD = os.environ.get('RMQ_PASSWORD', 'guest')
-HOST = os.environ.get('RMQ_HOST', autodiscover())
-PORT = 5672
-QUEUE = 'input'
-EXCHANGE = QUEUE
-ROUTING_KEY = QUEUE
-EXCHANGE_TYPE = 'direct'
diff --git a/docker-compose.yml b/docker-compose.yml
index 3aedf14..2b22db4 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,43 +1,7 @@
version: '3'
services:
- pdrmq:
- hostname: "pdrmq"
- image: pythondiscord/rmq:latest
- expose:
- - "15672"
- ports:
- - "15672:15672"
- networks:
- - sneknet
- environment:
- RABBITMQ_DEFAULT_USER: guest
- RABBITMQ_DEFAULT_PASS: guest
-
pdsnk:
- privileged: true
hostname: "pdsnk"
+ privileged: true
image: pythondiscord/snekbox:latest
- networks:
- - sneknet
- environment:
- RMQ_HOST: pdrmq
- RMQ_USERNAME: guest
- RMQ_PASSWORD: guest
-
- pdsnkweb:
- hostname: "pdsnkweb"
- image: pythondiscord/snekboxweb:latest
- networks:
- - sneknet
- ports:
- - "5000:5000"
- expose:
- - "5000"
- environment:
- RMQ_HOST: pdrmq
- RMQ_USERNAME: guest
- RMQ_PASSWORD: guest
-
-
-networks:
- sneknet:
+ network_mode: "host"
diff --git a/docker/Dockerfile b/docker/Dockerfile
index e8fa8a5..b8d5637 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,5 +1,7 @@
FROM pythondiscord/snekbox-base:latest
+RUN apk add --update tini
+
RUN mkdir -p /snekbox
COPY . /snekbox
WORKDIR /snekbox
@@ -7,4 +9,5 @@ WORKDIR /snekbox
RUN pipenv --rm
RUN pipenv sync
+ENTRYPOINT ["/sbin/tini", "--"]
CMD ["pipenv", "run", "snekbox"]
diff --git a/docker/Dockerfile.webapp b/docker/Dockerfile.webapp
deleted file mode 100644
index 988926d..0000000
--- a/docker/Dockerfile.webapp
+++ /dev/null
@@ -1,25 +0,0 @@
-FROM python:3.6.6-alpine3.7
-
-RUN apk add --update tini
-RUN apk add --update build-base
-
-ENV PIPENV_VENV_IN_PROJECT=1
-ENV PIPENV_IGNORE_VIRTUALENVS=1
-ENV PIPENV_NOSPIN=1
-ENV PIPENV_HIDE_EMOJIS=1
-ENV PYTHONPATH=/webapp
-
-RUN pip install pipenv
-
-RUN mkdir -p /webapp
-COPY Pipfile /webapp
-COPY Pipfile.lock /webapp
-COPY . /webapp
-WORKDIR /webapp
-
-RUN pipenv sync --dev
-
-EXPOSE 5000
-
-ENTRYPOINT ["/sbin/tini", "--"]
-CMD ["pipenv", "run", "snekweb"]
diff --git a/rmq.py b/rmq.py
deleted file mode 100644
index 493a38f..0000000
--- a/rmq.py
+++ /dev/null
@@ -1,132 +0,0 @@
-import pika
-import time
-import traceback
-
-from pika.exceptions import ConnectionClosed
-
-from config import USERNAME
-from config import PASSWORD
-from config import HOST
-from config import PORT
-from config import EXCHANGE_TYPE
-from config import QUEUE
-from config import ROUTING_KEY
-from config import EXCHANGE
-
-from logs import log
-
-
-class Rmq(object):
-
- def __init__(self,
- username=USERNAME,
- password=PASSWORD,
- host=HOST,
- port=PORT,
- exchange_type=EXCHANGE_TYPE):
-
- self.username = USERNAME
- self.password = PASSWORD
- self.host = HOST
- self.port = PORT
- self.exchange_type = EXCHANGE_TYPE
- self.credentials = pika.PlainCredentials(self.username, self.password)
- self.con_params = pika.ConnectionParameters(self.host, self.port, '/', self.credentials)
- self.properties = pika.BasicProperties(content_type='text/plain', delivery_mode=1)
-
- def _declare(self, channel, queue):
- channel.queue_declare(
- queue=queue,
- durable=False, # Do not commit messages to disk
- arguments={'x-message-ttl': 5000}, # Delete message automatically after x milliseconds
- auto_delete=True) # Delete queue when all connection are closed
-
- def consume(self, queue=QUEUE, callback=None, thread_ws=None, run_once=False):
- while True:
- try:
- connection = pika.BlockingConnection(self.con_params)
-
- try:
- channel = connection.channel()
- self._declare(channel, queue)
- channel.basic_qos(prefetch_count=1)
-
- if not run_once:
- channel.basic_consume(
- lambda ch, method, properties, body:
- callback(ch, method, properties, body, thread_ws=thread_ws),
- queue=queue)
-
- log.info(f"Connected to host: {self.host} port: {self.port} queue: {queue}")
-
- if thread_ws:
- if not thread_ws.closed:
- thread_ws.send('{"service": "connected"}')
-
- if run_once:
- return channel.basic_get(queue=queue)
-
- channel.start_consuming()
-
- except Exception:
- exc = traceback.format_exc()
- log.error(exc)
-
- finally:
- connection.close()
-
- except ConnectionClosed:
- if thread_ws:
- if not thread_ws.closed:
- log.error(f"Connection to {self.host} could not be established")
- thread_ws.send('{"service": "disconnected"}')
- exit(1)
-
- log.error(f"Connection lost, reconnecting to {self.host}")
-
- time.sleep(2)
-
- def publish(self,
- message,
- queue=QUEUE,
- routingkey=ROUTING_KEY,
- exchange=EXCHANGE):
-
- try:
- connection = pika.BlockingConnection(self.con_params)
-
- try:
- channel = connection.channel()
-
- self._declare(channel, queue)
-
- channel.exchange_declare(
- exchange=exchange,
- exchange_type=self.exchange_type)
-
- channel.queue_bind(
- exchange=exchange,
- queue=queue,
- routing_key=routingkey)
-
- result = channel.basic_publish(
- exchange=exchange,
- routing_key=routingkey,
- body=message,
- properties=self.properties)
-
- if result:
- return result
-
- else:
- log.error(f"Message '{message}' not delivered")
-
- except ConnectionClosed:
- log.error(f"Could not send message, connection to {self.host} was lost")
- exit(1)
-
- finally:
- connection.close()
-
- except ConnectionClosed:
- log.error(f"Could not connect to {self.host}")
diff --git a/snekbox.py b/snekbox.py
index ddde563..4e3e4fa 100644
--- a/snekbox.py
+++ b/snekbox.py
@@ -1,17 +1,14 @@
-import json
-import multiprocessing
import subprocess
import os
import sys
-from rmq import Rmq
+from flask import Flask, render_template, request, jsonify
class Snekbox(object):
def __init__(self,
nsjail_binary='nsjail',
python_binary=os.path.dirname(sys.executable)+os.sep+'python3.6'):
-
self.nsjail_binary = nsjail_binary
self.python_binary = python_binary
self.nsjail_workaround()
@@ -83,34 +80,32 @@ class Snekbox(object):
return output
- def execute(self, body):
- msg = body.decode('utf-8')
- result = ''
- snek_msg = json.loads(msg)
- snekid = snek_msg['snekid']
- snekcode = snek_msg['message'].strip()
-
- result = self.python3(snekcode)
-
- rmq.publish(result,
- queue=snekid,
- routingkey=snekid,
- exchange=snekid)
- exit(0)
-
- def message_handler(self, ch, method, properties, body, thread_ws=None):
- p = multiprocessing.Process(target=self.execute, args=(body,))
- p.daemon = True
- p.start()
-
- ch.basic_ack(delivery_tag=method.delivery_tag)
-
-
-if __name__ == '__main__':
- try:
- rmq = Rmq()
- snkbx = Snekbox()
- rmq.consume(callback=snkbx.message_handler)
- except KeyboardInterrupt:
- print('Exited')
- exit(0)
+
+snekbox = Snekbox()
+
+# Load app
+app = Flask(__name__)
+app.use_reloader = False
+
+# Logging
+log = app.logger
+
+
+def index():
+ return render_template('index.html')
+
+
[email protected]('/result', methods=["POST", "GET"])
+def result():
+ if request.method == "POST":
+ code = request.form["Code"]
+ output = snekbox.python3(code)
+ return render_template('result.html', code=code, result=output)
+
+
[email protected]('/input', methods=["POST"])
+def code_input():
+ body = request.get_json()
+ output = snekbox.python3(body["code"])
+ return jsonify(input=body["code"], output=output)
diff --git a/snekweb.py b/snekweb.py
deleted file mode 100644
index 92e0436..0000000
--- a/snekweb.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import traceback
-import threading
-import logging
-import json
-
-from flask import Flask
-from flask import render_template
-from flask_sockets import Sockets
-
-
-from rmq import Rmq
-
-# Load app
-app = Flask(__name__)
-app.jinja_env.auto_reload = True
-sockets = Sockets(app)
-
-# Logging
-gunicorn_logger = logging.getLogger('gunicorn.error')
-app.logger.handlers = gunicorn_logger.handlers
-app.logger.setLevel(gunicorn_logger.level)
-log = app.logger
-
-
-def index():
- return render_template('index.html')
-
-
[email protected]('/ws/<snekboxid>')
-def websocket_route(ws, snekboxid):
- localdata = threading.local()
- localdata.thread_ws = ws
-
- rmq = Rmq()
-
- def message_handler(ch, method, properties, body, thread_ws):
- msg = body.decode('utf-8')
- thread_ws.send(msg)
- ch.basic_ack(delivery_tag=method.delivery_tag)
-
- consumer_parameters = {'queue': snekboxid,
- 'callback': message_handler,
- 'thread_ws': localdata.thread_ws}
-
- consumer = threading.Thread(
- target=rmq.consume,
- kwargs=consumer_parameters)
-
- consumer.daemon = True
- consumer.start()
-
- try:
- while not ws.closed:
- message = ws.receive()
- if message:
- snek_msg = json.dumps({"snekid": snekboxid, "message": message})
- log.info(f"User {snekboxid} sends message\n{message.strip()}")
- rmq.publish(snek_msg)
-
- except Exception:
- log.info(traceback.format_exc())
-
- finally:
- if not ws.closed:
- ws.close()
-
-
-if __name__ == '__main__':
- from gevent import pywsgi
- from geventwebsocket.handler import WebSocketHandler
- server = pywsgi.WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler)
- server.serve_forever()
diff --git a/templates/index.html b/templates/index.html
index 8de9627..41980d1 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,106 +1,14 @@
<!DOCTYPE html>
-<meta charset="utf-8" />
-<title>snekboxweb</title>
-<script language="javascript" type="text/javascript">
-
-
-let _ready = false
-let snekbox_id
-var output;
-
-snekbox_id = sessionStorage.getItem("snekbox_id");
-console.log(snekbox_id)
-if (snekbox_id == null) {
- snekbox_id = generate_id()
- sessionStorage.setItem("snekbox_id", snekbox_id)
- console.log(snekbox_id)
-}
-
-function init(){
- output = document.getElementById("output");
- websocketHandler();
-}
-
-function websocketHandler(){
- var here = window.location.host;
- var wsUri = `ws://${here}/ws/`+snekbox_id;
- websocket = new WebSocket(wsUri);
- websocket.onopen = function(evt) { onOpen(evt) };
- websocket.onclose = function(evt) { onClose(evt) };
- websocket.onmessage = function(evt) { onMessage(evt) };
- websocket.onerror = function(evt) { onError(evt) };
-}
-
-function onOpen(evt){
- _ready = true
- console.log("CONNECTED");
-}
-
-function onClose(evt){
- _ready = false
- console.log("DISCONNECTED");
-}
-
-function onMessage(evt){
- writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
-}
-
-function exit(){
- websocket.close();
-}
-
-function onError(evt){
- _ready = false
- writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
-}
-
-function sendMessage(msg){
- waitForSocketConnection(function(){
- websocket.send(msg);
- });
- console.log("sent message "+msg)
-}
-
-function waitForSocketConnection(callback){
- setTimeout(
- function () {
- if (_ready === true) {
- if(callback != null){
- callback();}
- return;
- }
- else {
- waitForSocketConnection(callback);}
-
- }, 500); // milliseconds
-}
-
-function writeToScreen(message){
- var pre = document.createElement("p");
- pre.style.wordWrap = "break-word";
- pre.innerHTML = message;
- output.appendChild(pre);
-}
-
-function sendFromInput(){
- var msg = document.getElementById("field1").value;
- sendMessage(msg)
-}
-
-function generate_id(){
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
-}
-
-window.addEventListener("load", init, false);
-
-</script>
-
-<textarea rows="4" cols="50" type="text" id="field1">
+<html>
+ <meta charset="utf-8" />
+ <title>snekboxweb</title>
+ <body>
+ <form action="/result" method="POST">
+ <p>Code:<br><textarea name="Code" rows="20" cols="80">
def sum(a,b):
return a+b
-print( sum(1,2) )
-</textarea>
-<br>
-<button onclick="sendFromInput()">Send</button>
-<button onclick="exit()">disconnect from websocket</button>
-<div id="output"></div>
+print( sum(1,2) )</textarea></p>
+ <p><input type="submit" value="Run"></p>
+ </form>
+ </body>
+</html>
diff --git a/templates/result.html b/templates/result.html
new file mode 100644
index 0000000..e339605
--- /dev/null
+++ b/templates/result.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8" />
+ <title>snekboxweb</title>
+ <body>
+ <p>Code Evaluated:<br><pre>{{ code }}</pre></p>
+ <p>Results:<br><pre>{{ result }}</pre></p>
+ </body>
+</html>
diff --git a/tests/test_snekbox.py b/tests/test_snekbox.py
index e2505d6..cc79a2a 100644
--- a/tests/test_snekbox.py
+++ b/tests/test_snekbox.py
@@ -1,12 +1,6 @@
import unittest
-import pytest
-import os
-import json
from snekbox import Snekbox
-from rmq import Rmq
-
-r = Rmq()
snek = Snekbox()
@@ -24,12 +18,14 @@ class SnekTests(unittest.TestCase):
# self.assertEquals(result.strip(), 'timed out or memory limit exceeded')
def test_timeout(self):
- code = ('x = "*"\n'
- 'while True:\n'
- ' try:\n'
- ' x = x * 99\n'
- ' except:\n'
- ' continue\n')
+ code = (
+ 'x = "*"\n'
+ 'while True:\n'
+ ' try:\n'
+ ' x = x * 99\n'
+ ' except:\n'
+ ' continue\n'
+ )
result = snek.python3(code)
self.assertEquals(result.strip(), 'timed out or memory limit exceeded')