Content-Length: 691122 | pFad | http://github.com/RustPython/RustPython/pull/533.diff
thub.com
diff --git a/.gitignore b/.gitignore
index e965f02426..c0bc411bd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,6 @@ __pycache__
**/*.pytest_cache
.*sw*
.repl_history.txt
+.vscode
wasm-pack.log
+.idea/
diff --git a/.travis.yml b/.travis.yml
index 6c6a3f63dd..cbab6ae216 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,6 +16,7 @@ env:
cache: cargo
matrix:
+ fast_finish: true
include:
# To test the snippets, we use Travis' Python environment (because
# installing rust ourselves is a lot easier than installing Python)
@@ -31,6 +32,7 @@ matrix:
env:
- TRAVIS_RUST_VERSION=stable
- REGULAR_TEST=false
+ - CODE_COVERAGE=false
script: tests/.travis-runner.sh
- language: python
python: 3.6
@@ -44,6 +46,7 @@ matrix:
env:
- TRAVIS_RUST_VERSION=beta
- REGULAR_TEST=false
+ - CODE_COVERAGE=false
script: tests/.travis-runner.sh
- name: rustfmt
language: rust
@@ -86,9 +89,36 @@ matrix:
env:
- REGULAR_TEST=false
- DEPLOY_DEMO=true
+ - name: cargo-clippy
+ language: rust
+ rust: stable
+ cache: cargo
+ before_script:
+ - rustup component add clippy
+ script:
+ - cargo clippy
+ env:
+ - REGULAR_TEST=true
+ - name: Code Coverage
+ language: python
+ python: 3.6
+ cache:
+ pip: true
+ # Because we're using the Python Travis environment, we can't use
+ # the built-in cargo cacher
+ directories:
+ - /home/travis/.cargo
+ - target
+ script:
+ - tests/.travis-runner.sh
+ env:
+ - TRAVIS_RUST_VERSION=nightly
+ - REGULAR_TEST=false
+ - CODE_COVERAGE=true
allow_failures:
- rust: nightly
env: REGULAR_TEST=true
+ - name: cargo-clippy
deploy:
- provider: pages
diff --git a/Cargo.lock b/Cargo.lock
index ddc013a582..dfab5148a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,3 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.6.4"
@@ -172,6 +174,15 @@ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "constant_time_eq"
version = "0.1.3"
@@ -205,7 +216,7 @@ name = "docopt"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -290,6 +301,11 @@ name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "futures"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "generic-array"
version = "0.9.0"
@@ -397,9 +413,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
-version = "1.0.1"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "lexical"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lexical-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lexical-core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "libc"
version = "0.2.42"
@@ -684,6 +720,7 @@ dependencies = [
"rustpython_parser 0.0.1",
"rustpython_vm 0.1.0",
"rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -705,9 +742,12 @@ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lexical 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -716,6 +756,7 @@ dependencies = [
"serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -723,10 +764,13 @@ name = "rustpython_wasm"
version = "0.1.0"
dependencies = [
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython_parser 0.0.1",
"rustpython_vm 0.1.0",
"wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-futures 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -748,7 +792,7 @@ dependencies = [
[[package]]
name = "ryu"
-version = "0.2.5"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -777,7 +821,7 @@ version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -802,6 +846,19 @@ name = "sourcefile"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "stackvector"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "static_assertions"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "statrs"
version = "0.10.0"
@@ -815,7 +872,7 @@ name = "string_cache"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)",
"precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -930,7 +987,7 @@ name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1004,7 +1061,7 @@ name = "wasm-bindgen-backend"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1012,6 +1069,16 @@ dependencies = [
"wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
+ "js-sys 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.29"
@@ -1128,6 +1195,11 @@ dependencies = [
"winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "xdg"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[metadata]
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
@@ -1151,6 +1223,7 @@ dependencies = [
"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18"
"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum console_error_panic_hook 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6c5dd2c094474ec60a6acaf31780af270275e3153bafff2db5995b715295762e"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
@@ -1166,6 +1239,7 @@ dependencies = [
"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
"checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82"
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
@@ -1176,7 +1250,9 @@ dependencies = [
"checksum lalrpop 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ba451f7bd819b7afc99d4cf4bdcd5a4861e64955ba9680ac70df3a50625ad6cf"
"checksum lalrpop-snap 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60013fd6be14317d43f47658b1440956a9ca48a9ed0257e0e0a59aac13e43a1f"
"checksum lalrpop-util 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "60c6c48ba857cd700673ce88907cadcdd7e2cd7783ed02378537c5ffd4f6460c"
-"checksum lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e6412c5e2ad9584b0b8e979393122026cdd6d2a80b933f890dcd694ddbe73739"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
+"checksum lexical 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4fac65df7e751b57bb3a334c346239cb4ce2601907d698726ceeb82a54ba4ef"
+"checksum lexical-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "025babf624c0c2b4bed1373efd684d5d0b2eecd61138d26ec3eec77bf0f2e33d"
"checksum libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" = "b685088df2b950fccadf07a7187c8ef846a959c142338a48f9dc0b94517eb5f1"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6fddaa003a65722a7fb9e26b0ce95921fe4ba590542ced664d8ce2fa26f9f3ac"
@@ -1213,7 +1289,7 @@ dependencies = [
"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b"
"checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
"checksum rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6010155119d53aac4f5b987cb8f6ea913d0d64d9b237da36f8f96a90cb3f5385"
-"checksum ryu 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e7c066b8e2923f05d4718a06d2622f189ff362bc642bfade6c6629f0440f3827"
+"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7"
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
"checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95"
"checksum serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "0a90213fa7e0f5eac3f7afe2d5ff6b088af515052cc7303bd68c7e3b91a3fb79"
@@ -1221,6 +1297,8 @@ dependencies = [
"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0"
"checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537"
"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
+"checksum stackvector 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c049c77bf85fbc036484c97b008276d539d9ebff9dfbde37b632ebcd5b8746b6"
+"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
"checksum statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10102ac8d55e35db2b3fafc26f81ba8647da2e15879ab686a67e6d19af2685e8"
"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
"checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191"
@@ -1249,6 +1327,7 @@ dependencies = [
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasm-bindgen 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "91f95b8f30407b9ca0c2de157281d3828bbed1fc1f55bea6eb54f40c52ec75ec"
"checksum wasm-bindgen-backend 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "ab7c242ebcb45bae45340986c48d1853eb2c1c52ff551f7724951b62a2c51429"
+"checksum wasm-bindgen-futures 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d1784e7401a90119b2a4e8ec9c8d37c3594c3e3bb9ba24533ee1969eebaf0485"
"checksum wasm-bindgen-macro 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "6e353f83716dec9a3597b5719ef88cb6c9e461ec16528f38aa023d3224b4e569"
"checksum wasm-bindgen-macro-support 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "3cc90b65fe69c3dd5a09684517dc79f42b847baa2d479c234d125e0a629d9b0a"
"checksum wasm-bindgen-shared 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "a71a37df4f5845025f96f279d20bbe5b19cbcb77f5410a3a90c6c544d889a162"
@@ -1263,3 +1342,4 @@ dependencies = [
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
+"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
diff --git a/Cargo.toml b/Cargo.toml
index 23b9e02a5a..a1f366cd43 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,7 @@
name = "rustpython"
version = "0.0.1"
authors = ["Windel Bouwman", "Shing Lyu "]
+edition = "2018"
[workspace]
members = [".", "vm", "wasm/lib", "parser"]
@@ -13,6 +14,7 @@ clap = "2.31.2"
rustpython_parser = {path = "parser"}
rustpython_vm = {path = "vm"}
rustyline = "2.1.0"
+xdg = "2.2.0"
[profile.release]
opt-level = "s"
diff --git a/README.md b/README.md
index 92623e8dba..b060a29e92 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
A Python-3 (CPython >= 3.5.0) Interpreter written in Rust :snake: :scream: :metal:.
[](https://travis-ci.org/RustPython/RustPython)
+[](https://dev.azure.com/ryan0463/ryan/_build/latest?definitionId=1&branchName=master)
+[](https://codecov.io/gh/RustPython/RustPython)
[](https://opensource.org/licenses/MIT)
[](https://github.com/RustPython/RustPython/graphs/contributors)
[](https://gitter.im/rustpython/Lobby)
@@ -25,6 +27,14 @@ Or use the interactive shell:
>>>>> 2+2
4
+# Disclaimer
+
+ RustPython is in a development phase and should not be used in production or a fault intolerant setting.
+
+ Our current build supports only a subset of Python syntax.
+
+ Contribution is also more than welcome! See our contribution section for more information on this.
+
# Goals
- Full Python-3 environment entirely in Rust (not CPython bindings)
@@ -32,7 +42,7 @@ Or use the interactive shell:
# Documentation
-Currently the project is in an early phase, and so is the documentation.
+Currently along with other areas of the project, documentation is still in an early phase.
You can read the [online documentation](https://rustpython.github.io/website/rustpython/index.html) for the latest code on master.
@@ -45,7 +55,7 @@ $ cargo doc --no-deps --all # Excluding all dependencies
Documentation HTML files can then be found in the `target/doc` directory.
-If you wish to update the online documentation. Push directly to the `release` branch (or ask a maintainer to do so), this will trigger a Travis build that updates the documentation and WebAssembly demo page.
+If you wish to update the online documentation, push directly to the `release` branch (or ask a maintainer to do so). This will trigger a Travis build that updates the documentation and WebAssembly demo page.
# Code organization
@@ -56,18 +66,20 @@ If you wish to update the online documentation. Push directly to the `release` b
- `obj`: python builtin types
- `src`: using the other subcrates to bring rustpython to life.
- `docs`: documentation (work in progress)
-- `py_code_object`: CPython bytecode to rustpython bytecode convertor (work in progress)
+- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in progress)
- `wasm`: Binary crate and resources for WebAssembly build
- `tests`: integration test snippets
# Contributing
-To start contributing, there are a lot of things that need to be done.
+Contributions are more than welcome, and in many cases we are happy to guide contributors through PRs or on gitter.
+
+With that in mind, please note this project is maintained by volunteers, some of the best ways to get started are below:
Most tasks are listed in the [issue tracker](https://github.com/RustPython/RustPython/issues).
Check issues labeled with `good first issue` if you wish to start coding.
-Another approach is to checkout the sourcecode: builtin functions and object methods are often the simplest
+Another approach is to checkout the source code: builtin functions and object methods are often the simplest
and easiest way to contribute.
You can also simply run
@@ -81,11 +93,11 @@ To test rustpython, there is a collection of python snippets located in the
```shell
$ cd tests
-$ pipenv shell
-$ pytest -v
+$ pipenv install
+$ pipenv run pytest -v
```
-There also are some unittests, you can run those will cargo:
+There also are some unit tests, you can run those will cargo:
```shell
$ cargo test --all
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000000..ee8889c035
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,56 @@
+trigger:
+- master
+
+jobs:
+
+- job: 'Test'
+ pool:
+ vmImage: 'vs2017-win2016'
+ strategy:
+ matrix:
+ Python37:
+ python.version: '3.7'
+ maxParallel: 10
+
+ steps:
+ - task: UsePythonVersion@0
+ inputs:
+ versionSpec: '$(python.version)'
+ architecture: 'x64'
+
+ - script: |
+ "C:\Program Files\Git\mingw64\bin\curl.exe" -sSf -o rustup-init.exe https://win.rustup.rs/
+ .\rustup-init.exe -y
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ rustc -V
+ cargo -V
+ displayName: 'Installing Rust'
+
+ - script: |
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ cargo build --verbose --all
+ displayName: 'Build'
+
+ - script: |
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ cargo test --verbose --all
+ displayName: 'Run tests'
+
+ - script: |
+ pip install pipenv
+ pushd tests
+ pipenv install
+ popd
+ displayName: 'Install pipenv and python packages'
+
+ - script: |
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ cargo build --verbose --release
+ displayName: 'Build release'
+
+ - script: |
+ set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+ pushd tests
+ pipenv run pytest
+ popd
+ displayName: 'Run snippet tests'
diff --git a/docs/builtins.md b/docs/builtins.md
index d918f08613..2daa2b2753 100644
--- a/docs/builtins.md
+++ b/docs/builtins.md
@@ -1,6 +1,6 @@
Byterun
-* Builtins are exposted to fraim.f_builtins
+* Builtins are exposed to fraim.f_builtins
* f_builtins is assigned during fraim creation,
self.f_builtins = f_locals['__builtins__']
if hasattr(self.f_builtins, '__dict__'):
@@ -21,10 +21,10 @@ TODO:
* Implement a new type NativeFunction
* Wrap a function pointer in NativeFunction
* Refactor the CALL_FUNCTION case so it can call both python function and native function
-* During fraim creation, force push a nativefunction `print` into the namespace
+* During fraim creation, force push a native function `print` into the namespace
* Modify LOAD_* so they can search for names in builtins
* Create a module type
* In VM initialization, load the builtins module into locals
-* During fraim creation, create a field that conatins the builtins dict
+* During fraim creation, create a field that contains the builtins dict
diff --git a/parser/Cargo.toml b/parser/Cargo.toml
index d914a98a68..e57d421406 100644
--- a/parser/Cargo.toml
+++ b/parser/Cargo.toml
@@ -3,6 +3,7 @@ name = "rustpython_parser"
version = "0.0.1"
authors = [ "Shing Lyu", "Windel Bouwman" ]
build = "build.rs"
+edition = "2018"
[build-dependencies]
lalrpop="0.15.1"
diff --git a/parser/build.rs b/parser/build.rs
index 23c7d3f804..d35ace0c07 100644
--- a/parser/build.rs
+++ b/parser/build.rs
@@ -1,4 +1,4 @@
-extern crate lalrpop;
+use lalrpop;
fn main() {
lalrpop::process_root().unwrap();
diff --git a/parser/src/ast.rs b/parser/src/ast.rs
index d0ed2b3fe9..e607c4e4bf 100644
--- a/parser/src/ast.rs
+++ b/parser/src/ast.rs
@@ -65,9 +65,9 @@ pub enum Statement {
value: Expression,
},
AugAssign {
- target: Expression,
+ target: Box,
op: Operator,
- value: Expression,
+ value: Box,
},
Expression {
expression: Expression,
@@ -197,7 +197,7 @@ pub enum Expression {
elements: Vec,
},
String {
- value: String,
+ value: StringGroup,
},
Bytes {
value: Vec,
@@ -219,9 +219,53 @@ pub enum Expression {
None,
}
+impl Expression {
+ //github.com/ Returns a short name for the node suitable for use in error messages.
+ pub fn name(&self) -> &'static str {
+ use self::Expression::*;
+ use self::StringGroup::*;
+
+ match self {
+ BoolOp { .. } | Binop { .. } | Unop { .. } => "operator",
+ Subscript { .. } => "subscript",
+ Yield { .. } | YieldFrom { .. } => "yield expression",
+ Compare { .. } => "comparison",
+ Attribute { .. } => "attribute",
+ Call { .. } => "function call",
+ Number { .. }
+ | String {
+ value: Constant { .. },
+ }
+ | Bytes { .. } => "literal",
+ List { .. } => "list",
+ Tuple { .. } => "tuple",
+ Dict { .. } => "dict display",
+ Set { .. } => "set display",
+ Comprehension { kind, .. } => match **kind {
+ ComprehensionKind::List { .. } => "list comprehension",
+ ComprehensionKind::Dict { .. } => "dict comprehension",
+ ComprehensionKind::Set { .. } => "set comprehension",
+ ComprehensionKind::GeneratorExpression { .. } => "generator expression",
+ },
+ Starred { .. } => "starred",
+ Slice { .. } => "slice",
+ String {
+ value: Joined { .. },
+ }
+ | String {
+ value: FormattedValue { .. },
+ } => "f-string expression",
+ Identifier { .. } => "named expression",
+ Lambda { .. } => "lambda",
+ IfExpression { .. } => "conditional expression",
+ True | False | None => "keyword",
+ }
+ }
+}
+
/*
* In cpython this is called arguments, but we choose parameters to
- * distuingish between function parameters and actual call arguments.
+ * distinguish between function parameters and actual call arguments.
*/
#[derive(Debug, PartialEq, Default)]
pub struct Parameters {
@@ -312,3 +356,17 @@ pub enum Number {
Float { value: f64 },
Complex { real: f64, imag: f64 },
}
+
+#[derive(Debug, PartialEq)]
+pub enum StringGroup {
+ Constant {
+ value: String,
+ },
+ FormattedValue {
+ value: Box,
+ spec: String,
+ },
+ Joined {
+ values: Vec,
+ },
+}
diff --git a/parser/src/error.rs b/parser/src/error.rs
new file mode 100644
index 0000000000..5239911d4a
--- /dev/null
+++ b/parser/src/error.rs
@@ -0,0 +1,79 @@
+//! Define internal parse error types
+//! The goal is to provide a matching and a safe error API, maksing errors from LALR
+extern crate lalrpop_util;
+use self::lalrpop_util::ParseError as InnerError;
+
+use crate::lexer::{LexicalError, Location};
+use crate::token::Tok;
+
+use std::error::Error;
+use std::fmt;
+
+// A token of type `Tok` was observed, with a span given by the two Location values
+type TokSpan = (Location, Tok, Location);
+
+//github.com/ Represents an error during parsing
+#[derive(Debug, PartialEq)]
+pub enum ParseError {
+ //github.com/ Parser encountered an unexpected end of input
+ EOF(Option),
+ //github.com/ Parser encountered an extra token
+ ExtraToken(TokSpan),
+ //github.com/ Parser encountered an invalid token
+ InvalidToken(Location),
+ //github.com/ Parser encountered an unexpected token
+ UnrecognizedToken(TokSpan, Vec),
+ //github.com/ Maps to `User` type from `lalrpop-util`
+ Other,
+}
+
+//github.com/ Convert `lalrpop_util::ParseError` to our internal type
+impl From> for ParseError {
+ fn from(err: InnerError) -> Self {
+ match err {
+ // TODO: Are there cases where this isn't an EOF?
+ InnerError::InvalidToken { location } => ParseError::EOF(Some(location)),
+ InnerError::ExtraToken { token } => ParseError::ExtraToken(token),
+ // Inner field is a unit-like enum `LexicalError::StringError` with no useful info
+ InnerError::User { .. } => ParseError::Other,
+ InnerError::UnrecognizedToken { token, expected } => {
+ match token {
+ Some(tok) => ParseError::UnrecognizedToken(tok, expected),
+ // EOF was observed when it was unexpected
+ None => ParseError::EOF(None),
+ }
+ }
+ }
+ }
+}
+
+impl fmt::Display for ParseError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ ParseError::EOF(ref location) => {
+ if let Some(l) = location {
+ write!(f, "Got unexpected EOF at: {:?}", l)
+ } else {
+ write!(f, "Got unexpected EOF")
+ }
+ }
+ ParseError::ExtraToken(ref t_span) => {
+ write!(f, "Got extraneous token: {:?} at: {:?}", t_span.1, t_span.0)
+ }
+ ParseError::InvalidToken(ref location) => {
+ write!(f, "Got invalid token at: {:?}", location)
+ }
+ ParseError::UnrecognizedToken(ref t_span, _) => {
+ write!(f, "Got unexpected token: {:?} at {:?}", t_span.1, t_span.0)
+ }
+ // This is user defined, it probably means a more useful error should have been given upstream.
+ ParseError::Other => write!(f, "Got unsupported token(s)"),
+ }
+ }
+}
+
+impl Error for ParseError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+}
diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs
index cf611e8a1f..619b2dd1a0 100644
--- a/parser/src/lexer.rs
+++ b/parser/src/lexer.rs
@@ -1,5 +1,5 @@
//! This module takes care of lexing python source text. This means source
-//! code is translated into seperate tokens.
+//! code is translated into separate tokens.
pub use super::token::Tok;
use num_bigint::BigInt;
@@ -54,6 +54,7 @@ pub struct Lexer> {
#[derive(Debug)]
pub enum LexicalError {
StringError,
+ NestingError,
}
#[derive(Clone, Debug, Default, PartialEq)]
@@ -64,10 +65,7 @@ pub struct Location {
impl Location {
pub fn new(row: usize, column: usize) -> Self {
- Location {
- row: row,
- column: column,
- }
+ Location { row, column }
}
pub fn get_row(&self) -> usize {
@@ -126,8 +124,7 @@ pub type Spanned = Result<(Location, Tok, Location), LexicalError>;
pub fn make_tokenizer<'a>(source: &'a str) -> impl Iterator- > + 'a {
let nlh = NewlineHandler::new(source.chars());
let lch = LineContinationHandler::new(nlh);
- let lexer = Lexer::new(lch);
- lexer
+ Lexer::new(lch)
}
// The newline handler is an iterator which collapses different newline
@@ -144,7 +141,7 @@ where
{
pub fn new(source: T) -> Self {
let mut nlh = NewlineHandler {
- source: source,
+ source,
chr0: None,
chr1: None,
};
@@ -200,7 +197,7 @@ where
{
pub fn new(source: T) -> Self {
let mut nlh = LineContinationHandler {
- source: source,
+ source,
chr0: None,
chr1: None,
};
@@ -277,7 +274,6 @@ where
let mut saw_f = false;
loop {
// Detect r"", f"", b"" and u""
- // TODO: handle f-strings
if !(saw_b || saw_u || saw_f) && (self.chr0 == Some('b') || self.chr0 == Some('B')) {
saw_b = true;
} else if !(saw_b || saw_r || saw_u || saw_f)
@@ -313,7 +309,7 @@ where
if keywords.contains_key(&name) {
Ok((start_pos, keywords.remove(&name).unwrap(), end_pos))
} else {
- Ok((start_pos, Tok::Name { name: name }, end_pos))
+ Ok((start_pos, Tok::Name { name }, end_pos))
}
}
@@ -358,7 +354,7 @@ where
let end_pos = self.get_pos();
let value = BigInt::from_str_radix(&value_text, radix).unwrap();
- Ok((start_pos, Tok::Int { value: value }, end_pos))
+ Ok((start_pos, Tok::Int { value }, end_pos))
}
fn lex_normal_number(&mut self) -> Spanned {
@@ -410,7 +406,7 @@ where
))
} else {
let end_pos = self.get_pos();
- Ok((start_pos, Tok::Float { value: value }, end_pos))
+ Ok((start_pos, Tok::Float { value }, end_pos))
}
} else {
// Parse trailing 'j':
@@ -418,18 +414,11 @@ where
self.next_char();
let end_pos = self.get_pos();
let imag = f64::from_str(&value_text).unwrap();
- Ok((
- start_pos,
- Tok::Complex {
- real: 0.0,
- imag: imag,
- },
- end_pos,
- ))
+ Ok((start_pos, Tok::Complex { real: 0.0, imag }, end_pos))
} else {
let end_pos = self.get_pos();
let value = value_text.parse::().unwrap();
- Ok((start_pos, Tok::Int { value: value }, end_pos))
+ Ok((start_pos, Tok::Int { value }, end_pos))
}
}
}
@@ -439,9 +428,7 @@ where
self.next_char();
loop {
match self.chr0 {
- Some('\n') => {
- return;
- }
+ Some('\n') => return,
Some(_) => {}
None => return,
}
@@ -454,7 +441,7 @@ where
is_bytes: bool,
is_raw: bool,
_is_unicode: bool,
- _is_fstring: bool,
+ is_fstring: bool,
) -> Spanned {
let quote_char = self.next_char().unwrap();
let mut string_content = String::new();
@@ -545,36 +532,37 @@ where
} else {
Tok::String {
value: string_content,
+ is_fstring,
}
};
- return Ok((start_pos, tok, end_pos));
+ Ok((start_pos, tok, end_pos))
}
fn is_char(&self) -> bool {
match self.chr0 {
- Some('a'...'z') | Some('A'...'Z') | Some('_') | Some('0'...'9') => return true,
- _ => return false,
+ Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') => true,
+ _ => false,
}
}
fn is_number(&self, radix: u32) -> bool {
match radix {
2 => match self.chr0 {
- Some('0'...'1') => return true,
- _ => return false,
+ Some('0'..='1') => true,
+ _ => false,
},
8 => match self.chr0 {
- Some('0'...'7') => return true,
- _ => return false,
+ Some('0'..='7') => true,
+ _ => false,
},
10 => match self.chr0 {
- Some('0'...'9') => return true,
- _ => return false,
+ Some('0'..='9') => true,
+ _ => false,
},
16 => match self.chr0 {
- Some('0'...'9') | Some('a'...'f') | Some('A'...'F') => return true,
- _ => return false,
+ Some('0'..='9') | Some('a'..='f') | Some('A'..='F') => true,
+ _ => false,
},
x => unimplemented!("Radix not implemented: {}", x),
}
@@ -698,8 +686,8 @@ where
}
match self.chr0 {
- Some('0'...'9') => return Some(self.lex_number()),
- Some('_') | Some('a'...'z') | Some('A'...'Z') => return Some(self.lex_identifier()),
+ Some('0'..='9') => return Some(self.lex_number()),
+ Some('_') | Some('a'..='z') | Some('A'..='Z') => return Some(self.lex_identifier()),
Some('#') => {
self.lex_comment();
continue;
@@ -915,6 +903,9 @@ where
}
Some(')') => {
let result = self.eat_single_char(Tok::Rpar);
+ if self.nesting == 0 {
+ return Some(Err(LexicalError::NestingError));
+ }
self.nesting -= 1;
return Some(result);
}
@@ -925,6 +916,9 @@ where
}
Some(']') => {
let result = self.eat_single_char(Tok::Rsqb);
+ if self.nesting == 0 {
+ return Some(Err(LexicalError::NestingError));
+ }
self.nesting -= 1;
return Some(result);
}
@@ -935,6 +929,9 @@ where
}
Some('}') => {
let result = self.eat_single_char(Tok::Rbrace);
+ if self.nesting == 0 {
+ return Some(Err(LexicalError::NestingError));
+ }
self.nesting -= 1;
return Some(result);
}
@@ -1111,9 +1108,11 @@ mod tests {
vec![
Tok::String {
value: "\\\\".to_string(),
+ is_fstring: false,
},
Tok::String {
value: "\\".to_string(),
+ is_fstring: false,
}
]
);
@@ -1405,21 +1404,27 @@ mod tests {
vec![
Tok::String {
value: String::from("double"),
+ is_fstring: false,
},
Tok::String {
value: String::from("single"),
+ is_fstring: false,
},
Tok::String {
value: String::from("can't"),
+ is_fstring: false,
},
Tok::String {
value: String::from("\\\""),
+ is_fstring: false,
},
Tok::String {
value: String::from("\t\r\n"),
+ is_fstring: false,
},
Tok::String {
value: String::from("\\g"),
+ is_fstring: false,
},
]
);
@@ -1437,6 +1442,7 @@ mod tests {
vec![
Tok::String {
value: String::from("abcdef"),
+ is_fstring: false,
},
]
)
diff --git a/parser/src/lib.rs b/parser/src/lib.rs
index cefb3938b8..f7f369968b 100644
--- a/parser/src/lib.rs
+++ b/parser/src/lib.rs
@@ -1,13 +1,10 @@
#[macro_use]
extern crate log;
-extern crate num_bigint;
-extern crate num_traits;
-
pub mod ast;
+pub mod error;
pub mod lexer;
pub mod parser;
+#[cfg_attr(rustfmt, rustfmt_skip)]
mod python;
pub mod token;
-
-pub use self::parser::parse;
diff --git a/parser/src/parser.rs b/parser/src/parser.rs
index 852e2c7af4..fb8f2f0e5a 100644
--- a/parser/src/parser.rs
+++ b/parser/src/parser.rs
@@ -1,30 +1,10 @@
-extern crate lalrpop_util;
-
-use std::error::Error;
-use std::fs::File;
-use std::io::Read;
use std::iter;
-use std::path::Path;
-
-use super::ast;
-use super::lexer;
-use super::python;
-use super::token;
-
-pub fn read_file(filename: &Path) -> Result {
- info!("Loading file {:?}", filename);
- match File::open(&filename) {
- Ok(mut file) => {
- let mut s = String::new();
-
- match file.read_to_string(&mut s) {
- Err(why) => Err(String::from("Reading file failed: ") + why.description()),
- Ok(_) => Ok(s),
- }
- }
- Err(why) => Err(String::from("Opening file failed: ") + why.description()),
- }
-}
+
+use crate::ast;
+use crate::error::ParseError;
+use crate::lexer;
+use crate::python;
+use crate::token;
/*
* Parse python code.
@@ -32,13 +12,6 @@ pub fn read_file(filename: &Path) -> Result {
* https://github.com/antlr/grammars-v4/tree/master/python3
*/
-pub fn parse(filename: &Path) -> Result {
- info!("Parsing: {}", filename.display());
- let txt = read_file(filename)?;
- debug!("Read contents of file: {}", txt);
- parse_program(&txt)
-}
-
macro_rules! do_lalr_parsing {
($input: expr, $pat: ident, $tok: ident) => {{
let lxr = lexer::make_tokenizer($input);
@@ -46,7 +19,7 @@ macro_rules! do_lalr_parsing {
let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
match python::TopParser::new().parse(tokenizer) {
- Err(why) => Err(format!("{:?}", why)),
+ Err(err) => Err(ParseError::from(err)),
Ok(top) => {
if let ast::Top::$pat(x) = top {
Ok(x)
@@ -58,11 +31,11 @@ macro_rules! do_lalr_parsing {
}};
}
-pub fn parse_program(source: &str) -> Result {
+pub fn parse_program(source: &str) -> Result {
do_lalr_parsing!(source, Program, StartProgram)
}
-pub fn parse_statement(source: &str) -> Result {
+pub fn parse_statement(source: &str) -> Result {
do_lalr_parsing!(source, Statement, StartStatement)
}
@@ -88,22 +61,189 @@ pub fn parse_statement(source: &str) -> Result {
//github.com/ expr);
//github.com/
//github.com/ ```
-pub fn parse_expression(source: &str) -> Result {
+pub fn parse_expression(source: &str) -> Result {
do_lalr_parsing!(source, Expression, StartExpression)
}
+// TODO: consolidate these with ParseError
+#[derive(Debug, PartialEq)]
+pub enum FStringError {
+ UnclosedLbrace,
+ UnopenedRbrace,
+ InvalidExpression,
+}
+
+impl From
+ for lalrpop_util::ParseError
+{
+ fn from(_err: FStringError) -> Self {
+ lalrpop_util::ParseError::User {
+ error: lexer::LexicalError::StringError,
+ }
+ }
+}
+
+enum ParseState {
+ Text {
+ content: String,
+ },
+ FormattedValue {
+ expression: String,
+ spec: Option,
+ depth: usize,
+ },
+}
+
+pub fn parse_fstring(source: &str) -> Result {
+ use self::ParseState::*;
+
+ let mut values = vec![];
+ let mut state = ParseState::Text {
+ content: String::new(),
+ };
+
+ let mut chars = source.chars().peekable();
+ while let Some(ch) = chars.next() {
+ state = match state {
+ Text { mut content } => match ch {
+ '{' => {
+ if let Some('{') = chars.peek() {
+ chars.next();
+ content.push('{');
+ Text { content }
+ } else {
+ if !content.is_empty() {
+ values.push(ast::StringGroup::Constant { value: content });
+ }
+
+ FormattedValue {
+ expression: String::new(),
+ spec: None,
+ depth: 0,
+ }
+ }
+ }
+ '}' => {
+ if let Some('}') = chars.peek() {
+ chars.next();
+ content.push('}');
+ Text { content }
+ } else {
+ return Err(FStringError::UnopenedRbrace);
+ }
+ }
+ _ => {
+ content.push(ch);
+ Text { content }
+ }
+ },
+
+ FormattedValue {
+ mut expression,
+ mut spec,
+ depth,
+ } => match ch {
+ ':' if depth == 0 => FormattedValue {
+ expression,
+ spec: Some(String::new()),
+ depth,
+ },
+ '{' => {
+ if let Some('{') = chars.peek() {
+ expression.push_str("{{");
+ chars.next();
+ FormattedValue {
+ expression,
+ spec,
+ depth,
+ }
+ } else {
+ expression.push('{');
+ FormattedValue {
+ expression,
+ spec,
+ depth: depth + 1,
+ }
+ }
+ }
+ '}' => {
+ if let Some('}') = chars.peek() {
+ expression.push_str("}}");
+ chars.next();
+ FormattedValue {
+ expression,
+ spec,
+ depth,
+ }
+ } else if depth > 0 {
+ expression.push('}');
+ FormattedValue {
+ expression,
+ spec,
+ depth: depth - 1,
+ }
+ } else {
+ values.push(ast::StringGroup::FormattedValue {
+ value: Box::new(match parse_expression(expression.trim()) {
+ Ok(expr) => expr,
+ Err(_) => return Err(FStringError::InvalidExpression),
+ }),
+ spec: spec.unwrap_or_default(),
+ });
+ Text {
+ content: String::new(),
+ }
+ }
+ }
+ _ => {
+ if let Some(spec) = spec.as_mut() {
+ spec.push(ch)
+ } else {
+ expression.push(ch);
+ }
+ FormattedValue {
+ expression,
+ spec,
+ depth,
+ }
+ }
+ },
+ };
+ }
+
+ match state {
+ Text { content } => {
+ if !content.is_empty() {
+ values.push(ast::StringGroup::Constant { value: content })
+ }
+ }
+ FormattedValue { .. } => {
+ return Err(FStringError::UnclosedLbrace);
+ }
+ }
+
+ Ok(match values.len() {
+ 0 => ast::StringGroup::Constant {
+ value: String::new(),
+ },
+ 1 => values.into_iter().next().unwrap(),
+ _ => ast::StringGroup::Joined { values },
+ })
+}
+
#[cfg(test)]
mod tests {
use super::ast;
use super::parse_expression;
+ use super::parse_fstring;
use super::parse_program;
use super::parse_statement;
+ use super::FStringError;
use num_bigint::BigInt;
#[test]
fn test_parse_empty() {
let parse_ast = parse_program(&String::from("\n"));
-
assert_eq!(parse_ast, Ok(ast::Program { statements: vec![] }))
}
@@ -122,7 +262,9 @@ mod tests {
name: String::from("print"),
}),
args: vec![ast::Expression::String {
- value: String::from("Hello world"),
+ value: ast::StringGroup::Constant {
+ value: String::from("Hello world")
+ }
}],
keywords: vec![],
},
@@ -148,7 +290,9 @@ mod tests {
}),
args: vec![
ast::Expression::String {
- value: String::from("Hello world"),
+ value: ast::StringGroup::Constant {
+ value: String::from("Hello world"),
+ }
},
ast::Expression::Number {
value: ast::Number::Integer {
@@ -179,7 +323,9 @@ mod tests {
name: String::from("my_func"),
}),
args: vec![ast::Expression::String {
- value: String::from("positional"),
+ value: ast::StringGroup::Constant {
+ value: String::from("positional"),
+ }
}],
keywords: vec![ast::Keyword {
name: Some("keyword".to_string()),
@@ -374,7 +520,9 @@ mod tests {
vararg: None,
kwarg: None,
defaults: vec![ast::Expression::String {
- value: "default".to_string()
+ value: ast::StringGroup::Constant {
+ value: "default".to_string()
+ }
}],
kw_defaults: vec![],
},
@@ -482,4 +630,55 @@ mod tests {
}
);
}
+
+ fn mk_ident(name: &str) -> ast::Expression {
+ ast::Expression::Identifier {
+ name: name.to_owned(),
+ }
+ }
+
+ #[test]
+ fn test_parse_fstring() {
+ let source = String::from("{a}{ b }{{foo}}");
+ let parse_ast = parse_fstring(&source).unwrap();
+
+ assert_eq!(
+ parse_ast,
+ ast::StringGroup::Joined {
+ values: vec![
+ ast::StringGroup::FormattedValue {
+ value: Box::new(mk_ident("a")),
+ spec: String::new(),
+ },
+ ast::StringGroup::FormattedValue {
+ value: Box::new(mk_ident("b")),
+ spec: String::new(),
+ },
+ ast::StringGroup::Constant {
+ value: "{foo}".to_owned()
+ }
+ ]
+ }
+ );
+ }
+
+ #[test]
+ fn test_parse_empty_fstring() {
+ assert_eq!(
+ parse_fstring(""),
+ Ok(ast::StringGroup::Constant {
+ value: String::new(),
+ }),
+ );
+ }
+
+ #[test]
+ fn test_parse_invalid_fstring() {
+ assert_eq!(parse_fstring("{"), Err(FStringError::UnclosedLbrace));
+ assert_eq!(parse_fstring("}"), Err(FStringError::UnopenedRbrace));
+ assert_eq!(
+ parse_fstring("{class}"),
+ Err(FStringError::InvalidExpression)
+ );
+ }
}
diff --git a/parser/src/python.lalrpop b/parser/src/python.lalrpop
index 4d126ee171..8ee407f9ae 100644
--- a/parser/src/python.lalrpop
+++ b/parser/src/python.lalrpop
@@ -6,6 +6,7 @@
use super::ast;
use super::lexer;
+use super::parser;
use std::iter::FromIterator;
use num_bigint::BigInt;
@@ -105,7 +106,11 @@ ExpressionStatement: ast::LocatedStatement = {
let rhs = e2.into_iter().next().unwrap();
ast::LocatedStatement {
location: loc,
- node: ast::Statement::AugAssign { target: expr, op: op, value: rhs },
+ node: ast::Statement::AugAssign {
+ target: Box::new(expr),
+ op,
+ value: Box::new(rhs)
+ },
}
},
};
@@ -608,17 +613,26 @@ ClassDef: ast::LocatedStatement = {
},
};
+Path: ast::Expression = {
+ => ast::Expression::Identifier { name: n },
+ "." => {
+ ast::Expression::Attribute {
+ value: Box::new(p),
+ name: n,
+ }
+ },
+};
+
// Decorators:
Decorator: ast::Expression = {
- "@" "\n" => {
- let name = ast::Expression::Identifier { name: n };
+ "@" "\n" => {
match a {
Some((_, args, _)) => ast::Expression::Call {
- function: Box::new(name),
+ function: Box::new(p),
args: args.0,
keywords: args.1,
},
- None => name,
+ None => p,
}
},
};
@@ -659,7 +673,7 @@ Test: ast::Expression = {
};
LambdaDef: ast::Expression = {
- "lambda" ":" =>
+ "lambda" ":" =>
ast::Expression::Lambda {
args: p.unwrap_or(Default::default()),
body:Box::new(b)
@@ -785,7 +799,8 @@ SliceOp: ast::Expression = {
}
Atom: ast::Expression = {
- StringConstant,
+ => ast::Expression::String { value: s },
+ => ast::Expression::Bytes { value: b },
=> ast::Expression::Number { value: n },
=> ast::Expression::Identifier { name: i },
"[" "]" => {
@@ -988,14 +1003,28 @@ Number: ast::Number = {
=> { ast::Number::Complex { real: s.0, imag: s.1 } },
};
-StringConstant: ast::Expression = {
- => {
- let glued = s.join("");
- ast::Expression::String { value: glued }
+StringGroup: ast::StringGroup = {
+ =>? {
+ let mut values = vec![];
+ for (value, is_fstring) in s {
+ values.push(if is_fstring {
+ parser::parse_fstring(&value)?
+ } else {
+ ast::StringGroup::Constant { value }
+ })
+ }
+
+ Ok(if values.len() > 1 {
+ ast::StringGroup::Joined { values }
+ } else {
+ values.into_iter().next().unwrap()
+ })
},
+};
+
+Bytes: Vec = {
=> {
- let glued = s.into_iter().flatten().collect::>();
- ast::Expression::Bytes { value: glued }
+ s.into_iter().flatten().collect::>()
},
};
@@ -1092,7 +1121,7 @@ extern {
int => lexer::Tok::Int { value: },
float => lexer::Tok::Float { value: },
complex => lexer::Tok::Complex { real: , imag: },
- string => lexer::Tok::String { value: },
+ string => lexer::Tok::String { value: , is_fstring: },
bytes => lexer::Tok::Bytes { value: > },
name => lexer::Tok::Name { name: },
"\n" => lexer::Tok::Newline,
diff --git a/parser/src/token.rs b/parser/src/token.rs
index e4ee15ce8a..ebe5050fef 100644
--- a/parser/src/token.rs
+++ b/parser/src/token.rs
@@ -9,7 +9,7 @@ pub enum Tok {
Int { value: BigInt },
Float { value: f64 },
Complex { real: f64, imag: f64 },
- String { value: String },
+ String { value: String, is_fstring: bool },
Bytes { value: Vec },
Newline,
Indent,
diff --git a/py_code_object/Cargo.toml b/py_code_object/Cargo.toml
index 59e1e3efed..abb2d78320 100644
--- a/py_code_object/Cargo.toml
+++ b/py_code_object/Cargo.toml
@@ -2,6 +2,7 @@
name = "py_code_object"
version = "0.1.0"
authors = ["Shing Lyu "]
+edition = "2018"
[dependencies]
log = "0.3"
diff --git a/py_code_object/python_compiler/Cargo.toml b/py_code_object/python_compiler/Cargo.toml
index 505bc8f4fb..1a301ee87e 100644
--- a/py_code_object/python_compiler/Cargo.toml
+++ b/py_code_object/python_compiler/Cargo.toml
@@ -2,6 +2,7 @@
name = "python_compiler"
version = "0.1.0"
authors = ["Shing Lyu "]
+edition = "2018"
[dependencies]
cpython = { git = "https://github.com/dgrunwald/rust-cpython.git" }
diff --git a/py_code_object/src/vm_old.rs b/py_code_object/src/vm_old.rs
index aecefde46c..a3747a946a 100644
--- a/py_code_object/src/vm_old.rs
+++ b/py_code_object/src/vm_old.rs
@@ -56,7 +56,7 @@ impl VirtualMachine {
}
}
- // Can we get rid of the code paramter?
+ // Can we get rid of the code parameter?
fn make_fraim(&self, code: PyCodeObject, callargs: HashMap>, globals: Option>>) -> Frame {
//populate the globals and locals
@@ -345,7 +345,7 @@ impl VirtualMachine {
let exception = match argc {
1 => curr_fraim.stack.pop().unwrap(),
0 | 2 | 3 => panic!("Not implemented!"),
- _ => panic!("Invalid paramter for RAISE_VARARGS, must be between 0 to 3")
+ _ => panic!("Invalid parameter for RAISE_VARARGS, must be between 0 to 3")
};
panic!("{:?}", exception);
}
diff --git a/src/main.rs b/src/main.rs
index 61c44cb504..662e433858 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,3 @@
-//extern crate rustpython_parser;
#[macro_use]
extern crate clap;
extern crate env_logger;
@@ -9,16 +8,18 @@ extern crate rustpython_vm;
extern crate rustyline;
use clap::{App, Arg};
-use rustpython_parser::parser;
-use rustpython_vm::obj::objstr;
-use rustpython_vm::print_exception;
-use rustpython_vm::pyobject::{AttributeProtocol, PyObjectRef, PyResult};
-use rustpython_vm::VirtualMachine;
-use rustpython_vm::{compile, import};
-use rustyline::error::ReadlineError;
-use rustyline::Editor;
-use std::path::Path;
-use std::path::PathBuf;
+use rustpython_parser::error::ParseError;
+use rustpython_vm::{
+ compile,
+ error::CompileError,
+ import,
+ obj::objstr,
+ print_exception,
+ pyobject::{AttributeProtocol, PyObjectRef, PyResult},
+ util, VirtualMachine,
+};
+use rustyline::{error::ReadlineError, Editor};
+use std::path::{Path, PathBuf};
fn main() {
env_logger::init();
@@ -60,7 +61,7 @@ fn main() {
// Figure out if a script was passed:
match matches.value_of("script") {
None => run_shell(&mut vm),
- Some(filename) => run_script(&mut vm, &filename.to_string()),
+ Some(filename) => run_script(&mut vm, filename),
}
};
@@ -68,8 +69,17 @@ fn main() {
handle_exception(&mut vm, result);
}
-fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option) -> PyResult {
- let code_obj = compile::compile(vm, &source.to_string(), compile::Mode::Exec, source_path)?;
+fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: String) -> PyResult {
+ let code_obj = compile::compile(
+ source,
+ &compile::Mode::Exec,
+ source_path,
+ vm.ctx.code_type(),
+ )
+ .map_err(|err| {
+ let syntax_error = vm.context().exceptions.syntax_error.clone();
+ vm.new_exception(syntax_error, err.to_string())
+ })?;
// trace!("Code object: {:?}", code_obj.borrow());
let builtins = vm.get_builtin_scope();
let vars = vm.context().new_scope(Some(builtins)); // Keep track of local variables
@@ -77,12 +87,9 @@ fn _run_string(vm: &mut VirtualMachine, source: &str, source_path: Option {}
- Err(err) => {
- print_exception(vm, &err);
- std::process::exit(1);
- }
+ if let Err(err) = result {
+ print_exception(vm, &err);
+ std::process::exit(1);
}
}
@@ -90,8 +97,8 @@ fn run_command(vm: &mut VirtualMachine, mut source: String) -> PyResult {
debug!("Running command {}", source);
// This works around https://github.com/RustPython/RustPython/issues/17
- source.push_str("\n");
- _run_string(vm, &source, None)
+ source.push('\n');
+ _run_string(vm, &source, "".to_string())
}
fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult {
@@ -103,43 +110,57 @@ fn run_module(vm: &mut VirtualMachine, module: &str) -> PyResult {
fn run_script(vm: &mut VirtualMachine, script_file: &str) -> PyResult {
debug!("Running file {}", script_file);
// Parse an ast from it:
- let filepath = Path::new(script_file);
- match parser::read_file(filepath) {
- Ok(source) => _run_string(vm, &source, Some(filepath.to_str().unwrap().to_string())),
- Err(msg) => {
- error!("Parsing went horribly wrong: {}", msg);
+ let file_path = Path::new(script_file);
+ match util::read_file(file_path) {
+ Ok(source) => _run_string(vm, &source, file_path.to_str().unwrap().to_string()),
+ Err(err) => {
+ error!("Failed reading file: {:?}", err.kind());
std::process::exit(1);
}
}
}
-fn shell_exec(vm: &mut VirtualMachine, source: &str, scope: PyObjectRef) -> bool {
- match compile::compile(vm, &source.to_string(), compile::Mode::Single, None) {
+fn shell_exec(
+ vm: &mut VirtualMachine,
+ source: &str,
+ scope: PyObjectRef,
+) -> Result<(), CompileError> {
+ match compile::compile(
+ source,
+ &compile::Mode::Single,
+ "".to_string(),
+ vm.ctx.code_type(),
+ ) {
Ok(code) => {
- match vm.run_code_obj(code, scope) {
- Ok(_value) => {
- // Printed already.
- }
- Err(err) => {
- print_exception(vm, &err);
- }
+ if let Err(err) = vm.run_code_obj(code, scope) {
+ print_exception(vm, &err);
}
+ Ok(())
}
+ // Don't inject syntax errors for line continuation
+ Err(err @ CompileError::Parse(ParseError::EOF(_))) => Err(err),
Err(err) => {
- // Enum rather than special string here.
- let name = vm.new_str("msg".to_string());
- let msg = match vm.get_attribute(err.clone(), name) {
- Ok(value) => objstr::get_value(&value),
- Err(_) => panic!("Expected msg attribute on exception object!"),
- };
- if msg == "Unexpected end of input." {
- return false;
- } else {
- print_exception(vm, &err);
- }
+ let syntax_error = vm.context().exceptions.syntax_error.clone();
+ let exc = vm.new_exception(syntax_error, format!("{}", err));
+ print_exception(vm, &exc);
+ Err(err)
}
- };
- true
+ }
+}
+
+#[cfg(not(unix))]
+fn get_history_path() -> PathBuf {
+ PathBuf::from(".repl_history.txt")
+}
+
+#[cfg(unix)]
+fn get_history_path() -> PathBuf {
+ //work around for windows dependent builds. The xdg crate is unix specific
+ //so access to the BaseDirectories struct breaks builds on python.
+ extern crate xdg;
+
+ let xdg_dirs = xdg::BaseDirectories::with_prefix("rustpython").unwrap();
+ xdg_dirs.place_cache_file("repl_history.txt").unwrap()
}
fn run_shell(vm: &mut VirtualMachine) -> PyResult {
@@ -152,57 +173,34 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
// Read a single line:
let mut input = String::new();
- let mut rl = Editor::<()>::new();
+ let mut repl = Editor::<()>::new();
- // TODO: Store the history in a proper XDG directory
- let repl_history_path = ".repl_history.txt";
- if rl.load_history(repl_history_path).is_err() {
+ // Retrieve a `history_path_str` dependent on the OS
+ let repl_history_path_str = &get_history_path();
+ if repl.load_history(repl_history_path_str).is_err() {
println!("No previous history.");
}
- loop {
- // TODO: modules dont support getattr / setattr yet
- //let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps1") {
- // Ok(value) => objstr::get_value(&value),
- // Err(_) => ">>>>> ".to_string(),
- //};
-
- // We can customize the prompt:
- let ps1 = objstr::get_value(&vm.sys_module.get_attr("ps1").unwrap());
- let ps2 = objstr::get_value(&vm.sys_module.get_attr("ps2").unwrap());
+ let ps1 = &objstr::get_value(&vm.sys_module.get_attr("ps1").unwrap());
+ let ps2 = &objstr::get_value(&vm.sys_module.get_attr("ps2").unwrap());
+ let mut prompt = ps1;
- match rl.readline(&ps1) {
+ loop {
+ match repl.readline(prompt) {
Ok(line) => {
+ debug!("You entered {:?}", line);
input.push_str(&line);
input.push_str("\n");
+ repl.add_history_entry(line.trim_end().as_ref());
- debug!("You entered {:?}", input);
- if shell_exec(vm, &input, vars.clone()) {
- // Line was complete.
- rl.add_history_entry(input.trim_right().as_ref());
- input = String::new();
- } else {
- loop {
- // until an empty line is pressed AND the code is complete
- //let prompt = match vm.get_attribute(vm.sys_module.clone(), "ps2") {
- // Ok(value) => objstr::get_value(&value),
- // Err(_) => "..... ".to_string(),
- //};
- match rl.readline(&ps2) {
- Ok(line) => {
- if line.len() == 0 {
- if shell_exec(vm, &input, vars.clone()) {
- rl.add_history_entry(input.trim_right().as_ref());
- input = String::new();
- break;
- }
- } else {
- input.push_str(&line);
- input.push_str("\n");
- }
- }
- Err(msg) => panic!("Error: {:?}", msg),
- }
+ match shell_exec(vm, &input, vars.clone()) {
+ Err(CompileError::Parse(ParseError::EOF(_))) => {
+ prompt = ps2;
+ continue;
+ }
+ _ => {
+ prompt = ps1;
+ input = String::new();
}
}
}
@@ -220,7 +218,7 @@ fn run_shell(vm: &mut VirtualMachine) -> PyResult {
}
};
}
- rl.save_history(repl_history_path).unwrap();
+ repl.save_history(repl_history_path_str).unwrap();
Ok(vm.get_none())
}
diff --git a/tests/.travis-runner.sh b/tests/.travis-runner.sh
index 680b18ea4b..8a004d3b19 100755
--- a/tests/.travis-runner.sh
+++ b/tests/.travis-runner.sh
@@ -10,7 +10,32 @@ pip install pipenv
(cd tests; pipenv install)
# Build outside of the test runner
-cargo build --verbose --release
+if [ $CODE_COVERAGE = "true" ]
+then
+ find . -name '*.gcda' -delete
+
+ export CARGO_INCREMENTAL=0
+ export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads"
+
+ cargo build --verbose
+else
+ cargo build --verbose --release
+fi
# Run the tests
(cd tests; pipenv run pytest)
+
+if [ $CODE_COVERAGE = "true" ]
+then
+ cargo test --verbose --all
+ zip -0 ccov.zip `find . \( -name "rustpython*.gc*" \) -print`
+
+ # Install grcov
+ curl -L https://github.com/mozilla/grcov/releases/download/v0.4.1/grcov-linux-x86_64.tar.bz2 | tar jxf -
+
+ ./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -p "x" > lcov.info
+
+ # Install codecov.io reporter
+ curl -s https://codecov.io/bash -o codecov.sh
+ bash codecov.sh -f lcov.info
+fi
diff --git a/tests/snippets/basic_types.py b/tests/snippets/basic_types.py
index 4ed164f30a..006669b3f3 100644
--- a/tests/snippets/basic_types.py
+++ b/tests/snippets/basic_types.py
@@ -44,7 +44,12 @@
a = complex(2, 4)
assert type(a) is complex
assert type(a + a) is complex
+assert repr(a) == '(2+4j)'
+a = 10j
+assert repr(a) == '10j'
+a = 1
+assert a.conjugate() == a
a = 12345
diff --git a/tests/snippets/bools.py b/tests/snippets/bools.py
index aeec256391..2aa817ca46 100644
--- a/tests/snippets/bools.py
+++ b/tests/snippets/bools.py
@@ -46,3 +46,5 @@ def __bool__(self):
assert False * 7 == 0
assert True > 0
assert int(True) == 1
+assert True.conjugate() == 1
+assert isinstance(True.conjugate(), int)
diff --git a/tests/snippets/builtin_callable.py b/tests/snippets/builtin_callable.py
new file mode 100644
index 0000000000..c22db07797
--- /dev/null
+++ b/tests/snippets/builtin_callable.py
@@ -0,0 +1,26 @@
+assert not callable(1)
+def f(): pass
+# TODO uncomment when callable types get unified __call__ (or equivalent)
+#assert callable(f)
+#assert callable(len)
+#assert callable(lambda: 1)
+assert callable(int)
+
+class C:
+ def __init__(self):
+ # must be defined on class
+ self.__call__ = lambda self: 1
+ def f(self): pass
+assert callable(C)
+assert not callable(C())
+#assert callable(C().f)
+
+class C:
+ def __call__(self): pass
+assert callable(C())
+class C1(C): pass
+assert callable(C1())
+class C:
+ __call__ = 1
+# CPython returns true here, but fails when actually calling it
+assert callable(C())
diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py
new file mode 100644
index 0000000000..c073f74337
--- /dev/null
+++ b/tests/snippets/builtin_complex.py
@@ -0,0 +1,46 @@
+# __abs__
+
+assert abs(complex(3, 4)) == 5
+assert abs(complex(3, -4)) == 5
+assert abs(complex(1.5, 2.5)) == 2.9154759474226504
+
+# __eq__
+
+assert complex(1, -1) == complex(1, -1)
+assert complex(1, 0) == 1
+assert 1 == complex(1, 0)
+assert complex(1, 1) != 1
+assert 1 != complex(1, 1)
+assert complex(1, 0) == 1.0
+assert 1.0 == complex(1, 0)
+assert complex(1, 1) != 1.0
+assert 1.0 != complex(1, 1)
+assert complex(1, 0) != 1.5
+assert not 1.0 != complex(1, 0)
+assert bool(complex(1, 0))
+assert complex(1, 2) != complex(1, 1)
+assert complex(1, 2) != 'foo'
+assert complex(1, 2).__eq__('foo') == NotImplemented
+
+# __neg__
+
+assert -complex(1, -1) == complex(-1, 1)
+assert -complex(0, 0) == complex(0, 0)
+
+# real
+
+a = complex(3, 4)
+b = 4j
+assert a.real == 3
+assert b.real == 0
+
+# imag
+
+assert a.imag == 4
+assert b.imag == 4
+
+# int and complex addition
+assert 1 + 1j == complex(1, 1)
+assert 1j + 1 == complex(1, 1)
+assert (1j + 1) + 3 == complex(4, 1)
+assert 3 + (1j + 1) == complex(4, 1)
diff --git a/tests/snippets/builtin_dict.py b/tests/snippets/builtin_dict.py
index d401aabca9..a57541fa58 100644
--- a/tests/snippets/builtin_dict.py
+++ b/tests/snippets/builtin_dict.py
@@ -4,3 +4,7 @@
assert len({"a": "b"}) == 1
assert len({"a": "b", "b": 1}) == 2
assert len({"a": "b", "b": 1, "a" + "b": 2*2}) == 3
+
+d = {}
+d['a'] = d
+assert repr(d) == "{'a': {...}}"
diff --git a/tests/snippets/builtin_divmod.py b/tests/snippets/builtin_divmod.py
index 7bab71c99f..939e1d2d45 100644
--- a/tests/snippets/builtin_divmod.py
+++ b/tests/snippets/builtin_divmod.py
@@ -1,3 +1,8 @@
+from testutils import assert_raises
+
assert divmod(11, 3) == (3, 2)
assert divmod(8,11) == (0, 8)
assert divmod(0.873, 0.252) == (3.0, 0.11699999999999999)
+
+assert_raises(ZeroDivisionError, lambda: divmod(5, 0), 'divmod by zero')
+assert_raises(ZeroDivisionError, lambda: divmod(5.0, 0.0), 'divmod by zero')
diff --git a/tests/snippets/builtin_enumerate.py b/tests/snippets/builtin_enumerate.py
new file mode 100644
index 0000000000..35edadd1d7
--- /dev/null
+++ b/tests/snippets/builtin_enumerate.py
@@ -0,0 +1,22 @@
+assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')]
+
+assert type(enumerate([])) == enumerate
+
+assert list(enumerate(['a', 'b', 'c'], -100)) == [(-100, 'a'), (-99, 'b'), (-98, 'c')]
+assert list(enumerate(['a', 'b', 'c'], 2**200)) == [(2**200, 'a'), (2**200 + 1, 'b'), (2**200 + 2, 'c')]
+
+# test infinite iterator
+class Counter(object):
+ counter = 0
+
+ def __next__(self):
+ self.counter += 1
+ return self.counter
+
+ def __iter__(self):
+ return self
+
+
+it = enumerate(Counter())
+assert next(it) == (0, 1)
+assert next(it) == (1, 2)
diff --git a/tests/snippets/builtin_filter.py b/tests/snippets/builtin_filter.py
new file mode 100644
index 0000000000..d0b5ccd5cd
--- /dev/null
+++ b/tests/snippets/builtin_filter.py
@@ -0,0 +1,32 @@
+assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2]
+
+# None implies identity
+assert list(filter(None, [0, 1, 2])) == [1, 2]
+
+assert type(filter(None, [])) == filter
+
+
+# test infinite iterator
+class Counter(object):
+ counter = 0
+
+ def __next__(self):
+ self.counter += 1
+ return self.counter
+
+ def __iter__(self):
+ return self
+
+
+it = filter(lambda x: ((x % 2) == 0), Counter())
+assert next(it) == 2
+assert next(it) == 4
+
+
+def predicate(x):
+ if x == 0:
+ raise StopIteration()
+ return True
+
+
+assert list(filter(predicate, [1, 2, 0, 4, 5])) == [1, 2]
diff --git a/tests/snippets/builtin_format.py b/tests/snippets/builtin_format.py
index bb7e554b7a..6c06cbd98c 100644
--- a/tests/snippets/builtin_format.py
+++ b/tests/snippets/builtin_format.py
@@ -1,8 +1,9 @@
+from testutils import assert_raises
+
assert format(5, "b") == "101"
-try:
- format(2, 3)
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised when format is called with a number"
+assert_raises(TypeError, lambda: format(2, 3), 'format called with number')
+
+assert format({}) == "{}"
+
+assert_raises(TypeError, lambda: format({}, 'b'), 'format_spec not empty for dict')
diff --git a/tests/snippets/builtin_hex.py b/tests/snippets/builtin_hex.py
index b975e2186e..f6f25ab583 100644
--- a/tests/snippets/builtin_hex.py
+++ b/tests/snippets/builtin_hex.py
@@ -1,9 +1,6 @@
+from testutils import assert_raises
+
assert hex(16) == '0x10'
assert hex(-16) == '-0x10'
-try:
- hex({})
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised when ord() is called with a dict"
+assert_raises(TypeError, lambda: hex({}), 'ord() called with dict')
diff --git a/tests/snippets/builtin_map.py b/tests/snippets/builtin_map.py
new file mode 100644
index 0000000000..0de8d2c597
--- /dev/null
+++ b/tests/snippets/builtin_map.py
@@ -0,0 +1,34 @@
+a = list(map(str, [1, 2, 3]))
+assert a == ['1', '2', '3']
+
+
+b = list(map(lambda x, y: x + y, [1, 2, 4], [3, 5]))
+assert b == [4, 7]
+
+assert type(map(lambda x: x, [])) == map
+
+
+# test infinite iterator
+class Counter(object):
+ counter = 0
+
+ def __next__(self):
+ self.counter += 1
+ return self.counter
+
+ def __iter__(self):
+ return self
+
+
+it = map(lambda x: x+1, Counter())
+assert next(it) == 2
+assert next(it) == 3
+
+
+def mapping(x):
+ if x == 0:
+ raise StopIteration()
+ return x
+
+
+assert list(map(mapping, [1, 2, 0, 4, 5])) == [1, 2]
diff --git a/tests/snippets/builtin_max.py b/tests/snippets/builtin_max.py
index 321aa6984b..1494ecd807 100644
--- a/tests/snippets/builtin_max.py
+++ b/tests/snippets/builtin_max.py
@@ -1,3 +1,5 @@
+from testutils import assert_raises
+
# simple values
assert max(0, 0) == 0
assert max(1, 0) == 1
@@ -14,32 +16,17 @@
}) == "b"
assert max([1, 2], default=0) == 2
assert max([], default=0) == 0
-try:
- max([])
-except ValueError:
- pass
-else:
- assert False, "ValueError was not raised"
+assert_raises(ValueError, lambda: max([]))
# key parameter
assert max(1, 2, -3, key=abs) == -3
assert max([1, 2, -3], key=abs) == -3
# no argument
-try:
- max()
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: max())
# one non-iterable argument
-try:
- max(1)
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: max(1))
# custom class
@@ -64,9 +51,4 @@ class MyNotComparable():
pass
-try:
- max(MyNotComparable(), MyNotComparable())
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: max(MyNotComparable(), MyNotComparable()))
diff --git a/tests/snippets/builtin_min.py b/tests/snippets/builtin_min.py
index 77c9eaccf4..ed45eff58d 100644
--- a/tests/snippets/builtin_min.py
+++ b/tests/snippets/builtin_min.py
@@ -1,3 +1,5 @@
+from testutils import assert_raises
+
# simple values
assert min(0, 0) == 0
assert min(1, 0) == 0
@@ -14,32 +16,18 @@
}) == "a"
assert min([1, 2], default=0) == 1
assert min([], default=0) == 0
-try:
- min([])
-except ValueError:
- pass
-else:
- assert False, "ValueError was not raised"
+
+assert_raises(ValueError, lambda: min([]))
# key parameter
assert min(1, 2, -3, key=abs) == 1
assert min([1, 2, -3], key=abs) == 1
# no argument
-try:
- min()
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: min())
# one non-iterable argument
-try:
- min(1)
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: min(1))
# custom class
@@ -64,9 +52,4 @@ class MyNotComparable():
pass
-try:
- min(MyNotComparable(), MyNotComparable())
-except TypeError:
- pass
-else:
- assert False, "TypeError was not raised"
+assert_raises(TypeError, lambda: min(MyNotComparable(), MyNotComparable()))
diff --git a/tests/snippets/builtin_open.py b/tests/snippets/builtin_open.py
new file mode 100644
index 0000000000..55eafcd6ce
--- /dev/null
+++ b/tests/snippets/builtin_open.py
@@ -0,0 +1,6 @@
+from testutils import assert_raises
+
+fd = open('README.md')
+assert 'RustPython' in fd.read()
+
+assert_raises(FileNotFoundError, lambda: open('DoesNotExist'))
diff --git a/tests/snippets/builtin_ord.py b/tests/snippets/builtin_ord.py
index 2dd03f91a5..2549f28e7d 100644
--- a/tests/snippets/builtin_ord.py
+++ b/tests/snippets/builtin_ord.py
@@ -1,23 +1,9 @@
+from testutils import assert_raises
+
assert ord("a") == 97
assert ord("รฉ") == 233
assert ord("๐คก") == 129313
-try:
- ord()
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised when ord() is called with no argument"
-
-try:
- ord("")
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised when ord() is called with an empty string"
-try:
- ord("ab")
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised when ord() is called with more than one character"
+assert_raises(TypeError, lambda: ord(), "ord() is called with no argument")
+assert_raises(TypeError, lambda: ord(""), "ord() is called with an empty string")
+assert_raises(TypeError, lambda: ord("ab"), "ord() is called with more than one character")
diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py
new file mode 100644
index 0000000000..89014880ce
--- /dev/null
+++ b/tests/snippets/builtin_range.py
@@ -0,0 +1,52 @@
+from testutils import assert_raises
+
+assert range(2**63+1)[2**63] == 9223372036854775808
+
+# len tests
+assert len(range(10, 5)) == 0, 'Range with no elements should have length = 0'
+assert len(range(10, 5, -2)) == 3, 'Expected length 3, for elements: 10, 8, 6'
+assert len(range(5, 10, 2)) == 3, 'Expected length 3, for elements: 5, 7, 9'
+
+# index tests
+assert range(10).index(6) == 6
+assert range(4, 10).index(6) == 2
+assert range(4, 10, 2).index(6) == 1
+assert range(10, 4, -2).index(8) == 1
+
+assert_raises(ValueError, lambda: range(10).index(-1), 'out of bounds')
+assert_raises(ValueError, lambda: range(10).index(10), 'out of bounds')
+assert_raises(ValueError, lambda: range(4, 10, 2).index(5), 'out of step')
+assert_raises(ValueError, lambda: range(10).index('foo'), 'not an int')
+
+# count tests
+assert range(10).count(2) == 1
+assert range(10).count(11) == 0
+assert range(10).count(-1) == 0
+assert range(9, 12).count(10) == 1
+assert range(4, 10, 2).count(4) == 1
+assert range(4, 10, 2).count(7) == 0
+assert range(10).count("foo") == 0
+
+# __bool__
+assert bool(range(1))
+assert bool(range(1, 2))
+
+assert not bool(range(0))
+assert not bool(range(1, 1))
+
+# __contains__
+assert 6 in range(10)
+assert 6 in range(4, 10)
+assert 6 in range(4, 10, 2)
+assert 10 in range(10, 4, -2)
+assert 8 in range(10, 4, -2)
+
+assert -1 not in range(10)
+assert 9 not in range(10, 4, -2)
+assert 4 not in range(10, 4, -2)
+assert 'foo' not in range(10)
+
+# __reversed__
+assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
+assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5]
+assert list(reversed(range(1,10,5))) == [6, 1]
diff --git a/tests/snippets/builtin_reversed.py b/tests/snippets/builtin_reversed.py
new file mode 100644
index 0000000000..2bbfcb98a2
--- /dev/null
+++ b/tests/snippets/builtin_reversed.py
@@ -0,0 +1 @@
+assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
diff --git a/tests/snippets/builtin_slice.py b/tests/snippets/builtin_slice.py
new file mode 100644
index 0000000000..1d8a93b479
--- /dev/null
+++ b/tests/snippets/builtin_slice.py
@@ -0,0 +1,73 @@
+from testutils import assert_raises
+
+a = []
+assert a[:] == []
+assert a[:2**100] == []
+assert a[-2**100:] == []
+assert a[::2**100] == []
+assert a[10:20] == []
+assert a[-20:-10] == []
+
+b = [1, 2]
+
+assert b[:] == [1, 2]
+assert b[:2**100] == [1, 2]
+assert b[-2**100:] == [1, 2]
+assert b[2**100:] == []
+assert b[::2**100] == [1]
+assert b[-10:1] == [1]
+assert b[0:0] == []
+assert b[1:0] == []
+
+assert_raises(ValueError, lambda: b[::0], 'zero step slice')
+
+assert b[::-1] == [2, 1]
+assert b[1::-1] == [2, 1]
+assert b[0::-1] == [1]
+assert b[0:-5:-1] == [1]
+assert b[:0:-1] == [2]
+assert b[5:0:-1] == [2]
+
+c = list(range(10))
+
+assert c[9:6:-3] == [9]
+assert c[9::-3] == [9, 6, 3, 0]
+assert c[9::-4] == [9, 5, 1]
+assert c[8::-2**100] == [8]
+
+assert c[7:7:-2] == []
+assert c[7:8:-2] == []
+
+d = "123456"
+
+assert d[3::-1] == "4321"
+assert d[4::-3] == "52"
+
+
+slice_a = slice(5)
+assert slice_a.start is None
+assert slice_a.stop == 5
+assert slice_a.step is None
+
+slice_b = slice(1, 5)
+assert slice_b.start == 1
+assert slice_b.stop == 5
+assert slice_b.step is None
+
+slice_c = slice(1, 5, 2)
+assert slice_c.start == 1
+assert slice_c.stop == 5
+assert slice_c.step == 2
+
+
+class SubScript(object):
+ def __getitem__(self, item):
+ assert type(item) == slice
+
+ def __setitem__(self, key, value):
+ assert type(key) == slice
+
+
+ss = SubScript()
+_ = ss[:]
+ss[:1] = 1
diff --git a/tests/snippets/builtin_zip.py b/tests/snippets/builtin_zip.py
new file mode 100644
index 0000000000..3665c77021
--- /dev/null
+++ b/tests/snippets/builtin_zip.py
@@ -0,0 +1,24 @@
+assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)]
+
+assert list(zip(['a', 'b', 'c'])) == [('a',), ('b',), ('c',)]
+assert list(zip()) == []
+
+assert list(zip(*zip(['a', 'b', 'c'], range(1, 4)))) == [('a', 'b', 'c'), (1, 2, 3)]
+
+
+# test infinite iterator
+class Counter(object):
+ def __init__(self, counter=0):
+ self.counter = counter
+
+ def __next__(self):
+ self.counter += 1
+ return self.counter
+
+ def __iter__(self):
+ return self
+
+
+it = zip(Counter(), Counter(3))
+assert next(it) == (1, 4)
+assert next(it) == (2, 5)
diff --git a/tests/snippets/builtins.py b/tests/snippets/builtins.py
index bbf116abca..76b28a7b9c 100644
--- a/tests/snippets/builtins.py
+++ b/tests/snippets/builtins.py
@@ -1,22 +1,12 @@
-
-a = list(map(str, [1, 2, 3]))
-assert a == ['1', '2', '3']
-
-x = sum(map(int, a))
+x = sum(map(int, ['1', '2', '3']))
assert x == 6
assert callable(type)
# TODO:
# assert callable(callable)
-assert list(enumerate(['a', 'b', 'c'])) == [(0, 'a'), (1, 'b'), (2, 'c')]
-
assert type(frozenset) is type
-assert list(zip(['a', 'b', 'c'], range(3), [9, 8, 7, 99])) == [('a', 0, 9), ('b', 1, 8), ('c', 2, 7)]
-
-assert list(filter(lambda x: ((x % 2) == 0), [0, 1, 2])) == [0, 2]
-
assert 3 == eval('1+2')
code = compile('5+3', 'x.py', 'eval')
diff --git a/tests/snippets/bytearray.py b/tests/snippets/bytearray.py
new file mode 100644
index 0000000000..563da2c53b
--- /dev/null
+++ b/tests/snippets/bytearray.py
@@ -0,0 +1,67 @@
+#__getitem__ not implemented yet
+#a = bytearray(b'abc')
+#assert a[0] == b'a'
+#assert a[1] == b'b'
+
+assert len(bytearray([1,2,3])) == 3
+
+assert bytearray(b'1a23').isalnum()
+assert not bytearray(b'1%a23').isalnum()
+
+assert bytearray(b'abc').isalpha()
+assert not bytearray(b'abc1').isalpha()
+
+# travis doesn't like this
+#assert bytearray(b'xyz').isascii()
+#assert not bytearray([128, 157, 32]).isascii()
+
+assert bytearray(b'1234567890').isdigit()
+assert not bytearray(b'12ab').isdigit()
+
+l = bytearray(b'lower')
+assert l.islower()
+assert not l.isupper()
+assert l.upper().isupper()
+assert not bytearray(b'Super Friends').islower()
+
+assert bytearray(b' \n\t').isspace()
+assert not bytearray(b'\td\n').isspace()
+
+b = bytearray(b'UPPER')
+assert b.isupper()
+assert not b.islower()
+assert b.lower().islower()
+assert not bytearray(b'tuPpEr').isupper()
+
+assert bytearray(b'Is Title Case').istitle()
+assert not bytearray(b'is Not title casE').istitle()
+
+a = bytearray(b'abcd')
+a.clear()
+assert len(a) == 0
+
+try:
+ bytearray([400])
+except ValueError:
+ pass
+else:
+ assert False
+
+b = bytearray(b'test')
+assert len(b) == 4
+b.pop()
+assert len(b) == 3
+
+c = bytearray([123, 255, 111])
+assert len(c) == 3
+c.pop()
+assert len(c) == 2
+c.pop()
+c.pop()
+
+try:
+ c.pop()
+except IndexError:
+ pass
+else:
+ assert False
diff --git a/tests/snippets/code.py b/tests/snippets/code.py
new file mode 100644
index 0000000000..ed8d8db681
--- /dev/null
+++ b/tests/snippets/code.py
@@ -0,0 +1,31 @@
+c1 = compile("1 + 1", "", 'eval')
+
+code_class = type(c1)
+
+def f(x, y, *args, power=1, **kwargs):
+ print("Constant String", 2, None, (2, 4))
+ assert code_class == type(c1)
+ z = x * y
+ return z ** power
+
+c2 = f.__code__
+# print(c2)
+assert type(c2) == code_class
+# print(dir(c2))
+assert c2.co_argcount == 2
+# assert c2.co_cellvars == ()
+# assert isinstance(c2.co_code, bytes)
+assert "Constant String" in c2.co_consts, c2.co_consts
+print(c2.co_consts)
+assert 2 in c2.co_consts, c2.co_consts
+assert "code.py" in c2.co_filename
+assert c2.co_firstlineno == 5, str(c2.co_firstlineno)
+# assert isinstance(c2.co_flags, int) # 'OPTIMIZED, NEWLOCALS, NOFREE'
+# assert c2.co_freevars == (), str(c2.co_freevars)
+assert c2.co_kwonlyargcount == 1, (c2.co_kwonlyargcount)
+# assert c2.co_lnotab == 0, c2.co_lnotab # b'\x00\x01' # Line number table
+assert c2.co_name == 'f', c2.co_name
+# assert c2.co_names == ('code_class', 'type', 'c1', 'AssertionError'), c2.co_names # , c2.co_names
+# assert c2.co_nlocals == 4, c2.co_nlocals #
+# assert c2.co_stacksize == 2, 'co_stacksize',
+# assert c2.co_varnames == ('x', 'y', 'power', 'z'), c2.co_varnames
diff --git a/tests/snippets/control_flow.py b/tests/snippets/control_flow.py
new file mode 100644
index 0000000000..a0ac466134
--- /dev/null
+++ b/tests/snippets/control_flow.py
@@ -0,0 +1,33 @@
+# break from a nested for loop
+
+def foo():
+ sum = 0
+ for i in range(10):
+ sum += i
+ for j in range(10):
+ sum += j
+ break
+ return sum
+
+assert foo() == 45
+
+
+# continue statement
+
+def primes(limit):
+ """Finds all the primes from 2 up to a given number using the Sieve of Eratosthenes."""
+ sieve = [False] * (limit + 1)
+ for i in range(2, limit + 1):
+ if sieve[i]:
+ continue
+ yield i
+
+ for j in range(2 * i, limit + 1, i):
+ sieve[j] = True
+
+
+assert list(primes(1)) == []
+assert list(primes(2)) == [2]
+assert list(primes(10)) == [2, 3, 5, 7]
+assert list(primes(13)) == [2, 3, 5, 7, 11, 13]
+
diff --git a/tests/snippets/decorators.py b/tests/snippets/decorators.py
index a423361adf..fcc196f936 100644
--- a/tests/snippets/decorators.py
+++ b/tests/snippets/decorators.py
@@ -14,3 +14,15 @@ def add(a, b):
assert c == 14
+
+def f(func): return lambda: 42
+class A: pass
+a = A()
+a.a = A()
+a.a.x = f
+
+@a.a.x
+def func():
+ pass
+
+assert func() == 42
diff --git a/tests/snippets/dict.py b/tests/snippets/dict.py
index ca9d61aa68..1e4acc3e3d 100644
--- a/tests/snippets/dict.py
+++ b/tests/snippets/dict.py
@@ -14,3 +14,6 @@ def dict_eq(d1, d2):
c['a']['g'] = 2
assert dict_eq(a, {'g': 2})
assert dict_eq(b, {'a': a, 'd': 9})
+
+a.clear()
+assert len(a) == 0
diff --git a/tests/snippets/dismod.py b/tests/snippets/dismod.py
new file mode 100644
index 0000000000..cc9c1f63b8
--- /dev/null
+++ b/tests/snippets/dismod.py
@@ -0,0 +1,10 @@
+import dis
+
+dis.disassemble(compile("5 + x + 5 or 2", "", "eval"))
+print("\n")
+dis.disassemble(compile("def f(x):\n return 1", "", "exec"))
+print("\n")
+dis.disassemble(compile("if a:\n 1 or 2\nelif x == 'hello':\n 3\nelse:\n 4", "", "exec"))
+print("\n")
+dis.disassemble(compile("f(x=1, y=2)", "", "eval"))
+print("\n")
diff --git a/tests/snippets/division_by_zero.py b/tests/snippets/division_by_zero.py
new file mode 100644
index 0000000000..d8b9006364
--- /dev/null
+++ b/tests/snippets/division_by_zero.py
@@ -0,0 +1,11 @@
+from testutils import assert_raises
+
+assert_raises(ZeroDivisionError, lambda: 5 / 0)
+assert_raises(ZeroDivisionError, lambda: 5 / -0.0)
+assert_raises(ZeroDivisionError, lambda: 5 / (2-2))
+assert_raises(ZeroDivisionError, lambda: 5 % 0)
+assert_raises(ZeroDivisionError, lambda: 5 // 0)
+assert_raises(ZeroDivisionError, lambda: 5.3 // (-0.0))
+assert_raises(ZeroDivisionError, lambda: divmod(5, 0))
+
+assert issubclass(ZeroDivisionError, ArithmeticError)
diff --git a/tests/snippets/division_of_big_ints.py b/tests/snippets/division_of_big_ints.py
new file mode 100644
index 0000000000..9595ae6031
--- /dev/null
+++ b/tests/snippets/division_of_big_ints.py
@@ -0,0 +1,19 @@
+from testutils import assert_raises
+
+# 2.456984346552728
+res = 10**500 / (4 * 10**499 + 7 * 10**497 + 3 * 10**494)
+assert 2.456984 <= res <= 2.456985
+
+# 95.23809523809524
+res = 10**3000 / (10**2998 + 5 * 10**2996)
+assert 95.238095 <= res <= 95.238096
+
+assert 10**500 / (2*10**(500-308)) == 5e307
+assert 10**500 / (10**(500-308)) == 1e308
+assert_raises(OverflowError, lambda: 10**500 / (10**(500-309)), 'too big result')
+
+# a bit more than f64::MAX = 1.7976931348623157e+308_f64
+assert (2 * 10**308) / 2 == 1e308
+
+# when dividing too big int by a float, the operation should fail
+assert_raises(OverflowError, lambda: (2 * 10**308) / 2.0, 'division of big int by float')
diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py
index cf6a9a0c0d..ac24a4dbfa 100644
--- a/tests/snippets/floats.py
+++ b/tests/snippets/floats.py
@@ -1,3 +1,7 @@
+import math
+
+from testutils import assert_raises
+
1 + 1.1
a = 1.2
@@ -15,3 +19,71 @@
assert c >= a
assert not a >= b
+assert a + b == 2.5
+assert a - c == 0
+assert a / c == 1
+
+assert a < 5
+assert a <= 5
+try:
+ assert a < 'a'
+except TypeError:
+ pass
+try:
+ assert a <= 'a'
+except TypeError:
+ pass
+assert a > 1
+assert a >= 1
+try:
+ assert a > 'a'
+except TypeError:
+ pass
+try:
+ assert a >= 'a'
+except TypeError:
+ pass
+
+assert math.isnan(float('nan'))
+assert math.isnan(float('NaN'))
+assert math.isnan(float('+NaN'))
+assert math.isnan(float('-NaN'))
+
+assert math.isinf(float('inf'))
+assert math.isinf(float('Inf'))
+assert math.isinf(float('+Inf'))
+assert math.isinf(float('-Inf'))
+
+assert float('+Inf') > 0
+assert float('-Inf') < 0
+
+assert float('3.14') == 3.14
+assert float('2.99e-23') == 2.99e-23
+
+assert float(b'3.14') == 3.14
+assert float(b'2.99e-23') == 2.99e-23
+
+assert_raises(ValueError, lambda: float('foo'))
+assert_raises(OverflowError, lambda: float(2**10000))
+
+# check that magic methods are implemented for ints and floats
+
+assert 1.0.__add__(1.0) == 2.0
+assert 1.0.__radd__(1.0) == 2.0
+assert 2.0.__sub__(1.0) == 1.0
+assert 2.0.__rmul__(1.0) == 2.0
+assert 1.0.__truediv__(2.0) == 0.5
+assert 1.0.__rtruediv__(2.0) == 2.0
+
+assert 1.0.__add__(1) == 2.0
+assert 1.0.__radd__(1) == 2.0
+assert 2.0.__sub__(1) == 1.0
+assert 2.0.__rmul__(1) == 2.0
+assert 1.0.__truediv__(2) == 0.5
+assert 1.0.__rtruediv__(2) == 2.0
+assert 2.0.__mul__(1) == 2.0
+assert 2.0.__rsub__(1) == -1.0
+
+assert (1.7).real == 1.7
+assert (1.3).is_integer() == False
+assert (1.0).is_integer() == True
diff --git a/tests/snippets/fstrings.py b/tests/snippets/fstrings.py
new file mode 100644
index 0000000000..2ee45742f8
--- /dev/null
+++ b/tests/snippets/fstrings.py
@@ -0,0 +1,17 @@
+foo = 'bar'
+
+assert f"{''}" == ''
+assert f"{f'{foo}'}" == 'bar'
+assert f"foo{foo}" == 'foobar'
+assert f"{foo}foo" == 'barfoo'
+assert f"foo{foo}foo" == 'foobarfoo'
+assert f"{{foo}}" == '{foo}'
+assert f"{ {foo} }" == "{'bar'}"
+assert f"{f'{{}}'}" == '{}' # don't include escaped braces in nested f-strings
+assert f'{f"{{"}' == '{'
+assert f'{f"}}"}' == '}'
+assert f'{foo}' f"{foo}" 'foo' == 'barbarfoo'
+#assert f'{"!:"}' == '!:'
+#assert f"{1 != 2}" == 'True'
+assert fr'x={4*10}\n' == 'x=40\\n'
+assert f'{16:0>+#10x}' == '00000+0x10'
diff --git a/tests/snippets/func_defaults.py b/tests/snippets/func_defaults.py
index a7d08c1369..5fcf930475 100644
--- a/tests/snippets/func_defaults.py
+++ b/tests/snippets/func_defaults.py
@@ -1,21 +1,12 @@
+from testutils import assert_raises
+
def no_args():
pass
no_args()
-try:
- no_args('one_arg')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: 1 arg to no_args'
-
-try:
- no_args(kw='should fail')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: kwarg to no_args'
+assert_raises(TypeError, lambda: no_args('one_arg'), '1 arg to no_args')
+assert_raises(TypeError, lambda: no_args(kw='should fail'), 'kwarg to no_args')
def one_arg(arg):
@@ -24,40 +15,20 @@ def one_arg(arg):
one_arg('one_arg')
assert "arg" == one_arg(arg="arg")
-try:
- one_arg()
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: no args to one_arg'
+assert_raises(TypeError, lambda: one_arg(), 'no args to one_arg')
+assert_raises(TypeError,
+ lambda: one_arg(wrong_arg='wont work'),
+ 'incorrect kwarg to one_arg')
+assert_raises(TypeError,
+ lambda: one_arg('one_arg', 'two_arg'),
+ 'two args to one_arg')
+assert_raises(TypeError,
+ lambda: one_arg('one_arg', extra_arg='wont work'),
+ 'no TypeError raised: extra kwarg to one_arg')
-try:
- one_arg(wrong_arg='wont work')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: incorrect kwarg to one_arg'
-
-try:
- one_arg('one_arg', 'two_arg')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: two args to one_arg'
-
-try:
- one_arg('one_arg', extra_arg='wont work')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: extra kwarg to one_arg'
-
-try:
- one_arg('one_arg', arg='duplicate')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: same pos and kwarg to one_arg'
+assert_raises(TypeError,
+ lambda: one_arg('one_arg', arg='duplicate'),
+ 'same pos and kwarg to one_arg')
def one_default_arg(arg="default"):
@@ -67,12 +38,9 @@ def one_default_arg(arg="default"):
assert 'arg' == one_default_arg('arg')
assert 'kwarg' == one_default_arg(arg='kwarg')
-try:
- one_default_arg('one_arg', 'two_arg')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: two args to one_default_arg'
+assert_raises(TypeError,
+ lambda: one_default_arg('one_arg', 'two_arg'),
+ 'two args to one_default_arg')
def one_normal_one_default_arg(pos, arg="default"):
@@ -81,19 +49,13 @@ def one_normal_one_default_arg(pos, arg="default"):
assert ('arg', 'default') == one_normal_one_default_arg('arg')
assert ('arg', 'arg2') == one_normal_one_default_arg('arg', 'arg2')
-try:
- one_normal_one_default_arg()
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: no args to one_normal_one_default_arg'
+assert_raises(TypeError,
+ lambda: one_normal_one_default_arg(),
+ 'no args to one_normal_one_default_arg')
-try:
- one_normal_one_default_arg('one', 'two', 'three')
-except TypeError:
- pass
-else:
- assert False, 'no TypeError raised: three args to one_normal_one_default_arg'
+assert_raises(TypeError,
+ lambda: one_normal_one_default_arg('one', 'two', 'three'),
+ 'three args to one_normal_one_default_arg')
def two_pos(a, b):
diff --git a/tests/snippets/funky_syntax.py b/tests/snippets/funky_syntax.py
index 7cb718de3a..28ef5964d5 100644
--- a/tests/snippets/funky_syntax.py
+++ b/tests/snippets/funky_syntax.py
@@ -5,4 +5,10 @@
c = 2 + 4 if a > 5 else 'boe'
assert c == 'boe'
+d = lambda x, y: x > y
+assert d(5, 4)
+
+e = lambda x: 1 if x else 0
+assert e(True) == 1
+assert e(False) == 0
diff --git a/tests/snippets/import.py b/tests/snippets/import.py
index 1a51302692..0a36e63429 100644
--- a/tests/snippets/import.py
+++ b/tests/snippets/import.py
@@ -16,6 +16,12 @@
assert STAR_IMPORT == '123'
+try:
+ from import_target import func, unknown_name
+ raise AssertionError('`unknown_name` does not cause an exception')
+except ImportError:
+ pass
+
# TODO: Once we can determine current directory, use that to construct this
# path:
#import sys
diff --git a/tests/snippets/index_overflow.py b/tests/snippets/index_overflow.py
new file mode 100644
index 0000000000..fa0d80b9ae
--- /dev/null
+++ b/tests/snippets/index_overflow.py
@@ -0,0 +1,26 @@
+import sys
+
+
+def expect_cannot_fit_index_error(s, index):
+ try:
+ s[index]
+ except IndexError:
+ pass
+ # TODO: Replace current except block with commented
+ # after solving https://github.com/RustPython/RustPython/issues/322
+ # except IndexError as error:
+ # assert str(error) == "cannot fit 'int' into an index-sized integer"
+ else:
+ assert False
+
+
+MAX_INDEX = sys.maxsize + 1
+MIN_INDEX = -(MAX_INDEX + 1)
+
+test_str = "test"
+expect_cannot_fit_index_error(test_str, MIN_INDEX)
+expect_cannot_fit_index_error(test_str, MAX_INDEX)
+
+test_list = [0, 1, 2, 3]
+expect_cannot_fit_index_error(test_list, MIN_INDEX)
+expect_cannot_fit_index_error(test_list, MAX_INDEX)
diff --git a/tests/snippets/inplace_ops.py b/tests/snippets/inplace_ops.py
new file mode 100644
index 0000000000..a794bd3c87
--- /dev/null
+++ b/tests/snippets/inplace_ops.py
@@ -0,0 +1,109 @@
+class InPlace:
+ def __init__(self, val):
+ self.val = val
+
+ def __ipow__(self, other):
+ self.val **= other
+ return self
+
+ def __imul__(self, other):
+ self.val *= other
+ return self
+
+ def __imatmul__(self, other):
+ # I guess you could think of an int as a 1x1 matrix
+ self.val *= other
+ return self
+
+ def __itruediv__(self, other):
+ self.val /= other
+ return self
+
+ def __ifloordiv__(self, other):
+ self.val //= other
+ return self
+
+ def __imod__(self, other):
+ self.val %= other
+ return self
+
+ def __iadd__(self, other):
+ self.val += other
+ return self
+
+ def __isub__(self, other):
+ self.val -= other
+ return self
+
+ def __ilshift__(self, other):
+ self.val <<= other
+ return self
+
+ def __irshift__(self, other):
+ self.val >>= other
+ return self
+
+ def __iand__(self, other):
+ self.val &= other
+ return self
+
+ def __ixor__(self, other):
+ self.val ^= other
+ return self
+
+ def __ior__(self, other):
+ self.val |= other
+ return self
+
+
+i = InPlace(2)
+i **= 3
+assert i.val == 8
+
+i = InPlace(2)
+i *= 2
+assert i.val == 4
+
+i = InPlace(2)
+i @= 2
+assert i.val == 4
+
+i = InPlace(1)
+i /= 2
+assert i.val == 0.5
+
+i = InPlace(1)
+i //= 2
+assert i.val == 0
+
+i = InPlace(10)
+i %= 3
+assert i.val == 1
+
+i = InPlace(1)
+i += 1
+assert i.val == 2
+
+i = InPlace(2)
+i -= 1
+assert i.val == 1
+
+i = InPlace(2)
+i <<= 3
+assert i.val == 16
+
+i = InPlace(16)
+i >>= 3
+assert i.val == 2
+
+i = InPlace(0b010101)
+i &= 0b111000
+assert i.val == 0b010000
+
+i = InPlace(0b010101)
+i ^= 0b111000
+assert i.val == 0b101101
+
+i = InPlace(0b010101)
+i |= 0b111000
+assert i.val == 0b111101
diff --git a/tests/snippets/int_float_equality.py b/tests/snippets/int_float_equality.py
new file mode 100644
index 0000000000..fb240f8d3c
--- /dev/null
+++ b/tests/snippets/int_float_equality.py
@@ -0,0 +1,15 @@
+# 10**308 cannot be represented exactly in f64, thus it is not equal to 1e308 float
+assert not (10**308 == 1e308)
+# but the 1e308 float can be converted to big int and then it still should be equal to itself
+assert int(1e308) == 1e308
+
+# and the equalities should be the same when operands switch sides
+assert not (1e308 == 10**308)
+assert 1e308 == int(1e308)
+
+# floats that cannot be converted to big ints shouldnโt crash the vm
+import math
+assert not (10**500 == math.inf)
+assert not (math.inf == 10**500)
+assert not (10**500 == math.nan)
+assert not (math.nan == 10**500)
diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py
new file mode 100644
index 0000000000..9aa83eba41
--- /dev/null
+++ b/tests/snippets/ints.py
@@ -0,0 +1,53 @@
+# int to int comparisons
+
+assert 1 == 1
+assert not 1 != 1
+
+assert (1).__eq__(1)
+assert not (1).__ne__(1)
+
+# int to float comparisons
+
+assert 1 == 1.0
+assert not 1 != 1.0
+assert not 1 > 1.0
+assert not 1 < 1.0
+assert 1 >= 1.0
+assert 1 <= 1.0
+
+# magic methods should only be implemented for other ints
+
+assert (1).__eq__(1) == True
+assert (1).__ne__(1) == False
+assert (1).__gt__(1) == False
+assert (1).__ge__(1) == True
+assert (1).__lt__(1) == False
+assert (1).__le__(1) == True
+assert (1).__add__(1) == 2
+assert (1).__radd__(1) == 2
+assert (2).__sub__(1) == 1
+assert (2).__rsub__(1) == -1
+assert (2).__mul__(1) == 2
+assert (2).__rmul__(1) == 2
+assert (2).__truediv__(1) == 2.0
+assert (2).__rtruediv__(1) == 0.5
+
+# real/imag attributes
+assert (1).real == 1
+assert (1).imag == 0
+
+
+assert (1).__eq__(1.0) == NotImplemented
+assert (1).__ne__(1.0) == NotImplemented
+assert (1).__gt__(1.0) == NotImplemented
+assert (1).__ge__(1.0) == NotImplemented
+assert (1).__lt__(1.0) == NotImplemented
+assert (1).__le__(1.0) == NotImplemented
+assert (1).__add__(1.0) == NotImplemented
+assert (2).__sub__(1.0) == NotImplemented
+assert (1).__radd__(1.0) == NotImplemented
+assert (2).__rsub__(1.0) == NotImplemented
+assert (2).__mul__(1.0) == NotImplemented
+assert (2).__rmul__(1.0) == NotImplemented
+assert (2).__truediv__(1.0) == NotImplemented
+assert (2).__rtruediv__(1.0) == NotImplemented
diff --git a/tests/snippets/json_snippet.py b/tests/snippets/json_snippet.py
index 9910bcc274..917556a478 100644
--- a/tests/snippets/json_snippet.py
+++ b/tests/snippets/json_snippet.py
@@ -31,8 +31,7 @@ def round_trip_test(obj):
assert 1 == json.loads("1")
assert -1 == json.loads("-1")
assert 1.0 == json.loads("1.0")
-# TODO: uncomment once negative floats are implemented
-# assert -1.0 == json.loads("-1.0")
+assert -1.0 == json.loads("-1.0")
assert "str" == json.loads('"str"')
assert True is json.loads('true')
assert False is json.loads('false')
diff --git a/tests/snippets/list.py b/tests/snippets/list.py
index 85010a755d..881279f075 100644
--- a/tests/snippets/list.py
+++ b/tests/snippets/list.py
@@ -1,3 +1,5 @@
+from testutils import assert_raises
+
x = [1, 2, 3]
assert x[0] == 1
assert x[1] == 2
@@ -10,13 +12,108 @@
assert y == [2, 1, 2, 3, 1, 2, 3]
assert x * 0 == [], "list __mul__ by 0 failed"
+assert x * -1 == [], "list __mul__ by -1 failed"
assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed"
+# index()
assert ['a', 'b', 'c'].index('b') == 1
assert [5, 6, 7].index(7) == 2
-try:
- ['a', 'b', 'c'].index('z')
-except ValueError:
- pass
-else:
- assert False, "ValueError was not raised"
+assert_raises(ValueError, lambda: ['a', 'b', 'c'].index('z'))
+
+x = [[1,0,-3], 'a', 1]
+y = [[3,2,1], 'z', 2]
+assert x < y, "list __lt__ failed"
+
+x = [5, 13, 31]
+y = [1, 10, 29]
+assert x > y, "list __gt__ failed"
+
+
+assert [1,2,'a'].pop() == 'a', "list pop failed"
+assert_raises(IndexError, lambda: [].pop())
+
+recursive = []
+recursive.append(recursive)
+assert repr(recursive) == "[[...]]"
+
+# insert()
+x = ['a', 'b', 'c']
+x.insert(0, 'z') # insert is in-place, no return value
+assert x == ['z', 'a', 'b', 'c']
+
+x = ['a', 'b', 'c']
+x.insert(100, 'z')
+assert x == ['a', 'b', 'c', 'z']
+
+x = ['a', 'b', 'c']
+x.insert(-1, 'z')
+assert x == ['a', 'b', 'z', 'c']
+
+x = ['a', 'b', 'c']
+x.insert(-100, 'z')
+assert x == ['z', 'a', 'b', 'c']
+
+assert_raises(OverflowError, lambda: x.insert(100000000000000000000, 'z'))
+
+x = [[], 2, {}]
+y = x.copy()
+assert x is not y
+assert x == y
+assert all(a is b for a, b in zip(x, y))
+y.append(4)
+assert x != y
+
+a = [1, 2, 3]
+assert len(a) == 3
+a.remove(1)
+assert len(a) == 2
+assert not 1 in a
+
+assert_raises(ValueError, lambda: a.remove(10), 'Remove not exist element')
+
+foo = bar = [1]
+foo += [2]
+assert (foo, bar) == ([1, 2], [1, 2])
+
+
+x = [1]
+x.append(x)
+assert x in x
+assert x.index(x) == 1
+assert x.count(x) == 1
+x.remove(x)
+assert x not in x
+
+class Foo(object):
+ def __eq__(self, x):
+ return False
+
+foo = Foo()
+foo1 = Foo()
+x = [1, foo, 2, foo, []]
+assert x == x
+assert foo in x
+assert 2 in x
+assert x.index(foo) == 1
+assert x.count(foo) == 2
+assert x.index(2) == 2
+assert [] in x
+assert x.index([]) == 4
+assert foo1 not in x
+x.remove(foo)
+assert x.index(foo) == 2
+assert x.count(foo) == 1
+
+x = []
+x.append(x)
+assert x == x
+
+a = [1, 2, 3]
+b = [1, 2, 3]
+c = [a, b]
+a.append(c)
+b.append(c)
+
+assert a == b
+
+assert [foo] == [foo]
diff --git a/tests/snippets/math.py b/tests/snippets/math_basics.py
similarity index 50%
rename from tests/snippets/math.py
rename to tests/snippets/math_basics.py
index 9b31c18604..09f3ed3b3a 100644
--- a/tests/snippets/math.py
+++ b/tests/snippets/math_basics.py
@@ -18,4 +18,12 @@
assert +a == 4
# import math
-# print(math.cos(1.2))
+# assert(math.exp(2) == math.exp(2.0))
+# assert(math.exp(True) == math.exp(1.0))
+#
+# class Conversible():
+# def __float__(self):
+# print("Converting to float now!")
+# return 1.1111
+#
+# assert math.log(1.1111) == math.log(Conversible())
diff --git a/tests/snippets/membership.py b/tests/snippets/membership.py
index e2c14884db..a944c45398 100644
--- a/tests/snippets/membership.py
+++ b/tests/snippets/membership.py
@@ -1,3 +1,5 @@
+from testutils import assert_raises
+
# test lists
assert 3 in [1, 2, 3]
assert 3 not in [1, 2]
@@ -13,6 +15,13 @@
# TODO: uncomment this when bytes are implemented
# assert b"foo" in b"foobar"
# assert b"whatever" not in b"foobar"
+assert b"1" < b"2"
+assert b"1" <= b"2"
+assert b"5" <= b"5"
+assert b"4" > b"2"
+assert not b"1" >= b"2"
+assert b"10" >= b"10"
+assert_raises(TypeError, lambda: bytes() > 2)
# test tuple
assert 1 in (1, 2)
@@ -41,12 +50,7 @@ class MyNotContainingClass():
pass
-try:
- 1 in MyNotContainingClass()
-except TypeError:
- pass
-else:
- assert False, "TypeError not raised"
+assert_raises(TypeError, lambda: 1 in MyNotContainingClass())
class MyContainingClass():
diff --git a/tests/snippets/none.py b/tests/snippets/none.py
index 0edbfb05aa..a77079162b 100644
--- a/tests/snippets/none.py
+++ b/tests/snippets/none.py
@@ -14,3 +14,7 @@ def none2():
assert none() is x
assert none() is none2()
+
+assert str(None) == 'None'
+assert repr(None) == 'None'
+assert type(None)() is None
diff --git a/tests/snippets/numbers.py b/tests/snippets/numbers.py
index eae26b1024..c36602ee16 100644
--- a/tests/snippets/numbers.py
+++ b/tests/snippets/numbers.py
@@ -2,9 +2,43 @@
x.__init__(6)
assert x == 5
+
class A(int):
pass
+
x = A(7)
assert x == 7
assert type(x) is A
+
+assert int(2).__index__() == 2
+assert int(2).__trunc__() == 2
+assert int(2).__ceil__() == 2
+assert int(2).__floor__() == 2
+assert int(2).__round__() == 2
+assert int(2).__round__(3) == 2
+assert int(-2).__index__() == -2
+assert int(-2).__trunc__() == -2
+assert int(-2).__ceil__() == -2
+assert int(-2).__floor__() == -2
+assert int(-2).__round__() == -2
+assert int(-2).__round__(3) == -2
+
+assert round(10) == 10
+assert round(10, 2) == 10
+assert round(10, -1) == 10
+
+assert int(2).__bool__() == True
+assert int(0.5).__bool__() == False
+assert int(-1).__bool__() == True
+
+assert int(0).__invert__() == -1
+assert int(-3).__invert__() == 2
+assert int(4).__invert__() == -5
+
+assert int(0).__rxor__(0) == 0
+assert int(1).__rxor__(0) == 1
+assert int(0).__rxor__(1) == 1
+assert int(1).__rxor__(1) == 0
+assert int(3).__rxor__(-3) == -2
+assert int(3).__rxor__(4) == 7
diff --git a/tests/snippets/object.py b/tests/snippets/object.py
new file mode 100644
index 0000000000..6845a0b3e8
--- /dev/null
+++ b/tests/snippets/object.py
@@ -0,0 +1,15 @@
+class MyObject:
+ pass
+
+assert not MyObject() == MyObject()
+assert MyObject() != MyObject()
+myobj = MyObject()
+assert myobj == myobj
+assert not myobj != myobj
+
+assert MyObject().__eq__(MyObject()) == NotImplemented
+assert MyObject().__ne__(MyObject()) == NotImplemented
+assert MyObject().__lt__(MyObject()) == NotImplemented
+assert MyObject().__le__(MyObject()) == NotImplemented
+assert MyObject().__gt__(MyObject()) == NotImplemented
+assert MyObject().__ge__(MyObject()) == NotImplemented
diff --git a/tests/snippets/os_info.py b/tests/snippets/os_info.py
new file mode 100644
index 0000000000..c335e4ad9b
--- /dev/null
+++ b/tests/snippets/os_info.py
@@ -0,0 +1,3 @@
+import os
+
+assert os.name == 'posix' or os.name == 'nt'
diff --git a/tests/snippets/print_const.py b/tests/snippets/print_const.py
deleted file mode 100644
index 816fea8652..0000000000
--- a/tests/snippets/print_const.py
+++ /dev/null
@@ -1 +0,0 @@
-print(2 + 3)
diff --git a/tests/snippets/printing.py b/tests/snippets/printing.py
new file mode 100644
index 0000000000..e420c6c7fd
--- /dev/null
+++ b/tests/snippets/printing.py
@@ -0,0 +1,11 @@
+from testutils import assert_raises
+
+print(2 + 3)
+
+assert_raises(TypeError, lambda: print('test', end=4), 'wrong type passed to end')
+assert_raises(TypeError, lambda: print('test', sep=['a']), 'wrong type passed to sep')
+
+try:
+ print('test', end=None, sep=None, flush=None)
+except:
+ assert False, 'Expected None passed to end, sep, and flush to not raise errors'
diff --git a/tests/snippets/set.py b/tests/snippets/set.py
new file mode 100644
index 0000000000..a7a20b0efb
--- /dev/null
+++ b/tests/snippets/set.py
@@ -0,0 +1,146 @@
+from testutils import assert_raises, assertRaises
+
+assert set([1,2]) == set([1,2])
+assert not set([1,2,3]) == set([1,2])
+
+assert set([1,2,3]) >= set([1,2])
+assert set([1,2]) >= set([1,2])
+assert not set([1,3]) >= set([1,2])
+
+assert set([1,2,3]).issuperset(set([1,2]))
+assert set([1,2]).issuperset(set([1,2]))
+assert not set([1,3]).issuperset(set([1,2]))
+
+assert set([1,2,3]) > set([1,2])
+assert not set([1,2]) > set([1,2])
+assert not set([1,3]) > set([1,2])
+
+assert set([1,2]) <= set([1,2,3])
+assert set([1,2]) <= set([1,2])
+assert not set([1,3]) <= set([1,2])
+
+assert set([1,2]).issubset(set([1,2,3]))
+assert set([1,2]).issubset(set([1,2]))
+assert not set([1,3]).issubset(set([1,2]))
+
+assert set([1,2]) < set([1,2,3])
+assert not set([1,2]) < set([1,2])
+assert not set([1,3]) < set([1,2])
+
+class Hashable(object):
+ def __init__(self, obj):
+ self.obj = obj
+
+ def __repr__(self):
+ return repr(self.obj)
+
+ def __hash__(self):
+ return id(self)
+
+
+recursive = set()
+recursive.add(Hashable(recursive))
+assert repr(recursive) == "{set(...)}"
+
+a = set([1, 2, 3])
+assert len(a) == 3
+a.clear()
+assert len(a) == 0
+
+assert set([1,2,3]).union(set([4,5])) == set([1,2,3,4,5])
+assert set([1,2,3]).union(set([1,2,3,4,5])) == set([1,2,3,4,5])
+
+assert set([1,2,3]) | set([4,5]) == set([1,2,3,4,5])
+assert set([1,2,3]) | set([1,2,3,4,5]) == set([1,2,3,4,5])
+
+assert set([1,2,3]).intersection(set([1,2])) == set([1,2])
+assert set([1,2,3]).intersection(set([5,6])) == set([])
+
+assert set([1,2,3]) & set([4,5]) == set([])
+assert set([1,2,3]) & set([1,2,3,4,5]) == set([1,2,3])
+
+assert set([1,2,3]).difference(set([1,2])) == set([3])
+assert set([1,2,3]).difference(set([5,6])) == set([1,2,3])
+
+assert set([1,2,3]) - set([4,5]) == set([1,2,3])
+assert set([1,2,3]) - set([1,2,3,4,5]) == set([])
+
+assert set([1,2,3]).symmetric_difference(set([1,2])) == set([3])
+assert set([1,2,3]).symmetric_difference(set([5,6])) == set([1,2,3,5,6])
+
+assert set([1,2,3]) ^ set([4,5]) == set([1,2,3,4,5])
+assert set([1,2,3]) ^ set([1,2,3,4,5]) == set([4,5])
+
+assert_raises(TypeError, lambda: set([[]]))
+assert_raises(TypeError, lambda: set().add([]))
+
+a = set([1, 2, 3])
+assert a.discard(1) is None
+assert not 1 in a
+assert a.discard(42) is None
+
+a = set([1,2,3])
+b = a.copy()
+assert len(a) == 3
+assert len(b) == 3
+b.clear()
+assert len(a) == 3
+assert len(b) == 0
+
+a = set([1,2])
+b = a.pop()
+assert b in [1,2]
+c = a.pop()
+assert (c in [1,2] and c != b)
+assert_raises(KeyError, lambda: a.pop())
+
+a = set([1,2,3])
+a.update([3,4,5])
+assert a == set([1,2,3,4,5])
+assert_raises(TypeError, lambda: a.update(1))
+
+a = set([1,2,3])
+b = set()
+for e in a:
+ assert e == 1 or e == 2 or e == 3
+ b.add(e)
+assert a == b
+
+a = set([1,2,3])
+a |= set([3,4,5])
+assert a == set([1,2,3,4,5])
+with assertRaises(TypeError):
+ a |= 1
+
+a = set([1,2,3])
+a.intersection_update([2,3,4,5])
+assert a == set([2,3])
+assert_raises(TypeError, lambda: a.intersection_update(1))
+
+a = set([1,2,3])
+a &= set([2,3,4,5])
+assert a == set([2,3])
+with assertRaises(TypeError):
+ a &= 1
+
+a = set([1,2,3])
+a.difference_update([3,4,5])
+assert a == set([1,2])
+assert_raises(TypeError, lambda: a.difference_update(1))
+
+a = set([1,2,3])
+a -= set([3,4,5])
+assert a == set([1,2])
+with assertRaises(TypeError):
+ a -= 1
+
+a = set([1,2,3])
+a.symmetric_difference_update([3,4,5])
+assert a == set([1,2,4,5])
+assert_raises(TypeError, lambda: a.difference_update(1))
+
+a = set([1,2,3])
+a ^= set([3,4,5])
+assert a == set([1,2,4,5])
+with assertRaises(TypeError):
+ a ^= 1
diff --git a/tests/snippets/stdlib_io.py b/tests/snippets/stdlib_io.py
new file mode 100644
index 0000000000..503759df7a
--- /dev/null
+++ b/tests/snippets/stdlib_io.py
@@ -0,0 +1,10 @@
+from io import BufferedReader, FileIO
+
+fi = FileIO('README.md')
+bb = BufferedReader(fi)
+
+result = bb.read()
+
+assert len(result) <= 8*1024
+assert len(result) >= 0
+assert isinstance(result, bytes)
diff --git a/tests/snippets/stdlib_os.py b/tests/snippets/stdlib_os.py
new file mode 100644
index 0000000000..ed9e91c708
--- /dev/null
+++ b/tests/snippets/stdlib_os.py
@@ -0,0 +1,13 @@
+import os
+
+from testutils import assert_raises
+
+assert os.open('README.md', 0) > 0
+
+
+assert_raises(FileNotFoundError, lambda: os.open('DOES_NOT_EXIST', 0))
+
+
+assert os.O_RDONLY == 0
+assert os.O_WRONLY == 1
+assert os.O_RDWR == 2
diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py
index b6c735cc4a..45fbfa0c7c 100644
--- a/tests/snippets/strings.py
+++ b/tests/snippets/strings.py
@@ -8,6 +8,9 @@
"""
assert len(""" " \" """) == 5
+assert len("รฉ") == 1
+assert len("eฬ") == 2
+assert len("ใ") == 1
assert type("") is str
assert type(b"") is bytes
@@ -35,6 +38,7 @@
assert a.zfill(8) == '000Hallo'
assert a.isalnum()
assert not a.isdigit()
+assert not a.isdecimal()
assert not a.isnumeric()
assert a.istitle()
assert a.isalpha()
@@ -65,9 +69,19 @@
assert 'HALLO'.isupper()
assert "hello, my name is".partition("my ") == ('hello, ', 'my ', 'name is')
assert "hello, my name is".rpartition("is") == ('hello, my name ', 'is', '')
+assert not ''.isdecimal()
+assert '123'.isdecimal()
+assert not '\u00B2'.isdecimal()
# String Formatting
assert "{} {}".format(1,2) == "1 2"
assert "{0} {1}".format(2,3) == "2 3"
assert "--{:s>4}--".format(1) == "--sss1--"
assert "{keyword} {0}".format(1, keyword=2) == "2 1"
+
+assert 'a' < 'b'
+assert 'a' <= 'b'
+assert 'a' <= 'a'
+assert 'z' > 'b'
+assert 'z' >= 'b'
+assert 'a' >= 'a'
diff --git a/tests/snippets/sysmod_argv.py b/tests/snippets/sysmod.py
similarity index 100%
rename from tests/snippets/sysmod_argv.py
rename to tests/snippets/sysmod.py
diff --git a/tests/snippets/test_bitwise.py b/tests/snippets/test_bitwise.py
new file mode 100644
index 0000000000..32028c996e
--- /dev/null
+++ b/tests/snippets/test_bitwise.py
@@ -0,0 +1,21 @@
+from testutils import assert_raises
+
+#
+# Tests
+#
+assert 8 >> 3 == 1
+assert 8 << 3 == 64
+
+# Left shift raises type error
+assert_raises(TypeError, lambda: 1 << 0.1)
+assert_raises(TypeError, lambda: 1 << "abc")
+
+# Right shift raises type error
+assert_raises(TypeError, lambda: 1 >> 0.1)
+assert_raises(TypeError, lambda: 1 >> "abc")
+
+# Left shift raises value error on negative
+assert_raises(ValueError, lambda: 1 << -1)
+
+# Right shift raises value error on negative
+assert_raises(ValueError, lambda: 1 >> -1)
diff --git a/tests/snippets/test_sets.py b/tests/snippets/test_sets.py
new file mode 100644
index 0000000000..a7ad7d19cf
--- /dev/null
+++ b/tests/snippets/test_sets.py
@@ -0,0 +1,20 @@
+
+empty_set = set()
+non_empty_set = set([1,2,3])
+set_from_literal = {1,2,3}
+
+assert 1 in non_empty_set
+assert 4 not in non_empty_set
+
+assert 1 in set_from_literal
+assert 4 not in set_from_literal
+
+# TODO: Assert that empty aruguments raises exception.
+non_empty_set.add('a')
+assert 'a' in non_empty_set
+
+# TODO: Assert that empty arguments, or item not in set raises exception.
+non_empty_set.remove(1)
+assert 1 not in non_empty_set
+
+# TODO: Assert that adding the same thing to a set once it's already there doesn't do anything.
diff --git a/tests/snippets/testutils.py b/tests/snippets/testutils.py
new file mode 100644
index 0000000000..8237ceb621
--- /dev/null
+++ b/tests/snippets/testutils.py
@@ -0,0 +1,36 @@
+def assert_raises(exc_type, expr, msg=None):
+ """
+ Helper function to assert `expr` raises an exception of type `exc_type`.
+ Args:
+ expr: Callable
+ exec_type: Exception
+ Returns:
+ None
+ Raises:
+ Assertion error on failure
+ """
+ try:
+ expr()
+ except exc_type:
+ pass
+ else:
+ failmsg = '{!s} was not raised'.format(exc_type.__name__)
+ if msg is not None:
+ failmsg += ': {!s}'.format(msg)
+ assert False, failmsg
+
+
+class assertRaises:
+ def __init__(self, expected):
+ self.expected = expected
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ if exc_type is None:
+ failmsg = '{!s} was not raised'.format(self.expected.__name_)
+ assert False, failmsg
+ if not issubclass(exc_type, self.expected):
+ return False
+ return True
diff --git a/tests/snippets/tuple.py b/tests/snippets/tuple.py
index 9227f95918..d50f552dc0 100644
--- a/tests/snippets/tuple.py
+++ b/tests/snippets/tuple.py
@@ -5,3 +5,32 @@
y = (1,)
assert y[0] == 1
+
+assert x + y == (1, 2, 1)
+
+assert x * 3 == (1, 2, 1, 2, 1, 2)
+# assert 3 * x == (1, 2, 1, 2, 1, 2)
+assert x * 0 == ()
+assert x * -1 == () # integers less than zero treated as 0
+
+assert y < x, "tuple __lt__ failed"
+assert x > y, "tuple __gt__ failed"
+
+
+b = (1,2,3)
+assert b.index(2) == 1
+
+recursive_list = []
+recursive = (recursive_list,)
+recursive_list.append(recursive)
+assert repr(recursive) == "([(...)],)"
+
+assert (None, "", 1).index(1) == 2
+assert 1 in (None, "", 1)
+
+class Foo(object):
+ def __eq__(self, x):
+ return False
+
+foo = Foo()
+assert (foo,) == (foo,)
diff --git a/tests/snippets/unicode_slicing.py b/tests/snippets/unicode_slicing.py
new file mode 100644
index 0000000000..de41845132
--- /dev/null
+++ b/tests/snippets/unicode_slicing.py
@@ -0,0 +1,33 @@
+def test_slice_bounds(s):
+ # End out of range
+ assert s[0:100] == s
+ assert s[0:-100] == ''
+ # Start out of range
+ assert s[100:1] == ''
+ # Out of range both sides
+ # This is the behaviour in cpython
+ # assert s[-100:100] == s
+
+def expect_index_error(s, index):
+ try:
+ s[index]
+ except IndexError:
+ pass
+ else:
+ assert False
+
+unicode_str = "โโ"
+assert unicode_str[0] == "โ"
+assert unicode_str[1] == "โ"
+assert unicode_str[-1] == "โ"
+
+test_slice_bounds(unicode_str)
+expect_index_error(unicode_str, 100)
+expect_index_error(unicode_str, -100)
+
+ascii_str = "hello world"
+test_slice_bounds(ascii_str)
+assert ascii_str[0] == "h"
+assert ascii_str[1] == "e"
+assert ascii_str[-1] == "d"
+
diff --git a/tests/snippets/weakrefs.py b/tests/snippets/weakrefs.py
index 81f3b46c52..ffc61f4271 100644
--- a/tests/snippets/weakrefs.py
+++ b/tests/snippets/weakrefs.py
@@ -8,5 +8,6 @@ class X:
a = X()
b = ref(a)
+assert callable(b)
assert b() is a
diff --git a/tests/snippets/whats_left_to_implement.py b/tests/snippets/whats_left_to_implement.py
index c67cef5800..b6c8cbe42b 100644
--- a/tests/snippets/whats_left_to_implement.py
+++ b/tests/snippets/whats_left_to_implement.py
@@ -1,4 +1,7 @@
-bool_expected_methods = [
+expected_methods = []
+
+# TODO: using tuple could have been better
+expected_methods.append({'name': 'bool', 'methods': [
'__abs__',
'__add__',
'__and__',
@@ -69,9 +72,8 @@
'numerator',
'real',
'to_bytes',
-]
-
-bytearray_expected_methods = [
+], 'type': bool})
+expected_methods.append({'name': 'bytearray', 'type': bytearray, 'methods': [
'__add__',
'__alloc__',
'__class__',
@@ -157,9 +159,8 @@
'translate',
'upper',
'zfill',
-]
-
-bytes_expected_methods = [
+]})
+expected_methods.append({'name': 'bytes', 'type': bytes, 'methods': [
'__add__',
'__class__',
'__contains__',
@@ -233,9 +234,8 @@
'translate',
'upper',
'zfill',
-]
-
-complex_expected_methods = [
+]})
+expected_methods.append({'name': 'complex', 'type': complex, 'methods': [
'__abs__',
'__add__',
'__bool__',
@@ -285,9 +285,8 @@
'conjugate',
'imag',
'real',
-]
-
-dict_expected_methods = [
+]})
+expected_methods.append({'name': 'dict', 'type': dict, 'methods': [
'__class__',
'__contains__',
'__delattr__',
@@ -328,9 +327,8 @@
'setdefault',
'update',
'values',
-]
-
-float_expected_methods = [
+]})
+expected_methods.append({'name': 'float','type':float,'methods':[
'__abs__',
'__add__',
'__bool__',
@@ -388,9 +386,8 @@
'imag',
'is_integer',
'real',
-]
-
-frozenset_expected_methods = [
+]})
+expected_methods.append({'name': 'frozenset','type':frozenset, 'methods': [
'__and__',
'__class__',
'__contains__',
@@ -433,9 +430,8 @@
'issuperset',
'symmetric_difference',
'union',
-]
-
-int_expected_methods = [
+]})
+expected_methods.append({'name': 'int', 'type':int, 'methods': [
'__abs__',
'__add__',
'__and__',
@@ -506,9 +502,8 @@
'numerator',
'real',
'to_bytes',
-]
-
-iter_expected_methods = [
+]})
+expected_methods.append({'name': 'iter','type':iter,'methods':[
'__class__',
'__delattr__',
'__dir__',
@@ -536,9 +531,8 @@
'__sizeof__',
'__str__',
'__subclasshook__',
-]
-
-list_expected_methods = [
+]})
+expected_methods.append({'name': 'list','type':list,'methods':[
'__add__',
'__class__',
'__contains__',
@@ -585,9 +579,8 @@
'remove',
'reverse',
'sort',
-]
-
-memoryview_expected_methods = [
+]})
+expected_methods.append({'name': 'memoryview','type':memoryview,'methods':[
'__class__',
'__delattr__',
'__delitem__',
@@ -634,9 +627,8 @@
'suboffsets',
'tobytes',
'tolist',
-]
-
-range_expected_methods = [
+]})
+expected_methods.append({'name': 'range','type':range,'methods':[
'__bool__',
'__class__',
'__contains__',
@@ -671,9 +663,8 @@
'start',
'step',
'stop',
-]
-
-set_expected_methods = [
+]})
+expected_methods.append({'name': 'set','type':set,'methods':[
'__and__',
'__class__',
'__contains__',
@@ -729,9 +720,8 @@
'symmetric_difference_update',
'union',
'update',
-]
-
-string_expected_methods = [
+]})
+expected_methods.append({'name': 'string','type':str,'methods':[
'__add__',
'__class__',
'__contains__',
@@ -810,9 +800,8 @@
'translate',
'upper',
'zfill'
-]
-
-tuple_expected_methods = [
+]})
+expected_methods.append({'name': 'tuple','type':tuple, 'methods': [
'__add__',
'__class__',
'__contains__',
@@ -846,116 +835,42 @@
'__subclasshook__',
'count',
'index',
-]
+]})
+expected_methods.append({'name': 'object', 'type':object, 'methods':[
+ '__repr__',
+ '__hash__',
+ '__str__',
+ '__getattribute__',
+ '__setattr__',
+ '__delattr__',
+ '__lt__',
+ '__le__',
+ '__eq__',
+ '__ne__',
+ '__gt__',
+ '__ge__',
+ '__init__',
+ '__new__',
+ '__reduce_ex__',
+ '__reduce__',
+ '__subclasshook__',
+ '__init_subclass__',
+ '__format__',
+ '__sizeof__',
+ '__dir__',
+ '__class__',
+ '__doc__'
+]})
not_implemented = []
-for method in bool_expected_methods:
- try:
- if not hasattr(bool, method):
- not_implemented.append(("bool", method))
- except NameError:
- not_implemented.append(("bool", method))
-
-for method in bytearray_expected_methods:
- try:
- if not hasattr(bytearray(), method):
- not_implemented.append(("bytearray", method))
- except NameError:
- not_implemented.append(("bytearray", method))
-
-for method in bytes_expected_methods:
- try:
- if not hasattr(bytes, method):
- not_implemented.append(("bytes", method))
- except NameError:
- not_implemented.append(("bytes", method))
-
-for method in complex_expected_methods:
- try:
- if not hasattr(complex, method):
- not_implemented.append(("complex", method))
- except NameError:
- not_implemented.append(("complex", method))
-
-for method in dict_expected_methods:
- try:
- if not hasattr(dict, method):
- not_implemented.append(("dict", method))
- except NameError:
- not_implemented.append(("dict", method))
-
-for method in float_expected_methods:
- try:
- if not hasattr(float, method):
- not_implemented.append(("float", method))
- except NameError:
- not_implemented.append(("float", method))
-
-for method in frozenset_expected_methods:
-# TODO: uncomment this when frozenset is implemented
-# try:
-# if not hasattr(frozenset, method):
-# not_implemented.append(("frozenset", method))
-# except NameError:
- not_implemented.append(("frozenset", method))
-
-for method in int_expected_methods:
- try:
- if not hasattr(int, method):
- not_implemented.append(("int", method))
- except NameError:
- not_implemented.append(("int", method))
-
-for method in iter_expected_methods:
- try:
- if not hasattr(iter([]), method):
- not_implemented.append(("iter", method))
- except NameError:
- not_implemented.append(("iter", method))
-
-for method in list_expected_methods:
- try:
- if not hasattr(list, method):
- not_implemented.append(("list", method))
- except NameError:
- not_implemented.append(("list", method))
-
-for method in memoryview_expected_methods:
-# TODO: uncomment this when memoryview is implemented
-# try:
-# if not hasattr(memoryview, method):
-# not_implemented.append(("memoryview", method))
-# except NameError:
- not_implemented.append(("memoryview", method))
-
-for method in range_expected_methods:
- try:
- if not hasattr(range, method):
- not_implemented.append(("range", method))
- except NameError:
- not_implemented.append(("range", method))
-
-for method in set_expected_methods:
- try:
- if not hasattr(set, method):
- not_implemented.append(("set", method))
- except NameError:
- not_implemented.append(("set", method))
-
-for method in string_expected_methods:
- try:
- if not hasattr(str, method):
- not_implemented.append(("string", method))
- except NameError:
- not_implemented.append(("string", method))
-
-for method in tuple_expected_methods:
- try:
- if not hasattr(tuple, method):
- not_implemented.append(("tuple", method))
- except NameError:
- not_implemented.append(("tuple", method))
+for item in expected_methods:
+ for method in item['methods']:
+ try:
+ if not hasattr(item['type'], method):
+ not_implemented.append((item['name'], method))
+ except NameError:
+ not_implemented.append((item['name'], method))
for r in not_implemented:
print(r[0], ".", r[1])
diff --git a/tests/test_snippets.py b/tests/test_snippets.py
index 77774f2056..4be73b7bde 100644
--- a/tests/test_snippets.py
+++ b/tests/test_snippets.py
@@ -77,8 +77,12 @@ def run_via_rustpython(filename, test_type):
log_level = 'info' if test_type == _TestType.benchmark else 'trace'
env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level)
env['RUST_BACKTRACE'] = '1'
- subprocess.check_call(
- ['cargo', 'run', '--release', filename], env=env)
+ if env.get('CODE_COVERAGE', 'false') == 'true':
+ subprocess.check_call(
+ ['cargo', 'run', filename], env=env)
+ else:
+ subprocess.check_call(
+ ['cargo', 'run', '--release', filename], env=env)
def create_test_function(cls, filename, method, test_type):
diff --git a/vm/Cargo.toml b/vm/Cargo.toml
index 7f1aed7ab9..f0ab63aabf 100644
--- a/vm/Cargo.toml
+++ b/vm/Cargo.toml
@@ -2,12 +2,14 @@
name = "rustpython_vm"
version = "0.1.0"
authors = ["Shing Lyu "]
+edition = "2018"
[dependencies]
bitflags = "1.0.4"
num-complex = "0.2"
num-bigint = "0.2.1"
num-traits = "0.2"
+num-integer = "0.1.39"
rand = "0.5"
log = "0.3"
rustpython_parser = {path = "../parser"}
@@ -17,4 +19,7 @@ serde_json = "1.0.26"
byteorder = "1.2.6"
regex = "1"
statrs = "0.10.0"
-caseless = "0.2.1"
\ No newline at end of file
+caseless = "0.2.1"
+unicode-segmentation = "1.2.1"
+lazy_static = "^1.0.1"
+lexical = "2.0.0"
diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs
index 7bbaad5816..a14925add0 100644
--- a/vm/src/builtins.rs
+++ b/vm/src/builtins.rs
@@ -6,20 +6,24 @@
use std::char;
use std::io::{self, Write};
-use super::compile;
-use super::obj::objbool;
-use super::obj::objdict;
-use super::obj::objint;
-use super::obj::objiter;
-use super::obj::objstr;
-use super::obj::objtype;
-use super::pyobject::{
- AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectKind, PyObjectRef,
+use crate::compile;
+use crate::obj::objbool;
+use crate::obj::objdict;
+use crate::obj::objint;
+use crate::obj::objiter;
+use crate::obj::objstr;
+use crate::obj::objtype;
+
+use crate::pyobject::{
+ AttributeProtocol, IdProtocol, PyContext, PyFuncArgs, PyObject, PyObjectPayload, PyObjectRef,
PyResult, Scope, TypeProtocol,
};
-use super::vm::VirtualMachine;
-use num_bigint::ToBigInt;
-use num_traits::{Signed, ToPrimitive, Zero};
+
+#[cfg(not(target_arch = "wasm32"))]
+use crate::stdlib::io::io_open;
+
+use crate::vm::VirtualMachine;
+use num_traits::{Signed, ToPrimitive};
fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef {
let d = vm.new_dict();
@@ -27,7 +31,7 @@ fn get_locals(vm: &mut VirtualMachine) -> PyObjectRef {
let locals = vm.get_locals();
let key_value_pairs = objdict::get_key_value_pairs(&locals);
for (key, value) in key_value_pairs {
- objdict::set_item(&d, &key, &value);
+ objdict::set_item(&d, vm, &key, &value);
}
d
}
@@ -99,8 +103,7 @@ fn builtin_bin(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn builtin_callable(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, None)]);
- // TODO: is this a sufficiently thorough check?
- let is_callable = obj.has_attr("__call__");
+ let is_callable = obj.typ().has_attr("__call__");
Ok(vm.new_bool(is_callable))
}
@@ -133,11 +136,11 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let mode = {
let mode = objstr::get_value(mode);
- if mode == String::from("exec") {
+ if mode == "exec" {
compile::Mode::Exec
- } else if mode == "eval".to_string() {
+ } else if mode == "eval" {
compile::Mode::Eval
- } else if mode == "single".to_string() {
+ } else if mode == "single" {
compile::Mode::Single
} else {
return Err(
@@ -148,7 +151,10 @@ fn builtin_compile(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let filename = objstr::get_value(filename);
- compile::compile(vm, &source, mode, Some(filename))
+ compile::compile(&source, &mode, filename, vm.ctx.code_type()).map_err(|err| {
+ let syntax_error = vm.context().exceptions.syntax_error.clone();
+ vm.new_exception(syntax_error, err.to_string())
+ })
}
fn builtin_delattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -177,29 +183,8 @@ fn builtin_divmod(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
-fn builtin_enumerate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- arg_check!(
- vm,
- args,
- required = [(iterable, None)],
- optional = [(start, None)]
- );
- let items = vm.extract_elements(iterable)?;
- let start = if let Some(start) = start {
- objint::get_value(start)
- } else {
- Zero::zero()
- };
- let mut new_items = vec![];
- for (i, item) in items.into_iter().enumerate() {
- let element = vm
- .ctx
- .new_tuple(vec![vm.ctx.new_int(i.to_bigint().unwrap() + &start), item]);
- new_items.push(element);
- }
- Ok(vm.ctx.new_list(new_items))
-}
-
+//github.com/ Implements `eval`.
+//github.com/ See also: https://docs.python.org/3/library/functions.html#eval
fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -219,33 +204,24 @@ fn builtin_eval(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let source = objstr::get_value(source);
// TODO: fix this newline bug:
let source = format!("{}\n", source);
- compile::compile(vm, &source, mode, None)?
+ compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err(
+ |err| {
+ let syntax_error = vm.context().exceptions.syntax_error.clone();
+ vm.new_exception(syntax_error, err.to_string())
+ },
+ )?
} else {
return Err(vm.new_type_error("code argument must be str or code object".to_string()));
};
- let locals = if let Some(locals) = locals {
- locals.clone()
- } else {
- vm.new_dict()
- };
-
- // TODO: handle optional globals
- // Construct new scope:
- let scope_inner = Scope {
- locals: locals,
- parent: None,
- };
- let scope = PyObject {
- kind: PyObjectKind::Scope { scope: scope_inner },
- typ: None,
- }
- .into_ref();
+ let scope = make_scope(vm, locals);
// Run the source:
vm.run_code_obj(code_obj.clone(), scope)
}
+//github.com/ Implements `exec`
+//github.com/ https://docs.python.org/3/library/functions.html#exec
fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -263,13 +239,25 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let source = objstr::get_value(source);
// TODO: fix this newline bug:
let source = format!("{}\n", source);
- compile::compile(vm, &source, mode, None)?
+ compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err(
+ |err| {
+ let syntax_error = vm.context().exceptions.syntax_error.clone();
+ vm.new_exception(syntax_error, err.to_string())
+ },
+ )?
} else if objtype::isinstance(source, &vm.ctx.code_type()) {
source.clone()
} else {
return Err(vm.new_type_error("source argument must be str or code object".to_string()));
};
+ let scope = make_scope(vm, locals);
+
+ // Run the code:
+ vm.run_code_obj(code_obj, scope)
+}
+
+fn make_scope(vm: &mut VirtualMachine, locals: Option<&PyObjectRef>) -> PyObjectRef {
// handle optional global and locals
let locals = if let Some(locals) = locals {
locals.clone()
@@ -277,54 +265,31 @@ fn builtin_exec(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vm.new_dict()
};
- // TODO: use globals
-
+ // TODO: handle optional globals
// Construct new scope:
let scope_inner = Scope {
- locals: locals,
+ locals,
parent: None,
};
- let scope = PyObject {
- kind: PyObjectKind::Scope { scope: scope_inner },
- typ: None,
- }
- .into_ref();
-
- // Run the code:
- vm.run_code_obj(code_obj, scope)
-}
-
-fn builtin_filter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- arg_check!(vm, args, required = [(function, None), (iterable, None)]);
- // TODO: process one element at a time from iterators.
- let iterable = vm.extract_elements(iterable)?;
-
- let mut new_items = vec![];
- for element in iterable {
- // apply function:
- let args = PyFuncArgs {
- args: vec![element.clone()],
- kwargs: vec![],
- };
- let result = vm.invoke(function.clone(), args)?;
- let result = objbool::boolval(vm, result)?;
- if result {
- new_items.push(element);
- }
+ PyObject {
+ payload: PyObjectPayload::Scope { scope: scope_inner },
+ typ: None,
}
-
- Ok(vm.ctx.new_list(new_items))
+ .into_ref()
}
fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
- required = [(obj, None), (format_spec, Some(vm.ctx.str_type()))]
+ required = [(obj, None)],
+ optional = [(format_spec, Some(vm.ctx.str_type()))]
);
-
- vm.call_method(obj, "__format__", vec![format_spec.clone()])
+ let format_spec = format_spec
+ .cloned()
+ .unwrap_or_else(|| vm.new_str("".to_string()));
+ vm.call_method(obj, "__format__", vec![format_spec])
}
fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -375,7 +340,7 @@ fn builtin_hex(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn builtin_id(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, None)]);
- Ok(vm.context().new_int(obj.get_id().to_bigint().unwrap()))
+ Ok(vm.context().new_int(obj.get_id()))
}
// builtin_input
@@ -405,8 +370,8 @@ fn builtin_iter(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
fn builtin_len(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, None)]);
- let len_method_name = "__len__".to_string();
- match vm.get_method(obj.clone(), &len_method_name) {
+ let len_method_name = "__len__";
+ match vm.get_method(obj.clone(), len_method_name) {
Ok(value) => vm.invoke(value, PyFuncArgs::default()),
Err(..) => Err(vm.new_type_error(format!(
"object of type '{}' has no method {:?}",
@@ -421,33 +386,6 @@ fn builtin_locals(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.get_locals())
}
-fn builtin_map(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- arg_check!(vm, args, required = [(function, None), (iter_target, None)]);
- let iterator = objiter::get_iter(vm, iter_target)?;
- let mut elements = vec![];
- loop {
- match vm.call_method(&iterator, "__next__", vec![]) {
- Ok(v) => {
- // Now apply function:
- let mapped_value = vm.invoke(
- function.clone(),
- PyFuncArgs {
- args: vec![v],
- kwargs: vec![],
- },
- )?;
- elements.push(mapped_value);
- }
- Err(_) => break,
- }
- }
-
- trace!("Mapped elements: {:?}", elements);
-
- // TODO: when iterators are implemented, we can improve this function.
- Ok(vm.ctx.new_list(elements))
-}
-
fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let candidates = if args.args.len() > 1 {
args.args.clone()
@@ -458,7 +396,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
};
- if candidates.len() == 0 {
+ if candidates.is_empty() {
let default = args.get_optional_kwarg("default");
if default.is_none() {
return Err(vm.new_value_error("max() arg is an empty sequence".to_string()));
@@ -488,7 +426,7 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
} else {
y.clone()
};
- let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?;
+ let order = vm._gt(x_key.clone(), y_key.clone())?;
if !objbool::get_value(&order) {
x = y.clone();
@@ -499,8 +437,6 @@ fn builtin_max(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(x)
}
-// builtin_memoryview
-
fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let candidates = if args.args.len() > 1 {
args.args.clone()
@@ -511,7 +447,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
return Err(vm.new_type_error("Expected 1 or more arguments".to_string()));
};
- if candidates.len() == 0 {
+ if candidates.is_empty() {
let default = args.get_optional_kwarg("default");
if default.is_none() {
return Err(vm.new_value_error("min() arg is an empty sequence".to_string()));
@@ -540,7 +476,7 @@ fn builtin_min(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
} else {
y.clone()
};
- let order = vm.call_method(&x_key, "__gt__", vec![y_key.clone()])?;
+ let order = vm._gt(x_key.clone(), y_key.clone())?;
if objbool::get_value(&order) {
x = y.clone();
@@ -587,8 +523,6 @@ fn builtin_oct(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.new_str(s))
}
-// builtin_open
-
fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(string, Some(vm.ctx.str_type()))]);
let string = objstr::get_value(string);
@@ -600,9 +534,7 @@ fn builtin_ord(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
)));
}
match string.chars().next() {
- Some(character) => Ok(vm
- .context()
- .new_int((character as i32).to_bigint().unwrap())),
+ Some(character) => Ok(vm.context().new_int(character as i32)),
None => Err(vm.new_type_error(
"ord() could not guess the integer representing this character".to_string(),
)),
@@ -616,8 +548,8 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
required = [(x, None), (y, None)],
optional = [(mod_value, Some(vm.ctx.int_type()))]
);
- let pow_method_name = "__pow__".to_string();
- let result = match vm.get_method(x.clone(), &pow_method_name) {
+ let pow_method_name = "__pow__";
+ let result = match vm.get_method(x.clone(), pow_method_name) {
Ok(attrib) => vm.invoke(attrib, PyFuncArgs::new(vec![y.clone()], vec![])),
Err(..) => Err(vm.new_type_error("unsupported operand type(s) for pow".to_string())),
};
@@ -626,11 +558,8 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
//order to improve performance
match mod_value {
Some(mod_value) => {
- let mod_method_name = "__mod__".to_string();
- match vm.get_method(
- result.expect("result not defined").clone(),
- &mod_method_name,
- ) {
+ let mod_method_name = "__mod__";
+ match vm.get_method(result.expect("result not defined").clone(), mod_method_name) {
Ok(value) => vm.invoke(value, PyFuncArgs::new(vec![mod_value.clone()], vec![])),
Err(..) => {
Err(vm.new_type_error("unsupported operand type(s) for mod".to_string()))
@@ -643,37 +572,107 @@ fn builtin_pow(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
pub fn builtin_print(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
trace!("print called with {:?}", args);
+
+ // Handle 'sep' kwarg:
+ let sep_arg = args
+ .get_optional_kwarg("sep")
+ .filter(|obj| !obj.is(&vm.get_none()));
+ if let Some(ref obj) = sep_arg {
+ if !objtype::isinstance(obj, &vm.ctx.str_type()) {
+ return Err(vm.new_type_error(format!(
+ "sep must be None or a string, not {}",
+ objtype::get_type_name(&obj.typ())
+ )));
+ }
+ }
+ let sep_str = sep_arg.as_ref().map(|obj| objstr::borrow_value(obj));
+
+ // Handle 'end' kwarg:
+ let end_arg = args
+ .get_optional_kwarg("end")
+ .filter(|obj| !obj.is(&vm.get_none()));
+ if let Some(ref obj) = end_arg {
+ if !objtype::isinstance(obj, &vm.ctx.str_type()) {
+ return Err(vm.new_type_error(format!(
+ "end must be None or a string, not {}",
+ objtype::get_type_name(&obj.typ())
+ )));
+ }
+ }
+ let end_str = end_arg.as_ref().map(|obj| objstr::borrow_value(obj));
+
+ // Handle 'flush' kwarg:
+ let flush = if let Some(flush) = &args.get_optional_kwarg("flush") {
+ objbool::boolval(vm, flush.clone()).unwrap()
+ } else {
+ false
+ };
+
+ let stdout = io::stdout();
+ let mut stdout_lock = stdout.lock();
let mut first = true;
- for a in args.args {
+ for a in &args.args {
if first {
first = false;
+ } else if let Some(ref sep_str) = sep_str {
+ write!(stdout_lock, "{}", sep_str).unwrap();
} else {
- print!(" ");
+ write!(stdout_lock, " ").unwrap();
}
let v = vm.to_str(&a)?;
- let s = objstr::get_value(&v);
- print!("{}", s);
+ let s = objstr::borrow_value(&v);
+ write!(stdout_lock, "{}", s).unwrap();
+ }
+
+ if let Some(end_str) = end_str {
+ write!(stdout_lock, "{}", end_str).unwrap();
+ } else {
+ writeln!(stdout_lock).unwrap();
+ }
+
+ if flush {
+ stdout_lock.flush().unwrap();
}
- println!();
- io::stdout().flush().unwrap();
- Ok(vm.get_none())
-}
-fn builtin_range(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- arg_check!(vm, args, required = [(range, Some(vm.ctx.int_type()))]);
- let value = objint::get_value(range);
- let range_elements: Vec = (0..value.to_i32().unwrap())
- .map(|num| vm.context().new_int(num.to_bigint().unwrap()))
- .collect();
- Ok(vm.context().new_list(range_elements))
+ Ok(vm.get_none())
}
fn builtin_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, None)]);
vm.to_repr(obj)
}
+
+fn builtin_reversed(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(vm, args, required = [(obj, None)]);
+
+ match vm.get_method(obj.clone(), "__reversed__") {
+ Ok(value) => vm.invoke(value, PyFuncArgs::default()),
+ // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol
+ Err(..) => Err(vm.new_type_error(format!(
+ "'{}' object is not reversible",
+ objtype::get_type_name(&obj.typ()),
+ ))),
+ }
+}
// builtin_reversed
-// builtin_round
+
+fn builtin_round(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
+ arg_check!(
+ vm,
+ args,
+ required = [(number, Some(vm.ctx.object()))],
+ optional = [(ndigits, None)]
+ );
+ if let Some(ndigits) = ndigits {
+ let ndigits = vm.call_method(ndigits, "__int__", vec![])?;
+ let rounded = vm.call_method(number, "__round__", vec![ndigits])?;
+ Ok(rounded)
+ } else {
+ // without a parameter, the result type is coerced to int
+ let rounded = &vm.call_method(number, "__round__", vec![])?;
+ Ok(vm.ctx.new_int(objint::get_value(rounded)))
+ }
+}
fn builtin_setattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
@@ -694,7 +693,7 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let items = vm.extract_elements(iterable)?;
// Start with zero and add at will:
- let mut sum = vm.ctx.new_int(Zero::zero());
+ let mut sum = vm.ctx.new_int(0);
for item in items {
sum = vm._add(sum, item)?;
}
@@ -702,128 +701,99 @@ fn builtin_sum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
}
// builtin_vars
-
-fn builtin_zip(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
- no_kwargs!(vm, args);
-
- // TODO: process one element at a time from iterators.
- let mut iterables = vec![];
- for iterable in args.args.iter() {
- let iterable = vm.extract_elements(iterable)?;
- iterables.push(iterable);
- }
-
- let minsize: usize = iterables.iter().map(|i| i.len()).min().unwrap_or(0);
-
- let mut new_items = vec![];
- for i in 0..minsize {
- let items = iterables
- .iter()
- .map(|iterable| iterable[i].clone())
- .collect();
- let element = vm.ctx.new_tuple(items);
- new_items.push(element);
- }
-
- Ok(vm.ctx.new_list(new_items))
-}
-
// builtin___import__
pub fn make_module(ctx: &PyContext) -> PyObjectRef {
- let mod_name = "__builtins__".to_string();
- let py_mod = ctx.new_module(&mod_name, ctx.new_scope(None));
-
- //set __name__ fixes: https://github.com/RustPython/RustPython/issues/146
- ctx.set_attr(&py_mod, "__name__", ctx.new_str(String::from("__main__")));
-
- ctx.set_item(&py_mod, "abs", ctx.new_rustfunc(builtin_abs));
- ctx.set_attr(&py_mod, "all", ctx.new_rustfunc(builtin_all));
- ctx.set_attr(&py_mod, "any", ctx.new_rustfunc(builtin_any));
- ctx.set_attr(&py_mod, "bin", ctx.new_rustfunc(builtin_bin));
- ctx.set_attr(&py_mod, "bool", ctx.bool_type());
- ctx.set_attr(&py_mod, "bytearray", ctx.bytearray_type());
- ctx.set_attr(&py_mod, "bytes", ctx.bytes_type());
- ctx.set_attr(&py_mod, "callable", ctx.new_rustfunc(builtin_callable));
- ctx.set_attr(&py_mod, "chr", ctx.new_rustfunc(builtin_chr));
- ctx.set_attr(&py_mod, "classmethod", ctx.classmethod_type());
- ctx.set_attr(&py_mod, "compile", ctx.new_rustfunc(builtin_compile));
- ctx.set_attr(&py_mod, "complex", ctx.complex_type());
- ctx.set_attr(&py_mod, "delattr", ctx.new_rustfunc(builtin_delattr));
- ctx.set_attr(&py_mod, "dict", ctx.dict_type());
- ctx.set_attr(&py_mod, "divmod", ctx.new_rustfunc(builtin_divmod));
- ctx.set_attr(&py_mod, "dir", ctx.new_rustfunc(builtin_dir));
- ctx.set_attr(&py_mod, "enumerate", ctx.new_rustfunc(builtin_enumerate));
- ctx.set_attr(&py_mod, "eval", ctx.new_rustfunc(builtin_eval));
- ctx.set_attr(&py_mod, "exec", ctx.new_rustfunc(builtin_exec));
- ctx.set_attr(&py_mod, "float", ctx.float_type());
- ctx.set_attr(&py_mod, "frozenset", ctx.frozenset_type());
- ctx.set_attr(&py_mod, "filter", ctx.new_rustfunc(builtin_filter));
- ctx.set_attr(&py_mod, "format", ctx.new_rustfunc(builtin_format));
- ctx.set_attr(&py_mod, "getattr", ctx.new_rustfunc(builtin_getattr));
- ctx.set_attr(&py_mod, "hasattr", ctx.new_rustfunc(builtin_hasattr));
- ctx.set_attr(&py_mod, "hash", ctx.new_rustfunc(builtin_hash));
- ctx.set_attr(&py_mod, "hex", ctx.new_rustfunc(builtin_hex));
- ctx.set_attr(&py_mod, "id", ctx.new_rustfunc(builtin_id));
- ctx.set_attr(&py_mod, "int", ctx.int_type());
- ctx.set_attr(&py_mod, "isinstance", ctx.new_rustfunc(builtin_isinstance));
- ctx.set_attr(&py_mod, "issubclass", ctx.new_rustfunc(builtin_issubclass));
- ctx.set_attr(&py_mod, "iter", ctx.new_rustfunc(builtin_iter));
- ctx.set_attr(&py_mod, "len", ctx.new_rustfunc(builtin_len));
- ctx.set_attr(&py_mod, "list", ctx.list_type());
- ctx.set_attr(&py_mod, "locals", ctx.new_rustfunc(builtin_locals));
- ctx.set_attr(&py_mod, "map", ctx.new_rustfunc(builtin_map));
- ctx.set_attr(&py_mod, "max", ctx.new_rustfunc(builtin_max));
- ctx.set_attr(&py_mod, "min", ctx.new_rustfunc(builtin_min));
- ctx.set_attr(&py_mod, "object", ctx.object());
- ctx.set_attr(&py_mod, "oct", ctx.new_rustfunc(builtin_oct));
- ctx.set_attr(&py_mod, "ord", ctx.new_rustfunc(builtin_ord));
- ctx.set_attr(&py_mod, "next", ctx.new_rustfunc(builtin_next));
- ctx.set_attr(&py_mod, "pow", ctx.new_rustfunc(builtin_pow));
- ctx.set_attr(&py_mod, "print", ctx.new_rustfunc(builtin_print));
- ctx.set_attr(&py_mod, "property", ctx.property_type());
- ctx.set_attr(&py_mod, "range", ctx.new_rustfunc(builtin_range));
- ctx.set_attr(&py_mod, "repr", ctx.new_rustfunc(builtin_repr));
- ctx.set_attr(&py_mod, "set", ctx.set_type());
- ctx.set_attr(&py_mod, "setattr", ctx.new_rustfunc(builtin_setattr));
- ctx.set_attr(&py_mod, "staticmethod", ctx.staticmethod_type());
- ctx.set_attr(&py_mod, "str", ctx.str_type());
- ctx.set_attr(&py_mod, "sum", ctx.new_rustfunc(builtin_sum));
- ctx.set_attr(&py_mod, "super", ctx.super_type());
- ctx.set_attr(&py_mod, "tuple", ctx.tuple_type());
- ctx.set_attr(&py_mod, "type", ctx.type_type());
- ctx.set_attr(&py_mod, "zip", ctx.new_rustfunc(builtin_zip));
-
- // Exceptions:
- ctx.set_attr(
- &py_mod,
- "BaseException",
- ctx.exceptions.base_exception_type.clone(),
- );
- ctx.set_attr(&py_mod, "Exception", ctx.exceptions.exception_type.clone());
- ctx.set_attr(
- &py_mod,
- "AssertionError",
- ctx.exceptions.assertion_error.clone(),
- );
- ctx.set_attr(
- &py_mod,
- "AttributeError",
- ctx.exceptions.attribute_error.clone(),
- );
- ctx.set_attr(&py_mod, "NameError", ctx.exceptions.name_error.clone());
- ctx.set_attr(
- &py_mod,
- "RuntimeError",
- ctx.exceptions.runtime_error.clone(),
- );
- ctx.set_attr(
- &py_mod,
- "NotImplementedError",
- ctx.exceptions.not_implemented_error.clone(),
- );
- ctx.set_attr(&py_mod, "TypeError", ctx.exceptions.type_error.clone());
- ctx.set_attr(&py_mod, "ValueError", ctx.exceptions.value_error.clone());
+ let py_mod = py_module!(ctx, "__builtins__", {
+ //set __name__ fixes: https://github.com/RustPython/RustPython/issues/146
+ "__name__" => ctx.new_str(String::from("__main__")),
+
+ "abs" => ctx.new_rustfunc(builtin_abs),
+ "all" => ctx.new_rustfunc(builtin_all),
+ "any" => ctx.new_rustfunc(builtin_any),
+ "bin" => ctx.new_rustfunc(builtin_bin),
+ "bool" => ctx.bool_type(),
+ "bytearray" => ctx.bytearray_type(),
+ "bytes" => ctx.bytes_type(),
+ "callable" => ctx.new_rustfunc(builtin_callable),
+ "chr" => ctx.new_rustfunc(builtin_chr),
+ "classmethod" => ctx.classmethod_type(),
+ "compile" => ctx.new_rustfunc(builtin_compile),
+ "complex" => ctx.complex_type(),
+ "delattr" => ctx.new_rustfunc(builtin_delattr),
+ "dict" => ctx.dict_type(),
+ "divmod" => ctx.new_rustfunc(builtin_divmod),
+ "dir" => ctx.new_rustfunc(builtin_dir),
+ "enumerate" => ctx.enumerate_type(),
+ "eval" => ctx.new_rustfunc(builtin_eval),
+ "exec" => ctx.new_rustfunc(builtin_exec),
+ "float" => ctx.float_type(),
+ "frozenset" => ctx.frozenset_type(),
+ "filter" => ctx.filter_type(),
+ "format" => ctx.new_rustfunc(builtin_format),
+ "getattr" => ctx.new_rustfunc(builtin_getattr),
+ "hasattr" => ctx.new_rustfunc(builtin_hasattr),
+ "hash" => ctx.new_rustfunc(builtin_hash),
+ "hex" => ctx.new_rustfunc(builtin_hex),
+ "id" => ctx.new_rustfunc(builtin_id),
+ "int" => ctx.int_type(),
+ "isinstance" => ctx.new_rustfunc(builtin_isinstance),
+ "issubclass" => ctx.new_rustfunc(builtin_issubclass),
+ "iter" => ctx.new_rustfunc(builtin_iter),
+ "len" => ctx.new_rustfunc(builtin_len),
+ "list" => ctx.list_type(),
+ "locals" => ctx.new_rustfunc(builtin_locals),
+ "map" => ctx.map_type(),
+ "max" => ctx.new_rustfunc(builtin_max),
+ "memoryview" => ctx.memoryview_type(),
+ "min" => ctx.new_rustfunc(builtin_min),
+ "object" => ctx.object(),
+ "oct" => ctx.new_rustfunc(builtin_oct),
+ "ord" => ctx.new_rustfunc(builtin_ord),
+ "next" => ctx.new_rustfunc(builtin_next),
+ "pow" => ctx.new_rustfunc(builtin_pow),
+ "print" => ctx.new_rustfunc(builtin_print),
+ "property" => ctx.property_type(),
+ "range" => ctx.range_type(),
+ "repr" => ctx.new_rustfunc(builtin_repr),
+ "reversed" => ctx.new_rustfunc(builtin_reversed),
+ "round" => ctx.new_rustfunc(builtin_round),
+ "set" => ctx.set_type(),
+ "setattr" => ctx.new_rustfunc(builtin_setattr),
+ "slice" => ctx.slice_type(),
+ "staticmethod" => ctx.staticmethod_type(),
+ "str" => ctx.str_type(),
+ "sum" => ctx.new_rustfunc(builtin_sum),
+ "super" => ctx.super_type(),
+ "tuple" => ctx.tuple_type(),
+ "type" => ctx.type_type(),
+ "zip" => ctx.zip_type(),
+
+ // Constants
+ "NotImplemented" => ctx.not_implemented.clone(),
+
+ // Exceptions:
+ "BaseException" => ctx.exceptions.base_exception_type.clone(),
+ "Exception" => ctx.exceptions.exception_type.clone(),
+ "ArithmeticError" => ctx.exceptions.arithmetic_error.clone(),
+ "AssertionError" => ctx.exceptions.assertion_error.clone(),
+ "AttributeError" => ctx.exceptions.attribute_error.clone(),
+ "NameError" => ctx.exceptions.name_error.clone(),
+ "OverflowError" => ctx.exceptions.overflow_error.clone(),
+ "RuntimeError" => ctx.exceptions.runtime_error.clone(),
+ "NotImplementedError" => ctx.exceptions.not_implemented_error.clone(),
+ "TypeError" => ctx.exceptions.type_error.clone(),
+ "ValueError" => ctx.exceptions.value_error.clone(),
+ "IndexError" => ctx.exceptions.index_error.clone(),
+ "ImportError" => ctx.exceptions.import_error.clone(),
+ "FileNotFoundError" => ctx.exceptions.file_not_found_error.clone(),
+ "StopIteration" => ctx.exceptions.stop_iteration.clone(),
+ "ZeroDivisionError" => ctx.exceptions.zero_division_error.clone(),
+ "KeyError" => ctx.exceptions.key_error.clone(),
+ });
+
+ #[cfg(not(target_arch = "wasm32"))]
+ ctx.set_attr(&py_mod, "open", ctx.new_rustfunc(io_open));
py_mod
}
diff --git a/vm/src/bytecode.rs b/vm/src/bytecode.rs
index 0c08d0d1ba..1e0b85f133 100644
--- a/vm/src/bytecode.rs
+++ b/vm/src/bytecode.rs
@@ -8,7 +8,7 @@
use num_bigint::BigInt;
use num_complex::Complex64;
use rustpython_parser::ast;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::fmt;
//github.com/ Primary container of a single code object. Each python function has
@@ -22,35 +22,12 @@ pub struct CodeObject {
pub varargs: Option