Skip to content
Open
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
117 changes: 59 additions & 58 deletions server/src/core/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ impl Evaluation {
evals.push(Evaluation::new_unbound(name));
}
},
ExprOrIdent::Expr(Expr::Subscript(sub)) => 'subscript_block: {
ExprOrIdent::Expr(Expr::Subscript(sub)) => {
let (eval_left, diags) = Evaluation::eval_from_ast(session, &sub.value, parent.clone(), max_infer, false, required_dependencies);
diagnostics.extend(diags);
// TODO handle multiple eval_left
Expand All @@ -1161,73 +1161,74 @@ impl Evaluation {
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
}
let bases = Symbol::follow_ref(&base, session, &mut None, false, false, None);
if bases.len() != 1 {
if bases.is_empty() {
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
}
let base = &bases[0];
match base {
EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => {
if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) {
// This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List)
// TODO: handle generic types
let mut new_base = base.clone();
if for_annotation {
new_base.as_mut_weak().instance = Some(true);
for base in &bases {
match base {
EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => {
if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) {
// This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List)
// TODO: handle generic types
let mut new_base = base.clone();
if for_annotation {
new_base.as_mut_weak().instance = Some(true);
}
evals.push(Evaluation {
symbol: EvaluationSymbol {
sym: new_base,
get_symbol_hook: None,
},
value: None,
range: Some(sub.range())
});
continue;
}
evals.push(Evaluation {
symbol: EvaluationSymbol {
sym: new_base,
get_symbol_hook: None,
},
value: None,
range: Some(sub.range())
});
break 'subscript_block;
}
_ => {}
}
_ => {}
}
let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, false, &mut diagnostics);
diagnostics.extend(value.1);
if let Some(value) = value.0 {
if !base.is_weak() {
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
}
let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap();
let is_in_validation = match parent_file_or_func.borrow().typ().clone() {
SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => {
parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS
},
_ => {false}
};
let base = base.upgrade_weak().unwrap();
let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols;
if get_item.len() == 1 {
let get_item = &get_item[0];
let get_item = get_item.borrow();
if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 {
let get_item_eval = &get_item.evaluations().unwrap()[0];
if let Some(hook) = get_item_eval.symbol.get_symbol_hook {
context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value));
let old_range = context.as_mut().unwrap().remove(&S!("range"));
context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range()));
context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation));
let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone()));
if let Some(hook_result) = hook_result {
match hook_result {
EvaluationSymbolPtr::WEAK(ref weak) => {
if !weak.weak.is_expired() {
let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, false, &mut diagnostics);
diagnostics.extend(value.1);
if let Some(value) = value.0 {
if !base.is_weak() {
continue;
}
let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap();
let is_in_validation = match parent_file_or_func.borrow().typ().clone() {
SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => {
parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS
},
_ => {false}
};
let base = base.upgrade_weak().unwrap();
let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols;
if get_item.len() == 1 {
let get_item = &get_item[0];
let get_item = get_item.borrow();
if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 {
let get_item_eval = &get_item.evaluations().unwrap()[0];
if let Some(hook) = get_item_eval.symbol.get_symbol_hook {
context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value));
let old_range = context.as_mut().unwrap().remove(&S!("range"));
context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range()));
context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation));
let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone()));
if let Some(hook_result) = hook_result {
match hook_result {
EvaluationSymbolPtr::WEAK(ref weak) => {
if !weak.weak.is_expired() {
evals.push(Evaluation::eval_from_ptr(&hook_result));
}
},
_ => {
evals.push(Evaluation::eval_from_ptr(&hook_result));
}
},
_ => {
evals.push(Evaluation::eval_from_ptr(&hook_result));
}
}
context.as_mut().unwrap().remove(&S!("args"));
context.as_mut().unwrap().remove(&S!("is_in_validation"));
context.as_mut().unwrap().insert(S!("range"), old_range.unwrap());
}
context.as_mut().unwrap().remove(&S!("args"));
context.as_mut().unwrap().remove(&S!("is_in_validation"));
context.as_mut().unwrap().insert(S!("range"), old_range.unwrap());
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions server/src/core/python_arch_builder_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,27 @@ static arch_class_hooks: Lazy<Vec<PythonArchClassHook>> = Lazy::new(|| {vec![
let mut range = symbol.borrow().range().clone();
let slots = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__slots__")]), u32::MAX);
if slots.len() == 1 {
if slots.len() == 1 {
range = slots[0].borrow().range().clone();
}
range = slots[0].borrow().range().clone();
}
symbol.borrow_mut().add_new_variable(session, Sy!("env"), &range);
}
}
},
PythonArchClassHook {
odoo_entry: true,
trees: vec![
(Sy!("15.3"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("http")], vec![Sy!("Request")]))
],
func: |session: &mut SessionInfo, _entry_point: &Rc<RefCell<EntryPoint>>, request_class: Rc<RefCell<Symbol>>| {
// ----------- Request.env ------------
let has_env = !request_class.borrow().get_content_symbol(&Sy!("env"), u32::MAX).symbols.is_empty();
if has_env {
return;
}
let range = request_class.borrow().range().clone();
request_class.borrow_mut().add_new_variable(session, Sy!("env"), &range);
}
},
PythonArchClassHook {
odoo_entry: true,
trees: vec![
Expand Down
48 changes: 48 additions & 0 deletions server/src/core/python_arch_eval_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,54 @@ static arch_eval_file_hooks: Lazy<Vec<PythonArchEvalFileHook>> = Lazy::new(|| {v
env.set_doc_string(Some(S!("")));
}
}},
PythonArchEvalFileHook {odoo_entry: true,
trees: vec![(Sy!("0.0"), Sy!("15.3"), (vec![Sy!("odoo"), Sy!("http")], vec![Sy!("request")]))],
if_exist_only: true,
func: |_odoo: &mut SessionInfo, _entry: &Rc<RefCell<EntryPoint>>, file_symbol: Rc<RefCell<Symbol>>, symbol: Rc<RefCell<Symbol>>| {
// --------- request: WebRequest (before 15.3) ---------
let web_request_class = file_symbol.borrow().get_symbol(&(vec![], vec![Sy!("WebRequest")]), u32::MAX);
let Some(web_request_class) = web_request_class.last() else {
return;
};
let mut request = symbol.borrow_mut();
request.set_evaluations(vec![Evaluation::eval_from_symbol(&Rc::downgrade(web_request_class), Some(true))]);
}},
PythonArchEvalFileHook {odoo_entry: true,
trees: vec![(Sy!("15.3"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("http")], vec![Sy!("request")]))],
if_exist_only: true,
func: |_odoo: &mut SessionInfo, _entry: &Rc<RefCell<EntryPoint>>, file_symbol: Rc<RefCell<Symbol>>, symbol: Rc<RefCell<Symbol>>| {
// --------- request: Request (15.3+) ---------
let request_class = file_symbol.borrow().get_symbol(&(vec![], vec![Sy!("Request")]), u32::MAX);
let Some(request_class) = request_class.last() else {
return;
};
let mut request = symbol.borrow_mut();
request.set_evaluations(vec![Evaluation::eval_from_symbol(&Rc::downgrade(request_class), Some(true))]);
}},
PythonArchEvalFileHook {odoo_entry: true,
trees: vec![
(Sy!("0"), Sy!("15.3"), (vec![Sy!("odoo"), Sy!("http")], vec![Sy!("WebRequest"), Sy!("env")])),
(Sy!("15.3"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("http")], vec![Sy!("Request"), Sy!("env")]))
],
if_exist_only: true,
func: |odoo: &mut SessionInfo, _entry: &Rc<RefCell<EntryPoint>>, file_symbol: Rc<RefCell<Symbol>>, symbol: Rc<RefCell<Symbol>>| {
// --------- (Web)Request.env: Environment | None ---------
let env_file = odoo.sync_odoo.get_symbol(odoo.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("api")], vec![]), u32::MAX);
let Some(env_file) = env_file.last() else {
return;
};
let env_class = env_file.borrow().get_symbol(&(vec![], vec![Sy!("Environment")]), u32::MAX);
let Some(env_class) = env_class.last() else {
return;
};
// env is a property (function) before 15.3, and an instance variable in 15.3.
// In both cases the evaluation is Environment | None.
symbol.borrow_mut().set_evaluations(vec![
Evaluation::eval_from_symbol(&Rc::downgrade(env_class), Some(true)),
Evaluation::new_none()
]);
file_symbol.borrow_mut().add_dependency(&mut env_file.borrow_mut(), BuildSteps::ARCH_EVAL, BuildSteps::ARCH);
}},
PythonArchEvalFileHook {odoo_entry: true,
trees: vec![(Sy!("0.0"), Sy!("18.1"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("ids")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("ids")]))],
Expand Down
3 changes: 2 additions & 1 deletion server/tests/data/addons/module_1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import models
from . import data
from . import data
from . import controllers
2 changes: 2 additions & 0 deletions server/tests/data/addons/module_1/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import main
36 changes: 36 additions & 0 deletions server/tests/data/addons/module_1/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from odoo import http
from odoo.http import request


class TestController(http.Controller):

@http.route('/test/request', type='http', auth='public')
def test_request_type(self):
"""Test that request has correct type."""
# Hovering over 'request' should show Request class
req = request

# Hovering over 'request.env' should show Environment | None
env = request.env

# Accessing models via request.env
if request.env:
partner = request.env['res.partner']
users = request.env['res.users']

return "OK"

@http.route('/test/request/search', type='http', auth='user')
def test_request_env_usage(self):
"""Test using request.env in typical scenarios."""
# Direct model access
partners = request.env['res.partner'].search([])

# With sudo
admin_partners = request.env['res.partner'].sudo().search([])

# Accessing user
current_user = request.env.user

return "OK"
Loading