From cdbc98baf633adbb2702e574b652f59dff65bd4f Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:56:09 +0100 Subject: [PATCH 01/19] fix: save latest changes --- src/pyconverter/xml2py/ast_tree.py | 52 ++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index a256314da..f046ba37a 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -135,7 +135,12 @@ def get_quant_iter_pos(name: str) -> tuple: def to_py_arg_name(name: str) -> str: """Python-compatible term""" arg = str(name).lower().strip() - p = engine() + if arg == "": + return arg + elif arg.isdigit(): + return "" + if arg[0].isdigit(): + p = engine() if arg[0].isdigit(): if arg[1].isdigit(): raise ValueError(f"The code needs to be expanded to handle numbers") @@ -146,8 +151,11 @@ def to_py_arg_name(name: str) -> str: num_value = p.number_to_words(arg[:3]) arg = f"{num_value}{arg[3:]}" - if ("," in arg and "--" in arg) or arg == "–": - return "" + if "--" in arg or arg == "–": + arg = arg.replace("--", "") + arg = arg.replace("–", "") + arg = arg.replace(" ", "") + return arg for key, value in PY_ARG_CLEANUP.items(): arg = arg.replace(key, value) @@ -204,7 +212,7 @@ def str_types(types, join_str: str) -> str: def to_py_signature(py_arg_name, types) -> str: """Return the Python signature of the argument.""" - if py_arg_name not in ["--", "–", ""]: + if "," not in py_arg_name and py_arg_name != "": kwarg = f'{py_arg_name}: {str_types(types, " | ")} = ""' else: kwarg = None @@ -2119,16 +2127,17 @@ def _parse_list_entry(self): additional_args = argument_obj.multiple_args if len(additional_args) > 0: for arg in additional_args: - if arg.py_arg_name != "" and arg.py_arg_name not in self.py_arg_names: + arg_name = arg.py_arg_name + if ("," in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): self._arguments.append(arg) else: - if argument_obj.py_arg_name != "": - self._arguments.append(argument_obj) + self._arguments.append(argument_obj) def __iadd__(self, argument_list): for arg in argument_list.arguments: - if arg.py_arg_name not in self.py_arg_names: + arg_name = arg.py_arg_name + if ("," not in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): self._arguments.append(arg) return self @@ -2350,7 +2359,7 @@ def to_py_docstring( self, max_length=100, indent="", links=None, base_url=None, fcache=None ) -> List[str]: """Return a list of string to enable converting the element to an RST format.""" - if self.py_arg_name not in ["--", "–", ""]: + if "," not in self.py_arg_name and self.py_arg_name != "": docstring = [f'{indent}{self.py_arg_name} : {str_types(self.types, " or ")}'] if isinstance(self._description, str): rst_description = self._description @@ -2373,6 +2382,7 @@ def to_py_docstring( docstring = [f'{indent}{self.py_arg_name} : {str_types(self.types, " or ")}'] docstring.extend(list_description) + else: docstring = [] return docstring @@ -2442,6 +2452,8 @@ def arg_desc(self) -> List[Argument]: arguments = ArgumentList(child, self.args) else: arguments += ArgumentList(child, self.args) + if self.py_name == "secmodif": + print(arguments.py_arg_names) else: for elem in refsyn: @@ -2450,14 +2462,20 @@ def arg_desc(self) -> List[Argument]: arguments = ArgumentList(elem, self.args) else: arguments += ArgumentList(elem, self.args) + if self.py_name == "secmodif": + print("HERE") + print(arguments.py_arg_names) + + # if self.py_name in ["secmodif", "dmat"]: + # print(arguments.initial_args) + # print(arguments.py_arg_names) if arguments is not None: if len(arguments.py_arg_names) < len(arguments.initial_args): for arg in arguments.initial_args: if arg not in arguments.py_arg_names: new_arg = Argument(arg, arguments.initial_args, "") - if new_arg.py_arg_name != "": - arguments.arguments.append(new_arg) + arguments.arguments.append(new_arg) return arguments.arguments @@ -2899,9 +2917,15 @@ def py_source(self, custom_functions=None, indent=""): if len(self.arg_desc) > 0: command = 'command = f"' + self.name for arg in self.arg_desc: - command += ",{" - command += arg.py_arg_name - command += "}" + name = arg.py_arg_name + if "," in name: + command += f",{name}" + elif name == "": + command += "," + else: + command += ",{" + command += arg.py_arg_name + command += "}" command += '"\n' # ",{" + "},{".join(self.arg_desc.py_arg_name) + '}"\n' else: From c34cf9cbc07fda66f1ae98574fd418f7166ce165 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:22:17 +0100 Subject: [PATCH 02/19] fix: double args --- src/pyconverter/xml2py/ast_tree.py | 54 ++++++++++++++++++------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index f046ba37a..13f93d369 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -24,6 +24,7 @@ import textwrap from typing import List import warnings +from pathlib import Path from inflect import engine from lxml.etree import tostring @@ -2113,8 +2114,9 @@ class ProductName(Element): class ArgumentList: - def __init__(self, list_entry: VarlistEntry, args: List) -> None: + def __init__(self, py_name: str, list_entry: VarlistEntry, args: List) -> None: + self._py_name = py_name self._list_entry = list_entry self._arguments = [] self._initial_args = args @@ -2137,7 +2139,7 @@ def _parse_list_entry(self): def __iadd__(self, argument_list): for arg in argument_list.arguments: arg_name = arg.py_arg_name - if ("," not in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): + if ("," in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): self._arguments.append(arg) return self @@ -2148,6 +2150,10 @@ def arguments(self): @arguments.setter def arguments(self, argument): self._arguments.append(argument) + + @property + def py_name(self): + return self._py_name @property def initial_args(self): @@ -2449,33 +2455,39 @@ def arg_desc(self) -> List[Argument]: for child in elem: if isinstance(child, Variablelist): if arguments is None: - arguments = ArgumentList(child, self.args) + arguments = ArgumentList(self.py_name, child, self.args) else: - arguments += ArgumentList(child, self.args) - if self.py_name == "secmodif": - print(arguments.py_arg_names) + arguments += ArgumentList(self.py_name, child, self.args) else: for elem in refsyn: if isinstance(elem, Variablelist): if arguments is None: - arguments = ArgumentList(elem, self.args) + arguments = ArgumentList(self.py_name, elem, self.args) else: - arguments += ArgumentList(elem, self.args) - if self.py_name == "secmodif": - print("HERE") - print(arguments.py_arg_names) - - - # if self.py_name in ["secmodif", "dmat"]: - # print(arguments.initial_args) - # print(arguments.py_arg_names) + arguments += ArgumentList(self.py_name, elem, self.args) + if arguments is not None: - if len(arguments.py_arg_names) < len(arguments.initial_args): - for arg in arguments.initial_args: - if arg not in arguments.py_arg_names: - new_arg = Argument(arg, arguments.initial_args, "") - arguments.arguments.append(new_arg) + if len(arguments.py_arg_names) != len(arguments.initial_args): + # This function needs a special treatment + if Path("args.txt").exists(): + with open("args.txt", "r") as f: + for line in f: + pass + last_line = line + else: + last_line = "" + with open("args.txt", "a") as f: + if last_line != f"{arguments.py_arg_names}\n": + f.write("--------------------------------------------------\n") + f.write(f"{self.py_name}: {self.group}\n") + f.write(f"{arguments.initial_args}\n") + f.write(f"{arguments.py_arg_names}\n") + + # for arg in arguments.initial_args: + # if arg not in arguments.py_arg_names: + # new_arg = Argument(arg, arguments.initial_args, "") + # arguments.arguments.append(new_arg) return arguments.arguments From ec99972a339894e4808df841a57fce2ce5db5d0c Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:09:28 +0100 Subject: [PATCH 03/19] fix: latest changes --- src/pyconverter/xml2py/ast_tree.py | 90 +++++++++++------------------- src/pyconverter/xml2py/cli.py | 4 ++ 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 13f93d369..02bdb7ca2 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -21,10 +21,10 @@ # SOFTWARE. import logging +from pathlib import Path import textwrap from typing import List import warnings -from pathlib import Path from inflect import engine from lxml.etree import tostring @@ -135,14 +135,14 @@ def get_quant_iter_pos(name: str) -> tuple: def to_py_arg_name(name: str) -> str: """Python-compatible term""" - arg = str(name).lower().strip() - if arg == "": + initial_arg = str(name).lower().strip() + arg = initial_arg + if arg in ["--", "–", ""]: return arg elif arg.isdigit(): return "" if arg[0].isdigit(): p = engine() - if arg[0].isdigit(): if arg[1].isdigit(): raise ValueError(f"The code needs to be expanded to handle numbers") elif arg[1:3] not in superlatif: @@ -152,12 +152,6 @@ def to_py_arg_name(name: str) -> str: num_value = p.number_to_words(arg[:3]) arg = f"{num_value}{arg[3:]}" - if "--" in arg or arg == "–": - arg = arg.replace("--", "") - arg = arg.replace("–", "") - arg = arg.replace(" ", "") - return arg - for key, value in PY_ARG_CLEANUP.items(): arg = arg.replace(key, value) arg = arg.strip() @@ -213,7 +207,7 @@ def str_types(types, join_str: str) -> str: def to_py_signature(py_arg_name, types) -> str: """Return the Python signature of the argument.""" - if "," not in py_arg_name and py_arg_name != "": + if py_arg_name not in ["--", "–", ""]: kwarg = f'{py_arg_name}: {str_types(types, " | ")} = ""' else: kwarg = None @@ -999,9 +993,9 @@ def py_term(self, links=None, base_url=None): return f"{arg}" if self.term.tag in item_needing_links_base_url: - arg = self.term.to_rst(links=links, base_url=base_url).replace("--", "").strip() + arg = self.term.to_rst(links=links, base_url=base_url).strip() else: - arg = self.term.to_rst().replace("--", "").strip() + arg = self.term.to_rst().strip() # sanity check if "blank" in arg.lower(): @@ -1605,6 +1599,7 @@ def raw_args(self): cmd = cmd.replace("&fname2_arg;", self._terms["fname2_arg"]) cmd = cmd.replace("&pn006p;", self._terms["pn006p"]) cmd = cmd.replace("&ansysBrand;", self._terms["ansysBrand"]) + cmd = cmd.replace("``", "") split_args = cmd.split(",")[1:] return split_args @@ -1613,36 +1608,29 @@ def args(self): """Command arguments.""" args = [] for item in self.raw_args: - orig_arg = str(item).replace(",", "") - arg = orig_arg.lower().replace("--", "").replace("–", "").replace("-", "_").strip() - if arg == "": - continue - - if arg == "class": - arg = "class_" - elif arg == "type": - arg = "type_" + arg = to_py_arg_name(str(item)) # simply check if we can use this as a valid Python kwarg try: exec(f"{arg} = 1.0") except SyntaxError: - continue + arg = "" if "blank" in arg: - continue + arg = "" args.append(arg) # rename duplicate arguments if len(args) != len(set(args)): for arg in args: - i = 0 - if args.count(arg) > 1: - for j in range(len(args)): - if args[j] == arg: - args[j] = f"{arg}{i:d}" - i += 1 + if arg not in ["", "--", "–"]: + i = 0 + if args.count(arg) > 1: + for j in range(len(args)): + if args[j] == arg: + args[j] = f"{arg}{i:d}" + i += 1 return args @@ -2130,7 +2118,7 @@ def _parse_list_entry(self): if len(additional_args) > 0: for arg in additional_args: arg_name = arg.py_arg_name - if ("," in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): + if (arg_name in self._initial_args) and (arg_name not in self.py_arg_names): self._arguments.append(arg) else: @@ -2139,7 +2127,7 @@ def _parse_list_entry(self): def __iadd__(self, argument_list): for arg in argument_list.arguments: arg_name = arg.py_arg_name - if ("," in arg_name or arg_name == "") or (arg_name not in self.py_arg_names): + if (arg_name in self._initial_args) and (arg_name not in self.py_arg_names): self._arguments.append(arg) return self @@ -2150,7 +2138,7 @@ def arguments(self): @arguments.setter def arguments(self, argument): self._arguments.append(argument) - + @property def py_name(self): return self._py_name @@ -2216,10 +2204,9 @@ def multiple_args(self): if not self.is_arg_elipsis: for item_name in split_name: arg_name = item_name.strip() - if arg_name not in ["--", ""]: - new_arg = Argument(arg_name, self._initial_argument, self._description) - if new_arg.py_arg_name != "": - additional_args.append(new_arg) + new_arg = Argument(arg_name, self._initial_argument, self._description) + if new_arg.py_arg_name != "": + additional_args.append(new_arg) else: complete_args = get_complete_args_from_initial_arg( @@ -2304,19 +2291,6 @@ def multiple_args(self): return additional_args - def rec_find(self, _type: str, terms=None) -> Element | None: - """Find the first type matching a given type string recursively.""" - for item in self: - if type(item).__name__ == _type: - if _type == "Refname" or _type == "Refnamediv": - item.terms = terms - return item - if isinstance(item, Element): - subitem = item.rec_find(_type) - if subitem is not None: - return subitem - return None - @property def types(self) -> List[type]: """One or more parameter types. @@ -2466,24 +2440,26 @@ def arg_desc(self) -> List[Argument]: arguments = ArgumentList(self.py_name, elem, self.args) else: arguments += ArgumentList(self.py_name, elem, self.args) - + + arg_file = Path("args.txt") + if arguments is not None: if len(arguments.py_arg_names) != len(arguments.initial_args): # This function needs a special treatment - if Path("args.txt").exists(): - with open("args.txt", "r") as f: + if arg_file.exists(): + with open(arg_file, "r") as f: for line in f: pass last_line = line else: last_line = "" - with open("args.txt", "a") as f: + with open(arg_file, "a") as f: if last_line != f"{arguments.py_arg_names}\n": f.write("--------------------------------------------------\n") f.write(f"{self.py_name}: {self.group}\n") f.write(f"{arguments.initial_args}\n") f.write(f"{arguments.py_arg_names}\n") - + # for arg in arguments.initial_args: # if arg not in arguments.py_arg_names: # new_arg = Argument(arg, arguments.initial_args, "") @@ -2930,9 +2906,7 @@ def py_source(self, custom_functions=None, indent=""): command = 'command = f"' + self.name for arg in self.arg_desc: name = arg.py_arg_name - if "," in name: - command += f",{name}" - elif name == "": + if name in ["--", "–", ""]: command += "," else: command += ",{" diff --git a/src/pyconverter/xml2py/cli.py b/src/pyconverter/xml2py/cli.py index 5f5ca5dd6..84ef760a2 100644 --- a/src/pyconverter/xml2py/cli.py +++ b/src/pyconverter/xml2py/cli.py @@ -105,6 +105,10 @@ def create_package( download.download_template() command_map, name_map = wr.convert(xml_path) + arg_file = Path("args.txt") + if arg_file.exists(): + # Delete the file if it exists + arg_file.unlink() package_structure = wr.write_source( command_map, name_map, xml_path, target_path, custom_functions_path ) From 2e8c57b3439e648dd94cf9ccc93983c6970ee12f Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:05:53 +0100 Subject: [PATCH 04/19] fix: issues related to duplicated args --- .gitignore | 1 + src/pyconverter/xml2py/ast_tree.py | 218 +++++++++++++++++++---------- 2 files changed, 143 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index f37b84302..edc3b696a 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,4 @@ cython_debug/ helper/ package/ _autosummary/ +args.txt diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 02bdb7ca2..5b44abb68 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -83,6 +83,12 @@ NO_RESIZE_LIST = ["Variablelist"] +MISSING_ARGUMENT_DESCRIPTION = """The description of the argument is missing in the Python function. +Please, refer to the product documentation for further information.""" + +ADDITIONAL_ARGUMENT_DESCRIPTION = """Additional arguments can be passed to the intial command. +Please, refer to the product documentation for further information.""" + class NameMap: def __init__(self, name_map): @@ -138,6 +144,9 @@ def to_py_arg_name(name: str) -> str: initial_arg = str(name).lower().strip() arg = initial_arg if arg in ["--", "–", ""]: + return "" + elif "--" in arg: + arg = arg.replace("--", "") return arg elif arg.isdigit(): return "" @@ -207,7 +216,7 @@ def str_types(types, join_str: str) -> str: def to_py_signature(py_arg_name, types) -> str: """Return the Python signature of the argument.""" - if py_arg_name not in ["--", "–", ""]: + if py_arg_name != "": kwarg = f'{py_arg_name}: {str_types(types, " | ")} = ""' else: kwarg = None @@ -1624,7 +1633,7 @@ def args(self): # rename duplicate arguments if len(args) != len(set(args)): for arg in args: - if arg not in ["", "--", "–"]: + if arg != "": i = 0 if args.count(arg) > 1: for j in range(len(args)): @@ -2101,64 +2110,13 @@ class ProductName(Element): pass -class ArgumentList: - def __init__(self, py_name: str, list_entry: VarlistEntry, args: List) -> None: - - self._py_name = py_name - self._list_entry = list_entry - self._arguments = [] - self._initial_args = args - self._parse_list_entry() - - def _parse_list_entry(self): - for item in self._list_entry: - if isinstance(item, VarlistEntry): - argument_obj = Argument(item, self._initial_args) - additional_args = argument_obj.multiple_args - if len(additional_args) > 0: - for arg in additional_args: - arg_name = arg.py_arg_name - if (arg_name in self._initial_args) and (arg_name not in self.py_arg_names): - self._arguments.append(arg) - - else: - self._arguments.append(argument_obj) - - def __iadd__(self, argument_list): - for arg in argument_list.arguments: - arg_name = arg.py_arg_name - if (arg_name in self._initial_args) and (arg_name not in self.py_arg_names): - self._arguments.append(arg) - return self - - @property - def arguments(self): - return self._arguments - - @arguments.setter - def arguments(self, argument): - self._arguments.append(argument) - - @property - def py_name(self): - return self._py_name - - @property - def initial_args(self): - return self._initial_args - - @property - def py_arg_names(self): - return [arg.py_arg_name for arg in self._arguments] - - class Argument: """Argument object.""" def __init__( self, element: str | Element, - initial_argument: List, + initial_arguments: List, description: Element | str | None = None, ) -> None: if description is None: @@ -2177,7 +2135,7 @@ def __init__( name = element self._name = name self._description = description - self._initial_argument = initial_argument + self._initial_arguments = initial_arguments @property def py_arg_name(self) -> str: @@ -2204,27 +2162,26 @@ def multiple_args(self): if not self.is_arg_elipsis: for item_name in split_name: arg_name = item_name.strip() - new_arg = Argument(arg_name, self._initial_argument, self._description) - if new_arg.py_arg_name != "": - additional_args.append(new_arg) + new_arg = Argument(arg_name, self._initial_arguments, self._description) + additional_args.append(new_arg) else: complete_args = get_complete_args_from_initial_arg( - elipsis_args=split_name, initial_args=self._initial_argument + elipsis_args=split_name, initial_args=self._initial_arguments ) if len(complete_args) > 0: for item in complete_args: - new_arg = Argument(item, self._initial_argument, self._description) - if new_arg.py_arg_name != "": - additional_args.append(new_arg) + new_arg = Argument(item, self._initial_arguments, self._description) + additional_args.append(new_arg) else: for i, item_name in enumerate(split_name): item_name = item_name.strip() if item_name == "": - continue + new_arg = Argument(arg_name, self._initial_arguments, self._description) + additional_args.append(new_arg) elif is_elipsis(item_name): if "+" in split_name[i + 1]: @@ -2244,7 +2201,7 @@ def multiple_args(self): arg_name = split_name[i + 1].strip() arg_name = f"{arg_name[:initial_pos_final]}{j}{arg_name[end_pos_final:]}" # noqa : E501 new_arg = Argument( - arg_name, self._initial_argument, self._description + arg_name, self._initial_arguments, self._description ) if new_arg.py_arg_name != "": additional_args.append(new_arg) @@ -2276,15 +2233,14 @@ def multiple_args(self): for j in range(number_iter_prev + 1, number_iter_next): arg_name = f"{name_iter_prev}{j}" new_arg = Argument( - arg_name, self._initial_argument, self._description + arg_name, self._initial_arguments, self._description ) - if new_arg.py_arg_name != "": - additional_args.append(new_arg) + additional_args.append(new_arg) else: additional_args.append( Argument( name_iter_next, - self._initial_argument, + self._initial_arguments, self._description, ) ) @@ -2339,7 +2295,7 @@ def to_py_docstring( self, max_length=100, indent="", links=None, base_url=None, fcache=None ) -> List[str]: """Return a list of string to enable converting the element to an RST format.""" - if "," not in self.py_arg_name and self.py_arg_name != "": + if self.py_arg_name != "": docstring = [f'{indent}{self.py_arg_name} : {str_types(self.types, " or ")}'] if isinstance(self._description, str): rst_description = self._description @@ -2368,6 +2324,119 @@ def to_py_docstring( return docstring +class ArgumentList: + def __init__(self, py_name: str, list_entry: VarlistEntry, args: List) -> None: + + self._py_name = py_name + self._list_entry = list_entry + self._arguments = [] + self._additional_args = [] + self._initial_args = args + self._parse_list_entry() + + def _parse_list_entry(self): + "Parse the list entry to get the main arguments and the additional ones." + temp_args = {} + for item in self._list_entry: + if isinstance(item, VarlistEntry): + argument_obj = Argument(item, self._initial_args) + additional_args = argument_obj.multiple_args + if len(additional_args) > 0: + for arg in additional_args: + arg_name = arg.py_arg_name + if (arg_name in self._initial_args) and ( + arg_name == "" or arg_name not in self.py_arg_names + ): + temp_args[arg_name] = arg + + else: + temp_args[argument_obj.py_arg_name] = argument_obj + + for initial_arg in self._initial_args: + if initial_arg in temp_args.keys(): + self._arguments.append(temp_args[initial_arg]) + else: + self._arguments.append( + Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION) + ) # description is missing + + is_additional_arg = False + if len(temp_args) != len(self._initial_args): + for arg in temp_args: + if arg not in self.py_arg_names: + self._additional_args.append(temp_args[arg]) + is_additional_arg = True + + if is_additional_arg and "addional_command_arg" not in self.py_arg_names: + self._arguments.append( + Argument( + "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION + ) + ) + + def __iadd__(self, argument_list): + temp_args = {} + for arg in argument_list.arguments: + arg_name = arg.py_arg_name + if (arg_name in self._initial_args) and ( + arg_name == "" or arg_name not in self.py_arg_names + ): + temp_args[arg_name] = arg + + for initial_arg in self._initial_args: + if initial_arg in temp_args.keys(): + if initial_arg not in self.py_arg_names: + self._arguments.append(temp_args[initial_arg]) + else: + self._arguments[self.py_arg_names.index(initial_arg)] = temp_args[initial_arg] + else: + if initial_arg not in self.py_arg_names: + self._arguments.append( + Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION) + ) + + is_additional_arg = False + if len(temp_args) != len(self._initial_args): + for arg in temp_args: + if arg not in self.py_arg_names: + self._additional_args.append(temp_args[arg]) + is_additional_arg = True + + if is_additional_arg and "addional_command_arg" not in self.py_arg_names: + self._arguments.append( + Argument( + "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION + ) + ) + + return self + + @property + def arguments(self) -> List[Argument]: + "Return a list of Argument objects." + return self._arguments + + @arguments.setter + def arguments(self, argument): + self._arguments.append(argument) + + @property + def py_name(self): + return self._py_name + + @property + def initial_args(self): + return self._initial_args + + @property + def py_arg_names(self): + return [arg.py_arg_name for arg in self._arguments] + + @property + def additional_args(self): + return self._additional_args + + class XMLCommand(Element): """Provides the XML command from the documentation.""" @@ -2454,17 +2523,14 @@ def arg_desc(self) -> List[Argument]: else: last_line = "" with open(arg_file, "a") as f: - if last_line != f"{arguments.py_arg_names}\n": + if last_line != f"py_arg_name : {arguments.py_arg_names}\n": f.write("--------------------------------------------------\n") f.write(f"{self.py_name}: {self.group}\n") + f.write("initial_args : ") f.write(f"{arguments.initial_args}\n") + f.write("py_arg_name : ") f.write(f"{arguments.py_arg_names}\n") - # for arg in arguments.initial_args: - # if arg not in arguments.py_arg_names: - # new_arg = Argument(arg, arguments.initial_args, "") - # arguments.arguments.append(new_arg) - return arguments.arguments else: @@ -2906,7 +2972,7 @@ def py_source(self, custom_functions=None, indent=""): command = 'command = f"' + self.name for arg in self.arg_desc: name = arg.py_arg_name - if name in ["--", "–", ""]: + if name == "": command += "," else: command += ",{" From c55ef28f58c4687b8cfa083ceb2a294ea12b5ade Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:48:43 +0100 Subject: [PATCH 05/19] fix: revert change in ``py_term`` --- src/pyconverter/xml2py/ast_tree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 5b44abb68..7a153892e 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1002,9 +1002,9 @@ def py_term(self, links=None, base_url=None): return f"{arg}" if self.term.tag in item_needing_links_base_url: - arg = self.term.to_rst(links=links, base_url=base_url).strip() + arg = self.term.to_rst(links=links, base_url=base_url).replace("--", "").strip() else: - arg = self.term.to_rst().strip() + arg = self.term.to_rst().replace("--", "").strip() # sanity check if "blank" in arg.lower(): From 09d0ebe6e56a1439847608fab2b39ee4cee0bc97 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:51:49 +0100 Subject: [PATCH 06/19] fix: removing extra comas at the end of the command --- src/pyconverter/xml2py/ast_tree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 7a153892e..fee95c948 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2979,6 +2979,8 @@ def py_source(self, custom_functions=None, indent=""): command += arg.py_arg_name command += "}" command += '"\n' + while command != command.replace(',"', '"'): # remove extra commas at the end of the command + command = command.replace(',"', '"') # ",{" + "},{".join(self.arg_desc.py_arg_name) + '}"\n' else: command = 'command = f"' + self.name + '"\n' From 371003a09d7b0e81fe7e207c7d0e99438e4969d9 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:42:36 +0100 Subject: [PATCH 07/19] fix: removing extra argument instead of extra coma --- src/pyconverter/xml2py/ast_tree.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index fee95c948..e0201d910 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2435,6 +2435,9 @@ def py_arg_names(self): @property def additional_args(self): return self._additional_args + + def remove_last_arg(self): + self._arguments.pop() class XMLCommand(Element): @@ -2513,6 +2516,10 @@ def arg_desc(self) -> List[Argument]: arg_file = Path("args.txt") if arguments is not None: + # Remove last argument if it's empty + while arguments.py_arg_names[-1] == "": + arguments.remove_last_arg() + if len(arguments.py_arg_names) != len(arguments.initial_args): # This function needs a special treatment if arg_file.exists(): @@ -2979,9 +2986,6 @@ def py_source(self, custom_functions=None, indent=""): command += arg.py_arg_name command += "}" command += '"\n' - while command != command.replace(',"', '"'): # remove extra commas at the end of the command - command = command.replace(',"', '"') - # ",{" + "},{".join(self.arg_desc.py_arg_name) + '}"\n' else: command = 'command = f"' + self.name + '"\n' return_command = "return self.run(command, **kwargs)\n" From fbc0c7ad8fffd96799c09eff5208df3080cae18e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:16:47 +0100 Subject: [PATCH 08/19] feat: adding link in missing/ additional argument description --- config.yaml | 2 +- src/pyconverter/xml2py/ast_tree.py | 25 +++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/config.yaml b/config.yaml index f788f5df2..fb14ebaad 100644 --- a/config.yaml +++ b/config.yaml @@ -2,7 +2,7 @@ library_name_structured: # Future name of the library - pyconverter - generatedcommands -subfolder: +subfolders: - subfolder - subsubfolder diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index e0201d910..cd8a0f29a 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -84,10 +84,10 @@ NO_RESIZE_LIST = ["Variablelist"] MISSING_ARGUMENT_DESCRIPTION = """The description of the argument is missing in the Python function. -Please, refer to the product documentation for further information.""" +Please, refer to the `command documentation `_ for further information.""" -ADDITIONAL_ARGUMENT_DESCRIPTION = """Additional arguments can be passed to the intial command. -Please, refer to the product documentation for further information.""" +ADDITIONAL_ARGUMENT_DESCRIPTION = """Additional arguments can be passed to the initial command. +Please, refer to the `command documentation `_ for further information.""" class NameMap: @@ -2325,9 +2325,10 @@ def to_py_docstring( class ArgumentList: - def __init__(self, py_name: str, list_entry: VarlistEntry, args: List) -> None: + def __init__(self, py_name: str, url: str, list_entry: VarlistEntry, args: List) -> None: self._py_name = py_name + self._url = url self._list_entry = list_entry self._arguments = [] self._additional_args = [] @@ -2357,7 +2358,7 @@ def _parse_list_entry(self): self._arguments.append(temp_args[initial_arg]) else: self._arguments.append( - Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION) + Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}")) ) # description is missing is_additional_arg = False @@ -2370,7 +2371,7 @@ def _parse_list_entry(self): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( - "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION + "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}") ) ) @@ -2392,7 +2393,7 @@ def __iadd__(self, argument_list): else: if initial_arg not in self.py_arg_names: self._arguments.append( - Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION) + Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}")) ) is_additional_arg = False @@ -2405,7 +2406,7 @@ def __iadd__(self, argument_list): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( - "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION + "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}") ) ) @@ -2501,17 +2502,17 @@ def arg_desc(self) -> List[Argument]: for child in elem: if isinstance(child, Variablelist): if arguments is None: - arguments = ArgumentList(self.py_name, child, self.args) + arguments = ArgumentList(self.py_name, self.url, child, self.args) else: - arguments += ArgumentList(self.py_name, child, self.args) + arguments += ArgumentList(self.py_name, self.url, child, self.args) else: for elem in refsyn: if isinstance(elem, Variablelist): if arguments is None: - arguments = ArgumentList(self.py_name, elem, self.args) + arguments = ArgumentList(self.py_name, self.url, elem, self.args) else: - arguments += ArgumentList(self.py_name, elem, self.args) + arguments += ArgumentList(self.py_name, self.url, elem, self.args) arg_file = Path("args.txt") From abc9a1d5d78c7529e7be22c708f8c7888dfa73ee Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:20:31 +0100 Subject: [PATCH 09/19] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index cd8a0f29a..80a1c38f6 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2358,7 +2358,11 @@ def _parse_list_entry(self): self._arguments.append(temp_args[initial_arg]) else: self._arguments.append( - Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}")) + Argument( + initial_arg, + self._initial_args, + MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), + ) ) # description is missing is_additional_arg = False @@ -2371,7 +2375,9 @@ def _parse_list_entry(self): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( - "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}") + "addional_command_arg", + self._initial_args, + ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), ) ) @@ -2393,7 +2399,11 @@ def __iadd__(self, argument_list): else: if initial_arg not in self.py_arg_names: self._arguments.append( - Argument(initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}")) + Argument( + initial_arg, + self._initial_args, + MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), + ) ) is_additional_arg = False @@ -2406,7 +2416,9 @@ def __iadd__(self, argument_list): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( - "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}") + "addional_command_arg", + self._initial_args, + ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), ) ) @@ -2436,7 +2448,7 @@ def py_arg_names(self): @property def additional_args(self): return self._additional_args - + def remove_last_arg(self): self._arguments.pop() From 28787e89396b7a890d0fffd57502ea8132d40c00 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:56:11 +0100 Subject: [PATCH 10/19] feat: add ``project_name`` configuration --- config.yaml | 2 ++ src/pyconverter/xml2py/writer.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config.yaml b/config.yaml index fb14ebaad..02dab56b1 100644 --- a/config.yaml +++ b/config.yaml @@ -1,3 +1,5 @@ +project_name: PyConverter-GeneratedCommands # Name of the project + library_name_structured: # Future name of the library - pyconverter - generatedcommands diff --git a/src/pyconverter/xml2py/writer.py b/src/pyconverter/xml2py/writer.py index f95a00405..24123974d 100644 --- a/src/pyconverter/xml2py/writer.py +++ b/src/pyconverter/xml2py/writer.py @@ -220,6 +220,7 @@ def write_global__init__file(library_path: Path, config_path: Path) -> None: Path object of the directory containing the generated package. """ + project_name = get_config_data_value(config_path, "project_name") subfolder_values = get_config_data_value(config_path, "subfolders") if subfolder_values: @@ -244,7 +245,7 @@ def write_global__init__file(library_path: Path, config_path: Path) -> None: fid.write("except ModuleNotFoundError:\n") fid.write(" import importlib_metadata\n\n") fid.write("__version__ = importlib_metadata.version(__name__.replace('.', '-'))\n") - fid.write('"""PyConverter-GeneratedCommands version."""\n') + fid.write(f'"""{project_name} version."""\n') fid.close() From 5ece50c25e453634d7da08fec1f4bd439b073ab7 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:57:17 +0100 Subject: [PATCH 11/19] fix: missing ``mkdir`` command raised by @germa89 --- src/pyconverter/xml2py/ast_tree.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 80a1c38f6..ccb159d8a 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2510,13 +2510,12 @@ def arg_desc(self) -> List[Argument]: if refsyn is None: refsections = self.find_all("RefSection") for elem in refsections: - if elem.id is not None and "argdescript" in elem.id: - for child in elem: - if isinstance(child, Variablelist): - if arguments is None: - arguments = ArgumentList(self.py_name, self.url, child, self.args) - else: - arguments += ArgumentList(self.py_name, self.url, child, self.args) + for child in elem: + if isinstance(child, Variablelist): + if arguments is None: + arguments = ArgumentList(self.py_name, self.url, child, self.args) + else: + arguments += ArgumentList(self.py_name, self.url, child, self.args) else: for elem in refsyn: From 8c0f4478a7805bc50996b29455ccf06c05180efb Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:23:13 +0100 Subject: [PATCH 12/19] feat: using alphabetical order for doc --- src/pyconverter/xml2py/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/writer.py b/src/pyconverter/xml2py/writer.py index 24123974d..7f1f70a73 100644 --- a/src/pyconverter/xml2py/writer.py +++ b/src/pyconverter/xml2py/writer.py @@ -630,7 +630,7 @@ def write_docs( """ - for python_command_name in method_list: + for python_command_name in sorted(method_list): class_content += f" {class_name}.{python_command_name}\n" # Write the class file From d876306afb206bd84f5aedc522c643e9a3fa0cf2 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:52:12 +0100 Subject: [PATCH 13/19] fix: warnings ``invalid escape sequence`` --- src/pyconverter/xml2py/ast_tree.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index ccb159d8a..70200fc12 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -517,13 +517,18 @@ class Member(Element): def ponctuaction_whitespace(text, ponctuation): - extra_space = re.findall(f"\S\h+\{ponctuation}", text) + pattern = r"\S\h+\{ponctuation}".format(ponctuation=ponctuation) + extra_space = re.findall(pattern, text) if extra_space: for character in list(set(extra_space)): # remove duplicates in extra_space list - assigned_character = "\)" if character[0] == ")" else character[0] - text = re.sub( - f"{assigned_character}\h+\{ponctuation}", f"{assigned_character}{ponctuation}", text + assigned_character = r"\)" if character[0] == ")" else character[0] + pattern = r"{assigned_character}\h+\{ponctuation}".format( + assigned_character=assigned_character, ponctuation=ponctuation ) + repl = r"{assigned_character}{ponctuation}".format( + assigned_character=assigned_character, ponctuation=ponctuation + ) + text = re.sub(pattern, repl, text) return text @@ -2660,7 +2665,7 @@ def py_docstring(self, custom_functions: CustomFunctions) -> str: # final post-processing def replacer(match): - return match.group().replace("*", r"\*").replace("\\*", "\*") + return match.group().replace("*", r"\*").replace(r"\\*", r"\*") # sphinx doesn't like asterisk symbols docstr = re.sub(r"(?<=\S)\*|(\*\S)", replacer, docstr) @@ -2716,7 +2721,7 @@ def cmd_replacer(match): docstr = re.sub(r"[a-z0-9]*", cmd_replacer, docstr) def pipe_replacer(match): - return match.group().replace("|", "\|") + return match.group().replace("|", r"\|") docstr = re.sub(r"\|(.*)\|", pipe_replacer, docstr) From 75b3373f9edc0c3973e6a342c0f5080a440f6111 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:06:07 +0100 Subject: [PATCH 14/19] docs: adding docstrings for general ``ast_tree.py`` methods --- src/pyconverter/xml2py/ast_tree.py | 178 +++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 23 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 70200fc12..dd3b54a2d 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -98,7 +98,22 @@ def __init__(self, name_map): def to_py_name(name, name_map=None): - """Convert to a Python-compatible name.""" + """ + Return a Python-compatible name for a command using the global name map. + + Parameters + ---------- + name : str + Name of the command. + + name_map : dict + Dictionary containing the name map. + + Returns + ------- + str + Python-compatible command name. + """ if name_map is not None: global NAME_MAP_GLOB NAME_MAP_GLOB = name_map @@ -109,7 +124,19 @@ def to_py_name(name, name_map=None): def get_iter_values(name: str): - """Get the values of an iterator.""" + """ + Get the values of an iterator. + + Parameters + ---------- + name : str + Name of the parameter containing the iterator. + + Returns + ------- + tuple(str, int) + Tuple containing the name of the iterator and the iteration value. + """ output = re.search(r"([a-zA-Z_]*)(\d*)", name.strip()) groups = output.groups() name = groups[0] @@ -140,7 +167,19 @@ def get_quant_iter_pos(name: str) -> tuple: def to_py_arg_name(name: str) -> str: - """Python-compatible term""" + """ + Return a Python-compatible name for an argument. + + Parameters + ---------- + name : str + Name of the argument. + + Returns + ------- + str + Python-compatible argument name. + """ initial_arg = str(name).lower().strip() arg = initial_arg if arg in ["--", "–", ""]: @@ -180,8 +219,29 @@ def to_py_arg_name(name: str) -> str: def get_complete_args_from_initial_arg( initial_args: List[str], elipsis_args: List[str] ) -> List[str]: - # elipsis_args = ['Cname1', ' Cname2',' …'] or ['Cname1', '...', 'Cname6'] - # initial_args = ['energytype', 'cname1', 'cname2', 'cname3', 'cname4', 'cname5', 'cname6'] + """ + Get the complete argument list from a list with elipsis. + + Parameters + ---------- + initial_args : list + List of initial arguments. + + elipsis_args : list + List of containing the elipsed arguments. + + Returns + ------- + list + List of complete pythonnic arguments. + + Examples + -------- + >>> initial_args = ['energytype', 'cname1', 'cname2', 'cname3', 'cname4', 'cname5', 'cname6'] + >>> elipsis_args = ['Cname1', ' Cname2',' …'] + >>> get_complete_args_from_initial_arg(initial_args, elipsis_args) + ['cname1', 'cname2', 'cname3', 'cname4', 'cname5', 'cname6'] + """ first_arg_name = to_py_arg_name(elipsis_args[0]) name_without_iter, first_num = get_iter_values(first_arg_name) @@ -209,13 +269,60 @@ def is_elipsis(name: str) -> bool: def str_types(types, join_str: str) -> str: - """String representation of the parameter types.""" + """ + String representation of the parameter types. + + Parameters + ---------- + types : list + List of types. + + join_str : str + String to join the types. + + Returns + ------- + str + String representation of the parameter types. + + Examples + -------- + >>> types = [str, int, float] + >>> str_types(types, " | ") + 'str | int | float' + + >>> types = [str, int] + >>> str_types(types, " or ") + 'str or int' + """ ptype_str = join_str.join([parm_type.__name__ for parm_type in types]) return ptype_str def to_py_signature(py_arg_name, types) -> str: - """Return the Python signature of the argument.""" + """ + Return the Python signature of the argument. + + Parameters + ---------- + py_arg_name : str + Python-compatible argument name. + + types : list + List of types. + + Returns + ------- + str + Python signature of the argument. + + Examples + -------- + >>> py_arg_name = 'energytype' + >>> types = [str, int, float] + >>> to_py_signature(py_arg_name, types) + 'energytype: str | int | float = ""' + """ if py_arg_name != "": kwarg = f'{py_arg_name}: {str_types(types, " | ")} = ""' else: @@ -223,6 +330,46 @@ def to_py_signature(py_arg_name, types) -> str: return kwarg +def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", list=False): + """ + Resize the length of a text. + + Parameters + ---------- + text : str + Text to resize. + + max_length : int + Maximum length of the text to be resized. + + initial_indent : str + Initial indentation of the text. + + subsequent_indent : str + Subsequent indentation of the text. + + return_list : bool + If set to True, the function returns a list of strings. + Default is False. + + Returns + ------- + str or list + Resized text. + """ + text = text.replace(" .", ".") + wrapper = textwrap.TextWrapper( + width=max_length, + break_long_words=False, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + ) + if list is False: + return wrapper.fill(text=text) + else: + return wrapper.wrap(text=text) + + # ############################################################################ # Element class # ############################################################################ @@ -428,21 +575,6 @@ def tag(self): return self._element.tag -def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", list=False): - """Resize the length of a text.""" - text = text.replace(" .", ".") - wrapper = textwrap.TextWrapper( - width=max_length, - break_long_words=False, - initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - ) - if list is False: - return wrapper.fill(text=text) - else: - return wrapper.wrap(text=text) - - class ItemizedList(Element): """Provides the itemized list element.""" @@ -861,7 +993,7 @@ def source(self): def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" - header = f"\n\n{indent}.. code::\n\n" + header = f"\n\n{indent}.. code:: apdl\n\n" source_code = re.sub(r"[^\S\r\n]", " ", self.source) # Remove extra whitespaces rst_item = header + textwrap.indent(source_code, prefix=indent + " " * 3) + "\n" return rst_item From 2778bc07435f12247fb3cd05387b69368806faa3 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 20 Dec 2024 17:43:40 +0100 Subject: [PATCH 15/19] fix: most of indentation errors (except for ``fname``) --- src/pyconverter/xml2py/ast_tree.py | 83 ++++++++++++++++-------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index dd3b54a2d..e260fbc3a 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -358,16 +358,33 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", Resized text. """ text = text.replace(" .", ".") + while "\n\n\n" in text: + text = text.replace("\n\n\n", "\n\n") + wrapper = textwrap.TextWrapper( width=max_length, break_long_words=False, initial_indent=initial_indent, subsequent_indent=subsequent_indent, ) - if list is False: - return wrapper.fill(text=text) + + if "\n\n" in text: + text = text.split("\n\n") + else: + text = [text] + + for i, paragraph in enumerate(text): + text[i] = wrapper.fill(text=paragraph) + + if len(text) > 1: + output = "\n\n".join(text) else: - return wrapper.wrap(text=text) + output = text[0] + + if list is True: + output = output.splitlines() + + return output # ############################################################################ @@ -669,7 +686,6 @@ class OrderedList(Element): def to_rst(self, indent="", max_length=100, links=None, base_url=None): """Return a string to enable converting the element to an RST format.""" - # indent += " " * 4 ordered_list = [] for item in self: if item.tag in item_needing_links_base_url: @@ -835,7 +851,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No ) items.append(str_item) - rst_item = " ".join(items) + "\n" + rst_item = " ".join(items) + "\n\n" return rst_item @@ -876,7 +892,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None): content = f"{self[0]} " elif self.role == "italic": # TODO: this isn't the correct way of making text itallic - content = f"`{self[0]}` " + content = f"{self[0]} " # elif self.role == 'var': # content = f"``{self[0]}`` " else: @@ -999,11 +1015,12 @@ def to_rst(self, indent="", max_length=100): return rst_item -def resize_element_list(text, max_length=100): +def resize_element_list(text, max_length=100, indent=""): element_list = re.finditer(r"^\* ", text) - subsequent_indent = " " * 2 + initial_indent = indent + " " + subsequent_indent = indent + " " * 2 element_list = resize_length( - text, max_length, initial_indent="", subsequent_indent=subsequent_indent + text, max_length, initial_indent=initial_indent, subsequent_indent=subsequent_indent ) return element_list @@ -1040,10 +1057,10 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No if type(item) != str and len(item.children) > 1 and type(item[1]) != str: intersection_types = set(NO_RESIZE_LIST).intersection(set(item[1].children_types)) if len(intersection_types) == 0: - rst_item = resize_element_list(rst_item, max_length) + rst_item = resize_element_list(rst_item, max_length, indent=indent) else: - rst_item = resize_element_list(rst_item, max_length) + rst_item = resize_element_list(rst_item, max_length, indent=indent) active_items.append(rst_item) return "\n".join(active_items) + "\n" @@ -1740,11 +1757,9 @@ def terms(self, terms): def raw_args(self): """Raws containing the command arguments.""" cmd = str(self) - cmd = cmd.replace("&fname_arg;", self._terms["fname_arg"]) - cmd = cmd.replace("&fname1_arg;", self._terms["fname1_arg"]) - cmd = cmd.replace("&fname2_arg;", self._terms["fname2_arg"]) - cmd = cmd.replace("&pn006p;", self._terms["pn006p"]) - cmd = cmd.replace("&ansysBrand;", self._terms["ansysBrand"]) + for term in self._terms.keys(): + if type(self._terms[term]) == str: + cmd = cmd.replace(f"&{term};", self._terms[term]) cmd = cmd.replace("``", "") split_args = cmd.split(",")[1:] return split_args @@ -2419,32 +2434,26 @@ def resized_description( if description is None: description = self._description - if "* " in description: - output = description.split("\n") - else: - output = resize_length( - description, max_length, initial_indent=indent, subsequent_indent=indent, list=True - ) + output = resize_length( + description, max_length, initial_indent=indent, subsequent_indent=indent, list=True + ) return output - def to_py_docstring( - self, max_length=100, indent="", links=None, base_url=None, fcache=None - ) -> List[str]: + def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None) -> List[str]: """Return a list of string to enable converting the element to an RST format.""" if self.py_arg_name != "": - docstring = [f'{indent}{self.py_arg_name} : {str_types(self.types, " or ")}'] + docstring = [f'{self.py_arg_name} : {str_types(self.types, " or ")}'] if isinstance(self._description, str): rst_description = self._description else: rst_description = self._description.to_rst( - indent=indent, max_length=max_length, links=links, base_url=base_url, fcache=fcache, ) - description_indent = " " * 4 + indent + description_indent = " " * 4 if not "* " in rst_description: list_description = self.resized_description( rst_description, max_length, description_indent @@ -2453,11 +2462,12 @@ def to_py_docstring( rst_description = textwrap.indent(rst_description, description_indent) list_description = rst_description.split("\n") - docstring = [f'{indent}{self.py_arg_name} : {str_types(self.types, " or ")}'] + docstring = [f'{self.py_arg_name} : {str_types(self.types, " or ")}'] docstring.extend(list_description) else: docstring = [] + return docstring @@ -2787,7 +2797,8 @@ def py_docstring(self, custom_functions: CustomFunctions) -> str: ): items += [""] + custom_functions.py_returns[self.py_name] if self.notes is not None: - items += [""] + self.py_notes(custom_functions) + items += [""] + items.extend(self.py_notes(custom_functions)) if custom_functions is not None and ( self.py_name in custom_functions.py_names and self.py_name in custom_functions.py_examples @@ -3024,7 +3035,7 @@ def py_notes(self, custom_functions: CustomFunctions = None): else: notes = self.notes.to_rst() - if "flat-table" not in "".join(notes) and ".. code::" not in "".join(notes): + if "flat-table" not in notes and ".. code::" not in notes: notes = resize_length(notes, self._max_length, list=True) lines.extend(notes) else: @@ -3079,7 +3090,7 @@ def __repr__(self): return "\n".join(lines) - def py_parm(self, custom_functions=None, indent="", links=None, base_url=None, fcache=None): + def py_parm(self, custom_functions=None, links=None, base_url=None, fcache=None): """Python parameter's string.""" lines = [] arg_desc = self.arg_desc @@ -3096,9 +3107,7 @@ def py_parm(self, custom_functions=None, indent="", links=None, base_url=None, f lines.append("-" * 10) for argument in arg_desc: lines.extend( - argument.to_py_docstring( - self._max_length, indent, links, base_url, fcache - ) + argument.to_py_docstring(self._max_length, links, base_url, fcache) ) lines.append("") else: @@ -3107,9 +3116,7 @@ def py_parm(self, custom_functions=None, indent="", links=None, base_url=None, f elif len(arg_desc) > 0: lines.append("-" * 10) for argument in arg_desc: - lines.extend( - argument.to_py_docstring(self._max_length, indent, links, base_url, fcache) - ) + lines.extend(argument.to_py_docstring(self._max_length, links, base_url, fcache)) lines.append("") return lines From d157471201f0f7fa2cf9d8906aa717db06f5915a Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:42:37 +0100 Subject: [PATCH 16/19] fix: ``fname`` indentation --- src/pyconverter/xml2py/ast_tree.py | 60 ++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index e260fbc3a..5d4a61184 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2267,6 +2267,7 @@ class Argument: def __init__( self, + terms, element: str | Element, initial_arguments: List, description: Element | str | None = None, @@ -2286,6 +2287,7 @@ def __init__( else: name = element self._name = name + self._terms = terms self._description = description self._initial_arguments = initial_arguments @@ -2314,7 +2316,9 @@ def multiple_args(self): if not self.is_arg_elipsis: for item_name in split_name: arg_name = item_name.strip() - new_arg = Argument(arg_name, self._initial_arguments, self._description) + new_arg = Argument( + self._terms, arg_name, self._initial_arguments, self._description + ) additional_args.append(new_arg) else: @@ -2324,7 +2328,9 @@ def multiple_args(self): if len(complete_args) > 0: for item in complete_args: - new_arg = Argument(item, self._initial_arguments, self._description) + new_arg = Argument( + self._terms, item, self._initial_arguments, self._description + ) additional_args.append(new_arg) else: @@ -2332,7 +2338,9 @@ def multiple_args(self): for i, item_name in enumerate(split_name): item_name = item_name.strip() if item_name == "": - new_arg = Argument(arg_name, self._initial_arguments, self._description) + new_arg = Argument( + self._terms, arg_name, self._initial_arguments, self._description + ) additional_args.append(new_arg) elif is_elipsis(item_name): @@ -2353,7 +2361,10 @@ def multiple_args(self): arg_name = split_name[i + 1].strip() arg_name = f"{arg_name[:initial_pos_final]}{j}{arg_name[end_pos_final:]}" # noqa : E501 new_arg = Argument( - arg_name, self._initial_arguments, self._description + self._terms, + arg_name, + self._initial_arguments, + self._description, ) if new_arg.py_arg_name != "": additional_args.append(new_arg) @@ -2385,12 +2396,16 @@ def multiple_args(self): for j in range(number_iter_prev + 1, number_iter_next): arg_name = f"{name_iter_prev}{j}" new_arg = Argument( - arg_name, self._initial_arguments, self._description + self._terms, + arg_name, + self._initial_arguments, + self._description, ) additional_args.append(new_arg) else: additional_args.append( Argument( + self._terms, name_iter_next, self._initial_arguments, self._description, @@ -2453,6 +2468,14 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None base_url=base_url, fcache=fcache, ) + + # Replacing terms with their definitions + special_terms = re.findall(r"\&(\S+)\;", rst_description) + if len(special_terms) > 0: + for term in special_terms: + if term in self._terms: + rst_description = rst_description.replace(f"&{term};", self._terms[term]) + description_indent = " " * 4 if not "* " in rst_description: list_description = self.resized_description( @@ -2472,10 +2495,13 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None class ArgumentList: - def __init__(self, py_name: str, url: str, list_entry: VarlistEntry, args: List) -> None: + def __init__( + self, py_name: str, url: str, terms: dict, list_entry: VarlistEntry, args: List + ) -> None: self._py_name = py_name self._url = url + self._terms = terms self._list_entry = list_entry self._arguments = [] self._additional_args = [] @@ -2487,7 +2513,7 @@ def _parse_list_entry(self): temp_args = {} for item in self._list_entry: if isinstance(item, VarlistEntry): - argument_obj = Argument(item, self._initial_args) + argument_obj = Argument(self._terms, item, self._initial_args) additional_args = argument_obj.multiple_args if len(additional_args) > 0: for arg in additional_args: @@ -2506,6 +2532,7 @@ def _parse_list_entry(self): else: self._arguments.append( Argument( + self._terms, initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), @@ -2522,6 +2549,7 @@ def _parse_list_entry(self): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( + self._terms, "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), @@ -2547,6 +2575,7 @@ def __iadd__(self, argument_list): if initial_arg not in self.py_arg_names: self._arguments.append( Argument( + self._terms, initial_arg, self._initial_args, MISSING_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), @@ -2563,6 +2592,7 @@ def __iadd__(self, argument_list): if is_additional_arg and "addional_command_arg" not in self.py_arg_names: self._arguments.append( Argument( + self._terms, "addional_command_arg", self._initial_args, ADDITIONAL_ARGUMENT_DESCRIPTION.replace("url", f"{self._url}"), @@ -2660,17 +2690,25 @@ def arg_desc(self) -> List[Argument]: for child in elem: if isinstance(child, Variablelist): if arguments is None: - arguments = ArgumentList(self.py_name, self.url, child, self.args) + arguments = ArgumentList( + self.py_name, self.url, self._terms, child, self.args + ) else: - arguments += ArgumentList(self.py_name, self.url, child, self.args) + arguments += ArgumentList( + self.py_name, self.url, self._terms, child, self.args + ) else: for elem in refsyn: if isinstance(elem, Variablelist): if arguments is None: - arguments = ArgumentList(self.py_name, self.url, elem, self.args) + arguments = ArgumentList( + self.py_name, self.url, self._terms, elem, self.args + ) else: - arguments += ArgumentList(self.py_name, self.url, elem, self.args) + arguments += ArgumentList( + self.py_name, self.url, self._terms, elem, self.args + ) arg_file = Path("args.txt") From 1534a6156dcbb71bb24eaa68695dd8f7c324431e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 23 Dec 2024 15:03:47 +0100 Subject: [PATCH 17/19] fix: identation issues --- src/pyconverter/xml2py/ast_tree.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 5d4a61184..08a769fa5 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1015,10 +1015,8 @@ def to_rst(self, indent="", max_length=100): return rst_item -def resize_element_list(text, max_length=100, indent=""): +def resize_element_list(text, max_length=100, initial_indent="", subsequent_indent=""): element_list = re.finditer(r"^\* ", text) - initial_indent = indent + " " - subsequent_indent = indent + " " * 2 element_list = resize_length( text, max_length, initial_indent=initial_indent, subsequent_indent=subsequent_indent ) @@ -1057,10 +1055,13 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No if type(item) != str and len(item.children) > 1 and type(item[1]) != str: intersection_types = set(NO_RESIZE_LIST).intersection(set(item[1].children_types)) if len(intersection_types) == 0: - rst_item = resize_element_list(rst_item, max_length, indent=indent) + initial_indent = indent + " " * 2 + rst_item = resize_element_list(rst_item, max_length, initial_indent=initial_indent, subsequent_indent=initial_indent) else: - rst_item = resize_element_list(rst_item, max_length, indent=indent) + initial_indent = indent + " " + subsequent_indent = indent + " " * 2 + rst_item = resize_element_list(rst_item, max_length, initial_indent=initial_indent, subsequent_indent=subsequent_indent) active_items.append(rst_item) return "\n".join(active_items) + "\n" From 7590cc98a9dd9dce42d3f5238cdcae40e2dad5b2 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 23 Dec 2024 16:31:48 +0100 Subject: [PATCH 18/19] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 08a769fa5..c734454ec 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1056,12 +1056,22 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No intersection_types = set(NO_RESIZE_LIST).intersection(set(item[1].children_types)) if len(intersection_types) == 0: initial_indent = indent + " " * 2 - rst_item = resize_element_list(rst_item, max_length, initial_indent=initial_indent, subsequent_indent=initial_indent) + rst_item = resize_element_list( + rst_item, + max_length, + initial_indent=initial_indent, + subsequent_indent=initial_indent, + ) else: initial_indent = indent + " " subsequent_indent = indent + " " * 2 - rst_item = resize_element_list(rst_item, max_length, initial_indent=initial_indent, subsequent_indent=subsequent_indent) + rst_item = resize_element_list( + rst_item, + max_length, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + ) active_items.append(rst_item) return "\n".join(active_items) + "\n" From ccc90ab1f3bff87ec43864ccbfab5d7bfaaa7bb6 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:31:17 +0100 Subject: [PATCH 19/19] fix: enabling bold emphasis --- src/pyconverter/xml2py/ast_tree.py | 41 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index c734454ec..8b2a68b88 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -738,7 +738,10 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No ) items.append(rst_item) - return "\n".join(items) + + rst_list_item = "\n".join(items) + rst_list_item = rst_list_item.replace("*", "\*") + return rst_list_item class FileName(Element): @@ -889,7 +892,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None): if self.role == "bold": # TODO: this isn't the correct way of making text bold - content = f"{self[0]} " + content = f"**{self[0]}** " elif self.role == "italic": # TODO: this isn't the correct way of making text itallic content = f"{self[0]} " @@ -1074,7 +1077,8 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No ) active_items.append(rst_item) - return "\n".join(active_items) + "\n" + rst_varlist = "\n".join(active_items) + "\n" + return rst_varlist @property def terms(self): @@ -1107,7 +1111,8 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No items.append(item.to_rst(indent=indent)) else: items.append(str(item)) - return "\n".join(items) + rst_refsection = "\n".join(items) + return rst_refsection class VarlistEntry(Element): @@ -1247,7 +1252,8 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No py_term = self.py_term(links=links, base_url=base_url) if "``" in py_term: py_term = py_term.replace("``", "") - lines = [f"* ``{py_term}`` - {self.py_text(links=links, base_url=base_url, fcache=fcache)}"] + py_text = self.py_text(links=links, base_url=base_url, fcache=fcache) + lines = [f"* ``{py_term}`` - {py_text}"] text = "\n".join(lines) # if 'ID number to which this tip belongs' in text: # breakpoint() @@ -1666,7 +1672,8 @@ def to_rst(self, indent="", links=None, base_url=None): if len(rst_tbody) > 0: rows += rst_tbody - return "\n".join(rows) + rst_tgroup = "\n".join(rows) + return rst_tgroup class Table(Element): @@ -2121,7 +2128,8 @@ def to_rst(self, l_head, indent="", links=None, base_url=None): if type(row[1][0]) == Command: command = f" * - :ref:`{row[1][0].py_cmd}`" rst_rows.append(command) - strg = " - " + str(row[2][0]) + row_content = str(row[2][0]) + strg = f" - {row_content}" rst_rows.append(strg) return rst_rows @@ -2145,11 +2153,14 @@ def to_rst(self, indent="", links=None, base_url=None, fcache=None): for item in self: if isinstance(item, Element): if item.tag in item_needing_links_base_url: - items.append(item.to_rst(indent, links=links, base_url=base_url)) + entry_item = item.to_rst(indent, links=links, base_url=base_url) else: - items.append(item.to_rst(indent)) + entry_item= item.to_rst(indent) else: - items.append(str(item)) + entry_item = str(item) + # entry_item = entry_item.replace("*", "\*") + + items.append(entry_item) if self.morerows is not None: entry = f":rspan:`{content}` " + " ".join(items) @@ -2855,12 +2866,12 @@ def py_docstring(self, custom_functions: CustomFunctions) -> str: items += [""] + custom_functions.py_examples[self.py_name] docstr = "\n".join(items) - # final post-processing - def replacer(match): - return match.group().replace("*", r"\*").replace(r"\\*", r"\*") + # # final post-processing + # def replacer(match): + # return match.group().replace("*", r"\*").replace(r"\\*", r"\*") - # sphinx doesn't like asterisk symbols - docstr = re.sub(r"(?<=\S)\*|(\*\S)", replacer, docstr) + # # sphinx doesn't like asterisk symbols + # docstr = re.sub(r"(?<=\S)\*|(\*\S)", replacer, docstr) for key, value in CONST.items(): docstr = docstr.replace(key, value)