diff --git a/Cargo.lock b/Cargo.lock index 1194fd899..f81dfa142 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -473,7 +473,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)", @@ -838,7 +838,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)", ] @@ -913,14 +913,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)", @@ -1457,7 +1457,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" @@ -1500,7 +1500,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" diff --git a/Cargo.toml b/Cargo.toml index ef2838a1c..71db1d839 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/Lib/types.py b/Lib/types.py index 4ef461961..a7034901c 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -13,7 +13,7 @@ FunctionType = type(_f) LambdaType = type(lambda: None) # Same as FunctionType CodeType = type(_f.__code__) MappingProxyType = type(type.__dict__) -# SimpleNamespace = type(sys.implementation) +SimpleNamespace = type(sys.implementation) def _g(): yield 1 diff --git a/tests/snippets/stdlib_types.py b/tests/snippets/stdlib_types.py new file mode 100644 index 000000000..b8011bf40 --- /dev/null +++ b/tests/snippets/stdlib_types.py @@ -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 diff --git a/tests/snippets/sysmod.py b/tests/snippets/sysmod.py index 423ecf08d..409d961ed 100644 --- a/tests/snippets/sysmod.py +++ b/tests/snippets/sysmod.py @@ -7,3 +7,6 @@ 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) diff --git a/tests/snippets/test_re.py b/tests/snippets/test_re.py index 2463f380b..2bc7308e6 100644 --- a/tests/snippets/test_re.py +++ b/tests/snippets/test_re.py @@ -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' diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 333673985..c3267ff15 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -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 diff --git a/vm/src/obj/mod.rs b/vm/src/obj/mod.rs index c9401d8c4..ad7af74d5 100644 --- a/vm/src/obj/mod.rs +++ b/vm/src/obj/mod.rs @@ -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; diff --git a/vm/src/obj/objnamespace.rs b/vm/src/obj/objnamespace.rs new file mode 100644 index 000000000..f8bfc9369 --- /dev/null +++ b/vm/src/obj/objnamespace.rs @@ -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, 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); +} diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 722588b3f..afaad09b6 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -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; @@ -158,6 +159,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 +252,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); @@ -366,6 +369,7 @@ impl PyContext { readonly_property_type, generator_type, module_type, + namespace_type, bound_method_type, weakref_type, weakproxy_type, @@ -407,6 +411,7 @@ impl PyContext { objweakproxy::init(&context); objnone::init(&context); objmodule::init(&context); + objnamespace::init(&context); objmappingproxy::init(&context); exceptions::init(&context); context @@ -464,6 +469,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 +667,10 @@ impl PyContext { ) } + pub fn new_namespace(&self) -> PyObjectRef { + PyObject::new(PyNamespace, self.namespace_type(), Some(self.new_dict())) + } + pub fn new_rustfunc(&self, f: F) -> PyObjectRef where F: IntoPyNativeFunc, diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index b9f921502..f42738980 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -79,6 +79,10 @@ fn re_compile(pattern: PyStringRef, vm: &VirtualMachine) -> PyResult { 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, diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8c6bafe74..aefdb117b 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -48,6 +48,12 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef { pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) { let ctx = &vm.ctx; + // 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 = match env::var_os("PYTHONPATH") { Some(paths) => env::split_paths(&paths) .map(|path| { @@ -154,6 +160,7 @@ settrace() -- set the global debug tracing function "builtin_module_names" => ctx.new_tuple(module_names.iter().map(|v| v.into_pyobject(vm).unwrap()).collect()), "getrefcount" => ctx.new_rustfunc(sys_getrefcount), "getsizeof" => ctx.new_rustfunc(sys_getsizeof), + "implementation" => implementation, "intern" => ctx.new_rustfunc(sys_intern), "maxsize" => ctx.new_int(std::usize::MAX), "path" => path,