Merge branch 'master' of https://github.com/RustPython/RustPython into walrus

This commit is contained in:
Windel Bouwman committed 2019-06-09 11:49:02 +02:00
commit aa6b155e1e
92 files changed
+4662 -1078

No files matched your search

@@ -0,0 +1,16 @@
---
name: Report incompatibility
about: Report an incompatibility between RustPython and CPython
title: ''
labels: feat
assignees: ''
---
## Feature
<!-- What Python feature is missing from RustPython? Give a short description of the feature and how you ran into its absence. -->
## Python Documentation
<!-- Give a link to the feature in the CPython documentation (https://docs.python.org/3/) in order to assist in its implementation. -->
+24
View File
@@ -0,0 +1,24 @@
---
name: RFC
about: Make a suggestion in a Request for Comments format to RustPython
title: "[RFC] "
labels: RFC
assignees: ''
---
## Summary
<!-- A quick overview of your suggestion -->
## Detailed Explanation
<!-- Elaborate on your suggestion in all its details -->
## Drawbacks, Rationale, and Alternatives
<!-- What drawbacks might this solution have? Why do you feel it is necessary? What other options might there be to solving this problem? -->
## Unresolved Questions
<!-- What would you like feedback on for fleshing out your suggestion? -->
Generated
+134 -7
View File
@@ -78,6 +78,16 @@ dependencies = [
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bincode"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-set"
version = "0.5.1"
@@ -124,6 +134,11 @@ dependencies = [
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "build_const"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bumpalo"
version = "2.3.0"
@@ -195,6 +210,14 @@ dependencies = [
"python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "diff"
version = "0.1.11"
@@ -323,6 +346,31 @@ dependencies = [
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexf"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hexf-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexf-impl"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexf-parse"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "humantime"
version = "1.2.0"
@@ -331,6 +379,11 @@ dependencies = [
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indexmap"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.8.0"
@@ -448,7 +501,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nix"
version = "0.11.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -479,6 +532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"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)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -487,6 +541,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -553,6 +608,19 @@ name = "precomputed-hash"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-hack"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro-hack-impl"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.27"
@@ -561,6 +629,15 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pwd"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "python3-sys"
version = "0.2.1"
@@ -575,6 +652,11 @@ name = "quick-error"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.6.11"
@@ -795,7 +877,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython_parser 0.0.1",
"rustpython_vm 0.1.0",
"rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -818,6 +900,7 @@ dependencies = [
"num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (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.89 (registry+https://github.com/rust-lang/crates.io-index)",
"unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -826,9 +909,13 @@ dependencies = [
name = "rustpython_vm"
version = "0.1.0"
dependencies = [
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hexf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lexical 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -838,13 +925,13 @@ dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pwd 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version_runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rustpython_derive 0.1.0",
"rustpython_parser 0.0.1",
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
"statrs 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-casing 0.1.0 (git+https://github.com/OddCoincidence/unicode-casing?rev=90d6d1f02b9cc04ffb55a5f1c3fa1455a84231fb)",
@@ -869,14 +956,14 @@ dependencies = [
[[package]]
name = "rustyline"
version = "2.1.0"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -910,6 +997,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "serde"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
@@ -1014,6 +1104,16 @@ name = "strsim"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.15.29"
@@ -1024,6 +1124,14 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.1"
@@ -1147,6 +1255,11 @@ name = "unicode-width"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
@@ -1340,12 +1453,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400"
"checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d"
"checksum block-padding 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d75255892aeb580d3c566f213a2b6fdc1c66667839f45719ee1d30ebf2aea591"
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
"checksum bumpalo 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6a6bc2ba935b1e2f810787ceba8dfe86cbc164cee55925cae715541186673c4"
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
@@ -1356,6 +1471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
"checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
"checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
@@ -1372,7 +1488,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
"checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum hexf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e54653cc32d838771a36532647afad59c4bf7155745eeeec406f71fd5d7e7538"
"checksum hexf-impl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22eadcfadba76a730b2764eaa577d045f35e0ef5174b9c5b46adf1ee42b85e12"
"checksum hexf-parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79296f72d53a89096cbc9a88c9547ee8dfe793388674620e2207593d370550ac"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
"checksum js-sys 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "d4bda84b98977341bb6ba1086ecbd9eab8bc929de0f2923c118baf76c21dd0c8"
@@ -1387,7 +1507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
"checksum new_debug_unreachable 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718"
@@ -1401,9 +1521,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
"checksum proc-macro-hack 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b773f824ff2a495833f85fcdddcf85e096949971decada2e93249fa2c6c3d32f"
"checksum proc-macro-hack-impl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0f674ccc446da486175527473ec8aa064f980b0966bbf767ee743a5dff6244a7"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
"checksum pwd 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5dd32d8bece608e144ca20251e714ed107cdecdabb20c2d383cfc687825106a5"
"checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
@@ -1427,7 +1551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum rustc_version_runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6de8ecd7fad7731f306f69b6e10ec5a3178c61e464dcc06979427aa4cc891145"
"checksum rustyline 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6010155119d53aac4f5b987cb8f6ea913d0d64d9b237da36f8f96a90cb3f5385"
"checksum rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f47ea1ceb347d2deae482d655dc8eef4bd82363d3329baffa3818bd76fea48b"
"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 semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
@@ -1446,7 +1570,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da"
"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1825685f977249735d510a242a6727b46efe914bb67e38d30c071b1b72b1d5c2"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
@@ -1464,6 +1590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426"
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
+1 -1
View File
@@ -18,7 +18,7 @@ env_logger="0.5.10"
clap = "2.31.2"
rustpython_parser = {path = "parser"}
rustpython_vm = {path = "vm"}
rustyline = "2.1.0"
rustyline = "4.1.0"
xdg = "2.2.0"
[dev-dependencies.cpython]
+601
View File
@@ -0,0 +1,601 @@
"""Heap queue algorithm (a.k.a. priority queue).
Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
all k, counting elements from 0. For the sake of comparison,
non-existing elements are considered to be infinite. The interesting
property of a heap is that a[0] is always its smallest element.
Usage:
heap = [] # creates an empty heap
heappush(heap, item) # pushes a new item on the heap
item = heappop(heap) # pops the smallest item from the heap
item = heap[0] # smallest item on the heap without popping it
heapify(x) # transforms list into a heap, in-place, in linear time
item = heapreplace(heap, item) # pops and returns smallest item, and adds
# new item; the heap size is unchanged
Our API differs from textbook heap algorithms as follows:
- We use 0-based indexing. This makes the relationship between the
index for a node and the indexes for its children slightly less
obvious, but is more suitable since Python uses 0-based indexing.
- Our heappop() method returns the smallest item, not the largest.
These two make it possible to view the heap as a regular Python list
without surprises: heap[0] is the smallest item, and heap.sort()
maintains the heap invariant!
"""
# Original code by Kevin O'Connor, augmented by Tim Peters and Raymond Hettinger
__about__ = """Heap queues
[explanation by François Pinard]
Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
all k, counting elements from 0. For the sake of comparison,
non-existing elements are considered to be infinite. The interesting
property of a heap is that a[0] is always its smallest element.
The strange invariant above is meant to be an efficient memory
representation for a tournament. The numbers below are `k', not a[k]:
0
1 2
3 4 5 6
7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
In the tree above, each cell `k' is topping `2*k+1' and `2*k+2'. In
a usual binary tournament we see in sports, each cell is the winner
over the two cells it tops, and we can trace the winner down the tree
to see all opponents s/he had. However, in many computer applications
of such tournaments, we do not need to trace the history of a winner.
To be more memory efficient, when a winner is promoted, we try to
replace it by something else at a lower level, and the rule becomes
that a cell and the two cells it tops contain three different items,
but the top cell "wins" over the two topped cells.
If this heap invariant is protected at all time, index 0 is clearly
the overall winner. The simplest algorithmic way to remove it and
find the "next" winner is to move some loser (let's say cell 30 in the
diagram above) into the 0 position, and then percolate this new 0 down
the tree, exchanging values, until the invariant is re-established.
This is clearly logarithmic on the total number of items in the tree.
By iterating over all items, you get an O(n ln n) sort.
A nice feature of this sort is that you can efficiently insert new
items while the sort is going on, provided that the inserted items are
not "better" than the last 0'th element you extracted. This is
especially useful in simulation contexts, where the tree holds all
incoming events, and the "win" condition means the smallest scheduled
time. When an event schedule other events for execution, they are
scheduled into the future, so they can easily go into the heap. So, a
heap is a good structure for implementing schedulers (this is what I
used for my MIDI sequencer :-).
Various structures for implementing schedulers have been extensively
studied, and heaps are good for this, as they are reasonably speedy,
the speed is almost constant, and the worst case is not much different
than the average case. However, there are other representations which
are more efficient overall, yet the worst cases might be terrible.
Heaps are also very useful in big disk sorts. You most probably all
know that a big sort implies producing "runs" (which are pre-sorted
sequences, which size is usually related to the amount of CPU memory),
followed by a merging passes for these runs, which merging is often
very cleverly organised[1]. It is very important that the initial
sort produces the longest runs possible. Tournaments are a good way
to that. If, using all the memory available to hold a tournament, you
replace and percolate items that happen to fit the current run, you'll
produce runs which are twice the size of the memory for random input,
and much better for input fuzzily ordered.
Moreover, if you output the 0'th item on disk and get an input which
may not fit in the current tournament (because the value "wins" over
the last output value), it cannot fit in the heap, so the size of the
heap decreases. The freed memory could be cleverly reused immediately
for progressively building a second heap, which grows at exactly the
same rate the first heap is melting. When the first heap completely
vanishes, you switch heaps and start a new run. Clever and quite
effective!
In a word, heaps are useful memory structures to know. I use them in
a few applications, and I think it is good to keep a `heap' module
around. :-)
--------------------
[1] The disk balancing algorithms which are current, nowadays, are
more annoying than clever, and this is a consequence of the seeking
capabilities of the disks. On devices which cannot seek, like big
tape drives, the story was quite different, and one had to be very
clever to ensure (far in advance) that each tape movement will be the
most effective possible (that is, will best participate at
"progressing" the merge). Some tapes were even able to read
backwards, and this was also used to avoid the rewinding time.
Believe me, real good tape sorts were quite spectacular to watch!
From all times, sorting has always been a Great Art! :-)
"""
__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'merge',
'nlargest', 'nsmallest', 'heappushpop']
def heappush(heap, item):
"""Push item onto heap, maintaining the heap invariant."""
heap.append(item)
_siftdown(heap, 0, len(heap)-1)
def heappop(heap):
"""Pop the smallest item off the heap, maintaining the heap invariant."""
lastelt = heap.pop() # raises appropriate IndexError if heap is empty
if heap:
returnitem = heap[0]
heap[0] = lastelt
_siftup(heap, 0)
return returnitem
return lastelt
def heapreplace(heap, item):
"""Pop and return the current smallest value, and add the new item.
This is more efficient than heappop() followed by heappush(), and can be
more appropriate when using a fixed-size heap. Note that the value
returned may be larger than item! That constrains reasonable uses of
this routine unless written as part of a conditional replacement:
if item > heap[0]:
item = heapreplace(heap, item)
"""
returnitem = heap[0] # raises appropriate IndexError if heap is empty
heap[0] = item
_siftup(heap, 0)
return returnitem
def heappushpop(heap, item):
"""Fast version of a heappush followed by a heappop."""
if heap and heap[0] < item:
item, heap[0] = heap[0], item
_siftup(heap, 0)
return item
def heapify(x):
"""Transform list into a heap, in-place, in O(len(x)) time."""
n = len(x)
# Transform bottom-up. The largest index there's any point to looking at
# is the largest with a child index in-range, so must have 2*i + 1 < n,
# or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so
# j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is
# (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.
for i in reversed(range(n//2)):
_siftup(x, i)
def _heappop_max(heap):
"""Maxheap version of a heappop."""
lastelt = heap.pop() # raises appropriate IndexError if heap is empty
if heap:
returnitem = heap[0]
heap[0] = lastelt
_siftup_max(heap, 0)
return returnitem
return lastelt
def _heapreplace_max(heap, item):
"""Maxheap version of a heappop followed by a heappush."""
returnitem = heap[0] # raises appropriate IndexError if heap is empty
heap[0] = item
_siftup_max(heap, 0)
return returnitem
def _heapify_max(x):
"""Transform list into a maxheap, in-place, in O(len(x)) time."""
n = len(x)
for i in reversed(range(n//2)):
_siftup_max(x, i)
# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
# is the index of a leaf with a possibly out-of-order value. Restore the
# heap invariant.
def _siftdown(heap, startpos, pos):
newitem = heap[pos]
# Follow the path to the root, moving parents down until finding a place
# newitem fits.
while pos > startpos:
parentpos = (pos - 1) >> 1
parent = heap[parentpos]
if newitem < parent:
heap[pos] = parent
pos = parentpos
continue
break
heap[pos] = newitem
# The child indices of heap index pos are already heaps, and we want to make
# a heap at index pos too. We do this by bubbling the smaller child of
# pos up (and so on with that child's children, etc) until hitting a leaf,
# then using _siftdown to move the oddball originally at index pos into place.
#
# We *could* break out of the loop as soon as we find a pos where newitem <=
# both its children, but turns out that's not a good idea, and despite that
# many books write the algorithm that way. During a heap pop, the last array
# element is sifted in, and that tends to be large, so that comparing it
# against values starting from the root usually doesn't pay (= usually doesn't
# get us out of the loop early). See Knuth, Volume 3, where this is
# explained and quantified in an exercise.
#
# Cutting the # of comparisons is important, since these routines have no
# way to extract "the priority" from an array element, so that intelligence
# is likely to be hiding in custom comparison methods, or in array elements
# storing (priority, record) tuples. Comparisons are thus potentially
# expensive.
#
# On random arrays of length 1000, making this change cut the number of
# comparisons made by heapify() a little, and those made by exhaustive
# heappop() a lot, in accord with theory. Here are typical results from 3
# runs (3 just to demonstrate how small the variance is):
#
# Compares needed by heapify Compares needed by 1000 heappops
# -------------------------- --------------------------------
# 1837 cut to 1663 14996 cut to 8680
# 1855 cut to 1659 14966 cut to 8678
# 1847 cut to 1660 15024 cut to 8703
#
# Building the heap by using heappush() 1000 times instead required
# 2198, 2148, and 2219 compares: heapify() is more efficient, when
# you can use it.
#
# The total compares needed by list.sort() on the same lists were 8627,
# 8627, and 8632 (this should be compared to the sum of heapify() and
# heappop() compares): list.sort() is (unsurprisingly!) more efficient
# for sorting.
def _siftup(heap, pos):
endpos = len(heap)
startpos = pos
newitem = heap[pos]
# Bubble up the smaller child until hitting a leaf.
childpos = 2*pos + 1 # leftmost child position
while childpos < endpos:
# Set childpos to index of smaller child.
rightpos = childpos + 1
if rightpos < endpos and not heap[childpos] < heap[rightpos]:
childpos = rightpos
# Move the smaller child up.
heap[pos] = heap[childpos]
pos = childpos
childpos = 2*pos + 1
# The leaf at pos is empty now. Put newitem there, and bubble it up
# to its final resting place (by sifting its parents down).
heap[pos] = newitem
_siftdown(heap, startpos, pos)
def _siftdown_max(heap, startpos, pos):
'Maxheap variant of _siftdown'
newitem = heap[pos]
# Follow the path to the root, moving parents down until finding a place
# newitem fits.
while pos > startpos:
parentpos = (pos - 1) >> 1
parent = heap[parentpos]
if parent < newitem:
heap[pos] = parent
pos = parentpos
continue
break
heap[pos] = newitem
def _siftup_max(heap, pos):
'Maxheap variant of _siftup'
endpos = len(heap)
startpos = pos
newitem = heap[pos]
# Bubble up the larger child until hitting a leaf.
childpos = 2*pos + 1 # leftmost child position
while childpos < endpos:
# Set childpos to index of larger child.
rightpos = childpos + 1
if rightpos < endpos and not heap[rightpos] < heap[childpos]:
childpos = rightpos
# Move the larger child up.
heap[pos] = heap[childpos]
pos = childpos
childpos = 2*pos + 1
# The leaf at pos is empty now. Put newitem there, and bubble it up
# to its final resting place (by sifting its parents down).
heap[pos] = newitem
_siftdown_max(heap, startpos, pos)
def merge(*iterables, key=None, reverse=False):
'''Merge multiple sorted inputs into a single sorted output.
Similar to sorted(itertools.chain(*iterables)) but returns a generator,
does not pull the data into memory all at once, and assumes that each of
the input streams is already sorted (smallest to largest).
>>> list(merge([1,3,5,7], [0,2,4,8], [5,10,15,20], [], [25]))
[0, 1, 2, 3, 4, 5, 5, 7, 8, 10, 15, 20, 25]
If *key* is not None, applies a key function to each element to determine
its sort order.
>>> list(merge(['dog', 'horse'], ['cat', 'fish', 'kangaroo'], key=len))
['dog', 'cat', 'fish', 'horse', 'kangaroo']
'''
h = []
h_append = h.append
if reverse:
_heapify = _heapify_max
_heappop = _heappop_max
_heapreplace = _heapreplace_max
direction = -1
else:
_heapify = heapify
_heappop = heappop
_heapreplace = heapreplace
direction = 1
if key is None:
for order, it in enumerate(map(iter, iterables)):
try:
next = it.__next__
h_append([next(), order * direction, next])
except StopIteration:
pass
_heapify(h)
while len(h) > 1:
try:
while True:
value, order, next = s = h[0]
yield value
s[0] = next() # raises StopIteration when exhausted
_heapreplace(h, s) # restore heap condition
except StopIteration:
_heappop(h) # remove empty iterator
if h:
# fast case when only a single iterator remains
value, order, next = h[0]
yield value
yield from next.__self__
return
for order, it in enumerate(map(iter, iterables)):
try:
next = it.__next__
value = next()
h_append([key(value), order * direction, value, next])
except StopIteration:
pass
_heapify(h)
while len(h) > 1:
try:
while True:
key_value, order, value, next = s = h[0]
yield value
value = next()
s[0] = key(value)
s[2] = value
_heapreplace(h, s)
except StopIteration:
_heappop(h)
if h:
key_value, order, value, next = h[0]
yield value
yield from next.__self__
# Algorithm notes for nlargest() and nsmallest()
# ==============================================
#
# Make a single pass over the data while keeping the k most extreme values
# in a heap. Memory consumption is limited to keeping k values in a list.
#
# Measured performance for random inputs:
#
# number of comparisons
# n inputs k-extreme values (average of 5 trials) % more than min()
# ------------- ---------------- --------------------- -----------------
# 1,000 100 3,317 231.7%
# 10,000 100 14,046 40.5%
# 100,000 100 105,749 5.7%
# 1,000,000 100 1,007,751 0.8%
# 10,000,000 100 10,009,401 0.1%
#
# Theoretical number of comparisons for k smallest of n random inputs:
#
# Step Comparisons Action
# ---- -------------------------- ---------------------------
# 1 1.66 * k heapify the first k-inputs
# 2 n - k compare remaining elements to top of heap
# 3 k * (1 + lg2(k)) * ln(n/k) replace the topmost value on the heap
# 4 k * lg2(k) - (k/2) final sort of the k most extreme values
#
# Combining and simplifying for a rough estimate gives:
#
# comparisons = n + k * (log(k, 2) * log(n/k) + log(k, 2) + log(n/k))
#
# Computing the number of comparisons for step 3:
# -----------------------------------------------
# * For the i-th new value from the iterable, the probability of being in the
# k most extreme values is k/i. For example, the probability of the 101st
# value seen being in the 100 most extreme values is 100/101.
# * If the value is a new extreme value, the cost of inserting it into the
# heap is 1 + log(k, 2).
# * The probability times the cost gives:
# (k/i) * (1 + log(k, 2))
# * Summing across the remaining n-k elements gives:
# sum((k/i) * (1 + log(k, 2)) for i in range(k+1, n+1))
# * This reduces to:
# (H(n) - H(k)) * k * (1 + log(k, 2))
# * Where H(n) is the n-th harmonic number estimated by:
# gamma = 0.5772156649
# H(n) = log(n, e) + gamma + 1 / (2 * n)
# http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)#Rate_of_divergence
# * Substituting the H(n) formula:
# comparisons = k * (1 + log(k, 2)) * (log(n/k, e) + (1/n - 1/k) / 2)
#
# Worst-case for step 3:
# ----------------------
# In the worst case, the input data is reversed sorted so that every new element
# must be inserted in the heap:
#
# comparisons = 1.66 * k + log(k, 2) * (n - k)
#
# Alternative Algorithms
# ----------------------
# Other algorithms were not used because they:
# 1) Took much more auxiliary memory,
# 2) Made multiple passes over the data.
# 3) Made more comparisons in common cases (small k, large n, semi-random input).
# See the more detailed comparison of approach at:
# http://code.activestate.com/recipes/577573-compare-algorithms-for-heapqsmallest
def nsmallest(n, iterable, key=None):
"""Find the n smallest elements in a dataset.
Equivalent to: sorted(iterable, key=key)[:n]
"""
# Short-cut for n==1 is to use min()
if n == 1:
it = iter(iterable)
sentinel = object()
result = min(it, default=sentinel, key=key)
return [] if result is sentinel else [result]
# When n>=size, it's faster to use sorted()
try:
size = len(iterable)
except (TypeError, AttributeError):
pass
else:
if n >= size:
return sorted(iterable, key=key)[:n]
# When key is none, use simpler decoration
if key is None:
it = iter(iterable)
# put the range(n) first so that zip() doesn't
# consume one too many elements from the iterator
result = [(elem, i) for i, elem in zip(range(n), it)]
if not result:
return result
_heapify_max(result)
top = result[0][0]
order = n
_heapreplace = _heapreplace_max
for elem in it:
if elem < top:
_heapreplace(result, (elem, order))
top, _order = result[0]
order += 1
result.sort()
return [elem for (elem, order) in result]
# General case, slowest method
it = iter(iterable)
result = [(key(elem), i, elem) for i, elem in zip(range(n), it)]
if not result:
return result
_heapify_max(result)
top = result[0][0]
order = n
_heapreplace = _heapreplace_max
for elem in it:
k = key(elem)
if k < top:
_heapreplace(result, (k, order, elem))
top, _order, _elem = result[0]
order += 1
result.sort()
return [elem for (k, order, elem) in result]
def nlargest(n, iterable, key=None):
"""Find the n largest elements in a dataset.
Equivalent to: sorted(iterable, key=key, reverse=True)[:n]
"""
# Short-cut for n==1 is to use max()
if n == 1:
it = iter(iterable)
sentinel = object()
result = max(it, default=sentinel, key=key)
return [] if result is sentinel else [result]
# When n>=size, it's faster to use sorted()
try:
size = len(iterable)
except (TypeError, AttributeError):
pass
else:
if n >= size:
return sorted(iterable, key=key, reverse=True)[:n]
# When key is none, use simpler decoration
if key is None:
it = iter(iterable)
result = [(elem, i) for i, elem in zip(range(0, -n, -1), it)]
if not result:
return result
heapify(result)
top = result[0][0]
order = -n
_heapreplace = heapreplace
for elem in it:
if top < elem:
_heapreplace(result, (elem, order))
top, _order = result[0]
order -= 1
result.sort(reverse=True)
return [elem for (elem, order) in result]
# General case, slowest method
it = iter(iterable)
result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]
if not result:
return result
heapify(result)
top = result[0][0]
order = -n
_heapreplace = heapreplace
for elem in it:
k = key(elem)
if top < k:
_heapreplace(result, (k, order, elem))
top, _order, _elem = result[0]
order -= 1
result.sort(reverse=True)
return [elem for (k, order, elem) in result]
# If available, use C implementation
try:
from _heapq import *
except ImportError:
pass
try:
from _heapq import _heapreplace_max
except ImportError:
pass
try:
from _heapq import _heapify_max
except ImportError:
pass
try:
from _heapq import _heappop_max
except ImportError:
pass
if __name__ == "__main__":
import doctest
print(doctest.testmod())
+452 -3
View File
@@ -1,15 +1,464 @@
"""
Operator Interface
This module exports a set of functions corresponding to the intrinsic
operators of Python. For example, operator.add(x, y) is equivalent
to the expression x+y. The function names are those used for special
methods; variants without leading and trailing '__' are also provided
for convenience.
This is the pure Python implementation of the module.
"""
__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul',
'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift',
'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod',
'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift',
'setitem', 'sub', 'truediv', 'truth', 'xor']
from builtins import abs as _abs
# Comparison operations:
# Comparison Operations *******************************************************#
def lt(a, b):
"Same as a < b."
return a < b
def le(a, b):
"Same as a <= b."
return a <= b
def eq(a, b):
"Same as a == b."
return a == b
def ne(a, b):
"Same as a != b."
return a != b
def ge(a, b):
"Same as a >= b."
return a >= b
def gt(a, b):
"Same as a > b."
return a > b
# Logical Operations **********************************************************#
def not_(a):
"Same as not a."
return not a
def truth(a):
"Return True if a is true, False otherwise."
return True if a else False
def is_(a, b):
"Same as a is b."
return a is b
def is_not(a, b):
"Same as a is not b."
return a is not b
# Mathematical/Bitwise Operations *********************************************#
def abs(a):
"Same as abs(a)."
return _abs(a)
def add(a, b):
"Same as a + b."
return a + b
def and_(a, b):
"Same as a & b."
return a & b
def floordiv(a, b):
"Same as a // b."
return a // b
def index(a):
"Same as a.__index__()."
return a.__index__()
def inv(a):
"Same as ~a."
return ~a
invert = inv
def lshift(a, b):
"Same as a << b."
return a << b
def mod(a, b):
"Same as a % b."
return a % b
def mul(a, b):
"Same as a * b."
return a * b
def matmul(a, b):
"Same as a @ b."
return a @ b
def neg(a):
"Same as -a."
return -a
def or_(a, b):
"Same as a | b."
return a | b
def pos(a):
"Same as +a."
return +a
def pow(a, b):
"Same as a ** b."
return a ** b
def rshift(a, b):
"Same as a >> b."
return a >> b
def sub(a, b):
"Same as a - b."
return a - b
def truediv(a, b):
"Same as a / b."
return a / b
def xor(a, b):
"Same as a ^ b."
return a ^ b
# Sequence Operations *********************************************************#
def concat(a, b):
"Same as a + b, for a and b sequences."
if not hasattr(a, '__getitem__'):
msg = "'%s' object can't be concatenated" % type(a).__name__
raise TypeError(msg)
return a + b
def contains(a, b):
"Same as b in a (note reversed operands)."
return b in a
def countOf(a, b):
"Return the number of times b occurs in a."
count = 0
for i in a:
if i == b:
count += 1
return count
def delitem(a, b):
"Same as del a[b]."
del a[b]
def getitem(a, b):
"Same as a[b]."
return a[b]
def indexOf(a, b):
"Return the first index of b in a."
for i, j in enumerate(a):
if j == b:
return i
else:
raise ValueError('sequence.index(x): x not in sequence')
def setitem(a, b, c):
"Same as a[b] = c."
a[b] = c
def length_hint(obj, default=0):
"""
Return an estimate of the number of items in obj.
This is useful for presizing containers when building from an iterable.
If the object supports len(), the result will be exact. Otherwise, it may
over- or under-estimate by an arbitrary amount. The result will be an
integer >= 0.
"""
if not isinstance(default, int):
msg = ("'%s' object cannot be interpreted as an integer" %
type(default).__name__)
raise TypeError(msg)
try:
return len(obj)
except TypeError:
pass
try:
hint = type(obj).__length_hint__
except AttributeError:
return default
try:
val = hint(obj)
except TypeError:
return default
if val is NotImplemented:
return default
if not isinstance(val, int):
msg = ('__length_hint__ must be integer, not %s' %
type(val).__name__)
raise TypeError(msg)
if val < 0:
msg = '__length_hint__() should return >= 0'
raise ValueError(msg)
return val
# Generalized Lookup Objects **************************************************#
class attrgetter:
"""
Return a callable object that fetches the given attribute(s) from its operand.
After f = attrgetter('name'), the call f(r) returns r.name.
After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
After h = attrgetter('name.first', 'name.last'), the call h(r) returns
(r.name.first, r.name.last).
"""
__slots__ = ('_attrs', '_call')
def __init__(self, attr, *attrs):
if not attrs:
if not isinstance(attr, str):
raise TypeError('attribute name must be a string')
self._attrs = (attr,)
names = attr.split('.')
def func(obj):
for name in names:
obj = getattr(obj, name)
return obj
self._call = func
else:
self._attrs = (attr,) + attrs
getters = tuple(map(attrgetter, self._attrs))
def func(obj):
return tuple(getter(obj) for getter in getters)
self._call = func
def __call__(self, obj):
return self._call(obj)
def __repr__(self):
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__qualname__,
', '.join(map(repr, self._attrs)))
def __reduce__(self):
return self.__class__, self._attrs
class itemgetter:
"""
Return a callable object that fetches the given item(s) from its operand.
After f = itemgetter(2), the call f(r) returns r[2].
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
"""
__slots__ = ('_items', '_call')
def __init__(self, item, *items):
if not items:
self._items = (item,)
def func(obj):
return obj[item]
self._call = func
else:
self._items = items = (item,) + items
def func(obj):
return tuple(obj[i] for i in items)
self._call = func
def __call__(self, obj):
return self._call(obj)
def __repr__(self):
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__name__,
', '.join(map(repr, self._items)))
def __reduce__(self):
return self.__class__, self._items
class methodcaller:
"""
Return a callable object that calls the given method on its operand.
After f = methodcaller('name'), the call f(r) returns r.name().
After g = methodcaller('name', 'date', foo=1), the call g(r) returns
r.name('date', foo=1).
"""
__slots__ = ('_name', '_args', '_kwargs')
def __init__(*args, **kwargs):
if len(args) < 2:
msg = "methodcaller needs at least one argument, the method name"
raise TypeError(msg)
self = args[0]
self._name = args[1]
if not isinstance(self._name, str):
raise TypeError('method name must be a string')
self._args = args[2:]
self._kwargs = kwargs
def __call__(self, obj):
return getattr(obj, self._name)(*self._args, **self._kwargs)
def __repr__(self):
args = [repr(self._name)]
args.extend(map(repr, self._args))
args.extend('%s=%r' % (k, v) for k, v in self._kwargs.items())
return '%s.%s(%s)' % (self.__class__.__module__,
self.__class__.__name__,
', '.join(args))
def __reduce__(self):
if not self._kwargs:
return self.__class__, (self._name,) + self._args
else:
from functools import partial
return partial(self.__class__, self._name, **self._kwargs), self._args
# In-place Operations *********************************************************#
def iadd(a, b):
"Same as a += b."
a += b
return a
def iand(a, b):
"Same as a &= b."
a &= b
return a
def iconcat(a, b):
"Same as a += b, for a and b sequences."
if not hasattr(a, '__getitem__'):
msg = "'%s' object can't be concatenated" % type(a).__name__
raise TypeError(msg)
a += b
return a
def ifloordiv(a, b):
"Same as a //= b."
a //= b
return a
def ilshift(a, b):
"Same as a <<= b."
a <<= b
return a
def imod(a, b):
"Same as a %= b."
a %= b
return a
def imul(a, b):
"Same as a *= b."
a *= b
return a
def imatmul(a, b):
"Same as a @= b."
a @= b
return a
def ior(a, b):
"Same as a |= b."
a |= b
return a
def ipow(a, b):
"Same as a **= b."
a **=b
return a
def irshift(a, b):
"Same as a >>= b."
a >>= b
return a
def isub(a, b):
"Same as a -= b."
a -= b
return a
def itruediv(a, b):
"Same as a /= b."
a /= b
return a
def ixor(a, b):
"Same as a ^= b."
a ^= b
return a
try:
from _operator import *
except ImportError:
pass
else:
from _operator import __doc__
# All of these "__func__ = func" assignments have to happen after importing
# from _operator to make sure they're set to the right function
__lt__ = lt
__le__ = le
__eq__ = eq
__ne__ = ne
__ge__ = ge
__gt__ = gt
__not__ = not_
__abs__ = abs
__add__ = add
__and__ = and_
__floordiv__ = floordiv
__index__ = index
__inv__ = inv
__invert__ = invert
__lshift__ = lshift
__mod__ = mod
__mul__ = mul
__matmul__ = matmul
__neg__ = neg
__or__ = or_
__pos__ = pos
__pow__ = pow
__rshift__ = rshift
__sub__ = sub
__truediv__ = truediv
__xor__ = xor
__concat__ = concat
__contains__ = contains
__delitem__ = delitem
__getitem__ = getitem
__setitem__ = setitem
__iadd__ = iadd
__iand__ = iand
__iconcat__ = iconcat
__ifloordiv__ = ifloordiv
__ilshift__ = ilshift
__imod__ = imod
__imul__ = imul
__imatmul__ = imatmul
__ior__ = ior
__ipow__ = ipow
__irshift__ = irshift
__isub__ = isub
__itruediv__ = itruediv
__ixor__ = ixor
-31
View File
@@ -133,34 +133,3 @@ def getenv(key, default=None):
The optional second argument can specify an alternate default.
key, default and the result are str."""
return environ.get(key, default)
def fspath(path):
"""Return the path representation of a path-like object.
If str or bytes is passed in, it is returned unchanged. Otherwise the
os.PathLike interface is used to get the path representation. If the
path representation is not str or bytes, TypeError is raised. If the
provided path is not str, bytes, or os.PathLike, TypeError is raised.
"""
if isinstance(path, (str, bytes)):
return path
# Work from the object's type to match method resolution of other magic
# methods.
path_type = type(path)
try:
path_repr = path_type.__fspath__(path)
except AttributeError:
if hasattr(path_type, '__fspath__'):
raise
else:
raise TypeError("expected str, bytes or os.PathLike object, "
"not " + path_type.__name__)
if isinstance(path_repr, (str, bytes)):
return path_repr
else:
raise TypeError("expected {}.__fspath__() to return str or bytes, "
"not {}".format(path_type.__name__,
type(path_repr).__name__))
+161
View File
@@ -0,0 +1,161 @@
"""Redo the builtin repr() (representation) but with limits on most sizes."""
__all__ = ["Repr", "repr", "recursive_repr"]
import builtins
from itertools import islice
from _thread import get_ident
def recursive_repr(fillvalue='...'):
'Decorator to make a repr function return fillvalue for a recursive call'
def decorating_function(user_function):
repr_running = set()
def wrapper(self):
key = id(self), get_ident()
if key in repr_running:
return fillvalue
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
# Can't use functools.wraps() here because of bootstrap issues
wrapper.__module__ = getattr(user_function, '__module__')
wrapper.__doc__ = getattr(user_function, '__doc__')
wrapper.__name__ = getattr(user_function, '__name__')
wrapper.__qualname__ = getattr(user_function, '__qualname__')
wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
return wrapper
return decorating_function
class Repr:
def __init__(self):
self.maxlevel = 6
self.maxtuple = 6
self.maxlist = 6
self.maxarray = 5
self.maxdict = 4
self.maxset = 6
self.maxfrozenset = 6
self.maxdeque = 6
self.maxstring = 30
self.maxlong = 40
self.maxother = 30
def repr(self, x):
return self.repr1(x, self.maxlevel)
def repr1(self, x, level):
typename = type(x).__name__
if ' ' in typename:
parts = typename.split()
typename = '_'.join(parts)
if hasattr(self, 'repr_' + typename):
return getattr(self, 'repr_' + typename)(x, level)
else:
return self.repr_instance(x, level)
def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
n = len(x)
if level <= 0 and n:
s = '...'
else:
newlevel = level - 1
repr1 = self.repr1
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
if n > maxiter: pieces.append('...')
s = ', '.join(pieces)
if n == 1 and trail: right = trail + right
return '%s%s%s' % (left, s, right)
def repr_tuple(self, x, level):
return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',')
def repr_list(self, x, level):
return self._repr_iterable(x, level, '[', ']', self.maxlist)
def repr_array(self, x, level):
if not x:
return "array('%s')" % x.typecode
header = "array('%s', [" % x.typecode
return self._repr_iterable(x, level, header, '])', self.maxarray)
def repr_set(self, x, level):
if not x:
return 'set()'
x = _possibly_sorted(x)
return self._repr_iterable(x, level, '{', '}', self.maxset)
def repr_frozenset(self, x, level):
if not x:
return 'frozenset()'
x = _possibly_sorted(x)
return self._repr_iterable(x, level, 'frozenset({', '})',
self.maxfrozenset)
def repr_deque(self, x, level):
return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
def repr_dict(self, x, level):
n = len(x)
if n == 0: return '{}'
if level <= 0: return '{...}'
newlevel = level - 1
repr1 = self.repr1
pieces = []
for key in islice(_possibly_sorted(x), self.maxdict):
keyrepr = repr1(key, newlevel)
valrepr = repr1(x[key], newlevel)
pieces.append('%s: %s' % (keyrepr, valrepr))
if n > self.maxdict: pieces.append('...')
s = ', '.join(pieces)
return '{%s}' % (s,)
def repr_str(self, x, level):
s = builtins.repr(x[:self.maxstring])
if len(s) > self.maxstring:
i = max(0, (self.maxstring-3)//2)
j = max(0, self.maxstring-3-i)
s = builtins.repr(x[:i] + x[len(x)-j:])
s = s[:i] + '...' + s[len(s)-j:]
return s
def repr_int(self, x, level):
s = builtins.repr(x) # XXX Hope this isn't too slow...
if len(s) > self.maxlong:
i = max(0, (self.maxlong-3)//2)
j = max(0, self.maxlong-3-i)
s = s[:i] + '...' + s[len(s)-j:]
return s
def repr_instance(self, x, level):
try:
s = builtins.repr(x)
# Bugs in x.__repr__() can cause arbitrary
# exceptions -- then make up something
except Exception:
return '<%s instance at %#x>' % (x.__class__.__name__, id(x))
if len(s) > self.maxother:
i = max(0, (self.maxother-3)//2)
j = max(0, self.maxother-3-i)
s = s[:i] + '...' + s[len(s)-j:]
return s
def _possibly_sorted(x):
# Since not all sequences of items can be sorted and comparison
# functions may raise arbitrary exceptions, return an unsorted
# sequence in that case.
try:
return sorted(x)
except Exception:
return list(x)
aRepr = Repr()
repr = aRepr.repr
+295
View File
@@ -0,0 +1,295 @@
"""
Define names for built-in types that aren't directly accessible as a builtin.
"""
import sys
# Iterators in Python aren't a matter of type but of protocol. A large
# and changing number of builtin types implement *some* flavor of
# iterator. Don't check the type! Use hasattr to check for both
# "__iter__" and "__next__" attributes instead.
def _f(): pass
FunctionType = type(_f)
LambdaType = type(lambda: None) # Same as FunctionType
CodeType = type(_f.__code__)
MappingProxyType = type(type.__dict__)
SimpleNamespace = type(sys.implementation)
def _g():
yield 1
GeneratorType = type(_g())
# async def _c(): pass
# _c = _c()
# CoroutineType = type(_c)
# _c.close() # Prevent ResourceWarning
# async def _ag():
# yield
# _ag = _ag()
# AsyncGeneratorType = type(_ag)
class _C:
def _m(self): pass
MethodType = type(_C()._m)
BuiltinFunctionType = type(len)
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
WrapperDescriptorType = type(object.__init__)
MethodWrapperType = type(object().__str__)
MethodDescriptorType = type(str.join)
ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
ModuleType = type(sys)
# try:
# raise TypeError
# except TypeError:
# tb = sys.exc_info()[2]
# TracebackType = type(tb)
# FrameType = type(tb.tb_frame)
# tb = None; del tb
# For Jython, the following two types are identical
GetSetDescriptorType = type(FunctionType.__code__)
# MemberDescriptorType = type(FunctionType.__globals__)
del sys, _f, _g, _C # Not for export
# Provide a PEP 3115 compliant mechanism for class creation
def new_class(name, bases=(), kwds=None, exec_body=None):
"""Create a class object dynamically using the appropriate metaclass."""
resolved_bases = resolve_bases(bases)
meta, ns, kwds = prepare_class(name, resolved_bases, kwds)
if exec_body is not None:
exec_body(ns)
if resolved_bases is not bases:
ns['__orig_bases__'] = bases
return meta(name, resolved_bases, ns, **kwds)
def resolve_bases(bases):
"""Resolve MRO entries dynamically as specified by PEP 560."""
new_bases = list(bases)
updated = False
shift = 0
for i, base in enumerate(bases):
if isinstance(base, type):
continue
if not hasattr(base, "__mro_entries__"):
continue
new_base = base.__mro_entries__(bases)
updated = True
if not isinstance(new_base, tuple):
raise TypeError("__mro_entries__ must return a tuple")
else:
new_bases[i+shift:i+shift+1] = new_base
shift += len(new_base) - 1
if not updated:
return bases
return tuple(new_bases)
def prepare_class(name, bases=(), kwds=None):
"""Call the __prepare__ method of the appropriate metaclass.
Returns (metaclass, namespace, kwds) as a 3-tuple
*metaclass* is the appropriate metaclass
*namespace* is the prepared class namespace
*kwds* is an updated copy of the passed in kwds argument with any
'metaclass' entry removed. If no kwds argument is passed in, this will
be an empty dict.
"""
if kwds is None:
kwds = {}
else:
kwds = dict(kwds) # Don't alter the provided mapping
if 'metaclass' in kwds:
meta = kwds.pop('metaclass')
else:
if bases:
meta = type(bases[0])
else:
meta = type
if isinstance(meta, type):
# when meta is a type, we first determine the most-derived metaclass
# instead of invoking the initial candidate directly
meta = _calculate_meta(meta, bases)
if hasattr(meta, '__prepare__'):
ns = meta.__prepare__(name, bases, **kwds)
else:
ns = {}
return meta, ns, kwds
def _calculate_meta(meta, bases):
"""Calculate the most derived metaclass."""
winner = meta
for base in bases:
base_meta = type(base)
if issubclass(winner, base_meta):
continue
if issubclass(base_meta, winner):
winner = base_meta
continue
# else:
raise TypeError("metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases")
return winner
class DynamicClassAttribute:
"""Route attribute access on a class to __getattr__.
This is a descriptor, used to define attributes that act differently when
accessed through an instance and through a class. Instance access remains
normal, but access to an attribute through a class will be routed to the
class's __getattr__ method; this is done by raising AttributeError.
This allows one to have properties active on an instance, and have virtual
attributes on the class with the same name (see Enum for an example).
"""
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
# next two lines make DynamicClassAttribute act the same as property
self.__doc__ = doc or fget.__doc__
self.overwrite_doc = doc is None
# support for abstract methods
self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
def __get__(self, instance, ownerclass=None):
if instance is None:
if self.__isabstractmethod__:
return self
raise AttributeError()
elif self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(instance)
def __set__(self, instance, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance, value)
def __delete__(self, instance):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(instance)
def getter(self, fget):
fdoc = fget.__doc__ if self.overwrite_doc else None
result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
result.overwrite_doc = self.overwrite_doc
return result
def setter(self, fset):
result = type(self)(self.fget, fset, self.fdel, self.__doc__)
result.overwrite_doc = self.overwrite_doc
return result
def deleter(self, fdel):
result = type(self)(self.fget, self.fset, fdel, self.__doc__)
result.overwrite_doc = self.overwrite_doc
return result
class _GeneratorWrapper:
# TODO: Implement this in C.
def __init__(self, gen):
self.__wrapped = gen
self.__isgen = gen.__class__ is GeneratorType
self.__name__ = getattr(gen, '__name__', None)
self.__qualname__ = getattr(gen, '__qualname__', None)
def send(self, val):
return self.__wrapped.send(val)
def throw(self, tp, *rest):
return self.__wrapped.throw(tp, *rest)
def close(self):
return self.__wrapped.close()
@property
def gi_code(self):
return self.__wrapped.gi_code
@property
def gi_frame(self):
return self.__wrapped.gi_frame
@property
def gi_running(self):
return self.__wrapped.gi_running
@property
def gi_yieldfrom(self):
return self.__wrapped.gi_yieldfrom
cr_code = gi_code
cr_frame = gi_frame
cr_running = gi_running
cr_await = gi_yieldfrom
def __next__(self):
return next(self.__wrapped)
def __iter__(self):
if self.__isgen:
return self.__wrapped
return self
__await__ = __iter__
def coroutine(func):
"""Convert regular generator function to a coroutine."""
if not callable(func):
raise TypeError('types.coroutine() expects a callable')
if (func.__class__ is FunctionType and
getattr(func, '__code__', None).__class__ is CodeType):
co_flags = func.__code__.co_flags
# Check if 'func' is a coroutine function.
# (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
if co_flags & 0x180:
return func
# Check if 'func' is a generator function.
# (0x20 == CO_GENERATOR)
if co_flags & 0x20:
# TODO: Implement this in C.
co = func.__code__
func.__code__ = CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize,
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
co.co_code,
co.co_consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
co.co_cellvars)
return func
# The following code is primarily to support functions that
# return generator-like objects (for instance generators
# compiled with Cython).
# Delay functools and _collections_abc import for speeding up types import.
import functools
import _collections_abc
@functools.wraps(func)
def wrapped(*args, **kwargs):
coro = func(*args, **kwargs)
if (coro.__class__ is CoroutineType or
coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
# 'coro' is a native coroutine object or an iterable coroutine
return coro
if (isinstance(coro, _collections_abc.Generator) and
not isinstance(coro, _collections_abc.Coroutine)):
# 'coro' is either a pure Python generator iterator, or it
# implements collections.abc.Generator (and does not implement
# collections.abc.Coroutine).
return _GeneratorWrapper(coro)
# 'coro' is either an instance of collections.abc.Coroutine or
# some other object -- pass it through.
return coro
return wrapped
__all__ = [n for n in globals() if n[:1] != '_']
+9
View File
@@ -1,3 +1,5 @@
#![recursion_limit = "128"]
extern crate proc_macro;
#[macro_use]
@@ -38,3 +40,10 @@ pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as Item);
result_to_tokens(pyclass::impl_pyimpl(attr, item))
}
#[proc_macro_attribute]
pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as AttributeArgs);
let item = parse_macro_input!(item as Item);
result_to_tokens(pyclass::impl_pystruct_sequence(attr, item))
}
+103 -13
View File
@@ -2,7 +2,9 @@ use super::Diagnostic;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use std::collections::HashMap;
use syn::{Attribute, AttributeArgs, Ident, ImplItem, Item, Lit, Meta, MethodSig, NestedMeta};
use syn::{
Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig, NestedMeta,
};
enum ClassItem {
Method {
@@ -266,16 +268,12 @@ pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
Ok(ret)
}
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let (item, ident, attrs) = match item {
Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs),
Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs),
other => bail_span!(
other,
"#[pyclass] can only be on a struct or enum declaration"
),
};
fn generate_class_def(
ident: &Ident,
attr_name: &'static str,
attr: AttributeArgs,
attrs: &Vec<Attribute>,
) -> Result<TokenStream2, Diagnostic> {
let mut class_name = None;
for attr in attr {
if let NestedMeta::Meta(meta) = attr {
@@ -284,7 +282,11 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
if let Lit::Str(s) = name_value.lit {
class_name = Some(s.value());
} else {
bail_span!(name_value.lit, "#[pyclass(name = ...)] must be a string");
bail_span!(
name_value.lit,
"#[{}(name = ...)] must be a string",
attr_name
);
}
}
}
@@ -316,7 +318,6 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
};
let ret = quote! {
#item
impl ::rustpython_vm::pyobject::PyClassDef for #ident {
const NAME: &'static str = #class_name;
const DOC: Option<&'static str> = #doc;
@@ -324,3 +325,92 @@ pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Dia
};
Ok(ret)
}
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let (item, ident, attrs) = match item {
Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs),
Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs),
other => bail_span!(
other,
"#[pyclass] can only be on a struct or enum declaration"
),
};
let class_def = generate_class_def(&ident, "pyclass", attr, &attrs)?;
let ret = quote! {
#item
#class_def
};
Ok(ret)
}
pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
let struc = if let Item::Struct(struc) = item {
struc
} else {
bail_span!(
item,
"#[pystruct_sequence] can only be on a struct declaration"
)
};
let class_def = generate_class_def(&struc.ident, "pystruct_sequence", attr, &struc.attrs)?;
let mut properties = Vec::new();
let mut field_names = Vec::new();
for (i, field) in struc.fields.iter().enumerate() {
let idx = Index::from(i);
if let Some(ref field_name) = field.ident {
let field_name_str = field_name.to_string();
// TODO add doc to the generated property
let property = quote! {
class.set_str_attr(
#field_name_str,
::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx)
.add_getter(|zelf: &::rustpython_vm::obj::objtuple::PyTuple,
_vm: &::rustpython_vm::vm::VirtualMachine|
zelf.fast_getitem(#idx))
.create(),
);
};
properties.push(property);
field_names.push(quote!(#field_name));
} else {
field_names.push(quote!(#idx));
}
}
let ty = &struc.ident;
let ret = quote! {
#struc
#class_def
impl #ty {
fn into_struct_sequence(&self,
vm: &::rustpython_vm::vm::VirtualMachine,
cls: ::rustpython_vm::obj::objtype::PyClassRef,
) -> ::rustpython_vm::pyobject::PyResult<::rustpython_vm::obj::objtuple::PyTupleRef> {
let tuple: ::rustpython_vm::obj::objtuple::PyTuple =
vec![#(::rustpython_vm::pyobject::IntoPyObject
::into_pyobject(self.#field_names, vm)?
),*].into();
::rustpython_vm::pyobject::PyValue::into_ref_with_type(tuple, vm, cls)
}
}
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
fn impl_extend_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#(#properties)*
}
fn make_class(
ctx: &::rustpython_vm::pyobject::PyContext
) -> ::rustpython_vm::obj::objtype::PyClassRef {
let py_class = ctx.new_class(<Self as ::rustpython_vm::pyobject::PyClassDef>::NAME, ctx.tuple_type());
Self::extend_class(ctx, &py_class);
py_class
}
}
};
Ok(ret)
}
+1
View File
@@ -16,3 +16,4 @@ num-bigint = "0.2"
num-traits = "0.2"
unicode-xid = "0.1.0"
unic-emoji-char = "0.9.0"
serde = { version = "1.0.66", features = ["derive"] }
+4 -1
View File
@@ -4,6 +4,8 @@
pub use super::lexer::Location;
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
/*
#[derive(Debug)]
@@ -42,6 +44,7 @@ pub struct Located<T> {
pub type LocatedStatement = Located<Statement>;
/// Abstract syntax tree nodes for python statements.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq)]
pub enum Statement {
Break,
@@ -381,7 +384,7 @@ pub enum Number {
}
/// Transforms a value prior to formatting it.
#[derive(Copy, Clone, Debug, PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ConversionFlag {
/// Converts by calling `str(<value>)`.
Str,
+2 -1
View File
@@ -7,6 +7,7 @@ extern crate unicode_xid;
pub use super::token::Tok;
use num_bigint::BigInt;
use num_traits::Num;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::str::FromStr;
@@ -71,7 +72,7 @@ pub enum LexicalErrorType {
OtherError(String),
}
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Location {
row: usize,
column: usize,
+1 -1
View File
@@ -159,7 +159,7 @@ fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), Com
Ok(value) => {
// Save non-None values as "_"
use rustpython_vm::pyobject::{IdProtocol, IntoPyObject, ItemProtocol};
use rustpython_vm::pyobject::{IdProtocol, IntoPyObject};
if !value.is(&vm.get_none()) {
let key = objstr::PyString::from("_").into_pyobject(vm);
+8
View File
@@ -86,6 +86,14 @@ assert bool(complex(0, 0)) is False
assert bool(complex(0, 1)) is True
assert bool(complex(1, 0)) is True
# __hash__
assert hash(complex(1)) == hash(float(1)) == hash(int(1))
assert hash(complex(-1)) == hash(float(-1)) == hash(int(-1))
assert hash(complex(3.14)) == hash(float(3.14))
assert hash(complex(-float('inf'))) == hash(-float('inf'))
assert hash(1j) != hash(1)
# numbers.Complex
a = complex(3, 4)
+12
View File
@@ -1,3 +1,5 @@
assert isinstance(dir(), list)
assert '__builtins__' in dir()
class A:
def test():
@@ -21,6 +23,16 @@ assert ['h', 'q'] == dir(B())
# This calls type.__dir__ so isn't changed (but inheritance works)!
assert 'test' in dir(A)
# eval() takes any mapping-like type, so dir() must support them
# TODO: eval() should take any mapping as locals, not just dict-derived types
class A(dict):
def __getitem__(self, x):
return dir
def keys(self):
yield 6
yield 5
assert eval("dir()", {}, A()) == [5, 6]
import socket
assert "AF_INET" in dir(socket)
+11 -2
View File
@@ -6,5 +6,14 @@ assert 'RustPython' in fd.read()
assert_raises(FileNotFoundError, lambda: open('DoesNotExist'))
# Use open as a context manager
with open('README.md') as fp:
fp.read()
with open('README.md', 'rt') as fp:
contents = fp.read()
assert type(contents) == str, "type is " + str(type(contents))
with open('README.md', 'r') as fp:
contents = fp.read()
assert type(contents) == str, "type is " + str(type(contents))
with open('README.md', 'rb') as fp:
contents = fp.read()
assert type(contents) == bytes, "type is " + str(type(contents))
+15
View File
@@ -691,3 +691,18 @@ assert a == bytearray(b'abdabcd')
a = bytearray(b'hello, world')
a.reverse()
assert a == bytearray(b'dlrow ,olleh')
# __setitem__
a = bytearray(b'test')
a[0] = 1
assert a == bytearray(b'\x01est')
with assertRaises(TypeError):
a[0] = b'a'
with assertRaises(TypeError):
a[0] = memoryview(b'a')
a[:2] = [0, 9]
assert a == bytearray(b'\x00\x09st')
a[1:3] = b'test'
assert a == bytearray(b'\x00testt')
a[:6] = memoryview(b'test')
assert a == bytearray(b'test')
+19
View File
@@ -198,3 +198,22 @@ z = {'c': 3, 'd': 3, 'e': 3}
w = {1: 1, **x, 2: 2, **y, 3: 3, **z, 4: 4}
assert w == {1: 1, 'a': 1, 'b': 2, 'c': 3, 2: 2, 'd': 3, 3: 3, 'e': 3, 4: 4}
assert str({True: True, 1.0: 1.0}) == str({True: 1.0})
class A:
def __hash__(self):
return 1
def __eq__(self, other):
return isinstance(other, A)
class B:
def __hash__(self):
return 1
def __eq__(self, other):
return isinstance(other, B)
s = {1: 0, A(): 1, B(): 2}
assert len(s) == 3
assert s[1] == 0
assert s[A()] == 1
assert s[B()] == 2
+33
View File
@@ -98,6 +98,21 @@ assert float(b'2.99e-23') == 2.99e-23
assert_raises(ValueError, lambda: float('foo'))
assert_raises(OverflowError, lambda: float(2**10000))
# check eq and hash for small numbers
assert 1.0 == 1
assert 1.0 == True
assert 0.0 == 0
assert 0.0 == False
assert hash(1.0) == hash(1)
assert hash(1.0) == hash(True)
assert hash(0.0) == hash(0)
assert hash(0.0) == hash(False)
assert hash(1.0) != hash(1.0000000001)
assert 5.0 in {3, 4, 5}
assert {-1: 2}[-1.0] == 2
# check that magic methods are implemented for ints and floats
assert 1.0.__add__(1.0) == 2.0
@@ -181,3 +196,21 @@ assert str(1.123456789) == '1.123456789'
a = .5
assert a == 0.5
assert float.fromhex('0x0.0p+0') == 0.0
assert float.fromhex('-0x0.0p+0') == -0.0
assert float.fromhex('0x1.000000p+0') == 1.0
assert float.fromhex('-0x1.800000p+0') == -1.5
assert float.fromhex('inf') == float('inf')
assert math.isnan(float.fromhex('nan'))
assert (0.0).hex() == '0x0.0p+0'
assert (-0.0).hex() == '-0x0.0p+0'
assert (1.0).hex() == '0x1.0000000000000p+0'
assert (-1.5).hex() == '-0x1.8000000000000p+0'
assert float('inf').hex() == 'inf'
assert float('-inf').hex() == '-inf'
assert float('nan').hex() == 'nan'
#for _ in range(10000):
# f = random.random() * random.randint(0, 0x10000000000000000)
# assert f == float.fromhex(f.hex())
+2
View File
@@ -0,0 +1,2 @@
import __hello__
assert __hello__.initialized == True
+14
View File
@@ -81,3 +81,17 @@ def fubar(x, y, obj=None):
rest = [4, 5]
fubar(obj=6, *rest)
# https://www.python.org/dev/peps/pep-0468/
def func(**kwargs):
return list(kwargs.items())
empty_kwargs = func()
assert empty_kwargs == []
kwargs = func(a=1, b=2)
assert kwargs == [('a', 1), ('b', 2)]
kwargs = func(a=1, b=2, c=3)
assert kwargs == [('a', 1), ('b', 2), ('c', 3)]
+28
View File
@@ -0,0 +1,28 @@
import _imp
import time as import_time
assert _imp.is_builtin("time") == True
assert _imp.is_builtin("os") == False
assert _imp.is_builtin("not existing module") == False
assert _imp.is_frozen("__hello__") == True
assert _imp.is_frozen("os") == False
class FakeSpec:
def __init__(self, name):
self.name = name
A = FakeSpec("time")
imp_time = _imp.create_builtin(A)
assert imp_time.sleep == import_time.sleep
B = FakeSpec("not existing module")
assert _imp.create_builtin(B) == None
_imp.exec_builtin(imp_time) == 0
_imp.get_frozen_object("__hello__")
hello = _imp.init_frozen("__hello__")
assert hello.initialized == True
+7
View File
@@ -79,6 +79,13 @@ assert int.from_bytes(b'\x00\x10', 'little') == 4096
assert int.from_bytes(b'\xfc\x00', 'big', signed=True) == -1024
assert int.from_bytes(b'\xfc\x00', 'big', signed=False) == 64512
assert (1024).to_bytes(4, 'big') == b'\x00\x00\x04\x00'
assert (1024).to_bytes(2, 'little', signed=True) == b'\x00\x04'
assert (-1024).to_bytes(4, 'big', signed=True) == b'\xff\xff\xfc\x00'
assert (-1024).to_bytes(4, 'little', signed=True) == b'\x00\xfc\xff\xff'
assert (2147483647).to_bytes(8, 'big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff'
assert (-2147483648).to_bytes(8, 'little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff'
with assertRaises(TypeError):
int(base=2)
+10 -1
View File
@@ -1,5 +1,5 @@
import math
from testutils import assertRaises
from testutils import assertRaises, assert_raises
# assert(math.exp(2) == math.exp(2.0))
# assert(math.exp(True) == math.exp(1.0))
@@ -78,3 +78,12 @@ with assertRaises(TypeError):
math.ceil(object())
with assertRaises(TypeError):
math.floor(object())
assert str(math.frexp(0.0)) == str((+0.0, 0))
assert str(math.frexp(-0.0)) == str((-0.0, 0))
assert math.frexp(1) == (0.5, 1)
assert math.frexp(1.5) == (0.75, 1)
assert math.frexp(float('inf')) == (float('inf'), 0)
assert str(math.frexp(float('nan'))) == str((float('nan'), 0))
assert_raises(TypeError, lambda: math.frexp(None))
+60 -1
View File
@@ -27,6 +27,26 @@ assert set([1,2]) < set([1,2,3])
assert not set([1,2]) < set([1,2])
assert not set([1,3]) < set([1,2])
assert (set() == []) is False
assert set().__eq__([]) == NotImplemented
assert_raises(TypeError, lambda: set() < [], "'<' not supported between instances of 'set' and 'list'")
assert_raises(TypeError, lambda: set() <= [], "'<=' not supported between instances of 'set' and 'list'")
assert_raises(TypeError, lambda: set() > [], "'>' not supported between instances of 'set' and 'list'")
assert_raises(TypeError, lambda: set() >= [], "'>=' not supported between instances of 'set' and 'list'")
assert set().issuperset([])
assert set().issubset([])
assert not set().issuperset([1, 2, 3])
assert set().issubset([1, 2])
assert (set() == 3) is False
assert set().__eq__(3) == NotImplemented
assert_raises(TypeError, lambda: set() < 3, "'int' object is not iterable")
assert_raises(TypeError, lambda: set() <= 3, "'int' object is not iterable")
assert_raises(TypeError, lambda: set() > 3, "'int' object is not iterable")
assert_raises(TypeError, lambda: set() >= 3, "'int' object is not iterable")
assert_raises(TypeError, lambda: set().issuperset(3), "'int' object is not iterable")
assert_raises(TypeError, lambda: set().issubset(3), "'int' object is not iterable")
class Hashable(object):
def __init__(self, obj):
self.obj = obj
@@ -83,6 +103,12 @@ assert set([1,2,3]).isdisjoint(set([5,6])) == True
assert set([1,2,3]).isdisjoint(set([2,5,6])) == False
assert set([1,2,3]).isdisjoint([5,6]) == True
assert_raises(TypeError, lambda: set() & [])
assert_raises(TypeError, lambda: set() | [])
assert_raises(TypeError, lambda: set() ^ [])
assert_raises(TypeError, lambda: set() + [])
assert_raises(TypeError, lambda: set() - [])
assert_raises(TypeError, lambda: set([[]]))
assert_raises(TypeError, lambda: set().add([]))
@@ -103,7 +129,7 @@ a = set([1,2])
b = a.pop()
assert b in [1,2]
c = a.pop()
assert (c in [1,2] and c != b)
assert (c in [1,2] and c != b)
assert_raises(KeyError, lambda: a.pop())
a = set([1,2,3])
@@ -268,3 +294,36 @@ assert set([1,2,3]).symmetric_difference(frozenset([1,2])) == set([3])
assert frozenset([1,2,3]) ^ set([4,5]) == frozenset([1,2,3,4,5])
assert set([1,2,3]) ^ frozenset([4,5]) == set([1,2,3,4,5])
class A:
def __hash__(self):
return 1
class B:
def __hash__(self):
return 1
s = {1, A(), B()}
assert len(s) == 3
s = {True}
s.add(1.0)
assert str(s) == '{True}'
class EqObject:
def __init__(self, eq):
self.eq = eq
def __eq__(self, other):
return self.eq
def __hash__(self):
return bool(self.eq)
assert 'x' == (EqObject('x') == EqObject('x'))
s = {EqObject('x')}
assert EqObject('x') in s
assert '[]' == (EqObject('[]') == EqObject('[]'))
s = {EqObject([])}
assert EqObject([]) not in s
x = object()
assert x == (EqObject(x) == EqObject(x))
s = {EqObject(x)}
assert EqObject(x) in s
+4
View File
@@ -33,3 +33,7 @@ with assertRaises(ValueError):
with assertRaises(ValueError):
uh(b"nn") # Non-hexadecimal digit found
assert binascii.crc32(b"hello world") == 222957957
assert binascii.crc32(b"hello world", 555555) == 1216827162
assert binascii.crc32(b"goodbye interesting world",777777) == 1885538403
+6
View File
@@ -8,3 +8,9 @@ result = bb.read()
assert len(result) <= 8*1024
assert len(result) >= 0
assert isinstance(result, bytes)
with FileIO('README.md') as fio:
res = fio.read()
assert len(result) <= 8*1024
assert len(result) >= 0
assert isinstance(result, bytes)
+101
View File
@@ -3,6 +3,24 @@ import itertools
from testutils import assertRaises
# itertools.chain tests
chain = itertools.chain
# empty
assert list(chain()) == []
assert list(chain([], "", b"", ())) == []
assert list(chain([1, 2, 3, 4])) == [1, 2, 3, 4]
assert list(chain("ab", "cd", (), 'e')) == ['a', 'b', 'c', 'd', 'e']
with assertRaises(TypeError):
list(chain(1))
x = chain("ab", 1)
assert next(x) == 'a'
assert next(x) == 'b'
with assertRaises(TypeError):
next(x)
# itertools.count tests
# default arguments
@@ -82,3 +100,86 @@ with assertRaises(StopIteration):
r = itertools.repeat(1, -1)
with assertRaises(StopIteration):
next(r)
# itertools.starmap tests
starmap = itertools.starmap
assert list(starmap(pow, zip(range(3), range(1,7)))) == [0**1, 1**2, 2**3]
assert list(starmap(pow, [])) == []
assert list(starmap(pow, [iter([4,5])])) == [4**5]
with assertRaises(TypeError):
starmap(pow)
# itertools.takewhile tests
from itertools import takewhile as tw
t = tw(lambda n: n < 5, [1, 2, 5, 1, 3])
assert next(t) == 1
assert next(t) == 2
with assertRaises(StopIteration):
next(t)
# not iterable
with assertRaises(TypeError):
tw(lambda n: n < 1, 1)
# not callable
t = tw(5, [1, 2])
with assertRaises(TypeError):
next(t)
# non-bool predicate
t = tw(lambda n: n, [1, 2, 0])
assert next(t) == 1
assert next(t) == 2
with assertRaises(StopIteration):
next(t)
# bad predicate prototype
t = tw(lambda: True, [1])
with assertRaises(TypeError):
next(t)
# StopIteration before attempting to call (bad) predicate
t = tw(lambda: True, [])
with assertRaises(StopIteration):
next(t)
# doesn't try again after the first predicate failure
t = tw(lambda n: n < 1, [1, 0])
with assertRaises(StopIteration):
next(t)
with assertRaises(StopIteration):
next(t)
# itertools.islice tests
def assert_matches_seq(it, seq):
assert list(it) == list(seq)
i = itertools.islice
it = i([1, 2, 3, 4, 5], 3)
assert_matches_seq(it, [1, 2, 3])
it = i([0.5, 1, 1.5, 2, 2.5, 3, 4, 5], 1, 6, 2)
assert_matches_seq(it, [1, 2, 3])
it = i([1, 2], None)
assert_matches_seq(it, [1, 2])
it = i([1, 2, 3], None, None, None)
assert_matches_seq(it, [1, 2, 3])
it = i([1, 2, 3], 1, None, None)
assert_matches_seq(it, [2, 3])
it = i([1, 2, 3], None, 2, None)
assert_matches_seq(it, [1, 2])
it = i([1, 2, 3], None, None, 3)
assert_matches_seq(it, [1])
+7
View File
@@ -0,0 +1,7 @@
import marshal
orig = compile("1 + 1", "", 'eval')
dumped = marshal.dumps(orig)
loaded = marshal.loads(dumped)
assert eval(loaded) == eval(orig)
+9
View File
@@ -49,6 +49,10 @@ else:
assert os.altsep == None
assert os.pathsep == ":"
assert os.fspath("Testing") == "Testing"
assert os.fspath(b"Testing") == b"Testing"
assert_raises(TypeError, lambda: os.fspath([1,2,3]))
class TestWithTempDir():
def __enter__(self):
if os.name == "nt":
@@ -217,3 +221,8 @@ with TestWithTempDir() as tmpdir:
os.chdir(tmpdir)
assert os.getcwd() == tmpdir
os.path.exists(FILE_NAME)
# supports
assert isinstance(os.supports_fd, set)
assert isinstance(os.supports_dir_fd, set)
assert isinstance(os.supports_follow_symlinks, set)
+10
View File
@@ -0,0 +1,10 @@
import types
from testutils import assertRaises
ns = types.SimpleNamespace(a=2, b='Rust')
assert ns.a == 2
assert ns.b == "Rust"
with assertRaises(AttributeError):
_ = ns.c
+15 -2
View File
@@ -37,10 +37,11 @@ assert 3 * "xy" == "xyxyxy"
assert 0 * "x" == ""
assert -1 * "x" == ""
assert_raises(OverflowError, lambda: 'xy' * 234234234234234234234234234234)
a = 'Hallo'
assert a.lower() == 'hallo'
assert a.upper() == 'HALLO'
assert a.split('al') == ['H', 'lo']
assert a.startswith('H')
assert a.startswith(('H', 1))
assert a.startswith(('A', 'H'))
@@ -59,7 +60,9 @@ assert not a.isnumeric()
assert a.istitle()
assert a.isalpha()
s = '1 2 3'
assert s.split(' ', 1) == ['1', '2 3']
assert s.rsplit(' ', 1) == ['1 2', '3']
b = ' hallo '
assert b.strip() == 'hallo'
@@ -203,3 +206,13 @@ def try_mutate_str():
word[0] = 'x'
assert_raises(TypeError, try_mutate_str)
ss = ['Hello', '안녕', '👋']
bs = [b'Hello', b'\xec\x95\x88\xeb\x85\x95', b'\xf0\x9f\x91\x8b']
for s, b in zip(ss, bs):
assert s.encode() == b
for s, b, e in zip(ss, bs, ['u8', 'U8', 'utf-8', 'UTF-8', 'utf_8']):
assert s.encode(e) == b
# assert s.encode(encoding=e) == b
+11
View File
@@ -7,3 +7,14 @@ assert sys.platform == "linux" or sys.platform == "darwin" or sys.platform == "w
assert isinstance(sys.builtin_module_names, tuple)
assert 'sys' in sys.builtin_module_names
assert isinstance(sys.implementation.name, str)
assert isinstance(sys.implementation.cache_tag, str)
assert sys.getfilesystemencoding() == 'utf-8'
assert sys.getfilesystemencodeerrors().startswith('surrogate')
assert isinstance(sys.flags, tuple)
assert type(sys.flags).__name__ == "flags"
assert type(sys.flags.optimize) is int
assert sys.flags[3] == sys.flags.optimize
+2
View File
@@ -11,3 +11,5 @@ print(mo)
# assert isinstance(mo, re.Match)
assert mo.start() == 1
assert mo.end() == 5
assert re.escape('python.exe') == 'python\\.exe'
+3
View File
@@ -0,0 +1,3 @@
import _warnings
_warnings.warn("Test")
+11 -4
View File
@@ -6,8 +6,8 @@ edition = "2018"
[dependencies]
bitflags = "1.0.4"
num-complex = "0.2"
num-bigint = "0.2.1"
num-complex = { version = "0.2", features = ["serde"] }
num-bigint = { version = "0.2.1", features = ["serde"] }
num-traits = "0.2"
num-integer = "0.1.39"
num-rational = "0.2.1"
@@ -15,8 +15,7 @@ rand = "0.5"
log = "0.3"
rustpython_derive = {path = "../derive"}
rustpython_parser = {path = "../parser"}
serde = "1.0.66"
serde_derive = "1.0.66"
serde = { version = "1.0.66", features = ["derive"] }
serde_json = "1.0.26"
byteorder = "1.2.6"
regex = "1"
@@ -28,8 +27,16 @@ unicode-xid = "0.1.0"
lazy_static = "^1.0.1"
lexical = "2.0.0"
itertools = "^0.8.0"
hexf = "0.1.0"
indexmap = "1.0.2"
crc = "^1.0.0"
bincode = "1.1.4"
# TODO: release and publish to crates.io
[dependencies.unicode-casing]
git = "https://github.com/OddCoincidence/unicode-casing"
rev = "90d6d1f02b9cc04ffb55a5f1c3fa1455a84231fb"
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
pwd = "1"
+21 -11
View File
@@ -111,15 +111,13 @@ fn builtin_delattr(obj: PyObjectRef, attr: PyStringRef, vm: &VirtualMachine) ->
vm.del_attr(&obj, attr.into_object())
}
fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
if args.args.is_empty() {
Ok(vm.get_locals().into_object())
} else {
let obj = args.args.into_iter().next().unwrap();
let seq = vm.call_method(&obj, "__dir__", vec![])?;
let sorted = builtin_sorted(vm, PyFuncArgs::new(vec![seq], vec![]))?;
Ok(sorted)
}
fn builtin_dir(obj: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
let seq = match obj {
OptionalArg::Present(obj) => vm.call_method(&obj, "__dir__", vec![])?,
OptionalArg::Missing => vm.call_method(&vm.get_locals().into_object(), "keys", vec![])?,
};
let sorted = builtin_sorted(vm, PyFuncArgs::new(vec![seq], vec![]))?;
Ok(sorted)
}
fn builtin_divmod(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
@@ -136,6 +134,7 @@ fn builtin_divmod(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
/// Implements `eval`.
/// See also: https://docs.python.org/3/library/functions.html#eval
fn builtin_eval(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
// TODO: support any mapping for `locals`
arg_check!(
vm,
args,
@@ -303,8 +302,7 @@ fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &VirtualMachine) ->
fn builtin_hash(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(obj, None)]);
vm.call_method(obj, "__hash__", vec![])
vm._hash(obj).and_then(|v| Ok(vm.new_int(v)))
}
// builtin_help
@@ -614,6 +612,16 @@ impl Printer for std::io::StdoutLock<'_> {
}
}
pub fn builtin_exit(exit_code_arg: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<()> {
if let OptionalArg::Present(exit_code_obj) = exit_code_arg {
match i32::try_from_object(&vm, exit_code_obj.clone()) {
Ok(code) => std::process::exit(code),
_ => println!("{}", vm.to_str(&exit_code_obj)?.as_str()),
}
}
std::process::exit(0);
}
pub fn builtin_print(objects: Args, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> {
let stdout = io::stdout();
@@ -824,6 +832,8 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
"tuple" => ctx.tuple_type(),
"type" => ctx.type_type(),
"zip" => ctx.zip_type(),
"exit" => ctx.new_rustfunc(builtin_exit),
"quit" => ctx.new_rustfunc(builtin_exit),
"__import__" => ctx.new_rustfunc(builtin_import),
// Constants
+12 -9
View File
@@ -8,14 +8,16 @@
use num_bigint::BigInt;
use num_complex::Complex64;
use rustpython_parser::ast;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fmt;
/// Primary container of a single code object. Each python function has
/// a codeobject. Also a module has a codeobject.
#[derive(Clone, PartialEq)]
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct CodeObject {
pub instructions: Vec<Instruction>,
/// Jump targets.
pub label_map: HashMap<Label, usize>,
pub locations: Vec<ast::Location>,
pub arg_names: Vec<String>, // Names of positional arguments
@@ -29,6 +31,7 @@ pub struct CodeObject {
}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct FunctionOpArg: u8 {
const HAS_DEFAULTS = 0x01;
const HAS_KW_ONLY_DEFAULTS = 0x02;
@@ -38,7 +41,7 @@ bitflags! {
pub type Label = usize;
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum NameScope {
Local,
NonLocal,
@@ -46,7 +49,7 @@ pub enum NameScope {
}
/// A Single bytecode instruction.
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Instruction {
Import {
name: String,
@@ -187,14 +190,14 @@ pub enum Instruction {
use self::Instruction::*;
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CallType {
Positional(usize),
Keyword(usize),
Ex(bool),
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Constant {
Integer { value: BigInt },
Float { value: f64 },
@@ -208,7 +211,7 @@ pub enum Constant {
Ellipsis,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ComparisonOperator {
Greater,
GreaterOrEqual,
@@ -222,7 +225,7 @@ pub enum ComparisonOperator {
IsNot,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BinaryOperator {
Power,
Multiply,
@@ -240,7 +243,7 @@ pub enum BinaryOperator {
Or,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum UnaryOperator {
Not,
Invert,
@@ -248,7 +251,7 @@ pub enum UnaryOperator {
Plus,
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Varargs {
None,
Unnamed,
+96 -38
View File
@@ -1,18 +1,25 @@
use crate::obj::objbool;
use crate::obj::objint;
use crate::pyhash;
use crate::pyobject::{IdProtocol, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
use num_traits::ToPrimitive;
/// Ordered dictionary implementation.
/// Inspired by: https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html
/// And: https://www.youtube.com/watch?v=p33CVV29OG8
/// And: http://code.activestate.com/recipes/578375/
use std::collections::HashMap;
use std::collections::{hash_map::DefaultHasher, HashMap};
use std::hash::{Hash, Hasher};
/// hash value of an object returned by __hash__
type HashValue = pyhash::PyHash;
/// index calculated by resolving collision
type HashIndex = pyhash::PyHash;
/// entry index mapped in indices
type EntryIndex = usize;
#[derive(Clone)]
pub struct Dict<T = PyObjectRef> {
size: usize,
indices: HashMap<usize, usize>,
indices: HashMap<HashIndex, EntryIndex>,
entries: Vec<Option<DictEntry<T>>>,
}
@@ -28,7 +35,7 @@ impl<T> Default for Dict<T> {
#[derive(Clone)]
struct DictEntry<T> {
hash: usize,
hash: HashValue,
key: PyObjectRef,
value: T,
}
@@ -40,12 +47,27 @@ pub struct DictSize {
}
impl<T: Clone> Dict<T> {
pub fn new() -> Self {
Dict {
size: 0,
indices: HashMap::new(),
entries: Vec::new(),
}
fn unchecked_push(
&mut self,
hash_index: HashIndex,
hash_value: HashValue,
key: &PyObjectRef,
value: T,
) {
let entry = DictEntry {
hash: hash_value,
key: key.clone(),
value,
};
let entry_index = self.entries.len();
self.entries.push(Some(entry));
self.indices.insert(hash_index, entry_index);
self.size += 1;
}
fn unchecked_delete(&mut self, entry_index: EntryIndex) {
self.entries[entry_index] = None;
self.size -= 1;
}
/// Store a key
@@ -65,29 +87,21 @@ impl<T: Clone> Dict<T> {
hash_value,
} => {
// New key:
let entry = DictEntry {
hash: hash_value,
key: key.clone(),
value,
};
let index = self.entries.len();
self.entries.push(Some(entry));
self.indices.insert(hash_index, index);
self.size += 1;
self.unchecked_push(hash_index, hash_value, key, value);
Ok(())
}
}
}
pub fn contains(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<bool> {
if let LookupResult::Existing(_index) = self.lookup(vm, key)? {
if let LookupResult::Existing(_) = self.lookup(vm, key)? {
Ok(true)
} else {
Ok(false)
}
}
fn unchecked_get(&self, index: usize) -> T {
fn unchecked_get(&self, index: EntryIndex) -> T {
if let Some(entry) = &self.entries[index] {
entry.value.clone()
} else {
@@ -112,9 +126,7 @@ impl<T: Clone> Dict<T> {
/// Delete a key
pub fn delete(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<()> {
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
self.entries[index] = None;
self.size -= 1;
if self.delete_if_exists(vm, key)? {
Ok(())
} else {
let key_repr = vm.to_pystr(key)?;
@@ -122,6 +134,31 @@ impl<T: Clone> Dict<T> {
}
}
pub fn delete_if_exists(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<bool> {
if let LookupResult::Existing(entry_index) = self.lookup(vm, key)? {
self.unchecked_delete(entry_index);
Ok(true)
} else {
Ok(false)
}
}
pub fn delete_or_insert(
&mut self,
vm: &VirtualMachine,
key: &PyObjectRef,
value: T,
) -> PyResult<()> {
match self.lookup(vm, &key)? {
LookupResult::Existing(entry_index) => self.unchecked_delete(entry_index),
LookupResult::NewIndex {
hash_value,
hash_index,
} => self.unchecked_push(hash_index, hash_value, &key, value),
};
Ok(())
}
pub fn len(&self) -> usize {
self.size
}
@@ -137,7 +174,7 @@ impl<T: Clone> Dict<T> {
}
}
pub fn next_entry(&self, position: &mut usize) -> Option<(&PyObjectRef, &T)> {
pub fn next_entry(&self, position: &mut EntryIndex) -> Option<(&PyObjectRef, &T)> {
while *position < self.entries.len() {
if let Some(DictEntry { key, value, .. }) = &self.entries[*position] {
*position += 1;
@@ -152,11 +189,19 @@ impl<T: Clone> Dict<T> {
position.size != self.size || self.entries.len() != position.entries_size
}
pub fn keys<'a>(&'a self) -> Box<Iterator<Item = PyObjectRef> + 'a> {
Box::new(
self.entries
.iter()
.filter_map(|v| v.as_ref().and_then(|v| Some(v.key.clone()))),
)
}
/// Lookup the index for the given key.
fn lookup(&self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<LookupResult> {
let hash_value = calc_hash(vm, key)?;
let hash_value = collection_hash(vm, key)?;
let perturb = hash_value;
let mut hash_index: usize = hash_value;
let mut hash_index: HashIndex = hash_value;
loop {
if self.indices.contains_key(&hash_index) {
// Now we have an index, lets check the key.
@@ -199,33 +244,46 @@ impl<T: Clone> Dict<T> {
pub fn pop(&mut self, vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<T> {
if let LookupResult::Existing(index) = self.lookup(vm, key)? {
let value = self.unchecked_get(index);
self.entries[index] = None;
self.size -= 1;
self.unchecked_delete(index);
Ok(value)
} else {
let key_repr = vm.to_pystr(key)?;
Err(vm.new_key_error(format!("Key not found: {}", key_repr)))
}
}
pub fn pop_front(&mut self) -> Option<(PyObjectRef, T)> {
let mut entry_index = 0;
match self.next_entry(&mut entry_index) {
Some((key, value)) => {
let item = (key.clone(), value.clone());
self.unchecked_delete(entry_index - 1);
Some(item)
}
None => None,
}
}
}
enum LookupResult {
NewIndex {
hash_value: usize,
hash_index: usize,
hash_value: HashValue,
hash_index: HashIndex,
}, // return not found, index into indices
Existing(usize), // Existing record, index into entries
Existing(EntryIndex), // Existing record, index into entries
}
fn calc_hash(vm: &VirtualMachine, key: &PyObjectRef) -> PyResult<usize> {
let hash = vm.call_method(key, "__hash__", vec![])?;
Ok(objint::get_value(&hash).to_usize().unwrap())
fn collection_hash(vm: &VirtualMachine, object: &PyObjectRef) -> PyResult<HashValue> {
let raw_hash = vm._hash(object)?;
let mut hasher = DefaultHasher::new();
raw_hash.hash(&mut hasher);
Ok(hasher.finish() as HashValue)
}
/// Invoke __eq__ on two keys
fn do_eq(vm: &VirtualMachine, key1: &PyObjectRef, key2: &PyObjectRef) -> Result<bool, PyObjectRef> {
let result = vm._eq(key1.clone(), key2.clone())?;
Ok(objbool::get_value(&result))
objbool::boolval(vm, result)
}
#[cfg(test)]
@@ -235,7 +293,7 @@ mod tests {
#[test]
fn test_insert() {
let mut vm = VirtualMachine::new();
let mut dict = Dict::new();
let mut dict = Dict::default();
assert_eq!(0, dict.len());
let key1 = vm.new_bool(true);
+70 -34
View File
@@ -4,13 +4,16 @@ use crate::obj::objtype;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{create_type, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol};
use crate::vm::VirtualMachine;
use std::fs::File;
use std::io::{BufRead, BufReader};
fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
let zelf = args.args[0].clone();
let msg = if args.args.len() > 1 {
args.args[1].clone()
} else {
vm.new_str("No msg".to_string())
let empty_string = String::default();
vm.new_str(empty_string)
};
let traceback = vm.ctx.new_list(Vec::new());
vm.set_attr(&zelf, "msg", msg)?;
@@ -18,7 +21,7 @@ fn exception_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.get_none())
}
// print excption chain
/// Print exception chain
pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
let mut had_cause = false;
if let Ok(cause) = vm.get_attribute(exc.clone(), "__cause__") {
@@ -39,41 +42,71 @@ pub fn print_exception(vm: &VirtualMachine, exc: &PyObjectRef) {
print_exception_inner(vm, exc)
}
// Print exception including traceback:
fn print_source_line(filename: String, lineno: usize) {
// TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/master/Python/traceback.c#L393
// TODO: support different encodings
let file = match File::open(filename) {
Ok(file) => file,
Err(_) => {
return;
}
};
let file = BufReader::new(file);
for (i, line) in file.lines().enumerate() {
if i + 1 == lineno {
if let Ok(line) = line {
// Indented with 4 spaces
println!(" {}", line.trim_start());
}
return;
}
}
}
/// Print exception occurrence location from traceback element
fn print_traceback_entry(vm: &VirtualMachine, tb_entry: &PyObjectRef) {
if objtype::isinstance(&tb_entry, &vm.ctx.tuple_type()) {
let location_attrs = objsequence::get_elements_tuple(&tb_entry);
let filename = if let Ok(x) = vm.to_str(&location_attrs[0]) {
x.value.clone()
} else {
"<error>".to_string()
};
let lineno = if let Ok(x) = vm.to_str(&location_attrs[1]) {
x.value.clone()
} else {
"<error>".to_string()
};
let obj_name = if let Ok(x) = vm.to_str(&location_attrs[2]) {
x.value.clone()
} else {
"<error>".to_string()
};
println!(
r##" File "{}", line {}, in {}"##,
filename, lineno, obj_name
);
print_source_line(filename, lineno.parse().unwrap());
} else {
println!(" File ??");
return;
}
}
/// Print exception with traceback
pub fn print_exception_inner(vm: &VirtualMachine, exc: &PyObjectRef) {
if let Ok(tb) = vm.get_attribute(exc.clone(), "__traceback__") {
println!("Traceback (most recent call last):");
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
let mut elements = objsequence::get_elements(&tb).to_vec();
elements.reverse();
for element in elements.iter() {
if objtype::isinstance(&element, &vm.ctx.tuple_type()) {
let element = objsequence::get_elements(&element);
let filename = if let Ok(x) = vm.to_str(&element[0]) {
x.value.clone()
} else {
"<error>".to_string()
};
let mut tb_entries = objsequence::get_elements_list(&tb).to_vec();
tb_entries.reverse();
let lineno = if let Ok(x) = vm.to_str(&element[1]) {
x.value.clone()
} else {
"<error>".to_string()
};
let obj_name = if let Ok(x) = vm.to_str(&element[2]) {
x.value.clone()
} else {
"<error>".to_string()
};
println!(
r##" File "{}", line {}, in {}"##,
filename, lineno, obj_name
);
} else {
println!(" File ??");
}
for exc_location in tb_entries.iter() {
print_traceback_entry(vm, exc_location);
}
}
} else {
@@ -100,8 +133,11 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
} else {
panic!("Error message must be set");
};
let s = format!("{}: {}", exc.class().name, msg);
Ok(vm.new_str(s))
let mut exc_repr = exc.class().name.clone();
if !msg.is_empty() {
&exc_repr.push_str(&format!(": {}", msg));
}
Ok(vm.new_str(exc_repr))
}
#[derive(Debug)]
+57 -56
View File
@@ -23,6 +23,7 @@ use crate::pyobject::{
TypeProtocol,
};
use crate::vm::VirtualMachine;
use indexmap::IndexMap;
use itertools::Itertools;
/*
@@ -123,15 +124,15 @@ impl Scope {
self.locals.iter().next().cloned()
}
pub fn child_scope_with_locals(&self, locals: PyDictRef) -> Scope {
pub fn new_child_scope_with_locals(&self, locals: PyDictRef) -> Scope {
Scope {
locals: self.locals.clone().insert(locals),
globals: self.globals.clone(),
}
}
pub fn child_scope(&self, ctx: &PyContext) -> Scope {
self.child_scope_with_locals(ctx.new_dict())
pub fn new_child_scope(&self, ctx: &PyContext) -> Scope {
self.new_child_scope_with_locals(ctx.new_dict())
}
}
@@ -172,8 +173,7 @@ impl NameProtocol for Scope {
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {
self.locals
.iter()
.skip(1)
.next()
.nth(1)
.expect("no outer scope for non-local")
.set_item(name, value, vm)
.unwrap();
@@ -243,7 +243,7 @@ pub enum ExecutionResult {
Yield(PyObjectRef),
}
// A valid execution result, or an exception
/// A valid execution result, or an exception
pub type FrameResult = Result<Option<ExecutionResult>, PyObjectRef>;
impl Frame {
@@ -285,9 +285,11 @@ impl Frame {
Ok(Some(value)) => {
break Ok(value);
}
// Instruction raised an exception
Err(exception) => {
// unwind block stack on exception and find any handlers.
// Add an entry in the traceback:
// 1. Extract traceback from exception's '__traceback__' attr.
// 2. Add new entry with current execution position (filename, lineno, code_object) to traceback.
// 3. Unwind block stack till appropriate handler is found.
assert!(objtype::isinstance(
&exception,
&vm.ctx.exceptions.base_exception_type
@@ -296,13 +298,12 @@ impl Frame {
.get_attribute(exception.clone(), "__traceback__")
.unwrap();
trace!("Adding to traceback: {:?} {:?}", traceback, lineno);
let pos = vm.ctx.new_tuple(vec![
let raise_location = vm.ctx.new_tuple(vec![
vm.ctx.new_str(filename.clone()),
vm.ctx.new_int(lineno.get_row()),
vm.ctx.new_str(run_obj_name.clone()),
]);
objlist::PyListRef::try_from_object(vm, traceback)?.append(pos, vm);
// exception.__trace
objlist::PyListRef::try_from_object(vm, traceback)?.append(raise_location, vm);
match self.unwind_exception(vm, exception) {
None => {}
Some(exception) => {
@@ -333,7 +334,7 @@ impl Frame {
ins2
}
// Execute a single instruction:
/// Execute a single instruction.
fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult {
let instruction = self.fetch_instruction();
{
@@ -565,9 +566,7 @@ impl Frame {
} = &block.typ
{
debug_assert!(end1 == end2);
// call exit now with no exception:
self.with_exit(vm, &context_manager, None)?;
self.call_context_manager_exit_no_exception(vm, &context_manager)?;
} else {
unreachable!("Block stack is incorrect, expected a with block");
}
@@ -650,22 +649,22 @@ impl Frame {
// pop argc arguments
// argument: name, args, globals
let scope = self.scope.clone();
let obj = vm
let func_obj = vm
.ctx
.new_function(code_obj, scope, defaults, kw_only_defaults);
let name = qualified_name.value.split('.').next_back().unwrap();
vm.set_attr(&obj, "__name__", vm.new_str(name.to_string()))?;
vm.set_attr(&obj, "__qualname__", qualified_name)?;
vm.set_attr(&func_obj, "__name__", vm.new_str(name.to_string()))?;
vm.set_attr(&func_obj, "__qualname__", qualified_name)?;
let module = self
.scope
.globals
.get_item_option("__name__", vm)?
.unwrap_or_else(|| vm.get_none());
vm.set_attr(&obj, "__module__", module)?;
vm.set_attr(&obj, "__annotations__", annotations)?;
vm.set_attr(&func_obj, "__module__", module)?;
vm.set_attr(&func_obj, "__annotations__", annotations)?;
self.push_value(obj);
self.push_value(func_obj);
Ok(None)
}
bytecode::Instruction::CallFunction { typ } => {
@@ -674,7 +673,7 @@ impl Frame {
let args: Vec<PyObjectRef> = self.pop_multiple(*count);
PyFuncArgs {
args,
kwargs: vec![],
kwargs: IndexMap::new(),
}
}
bytecode::CallType::Keyword(count) => {
@@ -697,7 +696,7 @@ impl Frame {
.map(|elem| (objstr::get_value(&elem.0), elem.1))
.collect()
} else {
vec![]
IndexMap::new()
};
let args = self.pop_value();
let args = vm.extract_elements(&args)?;
@@ -949,7 +948,7 @@ impl Frame {
BlockType::With {
context_manager, ..
} => {
match self.with_exit(vm, &context_manager, None) {
match self.call_context_manager_exit_no_exception(vm, &context_manager) {
Ok(..) => {}
Err(exc) => {
// __exit__ went wrong,
@@ -976,7 +975,7 @@ impl Frame {
}
BlockType::With {
context_manager, ..
} => match self.with_exit(vm, &context_manager, None) {
} => match self.call_context_manager_exit_no_exception(vm, &context_manager) {
Ok(..) => {}
Err(exc) => {
panic!("Exception in with __exit__ {:?}", exc);
@@ -1006,12 +1005,12 @@ impl Frame {
end,
context_manager,
} => {
match self.with_exit(vm, &context_manager, Some(exc.clone())) {
Ok(exit_action) => {
match objbool::boolval(vm, exit_action) {
Ok(handle_exception) => {
if handle_exception {
// We handle the exception, so return!
match self.call_context_manager_exit(vm, &context_manager, exc.clone()) {
Ok(exit_result_obj) => {
match objbool::boolval(vm, exit_result_obj) {
// If __exit__ method returned True, suppress the exception and continue execution.
Ok(suppress_exception) => {
if suppress_exception {
self.jump(end);
return None;
} else {
@@ -1022,7 +1021,6 @@ impl Frame {
return Some(exit_exc);
}
}
// if objtype::isinstance
}
Err(exit_exc) => {
// TODO: what about original exception?
@@ -1031,36 +1029,39 @@ impl Frame {
}
}
BlockType::Loop { .. } => {}
// Exception was already poped on Raised.
// Exception was already popped on Raised.
BlockType::ExceptHandler => {}
}
}
Some(exc)
}
fn with_exit(
fn call_context_manager_exit_no_exception(
&self,
vm: &VirtualMachine,
context_manager: &PyObjectRef,
exc: Option<PyObjectRef>,
) -> PyResult {
// Assume top of stack is __exit__ method:
// TODO: do we want to put the exit call on the stack?
// let exit_method = self.pop_value();
// let args = PyFuncArgs::default();
// TODO: what happens when we got an error during handling exception?
let args = if let Some(exc) = exc {
let exc_type = exc.class().into_object();
let exc_val = exc.clone();
let exc_tb = vm.ctx.none(); // TODO: retrieve traceback?
vec![exc_type, exc_val, exc_tb]
} else {
let exc_type = vm.ctx.none();
let exc_val = vm.ctx.none();
let exc_tb = vm.ctx.none();
vec![exc_type, exc_val, exc_tb]
};
vm.call_method(context_manager, "__exit__", args)
// TODO: what happens when we got an error during execution of __exit__?
vm.call_method(
context_manager,
"__exit__",
vec![vm.ctx.none(), vm.ctx.none(), vm.ctx.none()],
)
}
fn call_context_manager_exit(
&self,
vm: &VirtualMachine,
context_manager: &PyObjectRef,
exc: PyObjectRef,
) -> PyResult {
// TODO: do we want to put the exit call on the stack?
// TODO: what happens when we got an error during execution of __exit__?
let exc_type = exc.class().into_object();
let exc_val = exc.clone();
let exc_tb = vm.ctx.none(); // TODO: retrieve traceback?
vm.call_method(context_manager, "__exit__", vec![exc_type, exc_val, exc_tb])
}
fn store_name(
@@ -1131,7 +1132,7 @@ impl Frame {
fn jump(&self, label: bytecode::Label) {
let target_pc = self.code.label_map[&label];
trace!("program counter from {:?} to {:?}", self.lasti, target_pc);
trace!("jump from {:?} to {:?}", self.lasti, target_pc);
*self.lasti.borrow_mut() = target_pc;
}
@@ -1317,14 +1318,14 @@ impl Frame {
|| objtype::isinstance(&exception, &vm.ctx.exceptions.base_exception_type)
{
Ok(exception)
} else if let Ok(exception) = PyClassRef::try_from_object(vm, exception) {
if objtype::issubclass(&exception, &vm.ctx.exceptions.base_exception_type) {
let exception = vm.new_empty_exception(exception)?;
} else if let Ok(exc_type) = PyClassRef::try_from_object(vm, exception) {
if objtype::issubclass(&exc_type, &vm.ctx.exceptions.base_exception_type) {
let exception = vm.new_empty_exception(exc_type)?;
Ok(exception)
} else {
let msg = format!(
"Can only raise BaseException derived types, not {}",
exception
exc_type
);
Err(vm.new_type_error(msg))
}
+11
View File
@@ -0,0 +1,11 @@
use std::collections::hash_map::HashMap;
const HELLO: &str = "initialized = True
print(\"Hello world!\")
";
pub fn get_module_inits() -> HashMap<String, &'static str> {
let mut modules = HashMap::new();
modules.insert("__hello__".to_string(), HELLO);
modules
}
+38 -32
View File
@@ -2,6 +2,8 @@ use std::collections::HashMap;
use std::mem;
use std::ops::RangeInclusive;
use indexmap::IndexMap;
use crate::obj::objtuple::PyTuple;
use crate::obj::objtype::{isinstance, PyClassRef};
use crate::pyobject::{
@@ -17,7 +19,8 @@ use self::OptionalArg::*;
#[derive(Debug, Default, Clone)]
pub struct PyFuncArgs {
pub args: Vec<PyObjectRef>,
pub kwargs: Vec<(String, PyObjectRef)>,
// sorted map, according to https://www.python.org/dev/peps/pep-0468/
pub kwargs: IndexMap<String, PyObjectRef>,
}
/// Conversion from vector of python objects to function arguments.
@@ -25,7 +28,7 @@ impl From<Vec<PyObjectRef>> for PyFuncArgs {
fn from(args: Vec<PyObjectRef>) -> Self {
PyFuncArgs {
args,
kwargs: vec![],
kwargs: IndexMap::new(),
}
}
}
@@ -34,7 +37,7 @@ impl From<PyObjectRef> for PyFuncArgs {
fn from(arg: PyObjectRef) -> Self {
PyFuncArgs {
args: vec![arg],
kwargs: vec![],
kwargs: IndexMap::new(),
}
}
}
@@ -58,9 +61,12 @@ impl FromArgs for PyFuncArgs {
impl PyFuncArgs {
pub fn new(mut args: Vec<PyObjectRef>, kwarg_names: Vec<String>) -> PyFuncArgs {
let mut kwargs = vec![];
for name in kwarg_names.iter().rev() {
kwargs.push((name.clone(), args.pop().unwrap()));
// last `kwarg_names.len()` elements of args in order of appearance in the call signature
let kwarg_values = args.drain((args.len() - kwarg_names.len())..);
let mut kwargs = IndexMap::new();
for (name, value) in kwarg_names.iter().zip(kwarg_values) {
kwargs.insert(name.clone(), value);
}
PyFuncArgs { args, kwargs }
}
@@ -79,21 +85,14 @@ impl PyFuncArgs {
}
pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef {
for (arg_name, arg_value) in self.kwargs.iter() {
if arg_name == key {
return arg_value.clone();
}
}
default.clone()
self.kwargs
.get(key)
.cloned()
.unwrap_or_else(|| default.clone())
}
pub fn get_optional_kwarg(&self, key: &str) -> Option<PyObjectRef> {
for (arg_name, arg_value) in self.kwargs.iter() {
if arg_name == key {
return Some(arg_value.clone());
}
}
None
self.kwargs.get(key).cloned()
}
pub fn get_optional_kwarg_with_type(
@@ -132,19 +131,12 @@ impl PyFuncArgs {
}
pub fn take_keyword(&mut self, name: &str) -> Option<PyObjectRef> {
// TODO: change kwarg representation so this scan isn't necessary
if let Some(index) = self
.kwargs
.iter()
.position(|(arg_name, _)| arg_name == name)
{
Some(self.kwargs.remove(index).1)
} else {
None
}
self.kwargs.remove(name)
}
pub fn remaining_keyword<'a>(&'a mut self) -> impl Iterator<Item = (String, PyObjectRef)> + 'a {
pub fn remaining_keywords<'a>(
&'a mut self,
) -> impl Iterator<Item = (String, PyObjectRef)> + 'a {
self.kwargs.drain(..)
}
@@ -192,7 +184,10 @@ impl PyFuncArgs {
given_args,
)))
} else if !self.kwargs.is_empty() {
Err(vm.new_type_error(format!("Unexpected keyword argument {}", self.kwargs[0].0)))
Err(vm.new_type_error(format!(
"Unexpected keyword argument {}",
self.kwargs.keys().next().unwrap()
)))
} else {
Ok(bound)
}
@@ -264,7 +259,7 @@ where
{
fn from_args(vm: &VirtualMachine, args: &mut PyFuncArgs) -> Result<Self, ArgumentError> {
let mut kwargs = HashMap::new();
for (name, value) in args.remaining_keyword() {
for (name, value) in args.remaining_keywords() {
kwargs.insert(name, T::try_from_object(vm, value)?);
}
Ok(KwArgs(kwargs))
@@ -379,6 +374,17 @@ impl<T> OptionalArg<T> {
Missing => f(),
}
}
pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where
D: FnOnce() -> U,
F: FnOnce(T) -> U,
{
match self {
Present(value) => f(value),
Missing => default(),
}
}
}
impl<T> FromArgs for OptionalArg<T>
@@ -538,7 +544,7 @@ pub fn single_or_tuple_any<T: PyValue, F: Fn(PyRef<T>) -> PyResult<bool>>(
match_class!(obj,
obj @ T => predicate(obj),
tuple @ PyTuple => {
for obj in tuple.elements.borrow().iter() {
for obj in tuple.elements.iter() {
let inner_val = PyRef::<T>::try_from_object(vm, obj.clone())?;
if predicate(inner_val)? {
return Ok(true);
+3 -1
View File
@@ -18,6 +18,8 @@ pub fn import_module(vm: &VirtualMachine, current_path: PathBuf, module_name: &s
// First, see if we already loaded the module:
if let Ok(module) = sys_modules.get_item(module_name.to_string(), vm) {
Ok(module)
} else if let Some(frozen) = vm.frozen.borrow().get(module_name) {
import_file(vm, module_name, "frozen".to_string(), frozen.to_string())
} else if let Some(make_module_func) = vm.stdlib_inits.borrow().get(module_name) {
let module = make_module_func(vm);
sys_modules.set_item(module_name, module.clone(), vm)?;
@@ -67,7 +69,7 @@ pub fn import_file(
fn find_source(vm: &VirtualMachine, current_path: PathBuf, name: &str) -> Result<PathBuf, String> {
let sys_path = vm.get_attribute(vm.sys_module.clone(), "path").unwrap();
let mut paths: Vec<PathBuf> = objsequence::get_elements(&sys_path)
let mut paths: Vec<PathBuf> = objsequence::get_elements_list(&sys_path)
.iter()
.map(|item| PathBuf::from(objstr::get_value(item)))
.collect();
+2 -7
View File
@@ -20,13 +20,6 @@ extern crate lexical;
#[macro_use]
extern crate log;
// extern crate env_logger;
extern crate num_bigint;
extern crate num_complex;
extern crate num_integer;
extern crate num_traits;
extern crate serde;
extern crate serde_json;
extern crate statrs;
extern crate rustpython_parser;
#[macro_use]
@@ -52,9 +45,11 @@ pub mod eval;
mod exceptions;
pub mod format;
pub mod frame;
mod frozen;
pub mod function;
pub mod import;
pub mod obj;
mod pyhash;
pub mod pyobject;
pub mod stdlib;
mod symboltable;
+13
View File
@@ -157,6 +157,19 @@ macro_rules! extend_class {
}
}
#[macro_export]
macro_rules! py_namespace {
( $vm:expr, { $($name:expr => $value:expr),* $(,)* }) => {
{
let namespace = $vm.ctx.new_namespace();
$(
$vm.set_attr(&namespace, $name, $value).unwrap();
)*
namespace
}
}
}
/// Macro to match on the built-in class of a Python object.
///
/// Like `match`, `match_class!` must be exhaustive, so a default arm with
+1
View File
@@ -23,6 +23,7 @@ pub mod objmap;
pub mod objmappingproxy;
pub mod objmemory;
pub mod objmodule;
pub mod objnamespace;
pub mod objnone;
pub mod objobject;
pub mod objproperty;
+1
View File
@@ -19,6 +19,7 @@ impl TryFromObject for bool {
}
}
/// Convert Python bool into Rust bool.
pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<bool> {
Ok(if let Ok(f) = vm.get_method(obj.clone(), "__bool__") {
let bool_res = vm.invoke(f, PyFuncArgs::default())?;
+12 -2
View File
@@ -135,7 +135,7 @@ impl PyByteArrayRef {
}
#[pymethod(name = "__hash__")]
fn hash(self, vm: &VirtualMachine) -> PyResult {
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
Err(vm.new_type_error("unhashable type: bytearray".to_string()))
}
@@ -166,6 +166,16 @@ impl PyByteArrayRef {
self.inner.borrow().getitem(needle, vm)
}
#[pymethod(name = "__setitem__")]
fn setitem(
self,
needle: Either<PyIntRef, PySliceRef>,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
self.inner.borrow_mut().setitem(needle, value, vm)
}
#[pymethod(name = "isalnum")]
fn isalnum(self, vm: &VirtualMachine) -> PyResult {
self.inner.borrow().isalnum(vm)
@@ -330,7 +340,7 @@ impl PyByteArrayRef {
let pos = bytes
.iter()
.position(|b| *b == x)
.ok_or(vm.new_value_error("value not found in bytearray".to_string()))?;
.ok_or_else(|| vm.new_value_error("value not found in bytearray".to_string()))?;
bytes.remove(pos);
+105 -24
View File
@@ -2,6 +2,7 @@ use crate::obj::objint::PyIntRef;
use crate::obj::objnone::PyNoneRef;
use crate::obj::objslice::PySliceRef;
use crate::obj::objtuple::PyTupleRef;
use crate::pyhash;
use crate::pyobject::Either;
use crate::pyobject::PyRef;
use crate::pyobject::PyValue;
@@ -12,17 +13,12 @@ use core::ops::Range;
use num_bigint::BigInt;
use crate::function::OptionalArg;
use crate::vm::VirtualMachine;
use crate::pyobject::{PyResult, TypeProtocol};
use crate::obj::objstr::{PyString, PyStringRef};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use crate::vm::VirtualMachine;
use super::objint;
use super::objsequence::{is_valid_slice_arg, PySliceableSequence};
use super::objstr::{PyString, PyStringRef};
use crate::obj::objint::PyInt;
use num_integer::Integer;
@@ -78,24 +74,33 @@ pub struct ByteInnerNewOptions {
encoding: OptionalArg<PyStringRef>,
}
//same algorithm as cpython
pub fn normalize_encoding(encoding: &str) -> String {
let mut res = String::new();
let mut punct = false;
for c in encoding.chars() {
if c.is_alphanumeric() || c == '.' {
if punct && !res.is_empty() {
res.push('_')
}
res.push(c.to_ascii_lowercase());
punct = false;
} else {
punct = true;
}
}
res
}
impl ByteInnerNewOptions {
pub fn get_value(self, vm: &VirtualMachine) -> PyResult<PyByteInner> {
// First handle bytes(string, encoding[, errors])
if let OptionalArg::Present(enc) = self.encoding {
if let OptionalArg::Present(eval) = self.val_option {
if let Ok(input) = eval.downcast::<PyString>() {
let encoding = enc.as_str();
if encoding.to_lowercase() == "utf8" || encoding.to_lowercase() == "utf-8"
// TODO: different encoding
{
return Ok(PyByteInner {
elements: input.value.as_bytes().to_vec(),
});
} else {
return Err(
vm.new_value_error(format!("unknown encoding: {}", encoding)), //should be lookup error
);
}
let inner = PyByteInner::from_string(&input.value, enc.as_str(), vm)?;
return Ok(inner);
} else {
return Err(vm.new_type_error("encoding without a string argument".to_string()));
}
@@ -315,6 +320,20 @@ impl ByteInnerSplitlinesOptions {
}
impl PyByteInner {
pub fn from_string(value: &str, encoding: &str, vm: &VirtualMachine) -> PyResult<Self> {
let normalized = normalize_encoding(encoding);
if normalized == "utf_8" || normalized == "utf8" || normalized == "u8" {
Ok(PyByteInner {
elements: value.as_bytes().to_vec(),
})
} else {
// TODO: different encoding
Err(
vm.new_value_error(format!("unknown encoding: {}", encoding)), // should be lookup error
)
}
}
pub fn repr(&self) -> PyResult<String> {
let mut res = String::with_capacity(self.elements.len());
for i in self.elements.iter() {
@@ -379,10 +398,8 @@ impl PyByteInner {
}
}
pub fn hash(&self) -> usize {
let mut hasher = DefaultHasher::new();
self.elements.hash(&mut hasher);
hasher.finish() as usize
pub fn hash(&self) -> pyhash::PyHash {
pyhash::hash_value(&self.elements)
}
pub fn add(&self, other: PyByteInner) -> Vec<u8> {
@@ -432,6 +449,70 @@ impl PyByteInner {
}
}
fn setindex(&mut self, int: PyIntRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(idx) = self.elements.get_pos(int.as_bigint().to_i32().unwrap()) {
let result = match_class!(object,
i @ PyInt => {
if let Some(value) = i.as_bigint().to_u8() {
Ok(value)
}else{
Err(vm.new_value_error("byte must be in range(0, 256)".to_string()))
}
},
_ => {Err(vm.new_type_error("an integer is required".to_string()))}
);
let value = result?;
self.elements[idx] = value;
Ok(vm.new_int(value))
} else {
Err(vm.new_index_error("index out of range".to_string()))
}
}
fn setslice(
&mut self,
slice: PySliceRef,
object: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let sec = match PyIterable::try_from_object(vm, object.clone()) {
Ok(sec) => {
let items: Result<Vec<PyObjectRef>, _> = sec.iter(vm)?.collect();
Ok(items?
.into_iter()
.map(|obj| u8::try_from_object(vm, obj))
.collect::<PyResult<Vec<_>>>()?)
}
_ => match_class!(object,
i @ PyMemoryView => {
Ok(i.get_obj_value().unwrap())
},
_ => Err(vm.new_index_error(
"can assign only bytes, buffers, or iterables of ints in range(0, 256)"
.to_string()))),
};
let items = sec?;
let range = self
.elements
.get_slice_range(&slice.start_index(vm)?, &slice.stop_index(vm)?);
self.elements.splice(range, items);
Ok(vm
.ctx
.new_bytes(self.elements.get_slice_items(vm, slice.as_object())?))
}
pub fn setitem(
&mut self,
needle: Either<PyIntRef, PySliceRef>,
object: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
match needle {
Either::A(int) => self.setindex(int, object, vm),
Either::B(slice) => self.setslice(slice, object, vm),
}
}
pub fn isalnum(&self, vm: &VirtualMachine) -> PyResult {
Ok(vm.new_bool(
!self.elements.is_empty()
@@ -692,7 +773,7 @@ impl PyByteInner {
Either::A(byte) => byte.elements,
Either::B(tuple) => {
let mut flatten = vec![];
for v in objsequence::get_elements(tuple.as_object()).to_vec() {
for v in objsequence::get_elements_tuple(tuple.as_object()).to_vec() {
flatten.extend(PyByteInner::try_from_object(vm, v)?.elements)
}
flatten
+9 -2
View File
@@ -1,8 +1,8 @@
use crate::obj::objint::PyIntRef;
use crate::obj::objslice::PySliceRef;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtuple::PyTupleRef;
use crate::pyhash;
use crate::pyobject::Either;
use crate::vm::VirtualMachine;
@@ -45,6 +45,13 @@ impl PyBytes {
inner: PyByteInner { elements },
}
}
pub fn from_string(value: &str, encoding: &str, vm: &VirtualMachine) -> PyResult<Self> {
Ok(PyBytes {
inner: PyByteInner::from_string(value, encoding, vm)?,
})
}
pub fn get_value(&self) -> &[u8] {
&self.inner.elements
}
@@ -125,7 +132,7 @@ impl PyBytesRef {
}
#[pymethod(name = "__hash__")]
fn hash(self, _vm: &VirtualMachine) -> usize {
fn hash(self, _vm: &VirtualMachine) -> pyhash::PyHash {
self.inner.hash()
}
+1
View File
@@ -34,6 +34,7 @@ impl PyValue for PyCode {
}
impl PyCodeRef {
#[allow(clippy::new_ret_no_self)]
fn new(_cls: PyClassRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error("Cannot directly create code object".to_string()))
}
+11 -2
View File
@@ -2,6 +2,7 @@ use num_complex::Complex64;
use num_traits::Zero;
use crate::function::OptionalArg;
use crate::pyhash;
use crate::pyobject::{
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
};
@@ -125,12 +126,12 @@ impl PyComplex {
#[pymethod(name = "__float__")]
fn float(&self, vm: &VirtualMachine) -> PyResult {
return Err(vm.new_type_error(String::from("Can't convert complex to float")));
Err(vm.new_type_error(String::from("Can't convert complex to float")))
}
#[pymethod(name = "__int__")]
fn int(&self, vm: &VirtualMachine) -> PyResult {
return Err(vm.new_type_error(String::from("Can't convert complex to int")));
Err(vm.new_type_error(String::from("Can't convert complex to int")))
}
#[pymethod(name = "__mul__")]
@@ -248,4 +249,12 @@ impl PyComplex {
let value = Complex64::new(real, imag);
PyComplex { value }.into_ref_with_type(vm, cls)
}
#[pymethod(name = "__hash__")]
fn hash(&self, _vm: &VirtualMachine) -> pyhash::PyHash {
let re_hash = pyhash::hash_float(self.value.re);
let im_hash = pyhash::hash_float(self.value.im);
re_hash + im_hash * pyhash::IMAG
}
}
+6 -10
View File
@@ -264,15 +264,11 @@ impl PyDictRef {
fn popitem(self, vm: &VirtualMachine) -> PyResult {
let mut entries = self.entries.borrow_mut();
let (key, value) = match entries.next_entry(&mut 0) {
Some((key, value)) => (key.clone(), value.clone()),
None => {
return Err(vm.new_key_error("popitem(): dictionary is empty".to_string()));
}
};
entries.delete(vm, &key)?;
Ok(vm.ctx.new_tuple(vec![key, value]))
if let Some((key, value)) = entries.pop_front() {
Ok(vm.ctx.new_tuple(vec![key, value]))
} else {
Err(vm.new_key_error("popitem(): dictionary is empty".to_string()))
}
}
/// Take a python dictionary and convert it to attributes.
@@ -298,7 +294,7 @@ impl PyDictRef {
Ok(PyDict { entries }.into_ref(vm))
}
fn hash(self, vm: &VirtualMachine) -> PyResult {
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
Err(vm.new_type_error("unhashable type".to_string()))
}
+78 -1
View File
@@ -3,15 +3,18 @@ use super::objint;
use super::objstr;
use super::objtype;
use crate::function::OptionalArg;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::pyhash;
use crate::pyobject::{
IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TypeProtocol,
};
use crate::vm::VirtualMachine;
use hexf;
use num_bigint::{BigInt, ToBigInt};
use num_rational::Ratio;
use num_traits::{ToPrimitive, Zero};
use num_traits::{float::Float, ToPrimitive, Zero};
/// Convert a string or number to a floating point number, if possible.
#[pyclass(name = "float")]
@@ -297,6 +300,11 @@ impl PyFloat {
)
}
#[pymethod(name = "__pos__")]
fn pos(&self, _vm: &VirtualMachine) -> f64 {
self.value
}
#[pymethod(name = "__neg__")]
fn neg(&self, _vm: &VirtualMachine) -> f64 {
-self.value
@@ -429,6 +437,11 @@ impl PyFloat {
zelf
}
#[pymethod(name = "__hash__")]
fn hash(&self, _vm: &VirtualMachine) -> pyhash::PyHash {
pyhash::hash_float(self.value)
}
#[pyproperty(name = "real")]
fn real(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyFloatRef {
zelf
@@ -467,6 +480,70 @@ impl PyFloat {
let denom = vm.ctx.new_int(ratio.denom().clone());
Ok(vm.ctx.new_tuple(vec![numer, denom]))
}
#[pymethod]
fn fromhex(repr: PyStringRef, vm: &VirtualMachine) -> PyResult<f64> {
hexf::parse_hexf64(&repr.value, false).or_else(|_| match repr.value.as_ref() {
"nan" => Ok(std::f64::NAN),
"inf" => Ok(std::f64::INFINITY),
"-inf" => Ok(std::f64::NEG_INFINITY),
_ => Err(vm.new_value_error("invalid hexadecimal floating-point string".to_string())),
})
}
#[pymethod]
fn hex(&self, _vm: &VirtualMachine) -> String {
to_hex(self.value)
}
}
fn to_hex(value: f64) -> String {
let (mantissa, exponent, sign) = value.integer_decode();
let sign_fmt = if sign < 0 { "-" } else { "" };
match value {
value if value.is_zero() => format!("{}0x0.0p+0", sign_fmt),
value if value.is_infinite() => format!("{}inf", sign_fmt),
value if value.is_nan() => "nan".to_string(),
_ => {
const BITS: i16 = 52;
const FRACT_MASK: u64 = 0xf_ffff_ffff_ffff;
format!(
"{}0x{:x}.{:013x}p{:+}",
sign_fmt,
mantissa >> BITS,
mantissa & FRACT_MASK,
exponent + BITS
)
}
}
}
#[test]
fn test_to_hex() {
use rand::Rng;
for _ in 0..20000 {
let bytes = rand::thread_rng().gen::<[u64; 1]>();
let f = f64::from_bits(bytes[0]);
if !f.is_finite() {
continue;
}
let hex = to_hex(f);
// println!("{} -> {}", f, hex);
let roundtrip = hexf::parse_hexf64(&hex, false).unwrap();
// println!(" -> {}", roundtrip);
assert!(f == roundtrip, "{} {} {}", f, hex, roundtrip);
}
}
pub fn ufrexp(value: f64) -> (f64, i32) {
if 0.0 == value {
(0.0, 0i32)
} else {
let bits = value.to_bits();
let exponent: i32 = ((bits >> 52) & 0x7ff) as i32 - 1022;
let mantissa_bits = bits & (0x000f_ffff_ffff_ffff) | (1022 << 52);
(f64::from_bits(mantissa_bits), exponent)
}
}
pub type PyFloatRef = PyRef<PyFloat>;
+1
View File
@@ -18,6 +18,7 @@ pub fn init(context: &PyContext) {
}
impl FrameRef {
#[allow(clippy::new_ret_no_self)]
fn new(_class: FrameRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error("Cannot directly create frame object".to_string()))
}
+80 -7
View File
@@ -1,5 +1,4 @@
use std::fmt;
use std::hash::{Hash, Hasher};
use num_bigint::{BigInt, Sign};
use num_integer::Integer;
@@ -7,6 +6,7 @@ use num_traits::{One, Pow, Signed, ToPrimitive, Zero};
use crate::format::FormatSpec;
use crate::function::{KwArgs, OptionalArg, PyFuncArgs};
use crate::pyhash;
use crate::pyobject::{
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
@@ -14,6 +14,7 @@ use crate::pyobject::{
use crate::vm::VirtualMachine;
use super::objbyteinner::PyByteInner;
use super::objbytes::PyBytes;
use super::objstr::{PyString, PyStringRef};
use super::objtype;
use crate::obj::objtype::PyClassRef;
@@ -111,8 +112,9 @@ impl_try_from_object_int!(
(u64, to_u64),
);
#[allow(clippy::collapsible_if)]
fn inner_pow(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {
Ok(if int2.value.is_negative() {
let result = if int2.value.is_negative() {
let v1 = int1.float(vm)?;
let v2 = int2.float(vm)?;
vm.ctx.new_float(v1.pow(v2))
@@ -132,7 +134,8 @@ fn inner_pow(int1: &PyInt, int2: &PyInt, vm: &VirtualMachine) -> PyResult {
// practically, exp over u64 is not possible to calculate anyway
vm.ctx.not_implemented()
}
})
};
Ok(result)
}
#[pyimpl]
@@ -400,10 +403,11 @@ impl PyInt {
}
#[pymethod(name = "__hash__")]
fn hash(&self, _vm: &VirtualMachine) -> u64 {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.value.hash(&mut hasher);
hasher.finish()
pub fn hash(&self, _vm: &VirtualMachine) -> pyhash::PyHash {
match self.value.to_i64() {
Some(value) => (value % pyhash::MODULUS as i64),
None => (&self.value % pyhash::MODULUS).to_i64().unwrap(),
}
}
#[pymethod(name = "__abs__")]
@@ -492,6 +496,7 @@ impl PyInt {
}
#[pymethod]
#[allow(clippy::match_bool)]
fn from_bytes(
bytes: PyByteInner,
byteorder: PyStringRef,
@@ -526,7 +531,75 @@ impl PyInt {
}
Ok(x)
}
#[pymethod]
#[allow(clippy::match_bool)]
fn to_bytes(
&self,
length: PyIntRef,
byteorder: PyStringRef,
kwargs: KwArgs,
vm: &VirtualMachine,
) -> PyResult<PyBytes> {
let mut signed = false;
let value = self.as_bigint();
for (key, value) in kwargs.into_iter() {
if key == "signed" {
signed = match_class!(value,
b @ PyInt => !b.as_bigint().is_zero(),
_ => false,
);
}
}
if value.sign() == Sign::Minus && !signed {
return Err(vm.new_overflow_error("can't convert negative int to unsigned".to_string()));
}
let byte_len;
if let Some(temp) = length.as_bigint().to_usize() {
byte_len = temp;
} else {
return Err(vm.new_value_error("length parameter is illegal".to_string()));
}
let mut origin_bytes = match byteorder.value.as_str() {
"big" => match signed {
true => value.to_signed_bytes_be(),
false => value.to_bytes_be().1,
},
"little" => match signed {
true => value.to_signed_bytes_le(),
false => value.to_bytes_le().1,
},
_ => {
return Err(
vm.new_value_error("byteorder must be either 'little' or 'big'".to_string())
);
}
};
let origin_len = origin_bytes.len();
if origin_len > byte_len {
return Err(vm.new_value_error("int too big to convert".to_string()));
}
let mut append_bytes = match value.sign() {
Sign::Minus => vec![255u8; byte_len - origin_len],
_ => vec![0u8; byte_len - origin_len],
};
let mut bytes = vec![];
match byteorder.value.as_str() {
"big" => {
bytes = append_bytes;
bytes.append(&mut origin_bytes);
}
"little" => {
bytes = origin_bytes;
bytes.append(&mut append_bytes);
}
_ => (),
}
Ok(PyBytes::new(bytes))
}
#[pyproperty]
fn real(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyIntRef {
zelf
+10 -10
View File
@@ -17,8 +17,8 @@ use super::objbool;
//use super::objint;
use super::objiter;
use super::objsequence::{
get_elements, get_elements_cell, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
SequenceIndex,
get_elements_cell, get_elements_list, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt,
seq_mul, SequenceIndex,
};
use super::objslice::PySliceRef;
use super::objtype;
@@ -129,7 +129,7 @@ impl PyListRef {
fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let e1 = self.elements.borrow();
let e2 = get_elements(&other);
let e2 = get_elements_list(&other);
let elements = e1.iter().chain(e2.iter()).cloned().collect();
Ok(vm.ctx.new_list(elements))
} else {
@@ -141,7 +141,7 @@ impl PyListRef {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
self.elements
.borrow_mut()
.extend_from_slice(&get_elements(&other));
.extend_from_slice(&get_elements_list(&other));
Ok(self.into_object())
} else {
Ok(vm.ctx.not_implemented())
@@ -387,7 +387,7 @@ impl PyListRef {
Ok(s)
}
fn hash(self, vm: &VirtualMachine) -> PyResult {
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
Err(vm.new_type_error("unhashable type".to_string()))
}
@@ -490,7 +490,7 @@ impl PyListRef {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let other = get_elements_list(&other);
let res = seq_equal(vm, &zelf, &other)?;
Ok(vm.new_bool(res))
} else {
@@ -501,7 +501,7 @@ impl PyListRef {
fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let other = get_elements_list(&other);
let res = seq_lt(vm, &zelf, &other)?;
Ok(vm.new_bool(res))
} else {
@@ -512,7 +512,7 @@ impl PyListRef {
fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let other = get_elements_list(&other);
let res = seq_gt(vm, &zelf, &other)?;
Ok(vm.new_bool(res))
} else {
@@ -523,7 +523,7 @@ impl PyListRef {
fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let other = get_elements_list(&other);
let res = seq_ge(vm, &zelf, &other)?;
Ok(vm.new_bool(res))
} else {
@@ -534,7 +534,7 @@ impl PyListRef {
fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.list_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let other = get_elements_list(&other);
let res = seq_le(vm, &zelf, &other)?;
Ok(vm.new_bool(res))
} else {
+27 -20
View File
@@ -1,18 +1,37 @@
use crate::obj::objbyteinner::try_as_byte;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
#[pyclass(name = "memoryview")]
#[derive(Debug)]
pub struct PyMemoryView {
obj_ref: PyObjectRef,
}
pub type PyMemoryViewRef = PyRef<PyMemoryView>;
#[derive(Debug)]
pub struct PyMemoryView {
obj: PyObjectRef,
}
#[pyimpl]
impl PyMemoryView {
pub fn get_obj_value(&self) -> Option<Vec<u8>> {
try_as_byte(&self.obj)
try_as_byte(&self.obj_ref)
}
#[pymethod(name = "__new__")]
fn new(
cls: PyClassRef,
bytes_object: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyMemoryViewRef> {
PyMemoryView {
obj_ref: bytes_object.clone(),
}
.into_ref_with_type(vm, cls)
}
#[pyproperty]
fn obj(&self, __vm: &VirtualMachine) -> PyObjectRef {
self.obj_ref.clone()
}
}
@@ -22,18 +41,6 @@ impl PyValue for PyMemoryView {
}
}
pub fn new_memory_view(
cls: PyClassRef,
bytes_object: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyMemoryViewRef> {
vm.set_attr(cls.as_object(), "obj", bytes_object.clone())?;
PyMemoryView { obj: bytes_object }.into_ref_with_type(vm, cls)
}
pub fn init(ctx: &PyContext) {
let memoryview_type = &ctx.memoryview_type;
extend_class!(ctx, memoryview_type, {
"__new__" => ctx.new_rustfunc(new_memory_view)
});
PyMemoryView::extend_class(ctx, &ctx.memoryview_type)
}
+32
View File
@@ -0,0 +1,32 @@
use crate::function::KwArgs;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyClassImpl, PyContext, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
/// A simple attribute-based namespace.
///
/// SimpleNamespace(**kwargs)
#[pyclass(name = "SimpleNamespace")]
#[derive(Debug)]
pub struct PyNamespace;
impl PyValue for PyNamespace {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.ctx.namespace_type()
}
}
#[pyimpl]
impl PyNamespace {
#[pymethod(name = "__init__")]
fn init(zelf: PyRef<Self>, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult<()> {
for (name, value) in kwargs.into_iter() {
vm.set_attr(zelf.as_object(), name, value)?;
}
Ok(())
}
}
pub fn init(context: &PyContext) {
PyNamespace::extend_class(context, &context.namespace_type);
}
+3 -2
View File
@@ -5,6 +5,7 @@ use super::objtype;
use crate::function::PyFuncArgs;
use crate::obj::objproperty::PropertyBuilder;
use crate::obj::objtype::PyClassRef;
use crate::pyhash;
use crate::pyobject::{
IdProtocol, ItemProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyResult, PyValue,
TryFromObject, TypeProtocol,
@@ -55,8 +56,8 @@ fn object_ge(_zelf: PyObjectRef, _other: PyObjectRef, vm: &VirtualMachine) -> Py
vm.ctx.not_implemented()
}
fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> u64 {
zelf.get_id() as u64
fn object_hash(zelf: PyObjectRef, _vm: &VirtualMachine) -> pyhash::PyHash {
zelf.get_id() as pyhash::PyHash
}
fn object_setattr(
+8 -10
View File
@@ -348,18 +348,19 @@ pub fn get_elements_cell<'a>(obj: &'a PyObjectRef) -> &'a RefCell<Vec<PyObjectRe
if let Some(list) = obj.payload::<PyList>() {
return &list.elements;
}
if let Some(tuple) = obj.payload::<PyTuple>() {
return &tuple.elements;
panic!("Cannot extract elements from non-sequence");
}
pub fn get_elements_list<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<PyObjectRef>> + 'a {
if let Some(list) = obj.payload::<PyList>() {
return list.elements.borrow();
}
panic!("Cannot extract elements from non-sequence");
}
pub fn get_elements<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<PyObjectRef>> + 'a {
if let Some(list) = obj.payload::<PyList>() {
return list.elements.borrow();
}
pub fn get_elements_tuple<'a>(obj: &'a PyObjectRef) -> impl Deref<Target = Vec<PyObjectRef>> + 'a {
if let Some(tuple) = obj.payload::<PyTuple>() {
return tuple.elements.borrow();
return &tuple.elements;
}
panic!("Cannot extract elements from non-sequence");
}
@@ -368,9 +369,6 @@ pub fn get_mut_elements<'a>(obj: &'a PyObjectRef) -> impl DerefMut<Target = Vec<
if let Some(list) = obj.payload::<PyList>() {
return list.elements.borrow_mut();
}
if let Some(tuple) = obj.payload::<PyTuple>() {
return tuple.elements.borrow_mut();
}
panic!("Cannot extract elements from non-sequence");
}
+333 -425
View File
File diff suppressed because it is too large. Load diff
+50 -28
View File
@@ -1,7 +1,6 @@
extern crate unicode_xid;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Range;
use std::str::FromStr;
use std::string::ToString;
@@ -13,12 +12,14 @@ use unicode_xid::UnicodeXID;
use crate::format::{FormatParseError, FormatPart, FormatString};
use crate::function::{single_or_tuple_any, OptionalArg, PyFuncArgs};
use crate::pyhash;
use crate::pyobject::{
IdProtocol, IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef,
PyResult, PyValue, TryFromObject, TryIntoRef, TypeProtocol,
};
use crate::vm::VirtualMachine;
use super::objbytes::PyBytes;
use super::objdict::PyDict;
use super::objint::{self, PyInt};
use super::objnone::PyNone;
@@ -172,10 +173,8 @@ impl PyString {
}
#[pymethod(name = "__hash__")]
fn hash(&self, _vm: &VirtualMachine) -> usize {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.value.hash(&mut hasher);
hasher.finish() as usize
fn hash(&self, _vm: &VirtualMachine) -> pyhash::PyHash {
pyhash::hash_value(&self.value)
}
#[pymethod(name = "__len__")]
@@ -185,22 +184,17 @@ impl PyString {
#[pymethod(name = "__mul__")]
fn mul(&self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
if objtype::isinstance(&val, &vm.ctx.int_type()) {
let value = &self.value;
let multiplier = objint::get_value(&val).to_i32().unwrap();
let capacity = if multiplier > 0 {
multiplier.to_usize().unwrap() * value.len()
} else {
0
};
let mut result = String::with_capacity(capacity);
for _x in 0..multiplier {
result.push_str(value.as_str());
}
Ok(result)
} else {
Err(vm.new_type_error(format!("Cannot multiply {} and {}", self, val)))
if !objtype::isinstance(&val, &vm.ctx.int_type()) {
return Err(vm.new_type_error(format!("Cannot multiply {} and {}", self, val)));
}
objint::get_value(&val)
.to_isize()
.map(|multiplier| multiplier.max(0))
.and_then(|multiplier| multiplier.to_usize())
.map(|multiplier| self.value.repeat(multiplier))
.ok_or_else(|| {
vm.new_overflow_error("cannot fit 'int' into an index-sized integer".to_string())
})
}
#[pymethod(name = "__rmul__")]
@@ -303,10 +297,13 @@ impl PyString {
let num_splits = num
.into_option()
.unwrap_or_else(|| value.split(pattern).count());
let elements = value
let mut elements: Vec<_> = value
.rsplitn(num_splits + 1, pattern)
.map(|o| vm.ctx.new_str(o.to_string()))
.collect();
// Unlike Python rsplit, Rust rsplitn returns an iterator that
// starts from the end of the string.
elements.reverse();
vm.ctx.new_list(elements)
}
@@ -866,7 +863,7 @@ impl PyString {
None => false,
};
// a string is not an identifier if it has whitespace or starts with a number
is_identifier_start && chars.all(|c| UnicodeXID::is_xid_continue(c))
is_identifier_start && chars.all(UnicodeXID::is_xid_continue)
}
// https://docs.python.org/3/library/stdtypes.html#str.translate
@@ -879,18 +876,18 @@ impl PyString {
match table.get_item(c as u32, vm) {
Ok(value) => {
if let Some(text) = value.payload::<PyString>() {
translated.extend(text.value.chars());
} else if let Some(_) = value.payload::<PyNone>() {
// Do Nothing
translated.push_str(&text.value);
} else if let Some(bigint) = value.payload::<PyInt>() {
match bigint.as_bigint().to_u32().and_then(std::char::from_u32) {
Some(ch) => translated.push(ch as char),
None => {
return Err(vm.new_value_error(format!(
"character mapping must be in range(0x110000)"
)));
return Err(vm.new_value_error(
"character mapping must be in range(0x110000)".to_owned(),
));
}
}
} else if let Some(_) = value.payload::<PyNone>() {
// Do Nothing
} else {
return Err(vm.new_type_error(
"character mapping must return integer, None or str".to_owned(),
@@ -961,6 +958,31 @@ impl PyString {
}
}
}
#[pymethod]
fn encode(
&self,
encoding: OptionalArg<PyObjectRef>,
_errors: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult {
let encoding = encoding.map_or_else(
|| Ok("utf-8".to_string()),
|v| {
if objtype::isinstance(&v, &vm.ctx.str_type()) {
Ok(get_value(&v))
} else {
Err(vm.new_type_error(format!(
"encode() argument 1 must be str, not {}",
v.class().name
)))
}
},
)?;
let encoded = PyBytes::from_string(&self.value, &encoding, vm)?;
Ok(encoded.into_pyobject(vm)?)
}
}
impl PyValue for PyString {
+35 -50
View File
@@ -1,23 +1,21 @@
use std::cell::{Cell, RefCell};
use std::cell::Cell;
use std::fmt;
use std::hash::{Hash, Hasher};
use crate::function::OptionalArg;
use crate::pyhash;
use crate::pyobject::{IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::{ReprGuard, VirtualMachine};
use super::objbool;
use super::objint;
use super::objiter;
use super::objsequence::{
get_elements, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
get_elements_tuple, get_item, seq_equal, seq_ge, seq_gt, seq_le, seq_lt, seq_mul,
};
use super::objtype::{self, PyClassRef};
pub struct PyTuple {
// TODO: shouldn't be public
// TODO: tuples are immutable, remove this RefCell
pub elements: RefCell<Vec<PyObjectRef>>,
pub elements: Vec<PyObjectRef>,
}
impl fmt::Debug for PyTuple {
@@ -29,9 +27,7 @@ impl fmt::Debug for PyTuple {
impl From<Vec<PyObjectRef>> for PyTuple {
fn from(elements: Vec<PyObjectRef>) -> Self {
PyTuple {
elements: RefCell::new(elements),
}
PyTuple { elements }
}
}
@@ -41,14 +37,19 @@ impl PyValue for PyTuple {
}
}
impl PyTuple {
pub fn fast_getitem(&self, idx: usize) -> PyObjectRef {
self.elements[idx].clone()
}
}
pub type PyTupleRef = PyRef<PyTuple>;
impl PyTupleRef {
fn lt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let res = seq_lt(vm, &zelf, &other)?;
let other = get_elements_tuple(&other);
let res = seq_lt(vm, &self.elements, &other)?;
Ok(vm.new_bool(res))
} else {
Ok(vm.ctx.not_implemented())
@@ -57,9 +58,8 @@ impl PyTupleRef {
fn gt(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let res = seq_gt(vm, &zelf, &other)?;
let other = get_elements_tuple(&other);
let res = seq_gt(vm, &self.elements, &other)?;
Ok(vm.new_bool(res))
} else {
Ok(vm.ctx.not_implemented())
@@ -68,9 +68,8 @@ impl PyTupleRef {
fn ge(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let res = seq_ge(vm, &zelf, &other)?;
let other = get_elements_tuple(&other);
let res = seq_ge(vm, &self.elements, &other)?;
Ok(vm.new_bool(res))
} else {
Ok(vm.ctx.not_implemented())
@@ -79,9 +78,8 @@ impl PyTupleRef {
fn le(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let zelf = self.elements.borrow();
let other = get_elements(&other);
let res = seq_le(vm, &zelf, &other)?;
let other = get_elements_tuple(&other);
let res = seq_le(vm, &self.elements, &other)?;
Ok(vm.new_bool(res))
} else {
Ok(vm.ctx.not_implemented())
@@ -90,9 +88,8 @@ impl PyTupleRef {
fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let e1 = self.elements.borrow();
let e2 = get_elements(&other);
let elements = e1.iter().chain(e2.iter()).cloned().collect();
let e2 = get_elements_tuple(&other);
let elements = self.elements.iter().chain(e2.iter()).cloned().collect();
Ok(vm.ctx.new_tuple(elements))
} else {
Ok(vm.ctx.not_implemented())
@@ -100,12 +97,12 @@ impl PyTupleRef {
}
fn bool(self, _vm: &VirtualMachine) -> bool {
!self.elements.borrow().is_empty()
!self.elements.is_empty()
}
fn count(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
let mut count: usize = 0;
for element in self.elements.borrow().iter() {
for element in self.elements.iter() {
if element.is(&needle) {
count += 1;
} else {
@@ -120,23 +117,16 @@ impl PyTupleRef {
fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::isinstance(&other, &vm.ctx.tuple_type()) {
let zelf = &self.elements.borrow();
let other = get_elements(&other);
let res = seq_equal(vm, &zelf, &other)?;
let other = get_elements_tuple(&other);
let res = seq_equal(vm, &self.elements, &other)?;
Ok(vm.new_bool(res))
} else {
Ok(vm.ctx.not_implemented())
}
}
fn hash(self, vm: &VirtualMachine) -> PyResult<u64> {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for element in self.elements.borrow().iter() {
let hash_result = vm.call_method(element, "__hash__", vec![])?;
let element_hash = objint::get_value(&hash_result);
element_hash.hash(&mut hasher);
}
Ok(hasher.finish())
fn hash(self, vm: &VirtualMachine) -> PyResult<pyhash::PyHash> {
pyhash::hash_iter(self.elements.iter(), vm)
}
fn iter(self, _vm: &VirtualMachine) -> PyTupleIterator {
@@ -147,13 +137,13 @@ impl PyTupleRef {
}
fn len(self, _vm: &VirtualMachine) -> usize {
self.elements.borrow().len()
self.elements.len()
}
fn repr(self, vm: &VirtualMachine) -> PyResult<String> {
let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) {
let mut str_parts = vec![];
for elem in self.elements.borrow().iter() {
for elem in self.elements.iter() {
let s = vm.to_repr(elem)?;
str_parts.push(s.value.clone());
}
@@ -170,21 +160,16 @@ impl PyTupleRef {
}
fn mul(self, counter: isize, vm: &VirtualMachine) -> PyObjectRef {
let new_elements = seq_mul(&self.elements.borrow(), counter);
let new_elements = seq_mul(&self.elements, counter);
vm.ctx.new_tuple(new_elements)
}
fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
get_item(
vm,
self.as_object(),
&self.elements.borrow(),
needle.clone(),
)
get_item(vm, self.as_object(), &self.elements, needle.clone())
}
fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
for (index, element) in self.elements.borrow().iter().enumerate() {
for (index, element) in self.elements.iter().enumerate() {
if element.is(&needle) {
return Ok(index);
}
@@ -197,7 +182,7 @@ impl PyTupleRef {
}
fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
for element in self.elements.borrow().iter() {
for element in self.elements.iter() {
if element.is(&needle) {
return Ok(true);
}
@@ -241,8 +226,8 @@ impl PyValue for PyTupleIterator {
impl PyTupleIterator {
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
if self.position.get() < self.tuple.elements.borrow().len() {
let ret = self.tuple.elements.borrow()[self.position.get()].clone();
if self.position.get() < self.tuple.elements.len() {
let ret = self.tuple.elements[self.position.get()].clone();
self.position.set(self.position.get() + 1);
Ok(ret)
} else {
+2 -2
View File
@@ -248,7 +248,7 @@ pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
let (typ, name, bases, dict) = args.bind(vm)?;
type_new_class(vm, typ, name, bases, dict).map(PyRef::into_object)
} else {
Err(vm.new_type_error(format!(": type_new: {:?}", args)))
Err(vm.new_type_error("type() takes 1 or 3 arguments".to_string()))
}
}
@@ -289,7 +289,7 @@ fn type_dict_setter(_instance: PyClassRef, _value: PyObjectRef, vm: &VirtualMach
))
}
// This is the internal get_attr implementation for fast lookup on a class.
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
if let Some(item) = class.attributes.borrow().get(attr_name).cloned() {
return Some(item);
+25 -10
View File
@@ -1,9 +1,10 @@
use super::objweakref::PyWeak;
use crate::function::OptionalArg;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
#[pyclass]
#[derive(Debug)]
pub struct PyWeakProxy {
weak: PyWeak,
@@ -17,21 +18,27 @@ impl PyValue for PyWeakProxy {
pub type PyWeakProxyRef = PyRef<PyWeakProxy>;
impl PyWeakProxyRef {
// TODO callbacks
#[pyimpl]
impl PyWeakProxy {
// TODO: callbacks
#[pymethod(name = "__new__")]
fn create(
cls: PyClassRef,
referent: PyObjectRef,
_callback: OptionalArg<PyObjectRef>,
callback: OptionalArg<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<Self> {
) -> PyResult<PyWeakProxyRef> {
if callback.is_present() {
panic!("Passed a callback to weakproxy, but weakproxy does not yet support proxies.");
}
PyWeakProxy {
weak: PyWeak::downgrade(&referent),
}
.into_ref_with_type(vm, cls)
}
fn getattr(self, attr_name: PyObjectRef, vm: &VirtualMachine) -> PyResult {
#[pymethod(name = "__getattr__")]
fn getattr(&self, attr_name: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match self.weak.upgrade() {
Some(obj) => vm.get_attribute(obj, attr_name),
None => Err(vm.new_exception(
@@ -40,11 +47,19 @@ impl PyWeakProxyRef {
)),
}
}
#[pymethod(name = "__setattr__")]
fn setattr(&self, attr_name: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match self.weak.upgrade() {
Some(obj) => vm.set_attr(&obj, attr_name, value),
None => Err(vm.new_exception(
vm.ctx.exceptions.reference_error.clone(),
"weakly-referenced object no longer exists".to_string(),
)),
}
}
}
pub fn init(context: &PyContext) {
extend_class!(context, &context.weakproxy_type, {
"__new__" => context.new_rustfunc(PyWeakProxyRef::create),
"__getattr__" => context.new_rustfunc(PyWeakProxyRef::getattr),
});
PyWeakProxy::extend_class(&context, &context.weakproxy_type);
}
+83
View File
@@ -0,0 +1,83 @@
use std::hash::{Hash, Hasher};
use crate::obj::objfloat;
use crate::pyobject::PyObjectRef;
use crate::pyobject::PyResult;
use crate::vm::VirtualMachine;
pub type PyHash = i64;
pub type PyUHash = u64;
/// Prime multiplier used in string and various other hashes.
pub const MULTIPLIER: PyHash = 1_000_003; // 0xf4243
/// Numeric hashes are based on reduction modulo the prime 2**_BITS - 1
pub const BITS: usize = 61;
pub const MODULUS: PyUHash = (1 << BITS) - 1;
pub const INF: PyHash = 314_159;
pub const NAN: PyHash = 0;
pub const IMAG: PyHash = MULTIPLIER;
// pub const CUTOFF: usize = 7;
pub fn hash_float(value: f64) -> PyHash {
// cpython _Py_HashDouble
if !value.is_finite() {
return if value.is_infinite() {
if value > 0.0 {
INF
} else {
-INF
}
} else {
NAN
};
}
let frexp = objfloat::ufrexp(value);
// process 28 bits at a time; this should work well both for binary
// and hexadecimal floating point.
let mut m = frexp.0;
let mut e = frexp.1;
let mut x: PyUHash = 0;
while m != 0.0 {
x = ((x << 28) & MODULUS) | x >> (BITS - 28);
m *= 268_435_456.0; // 2**28
e -= 28;
let y = m as PyUHash; // pull out integer part
m -= y as f64;
x += y;
if x >= MODULUS {
x -= MODULUS;
}
}
// adjust for the exponent; first reduce it modulo BITS
const BITS32: i32 = BITS as i32;
e = if e >= 0 {
e % BITS32
} else {
BITS32 - 1 - ((-1 - e) % BITS32)
};
x = ((x << e) & MODULUS) | x >> (BITS32 - e);
x as PyHash * value.signum() as PyHash
}
pub fn hash_value<T: Hash>(data: &T) -> PyHash {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
data.hash(&mut hasher);
hasher.finish() as PyHash
}
pub fn hash_iter<'a, I: std::iter::Iterator<Item = &'a PyObjectRef>>(
iter: I,
vm: &VirtualMachine,
) -> PyResult<PyHash> {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for element in iter {
let item_hash = vm._hash(&element)?;
item_hash.hash(&mut hasher);
}
Ok(hasher.finish() as PyHash)
}
+19 -2
View File
@@ -40,6 +40,7 @@ use crate::obj::objmap;
use crate::obj::objmappingproxy;
use crate::obj::objmemory;
use crate::obj::objmodule::{self, PyModule};
use crate::obj::objnamespace::{self, PyNamespace};
use crate::obj::objnone::{self, PyNone, PyNoneRef};
use crate::obj::objobject;
use crate::obj::objproperty;
@@ -56,6 +57,7 @@ use crate::obj::objweakproxy;
use crate::obj::objweakref;
use crate::obj::objzip;
use crate::vm::VirtualMachine;
use indexmap::IndexMap;
/* Python objects and references.
@@ -85,6 +87,7 @@ pub type PyResult<T = PyObjectRef> = Result<T, PyObjectRef>; // A valid value, o
/// For attributes we do not use a dict, but a hashmap. This is probably
/// faster, unordered, and only supports strings as keys.
/// TODO: class attributes should maintain insertion order (use IndexMap here)
pub type PyAttributes = HashMap<String, PyObjectRef>;
impl fmt::Display for PyObject<dyn PyObjectPayload> {
@@ -158,6 +161,7 @@ pub struct PyContext {
pub property_type: PyClassRef,
pub readonly_property_type: PyClassRef,
pub module_type: PyClassRef,
pub namespace_type: PyClassRef,
pub bound_method_type: PyClassRef,
pub weakref_type: PyClassRef,
pub weakproxy_type: PyClassRef,
@@ -250,6 +254,7 @@ impl PyContext {
let dict_type = create_type("dict", &type_type, &object_type);
let module_type = create_type("module", &type_type, &object_type);
let namespace_type = create_type("SimpleNamespace", &type_type, &object_type);
let classmethod_type = create_type("classmethod", &type_type, &object_type);
let staticmethod_type = create_type("staticmethod", &type_type, &object_type);
let function_type = create_type("function", &type_type, &object_type);
@@ -290,7 +295,7 @@ impl PyContext {
let zip_type = create_type("zip", &type_type, &object_type);
let bool_type = create_type("bool", &type_type, &int_type);
let memoryview_type = create_type("memoryview", &type_type, &object_type);
let code_type = create_type("code", &type_type, &int_type);
let code_type = create_type("code", &type_type, &object_type);
let range_type = create_type("range", &type_type, &object_type);
let rangeiterator_type = create_type("range_iterator", &type_type, &object_type);
let slice_type = create_type("slice", &type_type, &object_type);
@@ -366,6 +371,7 @@ impl PyContext {
readonly_property_type,
generator_type,
module_type,
namespace_type,
bound_method_type,
weakref_type,
weakproxy_type,
@@ -407,6 +413,7 @@ impl PyContext {
objweakproxy::init(&context);
objnone::init(&context);
objmodule::init(&context);
objnamespace::init(&context);
objmappingproxy::init(&context);
exceptions::init(&context);
context
@@ -464,6 +471,10 @@ impl PyContext {
self.module_type.clone()
}
pub fn namespace_type(&self) -> PyClassRef {
self.namespace_type.clone()
}
pub fn set_type(&self) -> PyClassRef {
self.set_type.clone()
}
@@ -658,6 +669,10 @@ impl PyContext {
)
}
pub fn new_namespace(&self) -> PyObjectRef {
PyObject::new(PyNamespace, self.namespace_type(), Some(self.new_dict()))
}
pub fn new_rustfunc<F, T, R>(&self, f: F) -> PyObjectRef
where
F: IntoPyNativeFunc<T, R>,
@@ -819,6 +834,7 @@ impl<T: PyValue> PyRef<T> {
pub fn as_object(&self) -> &PyObjectRef {
&self.obj
}
pub fn into_object(self) -> PyObjectRef {
self.obj
}
@@ -1075,7 +1091,7 @@ impl<T> PyIterable<T> {
self.method.clone(),
PyFuncArgs {
args: vec![],
kwargs: vec![],
kwargs: IndexMap::new(),
},
)?;
@@ -1224,6 +1240,7 @@ impl<T> PyObject<T>
where
T: Sized + PyObjectPayload,
{
#[allow(clippy::new_ret_no_self)]
pub fn new(payload: T, typ: PyClassRef, dict: Option<PyDictRef>) -> PyObjectRef {
PyObject { typ, dict, payload }.into_ref()
}
+1 -1
View File
@@ -457,7 +457,7 @@ fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyResult
fn map_ast<T>(
f: fn(vm: &VirtualMachine, &T) -> PyResult<AstNodeRef>,
vm: &VirtualMachine,
items: &Vec<T>,
items: &[T],
) -> PyResult<PyObjectRef> {
let list: PyResult<Vec<PyObjectRef>> =
items.iter().map(|x| Ok(f(vm, x)?.into_object())).collect();
+24
View File
@@ -1,7 +1,10 @@
use crate::function::PyFuncArgs;
use crate::obj::objbytes;
use crate::obj::objint;
use crate::pyobject::{PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
use crc::{crc32, Hasher32};
use num_traits::ToPrimitive;
fn hex_nibble(n: u8) -> u8 {
match n {
@@ -56,6 +59,26 @@ fn binascii_unhexlify(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_bytes(unhex))
}
fn binascii_crc32(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(data, Some(vm.ctx.bytes_type()))],
optional = [(value, None)]
);
let bytes = objbytes::get_value(data);
let crc = match value {
None => 0u32,
Some(value) => objint::get_value(&value).to_u32().unwrap(),
};
let mut digest = crc32::Digest::new_with_initial(crc32::IEEE, crc);
digest.write(&bytes);
Ok(vm.ctx.new_int(digest.sum32()))
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -64,5 +87,6 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"b2a_hex" => ctx.new_rustfunc(binascii_hexlify),
"unhexlify" => ctx.new_rustfunc(binascii_unhexlify),
"a2b_hex" => ctx.new_rustfunc(binascii_unhexlify),
"crc32" => ctx.new_rustfunc(binascii_crc32),
})
}
+97
View File
@@ -0,0 +1,97 @@
use crate::compile;
use crate::import::import_file;
use crate::obj::objcode::PyCodeRef;
use crate::obj::objmodule::PyModuleRef;
use crate::obj::objstr;
use crate::obj::objstr::PyStringRef;
use crate::pyobject::{ItemProtocol, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
fn imp_extension_suffixes(vm: &VirtualMachine) -> PyResult {
Ok(vm.ctx.new_list(vec![]))
}
fn imp_acquire_lock(_vm: &VirtualMachine) -> PyResult<()> {
// TODO
Ok(())
}
fn imp_release_lock(_vm: &VirtualMachine) -> PyResult<()> {
// TODO
Ok(())
}
fn imp_lock_held(_vm: &VirtualMachine) -> PyResult<()> {
// TODO
Ok(())
}
fn imp_is_builtin(name: PyStringRef, vm: &VirtualMachine) -> bool {
vm.stdlib_inits.borrow().contains_key(name.as_str())
}
fn imp_is_frozen(name: PyStringRef, vm: &VirtualMachine) -> bool {
vm.frozen.borrow().contains_key(name.as_str())
}
fn imp_create_builtin(spec: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let sys_modules = vm.get_attribute(vm.sys_module.clone(), "modules").unwrap();
let name = &objstr::get_value(&vm.get_attribute(spec.clone(), "name")?);
if let Ok(module) = sys_modules.get_item(name, vm) {
Ok(module)
} else {
if let Some(make_module_func) = vm.stdlib_inits.borrow().get(name) {
Ok(make_module_func(vm))
} else {
Ok(vm.get_none())
}
}
}
fn imp_exec_builtin(_mod: PyModuleRef, _vm: &VirtualMachine) -> i32 {
// TOOD: Should we do something here?
0
}
fn imp_get_frozen_object(name: PyStringRef, vm: &VirtualMachine) -> PyResult<PyCodeRef> {
if let Some(frozen) = vm.frozen.borrow().get(name.as_str()) {
compile::compile(vm, frozen, &compile::Mode::Exec, "frozen".to_string())
.map_err(|err| vm.new_syntax_error(&err))
} else {
Err(vm.new_import_error(format!("No such frozen object named {}", name.as_str())))
}
}
fn imp_init_frozen(name: PyStringRef, vm: &VirtualMachine) -> PyResult {
if let Some(frozen) = vm.frozen.borrow().get(name.as_str()) {
import_file(vm, name.as_str(), "frozen".to_string(), frozen.to_string())
} else {
Err(vm.new_import_error(format!("No such frozen object named {}", name.as_str())))
}
}
fn imp_is_frozen_package(_name: PyStringRef, _vm: &VirtualMachine) -> bool {
// TODO: Support frozen package.
false
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let module = py_module!(vm, "_imp", {
"extension_suffixes" => ctx.new_rustfunc(imp_extension_suffixes),
"acquire_lock" => ctx.new_rustfunc(imp_acquire_lock),
"release_lock" => ctx.new_rustfunc(imp_release_lock),
"lock_held" => ctx.new_rustfunc(imp_lock_held),
"is_builtin" => ctx.new_rustfunc(imp_is_builtin),
"is_frozen" => ctx.new_rustfunc(imp_is_frozen),
"create_builtin" => ctx.new_rustfunc(imp_create_builtin),
"exec_builtin" => ctx.new_rustfunc(imp_exec_builtin),
"get_frozen_object" => ctx.new_rustfunc(imp_get_frozen_object),
"init_frozen" => ctx.new_rustfunc(imp_init_frozen),
"is_frozen_package" => ctx.new_rustfunc(imp_is_frozen_package),
});
module
}
+195 -62
View File
@@ -1,9 +1,7 @@
/*
* I/O core tools.
*/
use std::cell::RefCell;
use std::collections::HashSet;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
@@ -294,6 +292,63 @@ fn text_io_base_read(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn split_mode_string(mode_string: String) -> Result<(String, String), String> {
let mut mode: char = '\0';
let mut typ: char = '\0';
let mut plus_is_set = false;
for ch in mode_string.chars() {
match ch {
'+' => {
if plus_is_set {
return Err(format!("invalid mode: '{}'", mode_string));
}
plus_is_set = true;
}
't' | 'b' => {
if typ != '\0' {
if typ == ch {
// no duplicates allowed
return Err(format!("invalid mode: '{}'", mode_string));
} else {
return Err("can't have text and binary mode at once".to_string());
}
}
typ = ch;
}
'a' | 'r' | 'w' => {
if mode != '\0' {
if mode == ch {
// no duplicates allowed
return Err(format!("invalid mode: '{}'", mode_string));
} else {
return Err(
"must have exactly one of create/read/write/append mode".to_string()
);
}
}
mode = ch;
}
_ => return Err(format!("invalid mode: '{}'", mode_string)),
}
}
if mode == '\0' {
return Err(
"Must have exactly one of create/read/write/append mode and at most one plus"
.to_string(),
);
}
let mut mode = mode.to_string();
if plus_is_set {
mode.push('+');
}
if typ == '\0' {
typ = 't';
}
Ok((mode, typ.to_string()))
}
pub fn io_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
@@ -301,69 +356,60 @@ pub fn io_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
required = [(file, Some(vm.ctx.str_type()))],
optional = [(mode, Some(vm.ctx.str_type()))]
);
let module = import::import_module(vm, PathBuf::default(), "io").unwrap();
//mode is optional: 'rt' is the default mode (open from reading text)
let rust_mode = mode.map_or("rt".to_string(), |m| objstr::get_value(m));
let mut raw_modes = HashSet::new();
//add raw modes
raw_modes.insert("a".to_string());
raw_modes.insert("r".to_string());
raw_modes.insert("x".to_string());
raw_modes.insert("w".to_string());
//This is not a terribly elegant way to separate the file mode from
//the "type" flag - this should be improved. The intention here is to
//match a valid flag for the file_io_init call:
//https://docs.python.org/3/library/io.html#io.FileIO
let modes: Vec<char> = rust_mode
.chars()
.filter(|a| raw_modes.contains(&a.to_string()))
.collect();
if modes.is_empty() || modes.len() > 1 {
return Err(vm.new_value_error("Invalid Mode".to_string()));
}
//Class objects (potentially) consumed by io.open
//RawIO: FileIO
//Buffered: BufferedWriter, BufferedReader
//Text: TextIOWrapper
let file_io_class = vm.get_attribute(module.clone(), "FileIO").unwrap();
let buffered_writer_class = vm.get_attribute(module.clone(), "BufferedWriter").unwrap();
let buffered_reader_class = vm.get_attribute(module.clone(), "BufferedReader").unwrap();
let text_io_wrapper_class = vm.get_attribute(module, "TextIOWrapper").unwrap();
//Construct a FileIO (subclass of RawIOBase)
//This is subsequently consumed by a Buffered Class.
let file_args = vec![file.clone(), vm.ctx.new_str(modes[0].to_string())];
let file_io = vm.invoke(file_io_class, file_args)?;
//Create Buffered class to consume FileIO. The type of buffered class depends on
//the operation in the mode.
//There are 3 possible classes here, each inheriting from the RawBaseIO
// creating || writing || appending => BufferedWriter
let buffered = if rust_mode.contains('w') {
vm.invoke(buffered_writer_class, vec![file_io.clone()])
// reading => BufferedReader
} else {
vm.invoke(buffered_reader_class, vec![file_io.clone()])
//TODO: updating => PyBufferedRandom
// mode is optional: 'rt' is the default mode (open from reading text)
let mode_string = mode.map_or("rt".to_string(), |m| objstr::get_value(m));
let (mode, typ) = match split_mode_string(mode_string) {
Ok((mode, typ)) => (mode, typ),
Err(error_message) => {
return Err(vm.new_value_error(error_message));
}
};
if rust_mode.contains('t') {
//If the mode is text this buffer type is consumed on construction of
//a TextIOWrapper which is subsequently returned.
vm.invoke(text_io_wrapper_class, vec![buffered.unwrap()])
} else {
let io_module = import::import_module(vm, PathBuf::default(), "io").unwrap();
// Construct a FileIO (subclass of RawIOBase)
// This is subsequently consumed by a Buffered Class.
let file_io_class = vm.get_attribute(io_module.clone(), "FileIO").unwrap();
let file_io_obj = vm.invoke(
file_io_class,
vec![file.clone(), vm.ctx.new_str(mode.clone())],
)?;
// Create Buffered class to consume FileIO. The type of buffered class depends on
// the operation in the mode.
// There are 3 possible classes here, each inheriting from the RawBaseIO
// creating || writing || appending => BufferedWriter
let buffered = match mode.chars().next().unwrap() {
'w' => {
let buffered_writer_class = vm
.get_attribute(io_module.clone(), "BufferedWriter")
.unwrap();
vm.invoke(buffered_writer_class, vec![file_io_obj.clone()])
}
'r' => {
let buffered_reader_class = vm
.get_attribute(io_module.clone(), "BufferedReader")
.unwrap();
vm.invoke(buffered_reader_class, vec![file_io_obj.clone()])
}
//TODO: updating => PyBufferedRandom
_ => unimplemented!("'a' mode is not yet implemented"),
};
let io_obj = match typ.chars().next().unwrap() {
// If the mode is text this buffer type is consumed on construction of
// a TextIOWrapper which is subsequently returned.
't' => {
let text_io_wrapper_class = vm.get_attribute(io_module, "TextIOWrapper").unwrap();
vm.invoke(text_io_wrapper_class, vec![buffered.unwrap()])
}
// If the mode is binary this Buffered class is returned directly at
// this point.
//For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field
buffered
}
// For Buffered class construct "raw" IO class e.g. FileIO and pass this into corresponding field
'b' => buffered,
_ => unreachable!(),
};
io_obj
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
@@ -377,7 +423,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
});
// IOBase Subclasses
let raw_io_base = py_class!(ctx, "RawIOBase", ctx.object(), {});
let raw_io_base = py_class!(ctx, "RawIOBase", io_base.clone(), {});
let buffered_io_base = py_class!(ctx, "BufferedIOBase", io_base.clone(), {
"__init__" => ctx.new_rustfunc(buffered_io_base_init)
@@ -439,3 +485,90 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"BytesIO" => bytes_io,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_mode_split_into(mode_string: &str, expected_mode: &str, expected_typ: &str) {
let (mode, typ) = split_mode_string(mode_string.to_string()).unwrap();
assert_eq!(mode, expected_mode);
assert_eq!(typ, expected_typ);
}
#[test]
fn test_split_mode_valid_cases() {
assert_mode_split_into("r", "r", "t");
assert_mode_split_into("rb", "r", "b");
assert_mode_split_into("rt", "r", "t");
assert_mode_split_into("r+t", "r+", "t");
assert_mode_split_into("w+t", "w+", "t");
assert_mode_split_into("r+b", "r+", "b");
assert_mode_split_into("w+b", "w+", "b");
}
#[test]
fn test_invalid_mode() {
assert_eq!(
split_mode_string("rbsss".to_string()),
Err("invalid mode: 'rbsss'".to_string())
);
assert_eq!(
split_mode_string("rrb".to_string()),
Err("invalid mode: 'rrb'".to_string())
);
assert_eq!(
split_mode_string("rbb".to_string()),
Err("invalid mode: 'rbb'".to_string())
);
}
#[test]
fn test_mode_not_specified() {
assert_eq!(
split_mode_string("".to_string()),
Err(
"Must have exactly one of create/read/write/append mode and at most one plus"
.to_string()
)
);
assert_eq!(
split_mode_string("b".to_string()),
Err(
"Must have exactly one of create/read/write/append mode and at most one plus"
.to_string()
)
);
assert_eq!(
split_mode_string("t".to_string()),
Err(
"Must have exactly one of create/read/write/append mode and at most one plus"
.to_string()
)
);
}
#[test]
fn test_text_and_binary_at_once() {
assert_eq!(
split_mode_string("rbt".to_string()),
Err("can't have text and binary mode at once".to_string())
);
}
#[test]
fn test_exactly_one_mode() {
assert_eq!(
split_mode_string("rwb".to_string()),
Err("must have exactly one of create/read/write/append mode".to_string())
);
}
#[test]
fn test_at_most_one_plus() {
assert_eq!(
split_mode_string("a++".to_string()),
Err("invalid mode: 'a++'".to_string())
);
}
}
+313 -5
View File
@@ -3,14 +3,76 @@ use std::cmp::Ordering;
use std::ops::{AddAssign, SubAssign};
use num_bigint::BigInt;
use num_traits::ToPrimitive;
use crate::function::OptionalArg;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objbool;
use crate::obj::objint;
use crate::obj::objint::{PyInt, PyIntRef};
use crate::obj::objiter::new_stop_iteration;
use crate::obj::objiter::{call_next, get_iter, new_stop_iteration};
use crate::obj::objtype;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{IdProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
#[pyclass(name = "chain")]
#[derive(Debug)]
struct PyItertoolsChain {
iterables: Vec<PyObjectRef>,
cur: RefCell<(usize, Option<PyObjectRef>)>,
}
impl PyValue for PyItertoolsChain {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("itertools", "chain")
}
}
#[pyimpl]
impl PyItertoolsChain {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(_cls: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
Ok(PyItertoolsChain {
iterables: args.args,
cur: RefCell::new((0, None)),
}
.into_ref(vm)
.into_object())
}
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
let (ref mut cur_idx, ref mut cur_iter) = *self.cur.borrow_mut();
while *cur_idx < self.iterables.len() {
if cur_iter.is_none() {
*cur_iter = Some(get_iter(vm, &self.iterables[*cur_idx])?);
}
// can't be directly inside the 'match' clause, otherwise the borrows collide.
let obj = call_next(vm, cur_iter.as_ref().unwrap());
match obj {
Ok(ok) => return Ok(ok),
Err(err) => {
if objtype::isinstance(&err, &vm.ctx.exceptions.stop_iteration) {
*cur_idx += 1;
*cur_iter = None;
} else {
return Err(err);
}
}
}
}
Err(new_stop_iteration(vm))
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
#[pyclass]
#[derive(Debug)]
struct PyItertoolsCount {
@@ -27,6 +89,7 @@ impl PyValue for PyItertoolsCount {
#[pyimpl]
impl PyItertoolsCount {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(
_cls: PyClassRef,
start: OptionalArg<PyIntRef>,
@@ -44,7 +107,7 @@ impl PyItertoolsCount {
Ok(PyItertoolsCount {
cur: RefCell::new(start),
step: step,
step,
}
.into_ref(vm)
.into_object())
@@ -79,6 +142,7 @@ impl PyValue for PyItertoolsRepeat {
#[pyimpl]
impl PyItertoolsRepeat {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(
_cls: PyClassRef,
object: PyObjectRef,
@@ -92,7 +156,7 @@ impl PyItertoolsRepeat {
Ok(PyItertoolsRepeat {
object: object.clone(),
times: times,
times,
}
.into_ref(vm)
.into_object())
@@ -121,17 +185,261 @@ impl PyItertoolsRepeat {
}
}
#[pyclass(name = "starmap")]
#[derive(Debug)]
struct PyItertoolsStarmap {
function: PyObjectRef,
iter: PyObjectRef,
}
impl PyValue for PyItertoolsStarmap {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("itertools", "starmap")
}
}
#[pyimpl]
impl PyItertoolsStarmap {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(
_cls: PyClassRef,
function: PyObjectRef,
iterable: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let iter = get_iter(vm, &iterable)?;
Ok(PyItertoolsStarmap { function, iter }
.into_ref(vm)
.into_object())
}
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
let obj = call_next(vm, &self.iter)?;
vm.invoke(self.function.clone(), vm.extract_elements(&obj)?)
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
#[pyclass]
#[derive(Debug)]
struct PyItertoolsTakewhile {
predicate: PyObjectRef,
iterable: PyObjectRef,
stop_flag: RefCell<bool>,
}
impl PyValue for PyItertoolsTakewhile {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("itertools", "takewhile")
}
}
#[pyimpl]
impl PyItertoolsTakewhile {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(
_cls: PyClassRef,
predicate: PyObjectRef,
iterable: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
let iter = get_iter(vm, &iterable)?;
Ok(PyItertoolsTakewhile {
predicate,
iterable: iter,
stop_flag: RefCell::new(false),
}
.into_ref(vm)
.into_object())
}
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
if *self.stop_flag.borrow() {
return Err(new_stop_iteration(vm));
}
// might be StopIteration or anything else, which is propaged upwwards
let obj = call_next(vm, &self.iterable)?;
let verdict = vm.invoke(self.predicate.clone(), vec![obj.clone()])?;
let verdict = objbool::boolval(vm, verdict)?;
if verdict {
Ok(obj)
} else {
*self.stop_flag.borrow_mut() = true;
Err(new_stop_iteration(vm))
}
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
#[pyclass(name = "islice")]
#[derive(Debug)]
struct PyItertoolsIslice {
iterable: PyObjectRef,
cur: RefCell<usize>,
next: RefCell<usize>,
stop: Option<usize>,
step: usize,
}
impl PyValue for PyItertoolsIslice {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("itertools", "islice")
}
}
fn pyobject_to_opt_usize(obj: PyObjectRef, vm: &VirtualMachine) -> Option<usize> {
let is_int = objtype::isinstance(&obj, &vm.ctx.int_type());
if is_int {
objint::get_value(&obj).to_usize()
} else {
None
}
}
#[pyimpl]
impl PyItertoolsIslice {
#[pymethod(name = "__new__")]
#[allow(clippy::new_ret_no_self)]
fn new(_cls: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
let (iter, start, stop, step) = match args.args.len() {
0 | 1 => {
return Err(vm.new_type_error(format!(
"islice expected at least 2 arguments, got {}",
args.args.len()
)));
}
2 => {
let (iter, stop): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
(iter, 0usize, stop, 1usize)
}
_ => {
let (iter, start, stop, step): (
PyObjectRef,
PyObjectRef,
PyObjectRef,
PyObjectRef,
) = args.bind(vm)?;
let start = if !start.is(&vm.get_none()) {
pyobject_to_opt_usize(start, &vm).ok_or_else(|| {
vm.new_value_error(
"Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.".to_string(),
)
})?
} else {
0usize
};
let step = if !step.is(&vm.get_none()) {
pyobject_to_opt_usize(step, &vm).ok_or_else(|| {
vm.new_value_error(
"Step for islice() must be a positive integer or None.".to_string(),
)
})?
} else {
1usize
};
(iter, start, stop, step)
}
};
let stop = if !stop.is(&vm.get_none()) {
Some(pyobject_to_opt_usize(stop, &vm).ok_or_else(|| {
vm.new_value_error(
"Stop argument for islice() must be None or an integer: 0 <= x <= sys.maxsize."
.to_string(),
)
})?)
} else {
None
};
let iter = get_iter(vm, &iter)?;
Ok(PyItertoolsIslice {
iterable: iter,
cur: RefCell::new(0),
next: RefCell::new(start),
stop,
step,
}
.into_ref(vm)
.into_object())
}
#[pymethod(name = "__next__")]
fn next(&self, vm: &VirtualMachine) -> PyResult {
while *self.cur.borrow() < *self.next.borrow() {
call_next(vm, &self.iterable)?;
*self.cur.borrow_mut() += 1;
}
if let Some(stop) = self.stop {
if *self.cur.borrow() >= stop {
return Err(new_stop_iteration(vm));
}
}
let obj = call_next(vm, &self.iterable)?;
*self.cur.borrow_mut() += 1;
// TODO is this overflow check required? attempts to copy CPython.
let (next, ovf) = (*self.next.borrow()).overflowing_add(self.step);
*self.next.borrow_mut() = if ovf { self.stop.unwrap() } else { next };
Ok(obj)
}
#[pymethod(name = "__iter__")]
fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
zelf
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let chain = PyItertoolsChain::make_class(ctx);
let count = ctx.new_class("count", ctx.object());
PyItertoolsCount::extend_class(ctx, &count);
let repeat = ctx.new_class("repeat", ctx.object());
PyItertoolsRepeat::extend_class(ctx, &repeat);
let starmap = PyItertoolsStarmap::make_class(ctx);
let takewhile = ctx.new_class("takewhile", ctx.object());
PyItertoolsTakewhile::extend_class(ctx, &takewhile);
let islice = PyItertoolsIslice::make_class(ctx);
py_module!(vm, "itertools", {
"chain" => chain,
"count" => count,
"repeat" => repeat,
"starmap" => starmap,
"takewhile" => takewhile,
"islice" => islice,
})
}
+5 -4
View File
@@ -57,10 +57,11 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> {
serializer.serialize_i64(v.to_i64().unwrap())
// Although this may seem nice, it does not give the right result:
// v.serialize(serializer)
} else if objtype::isinstance(self.pyobject, &self.vm.ctx.list_type())
|| objtype::isinstance(self.pyobject, &self.vm.ctx.tuple_type())
{
let elements = objsequence::get_elements(self.pyobject);
} else if objtype::isinstance(self.pyobject, &self.vm.ctx.list_type()) {
let elements = objsequence::get_elements_list(self.pyobject);
serialize_seq_elements(serializer, &elements)
} else if objtype::isinstance(self.pyobject, &self.vm.ctx.tuple_type()) {
let elements = objsequence::get_elements_tuple(self.pyobject);
serialize_seq_elements(serializer, &elements)
} else if objtype::isinstance(self.pyobject, &self.vm.ctx.dict_type()) {
let dict: PyDictRef = self.pyobject.clone().downcast().unwrap();
+24
View File
@@ -0,0 +1,24 @@
use crate::bytecode;
use crate::obj::objbytes::{PyBytes, PyBytesRef};
use crate::obj::objcode::{PyCode, PyCodeRef};
use crate::pyobject::{IntoPyObject, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
fn marshal_dumps(co: PyCodeRef, vm: &VirtualMachine) -> PyResult {
PyBytes::new(bincode::serialize(&co.code).unwrap()).into_pyobject(vm)
}
fn marshal_loads(code_bytes: PyBytesRef, vm: &VirtualMachine) -> PyResult {
let code = bincode::deserialize::<bytecode::CodeObject>(&code_bytes).unwrap();
let pycode = PyCode { code };
pycode.into_pyobject(vm)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
py_module!(vm, "marshal", {
"loads" => ctx.new_rustfunc(marshal_loads),
"dumps" => ctx.new_rustfunc(marshal_dumps)
})
}
+19
View File
@@ -209,6 +209,23 @@ fn math_floor(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
}
}
fn math_frexp(value: PyObjectRef, vm: &VirtualMachine) -> PyResult {
objfloat::try_float(&value, vm)?.map_or_else(
|| Err(vm.new_type_error(format!("must be real number, not {}", value.class()))),
|value| {
let (m, e) = if value.is_finite() {
let (m, e) = objfloat::ufrexp(value);
(m * value.signum(), e)
} else {
(value, 0)
};
Ok(vm
.ctx
.new_tuple(vec![vm.ctx.new_float(m), vm.ctx.new_int(e)]))
},
)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -256,6 +273,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"gamma" => ctx.new_rustfunc(math_gamma),
"lgamma" => ctx.new_rustfunc(math_lgamma),
"frexp" => ctx.new_rustfunc(math_frexp),
// Rounding functions:
"trunc" => ctx.new_rustfunc(math_trunc),
"ceil" => ctx.new_rustfunc(math_ceil),
+14 -2
View File
@@ -1,9 +1,11 @@
mod ast;
mod binascii;
mod dis;
mod imp;
mod itertools;
pub(crate) mod json;
mod keyword;
mod marshal;
mod math;
mod platform;
mod pystruct;
@@ -14,7 +16,7 @@ mod string;
mod thread;
mod time_module;
mod tokenize;
mod types;
mod warnings;
mod weakref;
use std::collections::HashMap;
@@ -24,6 +26,8 @@ use crate::vm::VirtualMachine;
pub mod io;
#[cfg(not(target_arch = "wasm32"))]
mod os;
#[cfg(all(unix, not(target_os = "android")))]
mod pwd;
use crate::pyobject::PyObjectRef;
@@ -40,6 +44,7 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("itertools".to_string(), Box::new(itertools::make_module));
modules.insert("json".to_string(), Box::new(json::make_module));
modules.insert("keyword".to_string(), Box::new(keyword::make_module));
modules.insert("marshal".to_string(), Box::new(marshal::make_module));
modules.insert("math".to_string(), Box::new(math::make_module));
modules.insert("platform".to_string(), Box::new(platform::make_module));
modules.insert("re".to_string(), Box::new(re::make_module));
@@ -49,8 +54,9 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("_thread".to_string(), Box::new(thread::make_module));
modules.insert("time".to_string(), Box::new(time_module::make_module));
modules.insert("tokenize".to_string(), Box::new(tokenize::make_module));
modules.insert("types".to_string(), Box::new(types::make_module));
modules.insert("_weakref".to_string(), Box::new(weakref::make_module));
modules.insert("_imp".to_string(), Box::new(imp::make_module));
modules.insert("_warnings".to_string(), Box::new(warnings::make_module));
// disable some modules on WASM
#[cfg(not(target_arch = "wasm32"))]
@@ -60,5 +66,11 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("socket".to_string(), Box::new(socket::make_module));
}
// Unix-only
#[cfg(all(unix, not(target_os = "android")))]
{
modules.insert("pwd".to_string(), Box::new(pwd::make_module));
}
modules
}
+177 -28
View File
@@ -7,17 +7,16 @@ use std::{env, fs};
use num_traits::cast::ToPrimitive;
use crate::function::PyFuncArgs;
use crate::function::{IntoPyNativeFunc, PyFuncArgs};
use crate::obj::objbytes::PyBytesRef;
use crate::obj::objdict::PyDictRef;
use crate::obj::objint;
use crate::obj::objint::PyIntRef;
use crate::obj::objint::{self, PyInt, PyIntRef};
use crate::obj::objiter;
use crate::obj::objstr;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::obj::objset::PySet;
use crate::obj::objstr::{self, PyString, PyStringRef};
use crate::obj::objtype::{self, PyClassRef};
use crate::pyobject::{
ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryIntoRef,
ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryIntoRef, TypeProtocol,
};
use crate::vm::VirtualMachine;
@@ -61,6 +60,14 @@ pub fn raw_file_number(handle: File) -> i64 {
unimplemented!();
}
fn make_path(_vm: &VirtualMachine, path: PyStringRef, dir_fd: &DirFd) -> PyStringRef {
if dir_fd.dir_fd.is_some() {
unimplemented!();
} else {
path
}
}
pub fn os_close(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(fileno, Some(vm.ctx.int_type()))]);
@@ -81,10 +88,19 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
required = [
(name, Some(vm.ctx.str_type())),
(mode, Some(vm.ctx.int_type()))
]
],
optional = [(dir_fd, Some(vm.ctx.int_type()))]
);
let fname = objstr::get_value(&name);
let name = name.clone().downcast::<PyString>().unwrap();
let dir_fd = if let Some(obj) = dir_fd {
DirFd {
dir_fd: Some(obj.clone().downcast::<PyInt>().unwrap()),
}
} else {
DirFd::default()
};
let fname = &make_path(vm, name, &dir_fd).value;
let handle = match objint::get_value(mode).to_u16().unwrap() {
0 => OpenOptions::new().read(true).open(&fname),
@@ -159,11 +175,13 @@ fn os_write(fd: PyIntRef, data: PyBytesRef, vm: &VirtualMachine) -> PyResult {
Ok(vm.ctx.new_int(written))
}
fn os_remove(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_remove(path: PyStringRef, dir_fd: DirFd, vm: &VirtualMachine) -> PyResult<()> {
let path = make_path(vm, path, &dir_fd);
fs::remove_file(&path.value).map_err(|s| vm.new_os_error(s.to_string()))
}
fn os_mkdir(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_mkdir(path: PyStringRef, dir_fd: DirFd, vm: &VirtualMachine) -> PyResult<()> {
let path = make_path(vm, path, &dir_fd);
fs::create_dir(&path.value).map_err(|s| vm.new_os_error(s.to_string()))
}
@@ -171,7 +189,8 @@ fn os_mkdirs(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fs::create_dir_all(&path.value).map_err(|s| vm.new_os_error(s.to_string()))
}
fn os_rmdir(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_rmdir(path: PyStringRef, dir_fd: DirFd, vm: &VirtualMachine) -> PyResult<()> {
let path = make_path(vm, path, &dir_fd);
fs::remove_dir(&path.value).map_err(|s| vm.new_os_error(s.to_string()))
}
@@ -219,6 +238,12 @@ impl PyValue for DirEntry {
}
}
#[derive(FromArgs, Default)]
struct DirFd {
#[pyarg(keyword_only, default = "None")]
dir_fd: Option<PyIntRef>,
}
#[derive(FromArgs)]
struct FollowSymlinks {
#[pyarg(keyword_only, default = "true")]
@@ -234,6 +259,7 @@ impl DirEntryRef {
self.entry.path().to_str().unwrap().to_string()
}
#[allow(clippy::match_bool)]
fn perform_on_metadata(
self,
follow_symlinks: FollowSymlinks,
@@ -272,8 +298,13 @@ impl DirEntryRef {
.is_symlink())
}
fn stat(self, follow_symlinks: FollowSymlinks, vm: &VirtualMachine) -> PyResult<StatResult> {
os_stat(self.path(vm).try_into_ref(vm)?, follow_symlinks, vm)
fn stat(
self,
dir_fd: DirFd,
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<StatResult> {
os_stat(self.path(vm).try_into_ref(vm)?, dir_fd, follow_symlinks, vm)
}
}
@@ -404,6 +435,7 @@ fn to_seconds_from_nanos(secs: i64, nanos: i64) -> f64 {
#[cfg(unix)]
macro_rules! os_unix_stat_inner {
( $path:expr, $follow_symlinks:expr, $vm:expr ) => {{
#[allow(clippy::match_bool)]
fn get_stats(path: &str, follow_symlinks: bool) -> io::Result<StatResult> {
let meta = match follow_symlinks {
true => fs::metadata(path)?,
@@ -432,30 +464,36 @@ macro_rules! os_unix_stat_inner {
#[cfg(target_os = "linux")]
fn os_stat(
path: PyStringRef,
dir_fd: DirFd,
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<StatResult> {
use std::os::linux::fs::MetadataExt;
let path = make_path(vm, path, &dir_fd);
os_unix_stat_inner!(path, follow_symlinks, vm)
}
#[cfg(target_os = "macos")]
fn os_stat(
path: PyStringRef,
dir_fd: DirFd,
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<StatResult> {
use std::os::macos::fs::MetadataExt;
let path = make_path(vm, path, &dir_fd);
os_unix_stat_inner!(path, follow_symlinks, vm)
}
#[cfg(target_os = "android")]
fn os_stat(
path: PyStringRef,
dir_fd: DirFd,
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<StatResult> {
use std::os::android::fs::MetadataExt;
let path = make_path(vm, path, &dir_fd);
os_unix_stat_inner!(path, follow_symlinks, vm)
}
@@ -483,6 +521,7 @@ fn attributes_to_mode(attr: u32) -> u32 {
#[cfg(windows)]
fn os_stat(
path: PyStringRef,
_dir_fd: DirFd, // TODO: error
follow_symlinks: FollowSymlinks,
vm: &VirtualMachine,
) -> PyResult<StatResult> {
@@ -523,13 +562,24 @@ fn os_stat(path: PyStringRef, vm: &VirtualMachine) -> PyResult {
}
#[cfg(unix)]
fn os_symlink(src: PyStringRef, dst: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_symlink(
src: PyStringRef,
dst: PyStringRef,
dir_fd: DirFd,
vm: &VirtualMachine,
) -> PyResult<()> {
use std::os::unix::fs as unix_fs;
let dst = make_path(vm, dst, &dir_fd);
unix_fs::symlink(&src.value, &dst.value).map_err(|s| vm.new_os_error(s.to_string()))
}
#[cfg(windows)]
fn os_symlink(src: PyStringRef, dst: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_symlink(
src: PyStringRef,
dst: PyStringRef,
_dir_fd: DirFd,
vm: &VirtualMachine,
) -> PyResult<()> {
use std::os::windows::fs as win_fs;
let ret = match fs::metadata(&dst.value) {
Ok(meta) => {
@@ -547,7 +597,12 @@ fn os_symlink(src: PyStringRef, dst: PyStringRef, vm: &VirtualMachine) -> PyResu
}
#[cfg(all(not(unix), not(windows)))]
fn os_symlink(src: PyStringRef, dst: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
fn os_symlink(
src: PyStringRef,
dst: PyStringRef,
dir_fd: DirFd,
vm: &VirtualMachine,
) -> PyResult<()> {
unimplemented!();
}
@@ -564,6 +619,19 @@ fn os_chdir(path: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
env::set_current_dir(&path.value).map_err(|s| vm.new_os_error(s.to_string()))
}
fn os_fspath(path: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if objtype::issubclass(&path.class(), &vm.ctx.str_type())
|| objtype::issubclass(&path.class(), &vm.ctx.bytes_type())
{
Ok(path)
} else {
Err(vm.new_type_error(format!(
"expected str or bytes object, not {}",
path.class()
)))
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -600,36 +668,117 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"st_mtime" => ctx.new_property(StatResultRef::st_mtime),
});
py_module!(vm, "_os", {
"open" => ctx.new_rustfunc(os_open),
struct SupportFunc<'a> {
name: &'a str,
func_obj: PyObjectRef,
fd: Option<bool>,
dir_fd: Option<bool>,
follow_symlinks: Option<bool>,
};
impl<'a> SupportFunc<'a> {
fn new<F, T, R>(
vm: &VirtualMachine,
name: &'a str,
func: F,
fd: Option<bool>,
dir_fd: Option<bool>,
follow_symlinks: Option<bool>,
) -> Self
where
F: IntoPyNativeFunc<T, R>,
{
let func_obj = vm.ctx.new_rustfunc(func);
Self {
name,
func_obj,
fd,
dir_fd,
follow_symlinks,
}
}
}
let support_funcs = vec![
SupportFunc::new(vm, "open", os_open, None, Some(false), None),
// access Some Some None
SupportFunc::new(vm, "chdir", os_chdir, Some(false), None, None),
// chflags Some, None Some
// chmod Some Some Some
// chown Some Some Some
// chroot Some None None
SupportFunc::new(vm, "listdir", os_listdir, Some(false), None, None),
SupportFunc::new(vm, "mkdir", os_mkdir, Some(false), Some(false), None),
// mkfifo Some Some None
// mknod Some Some None
// pathconf Some None None
// readlink Some Some None
SupportFunc::new(vm, "remove", os_remove, Some(false), Some(false), None),
// rename Some Some None
// replace Some Some None
SupportFunc::new(vm, "rmdir", os_rmdir, Some(false), Some(false), None),
SupportFunc::new(vm, "scandir", os_scandir, Some(false), None, None),
SupportFunc::new(vm, "stat", os_stat, Some(false), Some(false), Some(false)),
SupportFunc::new(vm, "symlink", os_symlink, None, Some(false), None),
// truncate Some None None
SupportFunc::new(vm, "unlink", os_remove, Some(false), Some(false), None),
// utime Some Some Some
];
let supports_fd = PySet::default().into_ref(vm);
let supports_dir_fd = PySet::default().into_ref(vm);
let supports_follow_symlinks = PySet::default().into_ref(vm);
let module = py_module!(vm, "_os", {
"close" => ctx.new_rustfunc(os_close),
"error" => ctx.new_rustfunc(os_error),
"fsync" => ctx.new_rustfunc(os_fsync),
"read" => ctx.new_rustfunc(os_read),
"write" => ctx.new_rustfunc(os_write),
"remove" => ctx.new_rustfunc(os_remove),
"unlink" => ctx.new_rustfunc(os_remove),
"mkdir" => ctx.new_rustfunc(os_mkdir),
"mkdirs" => ctx.new_rustfunc(os_mkdirs),
"rmdir" => ctx.new_rustfunc(os_rmdir),
"listdir" => ctx.new_rustfunc(os_listdir),
"putenv" => ctx.new_rustfunc(os_putenv),
"unsetenv" => ctx.new_rustfunc(os_unsetenv),
"environ" => environ,
"name" => ctx.new_str(os_name),
"scandir" => ctx.new_rustfunc(os_scandir),
"ScandirIter" => scandir_iter,
"DirEntry" => dir_entry,
"stat_result" => stat_result,
"stat" => ctx.new_rustfunc(os_stat),
"symlink" => ctx.new_rustfunc(os_symlink),
"getcwd" => ctx.new_rustfunc(os_getcwd),
"chdir" => ctx.new_rustfunc(os_chdir),
"fspath" => ctx.new_rustfunc(os_fspath),
"O_RDONLY" => ctx.new_int(0),
"O_WRONLY" => ctx.new_int(1),
"O_RDWR" => ctx.new_int(2),
"O_NONBLOCK" => ctx.new_int(4),
"O_APPEND" => ctx.new_int(8),
"O_CREAT" => ctx.new_int(512)
})
});
for support in support_funcs {
if support.fd.unwrap_or(false) {
supports_fd
.clone()
.add(support.func_obj.clone(), vm)
.unwrap();
}
if support.dir_fd.unwrap_or(false) {
supports_dir_fd
.clone()
.add(support.func_obj.clone(), vm)
.unwrap();
}
if support.follow_symlinks.unwrap_or(false) {
supports_follow_symlinks
.clone()
.add(support.func_obj.clone(), vm)
.unwrap();
}
vm.set_attr(&module, support.name, support.func_obj)
.unwrap();
}
extend_module!(vm, module, {
"supports_fd" => supports_fd.into_object(),
"supports_dir_fd" => supports_dir_fd.into_object(),
"supports_follow_symlinks" => supports_follow_symlinks.into_object(),
});
module
}
+85
View File
@@ -0,0 +1,85 @@
use pwd::Passwd;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
impl PyValue for Passwd {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("pwd", "struct_passwd")
}
}
type PasswdRef = PyRef<Passwd>;
impl PasswdRef {
fn pw_name(self, _vm: &VirtualMachine) -> String {
self.name.clone()
}
fn pw_passwd(self, _vm: &VirtualMachine) -> Option<String> {
self.passwd.clone()
}
fn pw_uid(self, _vm: &VirtualMachine) -> u32 {
self.uid
}
fn pw_gid(self, _vm: &VirtualMachine) -> u32 {
self.gid
}
fn pw_gecos(self, _vm: &VirtualMachine) -> Option<String> {
self.gecos.clone()
}
fn pw_dir(self, _vm: &VirtualMachine) -> String {
self.dir.clone()
}
fn pw_shell(self, _vm: &VirtualMachine) -> String {
self.shell.clone()
}
}
fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult<Passwd> {
match Passwd::from_name(&name.value) {
Ok(Some(passwd)) => Ok(passwd),
_ => {
let name_repr = vm.to_repr(name.as_object())?;
let message = format!("getpwnam(): name not found: {}", name_repr);
Err(vm.new_key_error(message))
}
}
}
fn pwd_getpwuid(uid: u32, vm: &VirtualMachine) -> PyResult<Passwd> {
match Passwd::from_uid(uid) {
Some(passwd) => Ok(passwd),
_ => {
let message = format!("getpwuid(): uid not found: {}", uid);
Err(vm.new_key_error(message))
}
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let passwd_type = py_class!(ctx, "struct_passwd", ctx.object(), {
"pw_name" => ctx.new_property(PasswdRef::pw_name),
"pw_passwd" => ctx.new_property(PasswdRef::pw_passwd),
"pw_uid" => ctx.new_property(PasswdRef::pw_uid),
"pw_gid" => ctx.new_property(PasswdRef::pw_gid),
"pw_gecos" => ctx.new_property(PasswdRef::pw_gecos),
"pw_dir" => ctx.new_property(PasswdRef::pw_dir),
"pw_shell" => ctx.new_property(PasswdRef::pw_shell),
});
py_module!(vm, "pwd", {
"struct_passwd" => passwd_type,
"getpwnam" => ctx.new_rustfunc(pwd_getpwnam),
"getpwuid" => ctx.new_rustfunc(pwd_getpwuid),
})
}
+5
View File
@@ -79,6 +79,10 @@ fn re_compile(pattern: PyStringRef, vm: &VirtualMachine) -> PyResult<Regex> {
make_regex(vm, &pattern.value)
}
fn re_escape(pattern: PyStringRef, _vm: &VirtualMachine) -> String {
regex::escape(&pattern.value)
}
impl PyRegexRef {
fn match_(self, text: PyStringRef, vm: &VirtualMachine) -> PyResult {
do_match(vm, &self, &text.value)
@@ -113,6 +117,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
py_module!(vm, "re", {
"compile" => ctx.new_rustfunc(re_compile),
"escape" => ctx.new_rustfunc(re_escape),
"Match" => match_type,
"match" => ctx.new_rustfunc(re_match),
"Pattern" => pattern_type,
+5 -5
View File
@@ -218,7 +218,7 @@ impl SocketRef {
}
}
fn listen(self, _num: PyIntRef, _vm: &VirtualMachine) -> () {}
fn listen(self, _num: PyIntRef, _vm: &VirtualMachine) {}
fn accept(self, vm: &VirtualMachine) -> PyResult {
let ret = match self.con.borrow_mut().as_mut() {
@@ -310,7 +310,7 @@ impl SocketRef {
}
}
fn close(self, _vm: &VirtualMachine) -> () {
fn close(self, _vm: &VirtualMachine) {
self.con.borrow_mut().take();
}
@@ -349,14 +349,14 @@ impl Address {
impl TryFromObject for Address {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let tuple = PyTupleRef::try_from_object(vm, obj)?;
if tuple.elements.borrow().len() != 2 {
if tuple.elements.len() != 2 {
Err(vm.new_type_error("Address tuple should have only 2 values".to_string()))
} else {
Ok(Address {
host: PyStringRef::try_from_object(vm, tuple.elements.borrow()[0].clone())?
host: PyStringRef::try_from_object(vm, tuple.elements[0].clone())?
.value
.to_string(),
port: PyIntRef::try_from_object(vm, tuple.elements.borrow()[1].clone())?
port: PyIntRef::try_from_object(vm, tuple.elements[1].clone())?
.as_bigint()
.to_usize()
.unwrap(),
-39
View File
@@ -1,39 +0,0 @@
/*
* Dynamic type creation and names for built in types.
*/
use crate::function::OptionalArg;
use crate::obj::objdict::PyDict;
use crate::obj::objstr::PyStringRef;
use crate::obj::objtype;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject};
use crate::VirtualMachine;
fn types_new_class(
name: PyStringRef,
bases: OptionalArg<PyIterable<PyClassRef>>,
vm: &VirtualMachine,
) -> PyResult<PyClassRef> {
// TODO kwds and exec_body parameter
let bases = match bases {
OptionalArg::Present(bases) => bases,
OptionalArg::Missing => PyIterable::try_from_object(vm, vm.ctx.new_tuple(vec![]))?,
};
let dict = PyDict::default().into_ref(vm);
objtype::type_new_class(vm, vm.ctx.type_type(), name, bases, dict)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
py_module!(vm, "types", {
"new_class" => ctx.new_rustfunc(types_new_class),
"FunctionType" => ctx.function_type(),
"MethodType" => ctx.bound_method_type(),
"LambdaType" => ctx.function_type(),
"CodeType" => ctx.code_type(),
"FrameType" => ctx.frame_type()
})
}
+37
View File
@@ -0,0 +1,37 @@
use crate::function::OptionalArg;
use crate::obj::objstr::PyStringRef;
use crate::pyobject::PyObjectRef;
use crate::vm::VirtualMachine;
#[derive(FromArgs)]
struct WarnArgs {
#[pyarg(positional_only, optional = false)]
message: PyStringRef,
#[pyarg(positional_or_keyword, optional = true)]
category: OptionalArg<PyObjectRef>,
#[pyarg(positional_or_keyword, optional = true)]
stacklevel: OptionalArg<u32>,
}
fn warnings_warn(args: WarnArgs, _vm: &VirtualMachine) {
// TODO: Implement correctly
let level = match args.stacklevel {
OptionalArg::Present(l) => l,
OptionalArg::Missing => 1,
};
eprintln!(
"Warning: {} , category: {:?}, level: {}",
args.message.as_str(),
args.category,
level
)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let module = py_module!(vm, "_warnings", {
"warn" => ctx.new_rustfunc(warnings_warn),
});
module
}
+16 -14
View File
@@ -16,7 +16,8 @@ pub fn make_symbol_table(program: &ast::Program) -> Result<SymbolScope, SymbolTa
let mut builder = SymbolTableBuilder::new();
builder.enter_scope();
builder.scan_program(program)?;
assert!(builder.scopes.len() == 1);
assert_eq!(builder.scopes.len(), 1);
let symbol_table = builder.scopes.pop().unwrap();
analyze_symbol_table(&symbol_table, None)?;
Ok(symbol_table)
@@ -28,7 +29,8 @@ pub fn statements_to_symbol_table(
let mut builder = SymbolTableBuilder::new();
builder.enter_scope();
builder.scan_statements(statements)?;
assert!(builder.scopes.len() == 1);
assert_eq!(builder.scopes.len(), 1);
let symbol_table = builder.scopes.pop().unwrap();
analyze_symbol_table(&symbol_table, None)?;
Ok(symbol_table)
@@ -42,8 +44,7 @@ pub enum SymbolRole {
Assigned,
}
/// Symbolscope captures all symbols in the current scope, and
/// has a list of subscopes in this scope.
/// Captures all symbols in the current scope, and has a list of subscopes in this scope.
pub struct SymbolScope {
/// A set of symbols present on this scope level.
pub symbols: HashMap<String, SymbolRole>,
@@ -101,7 +102,6 @@ fn analyze_symbol_table(
symbol_scope: &SymbolScope,
parent_symbol_scope: Option<&SymbolScope>,
) -> SymbolTableResult {
// println!("Analyzing {:?}, parent={:?} symbols={:?}", symbol_scope, parent_symbol_scope, symbol_scope.symbols);
// Analyze sub scopes:
for sub_scope in &symbol_scope.sub_scopes {
analyze_symbol_table(&sub_scope, Some(symbol_scope))?;
@@ -115,6 +115,7 @@ fn analyze_symbol_table(
Ok(())
}
#[allow(clippy::single_match)]
fn analyze_symbol(
symbol_name: &str,
symbol_role: &SymbolRole,
@@ -157,7 +158,6 @@ impl SymbolTableBuilder {
}
pub fn enter_scope(&mut self) {
// Create new scope and push into scope stack.
let scope = SymbolScope::new();
self.scopes.push(scope);
}
@@ -299,13 +299,15 @@ impl SymbolTableBuilder {
ast::Statement::Import { import_parts } => {
for part in import_parts {
if let Some(alias) = &part.alias {
// `import mymodule as myalias`
// `from mymodule import myimportname as myalias`
self.register_name(alias, SymbolRole::Assigned)?;
} else if let Some(symbol) = &part.symbol {
// `from mymodule import myimport`
self.register_name(symbol, SymbolRole::Assigned)?;
} else {
if let Some(symbol) = &part.symbol {
self.register_name(symbol, SymbolRole::Assigned)?;
} else {
self.register_name(&part.module, SymbolRole::Assigned)?;
}
// `import module`
self.register_name(&part.module, SymbolRole::Assigned)?;
}
}
}
@@ -494,7 +496,7 @@ impl SymbolTableBuilder {
}
fn enter_function(&mut self, args: &ast::Parameters) -> SymbolTableResult {
// Evaulate eventual default parameters:
// Evaluate eventual default parameters:
self.scan_expressions(&args.defaults)?;
for kw_default in &args.kw_defaults {
if let Some(expression) = kw_default {
@@ -541,13 +543,13 @@ impl SymbolTableBuilder {
Ok(())
}
#[allow(clippy::single_match)]
fn register_name(&mut self, name: &str, role: SymbolRole) -> SymbolTableResult {
let scope_depth = self.scopes.len();
let current_scope = self.scopes.last_mut().unwrap();
let location = Default::default();
if let Some(_old_role) = current_scope.symbols.get(name) {
if current_scope.symbols.contains_key(name) {
// Role already set..
// debug!("TODO: {:?}", old_role);
match role {
SymbolRole::Global => {
return Err(SymbolTableError {
+86 -8
View File
@@ -4,7 +4,7 @@ use std::{env, mem};
use crate::frame::FrameRef;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objstr::PyStringRef;
use crate::pyobject::{IntoPyObject, ItemProtocol, PyContext, PyObjectRef, PyResult};
use crate::pyobject::{IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult};
use crate::vm::VirtualMachine;
/*
@@ -27,6 +27,44 @@ fn getframe(offset: OptionalArg<usize>, vm: &VirtualMachine) -> PyResult<FrameRe
Ok(frame.clone())
}
/// sys.flags
///
/// Flags provided through command line arguments or environment vars.
#[pystruct_sequence(name = "flags")]
#[derive(Default, Debug)]
struct SysFlags {
/// -d
debug: bool,
/// -i
inspect: bool,
/// -i
interactive: bool,
/// -O or -OO
optimize: u8,
/// -B
dont_write_bytecode: bool,
/// -s
no_user_site: bool,
/// -S
no_site: bool,
/// -E
ignore_environment: bool,
/// -v
verbose: bool,
/// -b
bytes_warning: bool,
/// -q
quiet: bool,
/// -R
hash_randomization: bool,
/// -I
isolated: bool,
/// -X dev
dev_mode: bool,
/// -X utf8
utf8_mode: bool,
}
fn sys_getrefcount(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(object, None)]);
let size = Rc::strong_count(&object);
@@ -40,6 +78,21 @@ fn sys_getsizeof(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
Ok(vm.ctx.new_int(size))
}
fn sys_getfilesystemencoding(_vm: &VirtualMachine) -> String {
// TODO: implmement non-utf-8 mode.
"utf-8".to_string()
}
#[cfg(not(windows))]
fn sys_getfilesystemencodeerrors(_vm: &VirtualMachine) -> String {
"surrogateescape".to_string()
}
#[cfg(windows)]
fn sys_getfilesystemencodeerrors(_vm: &VirtualMachine) -> String {
"surrogatepass".to_string()
}
// TODO implement string interning, this will be key for performance
fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
value
@@ -48,17 +101,38 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) {
let ctx = &vm.ctx;
let path_list = match env::var_os("PYTHONPATH") {
Some(paths) => env::split_paths(&paths)
let flags_type = SysFlags::make_class(ctx);
// TODO parse command line arguments and environment variables to populate SysFlags
let flags = SysFlags::default()
.into_struct_sequence(vm, flags_type)
.unwrap();
// TODO Add crate version to this namespace
let implementation = py_namespace!(vm, {
"name" => ctx.new_str("RustPython".to_string()),
"cache_tag" => ctx.new_str("rustpython-01".to_string()),
});
let path_list = if cfg!(target_arch = "wasm32") {
vec![]
} else {
let get_paths = |paths| match paths {
Some(paths) => env::split_paths(paths),
None => env::split_paths(""),
};
let rustpy_path = env::var_os("RUSTPYTHONPATH");
let py_path = env::var_os("PYTHONPATH");
get_paths(rustpy_path.as_ref())
.chain(get_paths(py_path.as_ref()))
.map(|path| {
ctx.new_str(
path.to_str()
.expect("PYTHONPATH isn't valid unicode")
.to_string(),
path.into_os_string()
.into_string()
.expect("PYTHONPATH isn't valid unicode"),
)
})
.collect(),
None => vec![],
.collect()
};
let path = ctx.new_list(path_list);
@@ -152,8 +226,12 @@ settrace() -- set the global debug tracing function
extend_module!(vm, module, {
"argv" => argv(ctx),
"builtin_module_names" => ctx.new_tuple(module_names.iter().map(|v| v.into_pyobject(vm).unwrap()).collect()),
"flags" => flags,
"getrefcount" => ctx.new_rustfunc(sys_getrefcount),
"getsizeof" => ctx.new_rustfunc(sys_getsizeof),
"implementation" => implementation,
"getfilesystemencoding" => ctx.new_rustfunc(sys_getfilesystemencoding),
"getfilesystemencodeerrors" => ctx.new_rustfunc(sys_getfilesystemencodeerrors),
"intern" => ctx.new_rustfunc(sys_intern),
"maxsize" => ctx.new_int(std::usize::MAX),
"path" => path,
+38 -19
View File
@@ -16,6 +16,7 @@ use crate::builtins;
use crate::bytecode;
use crate::error::CompileError;
use crate::frame::{ExecutionResult, Frame, FrameRef, Scope};
use crate::frozen;
use crate::function::PyFuncArgs;
use crate::obj::objbool;
use crate::obj::objbuiltinfunc::PyBuiltinFunction;
@@ -23,12 +24,14 @@ use crate::obj::objcode::PyCodeRef;
use crate::obj::objdict::PyDictRef;
use crate::obj::objfunction::{PyFunction, PyMethod};
use crate::obj::objgenerator::PyGenerator;
use crate::obj::objint::PyInt;
use crate::obj::objiter;
use crate::obj::objsequence;
use crate::obj::objstr::{PyString, PyStringRef};
use crate::obj::objtuple::PyTupleRef;
use crate::obj::objtype;
use crate::obj::objtype::PyClassRef;
use crate::pyhash;
use crate::pyobject::{
IdProtocol, ItemProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, TryIntoRef,
TypeProtocol,
@@ -51,6 +54,7 @@ pub struct VirtualMachine {
pub frames: RefCell<Vec<FrameRef>>,
pub wasm_id: Option<String>,
pub exceptions: RefCell<Vec<PyObjectRef>>,
pub frozen: RefCell<HashMap<String, &'static str>>,
}
impl VirtualMachine {
@@ -63,6 +67,7 @@ impl VirtualMachine {
let sysmod = ctx.new_module("sys", ctx.new_dict());
let stdlib_inits = RefCell::new(stdlib::get_module_inits());
let frozen = RefCell::new(frozen::get_module_inits());
let vm = VirtualMachine {
builtins: builtins.clone(),
sys_module: sysmod.clone(),
@@ -71,6 +76,7 @@ impl VirtualMachine {
frames: RefCell::new(vec![]),
wasm_id: None,
exceptions: RefCell::new(vec![]),
frozen,
};
builtins::make_module(&vm, builtins.clone());
@@ -165,14 +171,13 @@ impl VirtualMachine {
self.invoke(exc_type.into_object(), args)
}
/// Create Python instance of `exc_type` with message
pub fn new_exception(&self, exc_type: PyClassRef, msg: String) -> PyObjectRef {
// TODO: exc_type may be user-defined exception, so we should return PyResult
// TODO: maybe there is a clearer way to create an instance:
info!("New exception created: {}", msg);
let pymsg = self.new_str(msg);
let args: Vec<PyObjectRef> = vec![pymsg];
// Call function:
self.invoke(exc_type.into_object(), args).unwrap()
}
@@ -250,6 +255,11 @@ impl VirtualMachine {
syntax_error
}
pub fn new_import_error(&self, msg: String) -> PyObjectRef {
let import_error = self.ctx.exceptions.import_error.clone();
self.new_exception(import_error, msg)
}
pub fn new_scope_with_builtins(&self) -> Scope {
Scope::with_builtins(None, self.ctx.new_dict(), self)
}
@@ -396,13 +406,13 @@ impl VirtualMachine {
scope: &Scope,
defaults: &Option<PyTupleRef>,
kw_only_defaults: &Option<PyDictRef>,
args: PyFuncArgs,
func_args: PyFuncArgs,
) -> PyResult {
let scope = scope.child_scope(&self.ctx);
let scope = scope.new_child_scope(&self.ctx);
self.fill_locals_from_args(
&code.code,
&scope.get_locals(),
args,
func_args,
defaults,
kw_only_defaults,
)?;
@@ -426,8 +436,8 @@ impl VirtualMachine {
) -> PyResult {
if let Some(PyFunction { code, scope, .. }) = &function.payload() {
let scope = scope
.child_scope_with_locals(cells)
.child_scope_with_locals(locals);
.new_child_scope_with_locals(cells)
.new_child_scope_with_locals(locals);
let frame = Frame::new(code.clone(), scope).into_ref(self);
return self.run_frame_full(frame);
}
@@ -441,11 +451,11 @@ impl VirtualMachine {
&self,
code_object: &bytecode::CodeObject,
locals: &PyDictRef,
args: PyFuncArgs,
func_args: PyFuncArgs,
defaults: &Option<PyTupleRef>,
kw_only_defaults: &Option<PyDictRef>,
) -> PyResult<()> {
let nargs = args.args.len();
let nargs = func_args.args.len();
let nexpected_args = code_object.arg_names.len();
// This parses the arguments from args and kwargs into
@@ -463,7 +473,7 @@ impl VirtualMachine {
// Copy positional arguments into local variables
for i in 0..n {
let arg_name = &code_object.arg_names[i];
let arg = &args.args[i];
let arg = &func_args.args[i];
locals.set_item(arg_name, arg.clone(), self)?;
}
@@ -472,7 +482,7 @@ impl VirtualMachine {
bytecode::Varargs::Named(ref vararg_name) => {
let mut last_args = vec![];
for i in n..nargs {
let arg = &args.args[i];
let arg = &func_args.args[i];
last_args.push(arg.clone());
}
let vararg_value = self.ctx.new_tuple(last_args);
@@ -502,7 +512,7 @@ impl VirtualMachine {
};
// Handle keyword arguments
for (name, value) in args.kwargs {
for (name, value) in func_args.kwargs {
// Check if we have a parameter with this name:
if code_object.arg_names.contains(&name) || code_object.kwonlyarg_names.contains(&name)
{
@@ -525,7 +535,7 @@ impl VirtualMachine {
// Add missing positional arguments, if we have fewer positional arguments than the
// function definition calls for
if nargs < nexpected_args {
let num_defaults_available = defaults.as_ref().map_or(0, |d| d.elements.borrow().len());
let num_defaults_available = defaults.as_ref().map_or(0, |d| d.elements.len());
// Given the number of defaults available, check all the arguments for which we
// _don't_ have defaults; if any are missing, raise an exception
@@ -545,7 +555,7 @@ impl VirtualMachine {
)));
}
if let Some(defaults) = defaults {
let defaults = defaults.elements.borrow();
let defaults = &defaults.elements;
// We have sufficient defaults, so iterate over the corresponding names and use
// the default if we don't already have a value
for (default_index, i) in (required_args..nexpected_args).enumerate() {
@@ -578,10 +588,10 @@ impl VirtualMachine {
pub fn extract_elements(&self, value: &PyObjectRef) -> PyResult<Vec<PyObjectRef>> {
// Extract elements from item, if possible:
let elements = if objtype::isinstance(value, &self.ctx.tuple_type())
|| objtype::isinstance(value, &self.ctx.list_type())
{
objsequence::get_elements(value).to_vec()
let elements = if objtype::isinstance(value, &self.ctx.tuple_type()) {
objsequence::get_elements_tuple(value).to_vec()
} else if objtype::isinstance(value, &self.ctx.list_type()) {
objsequence::get_elements_list(value).to_vec()
} else {
let iter = objiter::get_iter(self, value)?;
objiter::get_all(self, &iter)?
@@ -912,6 +922,15 @@ impl VirtualMachine {
})
}
pub fn _hash(&self, obj: &PyObjectRef) -> PyResult<pyhash::PyHash> {
let hash_obj = self.call_method(obj, "__hash__", vec![])?;
if objtype::isinstance(&hash_obj, &self.ctx.int_type()) {
Ok(hash_obj.payload::<PyInt>().unwrap().hash(self))
} else {
Err(self.new_type_error("__hash__ method should return an integer".to_string()))
}
}
// https://docs.python.org/3/reference/expressions.html#membership-test-operations
fn _membership_iter_search(&self, haystack: PyObjectRef, needle: PyObjectRef) -> PyResult {
let iter = objiter::get_iter(self, &haystack)?;
@@ -937,7 +956,7 @@ impl VirtualMachine {
}
}
pub fn push_exception(&self, exc: PyObjectRef) -> () {
pub fn push_exception(&self, exc: PyObjectRef) {
self.exceptions.borrow_mut().push(exc)
}
+60 -24
View File
@@ -82,12 +82,6 @@ snippets.addEventListener('change', updateSnippet);
// option selected for the `select`, but the textarea won't be updated)
updateSnippet();
const prompt = '>>>>> ';
const term = new Terminal();
term.open(document.getElementById('terminal'));
term.write(prompt);
function removeNonAscii(str) {
if (str === null || str === '') return false;
else str = str.toString();
@@ -99,38 +93,80 @@ function printToConsole(data) {
term.write(removeNonAscii(data) + '\r\n');
}
const term = new Terminal();
term.open(document.getElementById('terminal'));
const terminalVM = rp.vmStore.init('term_vm');
terminalVM.setStdout(printToConsole);
var input = '';
function getPrompt(name = 'ps1') {
terminalVM.exec(`
try:
import sys as __sys
__prompt = __sys.${name}
except:
__prompt = ''
finally:
del __sys
`);
return String(terminalVM.eval('__prompt'));
}
term.write(getPrompt());
function resetInput() {
continuedInput = [];
input = '';
continuing = false;
}
let continuedInput, input, continuing;
resetInput();
let ps2;
term.on('data', data => {
const code = data.charCodeAt(0);
if (code == 13) {
// CR
if (input[input.length - 1] == ':') {
input += data;
term.write('\r\n.....');
} else {
term.write('\r\n');
try {
terminalVM.execSingle(input);
} catch (err) {
if (err instanceof WebAssembly.RuntimeError) {
err = window.__RUSTPYTHON_ERROR || err;
}
printToConsole(err);
term.write('\r\n');
continuedInput.push(input);
if (continuing) {
if (input === '') {
continuing = false;
} else {
input = '';
term.write(ps2);
return;
}
term.write(prompt);
input = '';
}
} else if (code == 127) {
try {
terminalVM.execSingle(continuedInput.join('\n'));
} catch (err) {
if (err instanceof SyntaxError && err.message.includes('EOF')) {
ps2 = getPrompt('ps2');
term.write(ps2);
continuing = true;
input = '';
return;
} else if (err instanceof WebAssembly.RuntimeError) {
err = window.__RUSTPYTHON_ERROR || err;
}
printToConsole(err);
}
resetInput();
term.write(getPrompt());
} else if (code == 127 || code == 8) {
// Backspace
if (input.length > 0) {
term.write('\b \b');
input = input.slice(0, -1);
}
} else if (code < 32 || code == 127) {
} else if (code < 32) {
// Control
return;
term.write('\r\n' + getPrompt());
input = '';
continuedInput = [];
} else {
// Visible
term.write(data);
+3 -3
View File
@@ -40,10 +40,10 @@ pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyObjectRef) -> JsValue {
});
if let Ok(tb) = vm.get_attribute(py_err.clone(), "__traceback__") {
if objtype::isinstance(&tb, &vm.ctx.list_type()) {
let elements = objsequence::get_elements(&tb).to_vec();
let elements = objsequence::get_elements_list(&tb).to_vec();
if let Some(top) = elements.get(0) {
if objtype::isinstance(&top, &vm.ctx.tuple_type()) {
let element = objsequence::get_elements(&top);
let element = objsequence::get_elements_list(&top);
if let Some(lineno) = objint::to_int(vm, &element[1], 10)
.ok()
@@ -93,7 +93,7 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
let (key, val) = pair?;
py_func_args
.kwargs
.push((js_sys::JsString::from(key).into(), js_to_py(vm, val)));
.insert(js_sys::JsString::from(key).into(), js_to_py(vm, val));
}
}
let result = vm.invoke(py_obj.clone(), py_func_args);
+1 -1
View File
@@ -20,4 +20,4 @@ export PATH=$PATH:$PWD/geckodriver
pip install pipenv
(cd wasm/tests; pipenv install)
(cd wasm/demo; npm install; npm run ci)
(cd wasm/demo; npm install; npm run build; npm run ci)