fix some API map error & add MaixPy API dumplicate check

This commit is contained in:
Neucrack
2024-12-23 16:10:19 +08:00
parent 80695c9678
commit 8d9f4d556e
16 changed files with 118 additions and 63 deletions

View File

@@ -46,7 +46,7 @@ list(REMOVE_ITEM ADD_SRCS "${src_path}/zbar/svg.c")
#### Just for this component, won't affect other
#### modules, including component that depend
#### on this component
# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1)
list(APPEND ADD_DEFINITIONS_PRIVATE -Wno-parentheses)
#### Add compile option for this component
#### Add components that depend on this component

View File

@@ -586,7 +586,7 @@ namespace maix
/**
* Operator []
* @maixpy maix.tensor.Tensors.__getitem__
* @maixcdk maix.tensor.Tensors.[]
* @maixcdk maix.tensor.Tensors.operator[]
*/
tensor::Tensor &operator[](const std::string &key)
{

View File

@@ -228,9 +228,9 @@ namespace maix::time
* FPS is average value of recent n(buff_len) times, and you can call fps_set_buff_len(10) to change buffer length, default is 20.
* Multiple invoke this function will calculate fps between two invoke, and you can also call fps_start() fisrt to manually assign fps calulate start point.
* @return float type, current fps since last call this method
* @maixpy maix.time.FPS.fps
* @maixpy maix.time.FPS.end
*/
inline float end()
float end()
{
return fps();
}
@@ -409,7 +409,7 @@ namespace maix::time
* @param city string type, which city to set, can be empty means only get current, default empty.
* @return list type, return current timezone setting, first is region, second is city.
* @attention when set new timezone, time setting not take effect in this process for some API, so you need to restart program.
* @maixpy maix.time.timezone
* @maixpy maix.time.timezone2
*/
std::vector<std::string> timezone2(const std::string &region = "", const std::string &city = "");

View File

@@ -109,7 +109,7 @@ public:
/**
* @brief Get the battery voltage.
* @return uint16_t type, return battery voltage.
* @maixpy maix.ext_dev.pmu.PMU.get_vat_vol
* @maixpy maix.ext_dev.pmu.PMU.get_bat_vol
*/
uint16_t get_bat_vol();

View File

@@ -213,7 +213,7 @@ namespace maix::nn
/**
* Get object item
* @maixpy maix.nn.FaceObjects.__getitem__
* @maixcdk maix.nn.FaceObjects.[]
* @maixcdk maix.nn.FaceObjects.operator[]
*/
nn::FaceObject &operator[](int idx)
{

View File

@@ -324,7 +324,7 @@ namespace maix::nn
/**
* Get object item
* @maixpy maix.nn.Objects.__getitem__
* @maixcdk maix.nn.Objects.[]
* @maixcdk maix.nn.Objects.operator[]
*/
nn::Object &operator[](int idx)
{

View File

@@ -262,7 +262,7 @@ namespace maix::nn
/**
* Get object item
* @maixpy maix.nn.OCR_Objects.__getitem__
* @maixcdk maix.nn.OCR_Objects.[]
* @maixcdk maix.nn.OCR_Objects.operator[]
*/
nn::OCR_Object &operator[](int idx)
{

View File

@@ -523,7 +523,7 @@ namespace maix::image
* @param height new height, if value is -1, will use width to calculate aspect ratio
* @param method resize method, by default is bilinear
* @return new transformed image object
* @maixpy maix.image.Image.affine
* @maixpy maix.image.Image.perspective
*/
image::Image* perspective(std::vector<int> src_points, std::vector<int> dst_points, int width = -1, int height = -1, image::ResizeMethod method = image::ResizeMethod::BILINEAR);
@@ -1513,7 +1513,7 @@ namespace maix::image
* @param w original rectagle width, can be -1 if not use this arg, default -1.
* @param h original rectagle height, can be -1 if not use this arg, default -1.
* @return list type, [x, y] if map point, [x, y, w, h] if resize rectangle.
* @maixpy maix.image.resize_map_pos
* @maixpy maix.image.Image.resize_map_pos
*/
std::vector<int> resize_map_pos(int w_out, int h_out, image::Fit fit, int x, int y, int w = -1, int h = -1)
{

View File

@@ -202,7 +202,7 @@ namespace maix::image
* @param index 0 for width, 1 for height
* @return int& width or height
* @maixpy maix.image.Size.__getitem__
* @maixcdk maix.image.Size.[]
* @maixcdk maix.image.Size.operator[]
*/
int &operator[](int index)
{

View File

@@ -2847,7 +2847,7 @@ namespace maix::image
* @param type The line list type, @see image::LineType
* @param lines The line list
* @param points Point sets of line
* @maixpy maix.image.HaarCascade.__init__
* @maixpy maix.image.LineGroup.__init__
*/
LineGroup(int id, image::LineType type, std::vector<image::Line> lines, std::vector<std::vector<std::vector<int>>> points = std::vector<std::vector<std::vector<int>>>()) {
_id = id;

View File

@@ -46,7 +46,7 @@ namespace maix::http
/**
* @brief stop http
* @return error code, err::ERR_NONE means success, others means failed
* @maixpy maix.http.JpegStreamer.start
* @maixpy maix.http.JpegStreamer.stop
*/
err::Err stop();

View File

@@ -82,7 +82,7 @@ namespace maix::rtmp
/**
* @brief Get bitrate
* @return bitrate
* @maixpy maix.rtmp.Rtmp.push_video
* @maixpy maix.rtmp.Rtmp.bitrate
*/
int bitrate() {
return _bitrate;
@@ -234,7 +234,7 @@ namespace maix::rtmp
/**
* @brief Check whether push streaming has started
* @return If rtmp thread is running, returns true
* @maixpy maix.rtmp.Rtmp.get_path
* @maixpy maix.rtmp.Rtmp.is_started
*/
bool is_started() {
return _start ? true : false;

View File

@@ -105,7 +105,7 @@ namespace maix::rtsp
/**
* @brief stop rtsp
* @return error code, err::ERR_NONE means success, others means failed
* @maixpy maix.rtsp.Rtsp.start
* @maixpy maix.rtsp.Rtsp.stop
*/
err::Err stop();

View File

@@ -76,7 +76,7 @@ namespace maix::touchscreen
* @param y y coordinate
* @param pressed pressed state
* @return error code, err::ERR_NONE means success, others means failed, if no event return err::ERR_NOT_READY
* @maixcdk maix.touchscreen.TouchScreen.read
* @maixcdk maix.touchscreen.TouchScreen.read0
*/
err::Err read0(int &x, int &y, bool &pressed);
@@ -85,7 +85,7 @@ namespace maix::touchscreen
* @attention This method will return immediately if have event, so it's better to use available() to check if have more event in buffer,
* or too much event in buffer when your program call this read() interval is too long will make your program slow.
* @return Returns a list include x, y, pressed state
* @maixpy maix.touchscreen.TouchScreen.read
* @maixpy maix.touchscreen.TouchScreen.read0
*/
std::vector<int> read0();

View File

@@ -879,21 +879,21 @@ namespace maix::video
/**
* @brief Get sample rate of audio (only valid in the context of audio)
* @return sample rate
* @maixpy maix.video.Context.audio_sample_rate
* @maixpy maix.video.Decoder.audio_sample_rate
*/
int audio_sample_rate() { return _audio_sample_rate; }
/**
* @brief Get channels of audio (only valid in the context of audio)
* @return channels
* @maixpy maix.video.Context.audio_channels
* @maixpy maix.video.Decoder.audio_channels
*/
int audio_channels() { return _audio_channels; }
/**
* @brief Get format of audio (only valid in the context of audio)
* @return audio format. @see audio::Format
* @maixpy maix.video.Context.audio_format
* @maixpy maix.video.Decoder.audio_format
*/
audio::Format audio_format() { return _audio_format; }

View File

@@ -70,8 +70,12 @@ def get_code_def(code):
# std::string hello = "hello"
# func definition:
# std::map<std::string, int> get_dict(std::map<std::string, int> in, int i, const char *j = "10")
# Bytes &operator=(const Bytes &other)
idx = code.find(";")
idx3 = code.find("=")
if code.find("operator") >= 0:
idx3 = -1
else:
idx3 = code.find("=")
start_idx = code.find("(")
sub_count = 1
idx2 = -1
@@ -163,10 +167,28 @@ def get_var_name_value(definition):
if "=" in definition:
name, value = definition.rsplit("=", 1)
value = value.strip()
elif "{" in definition:
name, value = definition.rsplit("{", 1)
value = "{" + value.strip()
elif ";" in definition:
name = definition.rsplit(";", 1)[0]
value = None
else:
value = None
name = definition.strip()
name = name.rsplit(" ", 1)[1].strip()
words = name.split()
new_words = []
for word in words:
# remove word startswith const, static, volatile, mutable, __attribute__, __restrict, extern
rms = ["const", "static", "volatile", "mutable", "__attribute__", "__restrict", "extern"]
valid = True
for rm in rms:
if word.startswith(rm):
valid = False
break
if valid:
new_words.append(word)
name = new_words[-1].strip()
if name.startswith("&"):
name = name.rsplit("&", 1)[1]
elif name.startswith("*"):
@@ -243,7 +265,7 @@ def get_func_def_info(code):
func_name = re.findall(r"([\*&]*)([\S]+)", func_name)[0]
return_type = (return_type + func_name[0]).strip()
func_name = func_name[1]
if return_type.startswith("static"):
if return_type.startswith("static") or return_type.startswith("extern"):
return_type = return_type.split(" ", 1)[1].strip()
func_name = func_name.strip()
except_pair = {
@@ -386,11 +408,47 @@ def parse_api_item_info(item):
raise Exception("parse_api_info key {} value error: {} => {}".format(api, item, kv_str))
return api, info
def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, module_name = "maix"):
def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, module_name = "maix", header_path = None):
# if not header_name.endswith("api_example.hpp"):
# return apis, "", False, []
def parse_item_code_def(item):
def_code = code[item["start_idx"]:]
idx = def_code.find(api)
idx = def_code.find("*/", idx) + 2
idx = def_code.find("\n", idx) + 1
item["type"], definition, (idx, idx2) = get_code_def(def_code[idx:])
# parse args values flags etc.
if item["type"] == "class":
words = definition.split("\n")[0].split()
if "class" in words:
item["name"] = words[words.index("class") + 1].split(":")[0].split("{")[0]
else:
item["name"] = words[words.index("struct") + 1].split(":")[0].split("{")[0]
elif item["type"] == "enum":
item["values"] = get_enum_values(definition)
words = definition.split("\n")[0].split()
if "class" in words:
item["name"] = words[words.index("class") + 1].split(":")[0].split("{")[0]
else:
item["name"] = words[words.index("enum") + 1].split(":")[0].split("{")[0]
elif item["type"] == "func":
item["type"] = "func"
item["name"], item["args"], item["ret_type"] = get_func_def_info(definition)
elif item["type"] == "var":
item["name"], item["value"] = get_var_name_value(definition)
if definition.startswith("const"):
item["kv"]["readonly"] = True
elif item["type"] == "module":
item["name"] = definition.split()[1].split(":")[-1]
else:
raise Exception("item type no valid: {}, {}".format(item["type"], definition))
if definition.startswith("static"):
item["kv"]["static"] = True
item["def_idx"] = [idx, idx2]
item["def"] = definition
comments_parsed = find_comments(code)
# find all @maixpy items
@@ -409,7 +467,17 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
info["start_idx"] = comment["start_idx"]
comment.pop("start_idx")
if api in items:
# return None, "parse_api error: API {} multiple defined, info: {}, another: {}".format(api, info, items[api]), False, []
if ["maixpy"] == sdks: # only for maixpy, not allow overload
try:
parse_item_code_def(info)
parse_item_code_def(items[api])
def1 = info["def"]
def2 = items[api]["def"]
except Exception:
def1 = ""
def2 = ""
return None, "parse_api error:\n\nAPI \033[1;31m {} \033[0m multiple defined !!!\n\n\033[1;31mOne\033[0m:\n \033[1;33m{}\033[0m\n brief: {}\n info: {},\n\n\033[1;31mAnother\033[0m:\n \033[1;33m{}\033[0m\n brief: {}\n info: {}\n".format(
api, def1, info["doc"]["brief"], info, def2, items[api]["doc"]["brief"], items[api]), False, []
print("-- API {} is overloaded".format(api))
if not "overload" in items[api]:
items[api]["overload"] = []
@@ -459,36 +527,13 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
},
}
'''
def parse_item_code_def(item):
def_code = code[item["start_idx"]:]
idx = def_code.find(api)
idx = def_code.find("*/", idx) + 2
idx = def_code.find("\n", idx) + 1
item["type"], definition, (idx, idx2) = get_code_def(def_code[idx:])
# parse args values flags etc.
if item["type"] == "class":
item["name"] = definition.split()[1].split(":")[0] # class AA: B\n{}
elif item["type"] == "enum":
item["values"] = get_enum_values(definition)
item["name"] = definition.split()[1] # enum AA\n{}
elif item["type"] == "func":
item["type"] = "func"
item["name"], item["args"], item["ret_type"] = get_func_def_info(definition)
elif item["type"] == "var":
item["name"], item["value"] = get_var_name_value(definition)
if definition.startswith("const"):
item["kv"]["readonly"] = True
elif item["type"] == "module":
item["name"] = definition.split()[1]
else:
raise Exception("item type no valid: {}, {}".format(item["type"], definition))
if definition.startswith("static"):
item["kv"]["static"] = True
item["def_idx"] = [idx, idx2]
item["def"] = definition
for api, item in items.items():
parse_item_code_def(item)
py_name = api.split(".")[-1]
if not py_name.startswith("__") and item["name"] != py_name:
return None, "API name \033[1;31m{} \033[0m not match with code definition name \033[1;31m{}\033[0m, def: {}".format(
api, item["name"], item["def"]), False, []
for overload in item.get("overload", []):
parse_item_code_def(overload)
@@ -566,6 +611,11 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
parent = root
for name in parents:
parent = parent["members"][name]
if api_name in parent["members"]:
msg = "parse_api error: \033[1;31m{}\033[0m multiple define in different headers".format(api)
if "header_path" in parent["members"][api_name]:
msg += f": \033[1;31m{parent['members'][api_name]['header_path']}\033[0m and \033[1;31m{header_path}\033[0m"
raise Exception(msg)
return parent, api_name
for key in final_keys:
@@ -604,7 +654,8 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
"value": item["value"],
"static": item["kv"].get("static", False),
"readonly": item["kv"].get("readonly", False),
"def": item["def"]
"def": item["def"],
"header_path": header_path
}
elif item["type"] == "class":
parent, name = get_parent_node(apis, key)
@@ -613,7 +664,8 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
"name": item["name"],
"doc": item["doc"],
"members": {},
"def": item["def"]
"def": item["def"],
"header_path": header_path
}
elif item["type"] == "func":
parent, name = get_parent_node(apis, key)
@@ -624,7 +676,8 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
"args": item["args"], # [["const char *", "i", None], ["int", "j", "10"]]
"ret_type": item["ret_type"],
"static": item["kv"].get("static", False),
"def": item["def"]
"def": item["def"],
"header_path": header_path
}
# overload method
if "overload" in item:
@@ -637,7 +690,8 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
"args": overload["args"], # [["const char *", "i", None], ["int", "j", "10"]]
"ret_type": overload["ret_type"],
"static": overload["kv"].get("static", False),
"def": overload["def"]
"def": overload["def"],
"header_path": header_path
})
elif item["type"] == "enum":
parent, name = get_parent_node(apis, key)
@@ -646,7 +700,8 @@ def parse_api(code, apis, sdks = ["maixpy", "maixcdk"], header_name = None, modu
"name": item["name"],
"doc": item["doc"],
"values": item["values"], # [("KIND_DOG", "0"), ("KIND_CAT", None)]
"def": item["def"]
"def": item["def"],
"header_path": header_path
}
else:
return None, "parse_api error: {} not support when generate tree".format(item["type"]), False, []
@@ -657,13 +712,13 @@ def parse_api_from_header(header_path, api_tree = {}, sdks = ["maixpy"], module_
with open(header_path, "r", encoding="utf-8") as f:
code = f.read()
try:
api_tree, msg, updated, keys = parse_api(code, api_tree, sdks, os.path.basename(header_path), module_name)
if api_tree is None:
raise Exception("parse_api_from_header {} error: {}".format(header_path, msg))
api_tree, msg, updated, keys = parse_api(code, api_tree, sdks, os.path.basename(header_path), module_name, header_path=header_path)
except Exception as e:
import traceback
traceback.print_exc()
raise Exception("parse_api_from_header {} error: {}".format(header_path, e))
raise Exception("\n\nparse_api_from_header \033[1;31m {} \033[0m error:\n{}".format(header_path, e))
if api_tree is None:
raise Exception("\n\nparse_api_from_header \033[1;31m {} \033[0m error:\n{}".format(header_path, msg))
return api_tree, updated, keys