Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ pub struct Program {
pub statements: Vec<Statement>,
}

#[derive(Debug, PartialEq)]
pub struct SingleImport {
pub module: String,
// (symbol name in module, name it should be assigned locally)
pub symbol: Option<String>,
pub alias: Option<String>,
}

#[derive(Debug, PartialEq)]
pub enum Statement {
Break,
Expand All @@ -28,7 +36,7 @@ pub enum Statement {
value: Option<Vec<Expression>>,
},
Import {
name: String,
import_parts: Vec<SingleImport>,
},
Pass,
Assert {
Expand Down
33 changes: 32 additions & 1 deletion parser/src/python.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,37 @@ FlowStatement: ast::Statement = {
};

ImportStatement: ast::Statement = {
"import" <n:DottedName> => ast::Statement::Import { name: n },
"import" <i: Comma<ImportPart<<DottedName>>>> => {
ast::Statement::Import {
import_parts: i
.iter()
.map(|(n, a)|
ast::SingleImport {
module: n.to_string(),
symbol: None,
alias: a.clone()
})
.collect()
}
},
"from" <n:DottedName> "import" <i: Comma<ImportPart<Identifier>>> => {
ast::Statement::Import {
import_parts: i
.iter()
.map(|(i, a)|
ast::SingleImport {
module: n.to_string(),
symbol: Some(i.to_string()),
alias: a.clone()
})
.collect()
}
},
};

#[inline]
ImportPart<I>: (String, Option<String>) = {
<i:I> <a: ("as" Identifier)?> => (i, a.map(|a| a.1)),
};

DottedName: String = {
Expand Down Expand Up @@ -436,6 +466,7 @@ extern {
"in" => lexer::Tok::In,
"is" => lexer::Tok::Is,
"import" => lexer::Tok::Import,
"from" => lexer::Tok::From,
"not" => lexer::Tok::Not,
"or" => lexer::Tok::Or,
"pass" => lexer::Tok::Pass,
Expand Down
21 changes: 21 additions & 0 deletions tests/snippets/import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import import_target, import_target as aliased
from import_target import func, other_func
from import_target import func as aliased_func, other_func as aliased_other_func

assert import_target.X == import_target.func()
assert import_target.X == func()

assert import_target.Y == other_func()

assert import_target.X == aliased.X
assert import_target.Y == aliased.Y

assert import_target.X == aliased_func()
assert import_target.Y == aliased_other_func()

#try:
# X
#except NameError:
# pass
#else:
# raise AssertionError('X should not be imported')
10 changes: 10 additions & 0 deletions tests/snippets/import_target.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This is used by import.py; the two should be modified in concert

X = '123'
Y = 'abc'

def func():
return X

def other_func():
return Y
10 changes: 8 additions & 2 deletions tests/test_snippets.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ def perform_test(filename, method, test_type):

def run_via_cpython(filename):
""" Simply invoke python itself on the script """
subprocess.check_call([sys.executable, filename])
env = os.environ.copy()
env['PYTHONPATH'] = '.'
subprocess.check_call([sys.executable, filename], env=env)


def run_via_cpython_bytecode(filename, test_type):
Expand All @@ -76,8 +78,12 @@ def run_via_rustpython(filename, test_type):
log_level = 'info' if test_type == _TestType.benchmark else 'trace'
env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level)
env['RUST_BACKTRACE'] = '1'
# XXX: Once we support PYTHONPATH (or similar), we should use that instead
# of changing directory
cwd = os.path.dirname(filename)
with pushd(RUSTPYTHON_RUNNER_DIR):
subprocess.check_call(['cargo', 'run', '--release', filename], env=env)
subprocess.check_call(
['cargo', 'run', '--release', filename], env=env, cwd=cwd)


def create_test_function(cls, filename, method, test_type):
Expand Down
5 changes: 4 additions & 1 deletion vm/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ pub type Label = usize;

#[derive(Debug, Clone)]
pub enum Instruction {
Import { name: String },
Import {
name: String,
symbol: Option<String>,
},
LoadName { name: String },
StoreName { name: String },
StoreSubscript,
Expand Down
24 changes: 21 additions & 3 deletions vm/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,27 @@ impl Compiler {
fn compile_statement(&mut self, statement: &ast::Statement) {
trace!("Compiling {:?}", statement);
match statement {
ast::Statement::Import { name } => {
self.emit(Instruction::Import { name: name.clone() });
self.emit(Instruction::StoreName { name: name.clone() });
ast::Statement::Import { import_parts } => {
for ast::SingleImport {
module,
symbol,
alias,
} in import_parts
{
self.emit(Instruction::Import {
name: module.clone(),
symbol: symbol.clone().map(|s| s.clone()),
});
self.emit(Instruction::StoreName {
name: match alias {
Some(alias) => alias.clone(),
None => match symbol {
Some(symbol) => symbol.clone(),
None => module.clone(),
},
},
});
}
}
ast::Statement::Expression { expression } => {
self.compile_expression(expression);
Expand Down
31 changes: 20 additions & 11 deletions vm/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use super::compile;
use super::pyobject::{PyObject, PyObjectKind, PyResult, DictProtocol};
use super::vm::VirtualMachine;

pub fn import(vm: &mut VirtualMachine, name: &String) -> PyResult {
fn import_module(vm: &mut VirtualMachine, module: &String) -> PyResult {
// First, see if we already loaded the module:
let sys_modules = vm.sys_module.get_item(&"modules".to_string());
if sys_modules.contains_key(name) {
return Ok(sys_modules.get_item(name))
if sys_modules.contains_key(module) {
return Ok(sys_modules.get_item(module));
}

// Time to search for module in any place:
let filepath = find_source(name).map_err(|e| vm.new_exception(format!("Error: {:?}", e)))?;
let filepath = find_source(module).map_err(|e| vm.new_exception(format!("Error: {:?}", e)))?;
let source = parser::read_file(filepath.as_path())
.map_err(|e| vm.new_exception(format!("Error: {:?}", e)))?;

Expand All @@ -42,14 +42,23 @@ pub fn import(vm: &mut VirtualMachine, name: &String) -> PyResult {
Ok(_) => {}
Err(value) => return Err(value),
}
Ok(scope)
}

let obj = PyObject::new(
PyObjectKind::Module {
name: name.clone(),
dict: scope.clone(),
},
vm.get_type(),
);
pub fn import(vm: &mut VirtualMachine, module: &String, symbol: &Option<String>) -> PyResult {
let scope = import_module(vm, module)?;
// If we're importing a symbol, look it up and use it, otherwise construct a module and return
// that
let obj = match symbol {
Some(symbol) => scope.get_item(symbol),
None => PyObject::new(
PyObjectKind::Module {
name: module.clone(),
dict: scope.clone(),
},
vm.get_type(),
),
};
Ok(obj)
}

Expand Down
9 changes: 6 additions & 3 deletions vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,8 @@ impl VirtualMachine {
}
}

fn import(&mut self, name: &String) -> Option<PyResult> {
let obj = match import(self, name) {
fn import(&mut self, module: &String, symbol: &Option<String>) -> Option<PyResult> {
let obj = match import(self, module, symbol) {
Ok(value) => value,
Err(value) => return Some(Err(value)),
};
Expand Down Expand Up @@ -564,7 +564,10 @@ impl VirtualMachine {
self.push_value(obj);
None
}
bytecode::Instruction::Import { ref name } => self.import(name),
bytecode::Instruction::Import {
ref name,
ref symbol,
} => self.import(name, symbol),
bytecode::Instruction::LoadName { ref name } => self.load_name(name),
bytecode::Instruction::StoreName { ref name } => {
// take top of stack and assign in scope:
Expand Down