Skip to content

XML Support - Part 2 #325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
16 changes: 16 additions & 0 deletions server/error_code.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,18 @@ For compute, search, inverse arguments, this error is shown when the method is n
The compute method is set to modify a certain field(s).
Consider marking the modified field with the compute method

### OLS30329
"Unknown XML ID"
The XML ID you referenced has not been found in any XML in this module or its dependencies

### OLS30330
"Unspecified module. Add the module name before the XML ID: 'module.xml_id'"
Your provided an XML ID that has no module specified. Specify the module which XML_ID belong to with 'module.xml_id'

### OLS30331
"Unknown module"
The given module is unknown

### OLS30400
"Invalid attribute"
odoo, openerp and data nodes can not contain this attribute.
Expand Down Expand Up @@ -304,3 +316,7 @@ This attribute is not valid in a menuitem node

### OLS30445
"Data file should be an XML or a CSV file"

### OLS30446
"XML ID should not contain more than one dot"
An XML_ID should be in the format 'xml_id' or 'module.xml_id', but can't contains more dots
18 changes: 14 additions & 4 deletions server/src/core/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -931,12 +931,9 @@ impl Evaluation {
if base_sym.borrow().evaluations().is_some() {
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::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => {
parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS
},
SymType::FUNCTION => {
true //functions are always evaluated at validation step
}
_ => {false}
};
let call_parent = match base_sym_weak_eval.context.get(&S!("base_attr")){
Expand All @@ -959,6 +956,8 @@ impl Evaluation {
));
}
context.as_mut().unwrap().insert(S!("base_call"), ContextValue::SYMBOL(call_parent));
context.as_mut().unwrap().insert(S!("parameters"), ContextValue::ARGUMENTS(expr.arguments.clone()));
context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation));
for eval in base_sym.borrow().evaluations().unwrap().iter() {
let eval_ptr = eval.symbol.get_symbol_weak_transformed(session, context, &mut diagnostics, Some(parent.borrow().get_file().unwrap().upgrade().unwrap().clone()));
evals.push(Evaluation{
Expand All @@ -971,6 +970,8 @@ impl Evaluation {
});
}
context.as_mut().unwrap().remove(&S!("base_call"));
context.as_mut().unwrap().remove(&S!("parameters"));
context.as_mut().unwrap().remove(&S!("is_in_validation"));
}
}
}
Expand Down Expand Up @@ -1086,6 +1087,13 @@ impl Evaluation {
if bases.len() != 1 {
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 value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, &mut diagnostics);
diagnostics.extend(value.1);
if let Some(value) = value.0 {
Expand All @@ -1104,6 +1112,7 @@ impl Evaluation {
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 {
Expand All @@ -1118,6 +1127,7 @@ impl Evaluation {
}
}
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
2 changes: 1 addition & 1 deletion server/src/core/python_arch_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ impl PythonArchBuilder {
if add_noqa {
session.noqas_stack.pop();
}
PythonArchBuilderHooks::on_class_def(session, sym);
PythonArchBuilderHooks::on_class_def(session, &self.entry_point, sym);
Ok(())
}

Expand Down
200 changes: 139 additions & 61 deletions server/src/core/python_arch_builder_hooks.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,162 @@
use std::path::PathBuf;
use std::rc::Rc;
use std::cell::RefCell;
use once_cell::sync::Lazy;
use ruff_text_size::{TextRange, TextSize};
use tracing::warn;
use crate::core::entry_point::EntryPoint;
use crate::core::symbols::symbol::Symbol;
use crate::threads::SessionInfo;
use crate::{Sy, S};
use crate::constants::OYarn;

use super::odoo::SyncOdoo;

pub struct PythonArchBuilderHooks {}
type PythonArchClassHookFn = fn (session: &mut SessionInfo, entry: &Rc<RefCell<EntryPoint>>, symbol: Rc<RefCell<Symbol>>);

impl PythonArchBuilderHooks {
pub struct PythonArchClassHook {
pub odoo_entry: bool,
pub trees: Vec<(OYarn, OYarn, (Vec<OYarn>, Vec<OYarn>))>,
pub func: PythonArchClassHookFn
}

pub fn on_class_def(session: &mut SessionInfo, symbol: Rc<RefCell<Symbol>>) {
let mut sym = symbol.borrow_mut();
let name = &sym.name();
match name.as_str() {
"BaseModel" => {
if sym.get_main_entry_tree(session) == (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")]) {
// ----------- env ------------
let env = sym.get_symbol(&(vec![], vec![Sy!("env")]), u32::MAX);
if env.is_empty() {
let mut range = sym.range().clone();
let slots = sym.get_symbol(&(vec![], vec![Sy!("__slots__")]), u32::MAX);
if slots.len() == 1 {
if slots.len() == 1 {
range = slots[0].borrow().range().clone();
}
}
sym.add_new_variable(session, Sy!("env"), &range);
#[allow(non_upper_case_globals)]
static arch_class_hooks: Lazy<Vec<PythonArchClassHook>> = Lazy::new(|| {vec![
PythonArchClassHook {
odoo_entry: true,
trees: vec![
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel")]))
],
func: |session: &mut SessionInfo, entry_point: &Rc<RefCell<EntryPoint>>, symbol: Rc<RefCell<Symbol>>| {
// ----------- env ------------
let env = symbol.borrow().get_symbol(&(vec![], vec![Sy!("env")]), u32::MAX);
if env.is_empty() {
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();
}
}
},
"Environment" => {
if sym.get_main_entry_tree(session) == (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment")]) {
let new_sym = sym.get_symbol(&(vec![], vec![Sy!("__new__")]), u32::MAX);
let mut range = sym.range().clone();
if new_sym.len() == 1 {
range = new_sym[0].borrow().range().clone();
}
// ----------- env.cr ------------
sym.add_new_variable(session, Sy!("cr"), &range);
// ----------- env.uid ------------
let uid_sym = sym.add_new_variable(session, Sy!("uid"), &range);
uid_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current user id (for access rights checks)"));
// ----------- env.context ------------
let context_sym = sym.add_new_variable(session, Sy!("context"), &range);
context_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current context"));
// ----------- env.su ------------
let su_sym = sym.add_new_variable(session, Sy!("su"), &range);
su_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("whether in superuser mode"));
// ----------- env.registry -----------
let _ = sym.add_new_variable(session, Sy!("registry"), &range);
symbol.borrow_mut().add_new_variable(session, Sy!("env"), &range);
}
}
},
PythonArchClassHook {
odoo_entry: true,
trees: vec![
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("environments")], vec![Sy!("Environment")]))
],
func: |session: &mut SessionInfo, entry_point: &Rc<RefCell<EntryPoint>>, symbol: Rc<RefCell<Symbol>>| {
let new_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__new__")]), u32::MAX);
let mut range = symbol.borrow().range().clone();
if new_sym.len() == 1 {
range = new_sym[0].borrow().range().clone();
}
// ----------- env.cr ------------
symbol.borrow_mut().add_new_variable(session, Sy!("cr"), &range);
// ----------- env.uid ------------
let uid_sym = symbol.borrow_mut().add_new_variable(session, Sy!("uid"), &range);
uid_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current user id (for access rights checks)"));
// ----------- env.context ------------
let context_sym = symbol.borrow_mut().add_new_variable(session, Sy!("context"), &range);
context_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current context"));
// ----------- env.su ------------
let su_sym = symbol.borrow_mut().add_new_variable(session, Sy!("su"), &range);
su_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("whether in superuser mode"));
// ----------- env.registry -----------
let _ = symbol.borrow_mut().add_new_variable(session, Sy!("registry"), &range);
}
},
PythonArchClassHook {
odoo_entry: true,
trees: vec![
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Boolean")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Integer")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Float")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Monetary")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Char")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Text")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Html")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Date")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Datetime")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Binary")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Image")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Selection")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Reference")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2one")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2oneReference")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Json")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Properties")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("PropertiesDefinition")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("One2many")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2many")])),
(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Id")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Boolean")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Integer")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Float")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Monetary")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Char")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Text")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Html")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Date")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Datetime")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Binary")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Image")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_selection")], vec![Sy!("Selection")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_reference")], vec![Sy!("Reference")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2one")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_reference")], vec![Sy!("Many2oneReference")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Json")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("Properties")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("PropertiesDefinition")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("One2many")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2many")])),
(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Id")])),
],
func: |session: &mut SessionInfo, entry_point: &Rc<RefCell<EntryPoint>>, symbol: Rc<RefCell<Symbol>>| {
// ----------- __get__ ------------
let get_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__get__")]), u32::MAX);
if get_sym.is_empty() {
let range = symbol.borrow().range().clone();
symbol.borrow_mut().add_new_function(session, &S!("__get__"), &range, &range.end());
} else {
if !["Id", "One2many"].contains(&symbol.borrow().name().as_str()){
warn!("Found __get__ function for field of name ({})", symbol.borrow().name());
}
},
"Boolean" | "Integer" | "Float" | "Monetary" | "Char" | "Text" | "Html" | "Date" | "Datetime" |
"Binary" | "Image" | "Selection" | "Reference" | "Many2one" | "Many2oneReference" | "Json" |
"Properties" | "PropertiesDefinition" | "One2many" | "Many2many" | "Id" => {
if sym.get_main_entry_tree(session).0 == vec![Sy!("odoo"), Sy!("fields")] {
// ----------- __get__ ------------
let get_sym = sym.get_symbol(&(vec![], vec![Sy!("__get__")]), u32::MAX);
if get_sym.is_empty() {
let range = sym.range().clone();
sym.add_new_function(session, &S!("__get__"), &range, &range.end());
} else {
if !["Id", "One2many"].contains(&name.as_str()){
warn!("Found __get__ function for field of name ({})", name);
}
}
// ----------- __init__ ------------
let get_sym = sym.get_symbol(&(vec![], vec![Sy!("__init__")]), u32::MAX);
if get_sym.is_empty() {
let range = sym.range().clone();
sym.add_new_function(session, &S!("__init__"), &range, &range.end());
}
// ----------- __init__ ------------
let get_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__init__")]), u32::MAX);
if get_sym.is_empty() {
let range = symbol.borrow().range().clone();
symbol.borrow_mut().add_new_function(session, &S!("__init__"), &range, &range.end());
}
}
},
]});

pub struct PythonArchBuilderHooks {}

impl PythonArchBuilderHooks {

pub fn on_class_def(session: &mut SessionInfo, entry_point: &Rc<RefCell<EntryPoint>>, symbol: Rc<RefCell<Symbol>>) {
let tree = symbol.borrow().get_tree();
let odoo_tree = symbol.borrow().get_main_entry_tree(session);
let name = symbol.borrow().name().clone();
for hook in arch_class_hooks.iter() {
for hook_tree in hook.trees.iter() {
if hook_tree.0 >= session.sync_odoo.full_version ||
hook_tree.1 <= session.sync_odoo.full_version {
continue; //skip if version not in range
}
if name.eq(hook_tree.2.1.last().unwrap()) {
if (hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree == hook_tree.2) || (!hook.odoo_entry && tree == hook_tree.2) {
(hook.func)(session, entry_point, symbol.clone());
}
}
}
_ => {}
}
}

Expand Down
Loading