Skip to content

Commit 60c9d50

Browse files
committed
Add SimpleNamespace and sys.implementation
1 parent 8359c6e commit 60c9d50

File tree

8 files changed

+80
-1
lines changed

8 files changed

+80
-1
lines changed

Lib/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def _f(): pass
1313
LambdaType = type(lambda: None) # Same as FunctionType
1414
CodeType = type(_f.__code__)
1515
MappingProxyType = type(type.__dict__)
16-
# SimpleNamespace = type(sys.implementation)
16+
SimpleNamespace = type(sys.implementation)
1717

1818
def _g():
1919
yield 1

tests/snippets/stdlib_types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import types
2+
3+
from testutils import assertRaises
4+
5+
ns = types.SimpleNamespace(a=2, b='Rust')
6+
7+
assert ns.a == 2
8+
assert ns.b == "Rust"
9+
with assertRaises(AttributeError):
10+
_ = ns.c

tests/snippets/sysmod.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77

88
assert isinstance(sys.builtin_module_names, tuple)
99
assert 'sys' in sys.builtin_module_names
10+
11+
assert isinstance(sys.implementation.name, str)
12+
assert isinstance(sys.implementation.cache_tag, str)

vm/src/macros.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ macro_rules! extend_class {
157157
}
158158
}
159159

160+
#[macro_export]
161+
macro_rules! py_namespace {
162+
( $vm:expr, { $($name:expr => $value:expr),* $(,)* }) => {
163+
{
164+
let namespace = $vm.ctx.new_namespace();
165+
$(
166+
$vm.set_attr(&namespace, $name, $value).unwrap();
167+
)*
168+
namespace
169+
}
170+
}
171+
}
172+
160173
/// Macro to match on the built-in class of a Python object.
161174
///
162175
/// Like `match`, `match_class!` must be exhaustive, so a default arm with

vm/src/obj/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub mod objmap;
2323
pub mod objmappingproxy;
2424
pub mod objmemory;
2525
pub mod objmodule;
26+
pub mod objnamespace;
2627
pub mod objnone;
2728
pub mod objobject;
2829
pub mod objproperty;

vm/src/obj/objnamespace.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::function::KwArgs;
2+
use crate::obj::objtype::PyClassRef;
3+
use crate::pyobject::{PyClassImpl, PyContext, PyRef, PyResult, PyValue};
4+
use crate::vm::VirtualMachine;
5+
6+
/// A simple attribute-based namespace.
7+
///
8+
/// SimpleNamespace(**kwargs)
9+
#[pyclass(name = "SimpleNamespace")]
10+
#[derive(Debug)]
11+
pub struct PyNamespace;
12+
13+
impl PyValue for PyNamespace {
14+
fn class(vm: &VirtualMachine) -> PyClassRef {
15+
vm.ctx.namespace_type()
16+
}
17+
}
18+
19+
#[pyimpl]
20+
impl PyNamespace {
21+
#[pymethod(name = "__init__")]
22+
fn init(zelf: PyRef<PyNamespace>, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult<()> {
23+
for (name, value) in kwargs.into_iter() {
24+
vm.set_attr(zelf.as_object(), name, value)?;
25+
}
26+
Ok(())
27+
}
28+
}
29+
30+
pub fn init(context: &PyContext) {
31+
PyNamespace::extend_class(context, &context.namespace_type);
32+
}

vm/src/pyobject.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::obj::objmap;
4040
use crate::obj::objmappingproxy;
4141
use crate::obj::objmemory;
4242
use crate::obj::objmodule::{self, PyModule};
43+
use crate::obj::objnamespace::{self, PyNamespace};
4344
use crate::obj::objnone::{self, PyNone, PyNoneRef};
4445
use crate::obj::objobject;
4546
use crate::obj::objproperty;
@@ -158,6 +159,7 @@ pub struct PyContext {
158159
pub property_type: PyClassRef,
159160
pub readonly_property_type: PyClassRef,
160161
pub module_type: PyClassRef,
162+
pub namespace_type: PyClassRef,
161163
pub bound_method_type: PyClassRef,
162164
pub weakref_type: PyClassRef,
163165
pub weakproxy_type: PyClassRef,
@@ -250,6 +252,7 @@ impl PyContext {
250252

251253
let dict_type = create_type("dict", &type_type, &object_type);
252254
let module_type = create_type("module", &type_type, &object_type);
255+
let namespace_type = create_type("SimpleNamespace", &type_type, &object_type);
253256
let classmethod_type = create_type("classmethod", &type_type, &object_type);
254257
let staticmethod_type = create_type("staticmethod", &type_type, &object_type);
255258
let function_type = create_type("function", &type_type, &object_type);
@@ -366,6 +369,7 @@ impl PyContext {
366369
readonly_property_type,
367370
generator_type,
368371
module_type,
372+
namespace_type,
369373
bound_method_type,
370374
weakref_type,
371375
weakproxy_type,
@@ -407,6 +411,7 @@ impl PyContext {
407411
objweakproxy::init(&context);
408412
objnone::init(&context);
409413
objmodule::init(&context);
414+
objnamespace::init(&context);
410415
objmappingproxy::init(&context);
411416
exceptions::init(&context);
412417
context
@@ -464,6 +469,10 @@ impl PyContext {
464469
self.module_type.clone()
465470
}
466471

472+
pub fn namespace_type(&self) -> PyClassRef {
473+
self.namespace_type.clone()
474+
}
475+
467476
pub fn set_type(&self) -> PyClassRef {
468477
self.set_type.clone()
469478
}
@@ -658,6 +667,10 @@ impl PyContext {
658667
)
659668
}
660669

670+
pub fn new_namespace(&self) -> PyObjectRef {
671+
PyObject::new(PyNamespace, self.namespace_type(), Some(self.new_dict()))
672+
}
673+
661674
pub fn new_rustfunc<F, T, R>(&self, f: F) -> PyObjectRef
662675
where
663676
F: IntoPyNativeFunc<T, R>,

vm/src/sysmodule.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ fn sys_intern(value: PyStringRef, _vm: &VirtualMachine) -> PyStringRef {
4848
pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) {
4949
let ctx = &vm.ctx;
5050

51+
// TODO Add crate version to this namespace
52+
let implementation = py_namespace!(vm, {
53+
"name" => ctx.new_str("RustPython".to_string()),
54+
"cache_tag" => ctx.new_str("rustpython-01".to_string()),
55+
});
56+
5157
let path_list = match env::var_os("PYTHONPATH") {
5258
Some(paths) => env::split_paths(&paths)
5359
.map(|path| {
@@ -154,6 +160,7 @@ settrace() -- set the global debug tracing function
154160
"builtin_module_names" => ctx.new_tuple(module_names.iter().map(|v| v.into_pyobject(vm).unwrap()).collect()),
155161
"getrefcount" => ctx.new_rustfunc(sys_getrefcount),
156162
"getsizeof" => ctx.new_rustfunc(sys_getsizeof),
163+
"implementation" => implementation,
157164
"intern" => ctx.new_rustfunc(sys_intern),
158165
"maxsize" => ctx.new_int(std::usize::MAX),
159166
"path" => path,

0 commit comments

Comments
 (0)