Skip to content

Commit 38a8e05

Browse files
committed
Merge branch 'master' of https://github.com/RustPython/RustPython into feature-symtable
2 parents 12df480 + c23f279 commit 38a8e05

File tree

5 files changed

+120
-12
lines changed

5 files changed

+120
-12
lines changed

Lib/test/test_sys_executable.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import unittest
2+
import subprocess
3+
import sys
4+
import os
5+
6+
# These should go into a test_sys.py file, but given these are the only
7+
# ones being tested currently that seems unnecessary.
8+
9+
10+
class SysExecutableTest(unittest.TestCase):
11+
12+
# This is a copy of test.test_sys.SysModuleTest.test_executable from cpython.
13+
def cpython_tests(self):
14+
# sys.executable should be absolute
15+
self.assertEqual(os.path.abspath(sys.executable), sys.executable)
16+
17+
# Issue #7774: Ensure that sys.executable is an empty string if argv[0]
18+
# has been set to a non existent program name and Python is unable to
19+
# retrieve the real program name
20+
21+
# For a normal installation, it should work without 'cwd'
22+
# argument. For test runs in the build directory, see #7774.
23+
python_dir = os.path.dirname(os.path.realpath(sys.executable))
24+
p = subprocess.Popen(
25+
[
26+
"nonexistent",
27+
"-c",
28+
'import sys; print(sys.executable.encode("ascii", "backslashreplace"))',
29+
],
30+
executable=sys.executable,
31+
stdout=subprocess.PIPE,
32+
cwd=python_dir,
33+
)
34+
stdout = p.communicate()[0]
35+
executable = stdout.strip().decode("ASCII")
36+
p.wait()
37+
self.assertIn(
38+
executable,
39+
["b''", repr(sys.executable.encode("ascii", "backslashreplace"))],
40+
)
41+
42+
def test_no_follow_symlink(self):
43+
paths = [os.path.abspath("test_symlink"), "./test_symlink"]
44+
for path in paths:
45+
with self.subTest(path=path):
46+
os.symlink(sys.executable, path)
47+
command = [
48+
path,
49+
"-c",
50+
"import sys; print(sys.executable, end='')",
51+
]
52+
try:
53+
process = subprocess.run(command, capture_output=True)
54+
finally:
55+
os.remove(path)
56+
self.assertEqual(
57+
os.path.abspath(path), process.stdout.decode("utf-8")
58+
)
59+
60+
61+
if __name__ == "__main__":
62+
unittest.main()

derive/src/pymodule.rs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
1515

1616
#[derive(Default)]
1717
struct Module {
18-
items: HashSet<ModuleItem>,
18+
items: HashSet<(ModuleItem, Vec<Meta>)>,
1919
}
2020

2121
#[derive(PartialEq, Eq, Hash)]
@@ -25,13 +25,13 @@ enum ModuleItem {
2525
}
2626

2727
impl Module {
28-
fn add_item(&mut self, item: ModuleItem, span: Span) -> Result<(), Diagnostic> {
28+
fn add_item(&mut self, item: (ModuleItem, Vec<Meta>), span: Span) -> Result<(), Diagnostic> {
2929
if self.items.insert(item) {
3030
Ok(())
3131
} else {
3232
Err(Diagnostic::span_error(
3333
span,
34-
"Duplicate #[py*] attribute on pyimpl".to_owned(),
34+
"Duplicate #[py*] attribute on pymodule".to_owned(),
3535
))
3636
}
3737
}
@@ -70,12 +70,35 @@ impl Module {
7070
})
7171
}
7272

73+
fn extract_struct_sequence(ident: &Ident, meta: Meta) -> Result<ModuleItem, Diagnostic> {
74+
let nesteds = meta_to_vec(meta).map_err(|meta| {
75+
err_span!(
76+
meta,
77+
"#[pystruct_sequence = \"...\"] cannot be a name/value, you probably meant \
78+
#[pystruct_sequence(name = \"...\")]",
79+
)
80+
})?;
81+
82+
let item_meta = ItemMeta::from_nested_meta(
83+
"pystruct_sequence",
84+
&ident,
85+
&nesteds,
86+
ItemMeta::STRUCT_SEQUENCE_NAMES,
87+
)?;
88+
Ok(ModuleItem::Class {
89+
item_ident: ident.clone(),
90+
py_name: item_meta.simple_name()?,
91+
})
92+
}
93+
7394
fn extract_item_from_syn(
7495
&mut self,
7596
attrs: &mut Vec<Attribute>,
7697
ident: &Ident,
7798
) -> Result<(), Diagnostic> {
7899
let mut attr_idxs = Vec::new();
100+
let mut items = Vec::new();
101+
let mut cfgs = Vec::new();
79102
for (i, meta) in attrs
80103
.iter()
81104
.filter_map(|attr| attr.parse_meta().ok())
@@ -86,17 +109,28 @@ impl Module {
86109
Some(name) => name,
87110
None => continue,
88111
};
89-
let item = match name.to_string().as_str() {
112+
match name.to_string().as_str() {
90113
"pyfunction" => {
91114
attr_idxs.push(i);
92-
Self::extract_function(ident, meta)?
115+
items.push((Self::extract_function(ident, meta)?, meta_span));
116+
}
117+
"pyclass" => {
118+
items.push((Self::extract_class(ident, meta)?, meta_span));
119+
}
120+
"pystruct_sequence" => {
121+
items.push((Self::extract_struct_sequence(ident, meta)?, meta_span));
122+
}
123+
"cfg" => {
124+
cfgs.push(meta);
125+
continue;
93126
}
94-
"pyclass" => Self::extract_class(ident, meta)?,
95127
_ => {
96128
continue;
97129
}
98130
};
99-
self.add_item(item, meta_span)?;
131+
}
132+
for (item, meta) in items {
133+
self.add_item((item, cfgs.clone()), meta)?;
100134
}
101135
let mut i = 0;
102136
let mut attr_idxs = &*attr_idxs;
@@ -130,7 +164,7 @@ fn extract_module_items(
130164
);
131165
}
132166

133-
let functions = module.items.into_iter().map(|item| match item {
167+
let functions = module.items.into_iter().map(|(item, cfgs)| match item {
134168
ModuleItem::Function {
135169
item_ident,
136170
py_name,
@@ -140,6 +174,7 @@ fn extract_module_items(
140174
#module_name.to_owned(),
141175
#py_name.to_owned()));
142176
quote! {
177+
#( #[ #cfgs ])*
143178
vm.__module_set_attr(&module, #py_name, vm.ctx#new_func).unwrap();
144179
}
145180
}

derive/src/util.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ pub struct ItemMeta<'a> {
8888

8989
impl<'a> ItemMeta<'a> {
9090
pub const SIMPLE_NAMES: &'static [&'static str] = &["name"];
91+
pub const STRUCT_SEQUENCE_NAMES: &'static [&'static str] = &["module", "name"];
9192
pub const ATTRIBUTE_NAMES: &'static [&'static str] = &["name", "magic"];
9293
pub const PROPERTY_NAMES: &'static [&'static str] = &["name", "magic", "setter"];
9394

vm/src/frozen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub fn get_module_inits() -> HashMap<String, FrozenModule> {
1818
// Python modules that the vm calls into, but are not actually part of the stdlib. They could
1919
// in theory be implemented in Rust, but are easiest to do in Python for one reason or another.
2020
// Includes _importlib_bootstrap and _importlib_bootstrap_external
21+
// For Windows: did you forget to run `powershell scripts\symlinks-to-hardlinks.ps1`?
2122
ext_modules!(dir = "Lib/python_builtins/");
2223

2324
#[cfg(not(feature = "freeze-stdlib"))]

vm/src/sysmodule.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{env, mem};
1+
use std::{env, mem, path};
22

33
use crate::builtins;
44
use crate::frame::FrameRef;
@@ -28,9 +28,18 @@ fn argv(vm: &VirtualMachine) -> PyObjectRef {
2828
}
2929

3030
fn executable(ctx: &PyContext) -> PyObjectRef {
31-
if let Ok(path) = env::current_exe() {
32-
if let Ok(path) = path.into_os_string().into_string() {
33-
return ctx.new_str(path);
31+
if let Some(exec_path) = env::args().next() {
32+
if path::Path::new(&exec_path).is_absolute() {
33+
return ctx.new_str(exec_path);
34+
}
35+
if let Ok(dir) = env::current_dir() {
36+
if let Ok(dir) = dir.into_os_string().into_string() {
37+
return ctx.new_str(format!(
38+
"{}/{}",
39+
dir,
40+
exec_path.strip_prefix("./").unwrap_or(&exec_path)
41+
));
42+
}
3443
}
3544
}
3645
ctx.none()

0 commit comments

Comments
 (0)