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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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/67] 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) From 21f9148ec7d7405e36a114eea1c28d4f3637a2a0 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 3 Jan 2025 18:32:38 +0100 Subject: [PATCH 20/67] feat: adding latest enhancements and fixes (italic, bold, indentation fixes, etc) --- src/pyconverter/xml2py/__init__.py | 2 +- src/pyconverter/xml2py/ast_tree.py | 113 +++++++++--------- src/pyconverter/xml2py/cli.py | 2 +- src/pyconverter/xml2py/custom_functions.py | 2 +- src/pyconverter/xml2py/directory_format.py | 2 +- src/pyconverter/xml2py/download.py | 2 +- src/pyconverter/xml2py/formatter.py | 2 +- src/pyconverter/xml2py/load_xml_doc.py | 2 +- src/pyconverter/xml2py/utils/__init__.py | 2 +- src/pyconverter/xml2py/utils/regex_pattern.py | 2 +- src/pyconverter/xml2py/utils/utils.py | 2 +- src/pyconverter/xml2py/version_variables.py | 2 +- src/pyconverter/xml2py/writer.py | 2 +- tests/conftest.py | 2 +- tests/customized_functions/inquire.py | 2 +- tests/customized_functions/k.py | 2 +- tests/customized_functions/kdist.py | 2 +- tests/test_cli.py | 2 +- tests/test_custom_functions.py | 2 +- tests/test_directory_format.py | 2 +- tests/test_load_xml_doc.py | 2 +- tests/test_writer.py | 2 +- tests/test_xml_ast.py | 2 +- 23 files changed, 77 insertions(+), 80 deletions(-) diff --git a/src/pyconverter/xml2py/__init__.py b/src/pyconverter/xml2py/__init__.py index 3925d520b..e5c97f8d6 100644 --- a/src/pyconverter/xml2py/__init__.py +++ b/src/pyconverter/xml2py/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 8b2a68b88..5a495c9c3 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # @@ -387,6 +387,25 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", return output +def replace_asterisks(initial_text): + + # Replace all * with \* + text = re.sub( + r"([^\*])(\*)([A-Z]+) ", r"\1" + r"\*" + r"\3 ", initial_text + ) # Replace ``*DIM`` configurations into ``\*DIM`` + text = re.sub( + r"([^\*\s\\]+)(\*)([^\*\s]+)", r"\1\\2\3", text + ) # Replace ``fac1*fac2`` configurations into ``fac1\*fac2`` + text = re.sub( + r"([^\*])(\*\*)(\*)([A-Z]+)(\*\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + ) # Replace ``***DIM**`` configurations into ``**\*DIM**`` + text = re.sub( + r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + ) # Replace ``**DIM*`` configurations into ``*\*DIM*`` + + return text + + # ############################################################################ # Element class # ############################################################################ @@ -633,8 +652,6 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No new_rst_list = [] for line in rst_list: - line = ponctuaction_whitespace(line, ".") - line = ponctuaction_whitespace(line, ",") new_rst_list.extend( resize_length( line, @@ -649,7 +666,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No # lists must have at least one line proceeding lines = ["", ""] + lines + [""] - return "\n".join(lines) + return "\n\n".join(lines) class SimpleList(ItemizedList): @@ -666,14 +683,19 @@ class Member(Element): def ponctuaction_whitespace(text, ponctuation): - pattern = r"\S\h+\{ponctuation}".format(ponctuation=ponctuation) + 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 = r"\)" if character[0] == ")" else character[0] - pattern = r"{assigned_character}\h+\{ponctuation}".format( - assigned_character=assigned_character, ponctuation=ponctuation - ) + assigned_character = character[0] + if assigned_character in ["*", ")"]: + pattern = r"\{assigned_character}\h+\{ponctuation}".format( + assigned_character=assigned_character, ponctuation=ponctuation + ) + else: + 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 ) @@ -693,12 +715,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None): else: rst_item = item.to_rst(indent) rst_item = re.sub(r"\s+", " ", rst_item.lstrip()) # Remove extra whitespaces - rst_item = ponctuaction_whitespace( - rst_item, "." - ) # Remove extra whitespace before period - rst_item = ponctuaction_whitespace( - rst_item, "," - ) # Remove extra whitespace before comma + resized_item = resize_length( rst_item, max_length=max_length, initial_indent="", subsequent_indent="" ) @@ -740,7 +757,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No items.append(rst_item) rst_list_item = "\n".join(items) - rst_list_item = rst_list_item.replace("*", "\*") + # rst_list_item = rst_list_item.replace("*", "\*") return rst_list_item @@ -889,13 +906,13 @@ def role(self): 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.""" - + content = str(self[0]) if self.role == "bold": # TODO: this isn't the correct way of making text bold - content = f"**{self[0]}** " + content = f"**{content}** " elif self.role == "italic": # TODO: this isn't the correct way of making text itallic - content = f"{self[0]} " + content = f"*{content}* " # elif self.role == 'var': # content = f"``{self[0]}`` " else: @@ -1250,9 +1267,10 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No return "\n".join(lines) py_term = self.py_term(links=links, base_url=base_url) + py_text = self.py_text(links=links, base_url=base_url, fcache=fcache) + if "``" in py_term: py_term = py_term.replace("``", "") - 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: @@ -1694,13 +1712,13 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None): # For now, Tables don't support ``max_length`` lines = [] if self.title is not None: - lines.append(f"{self.title}".strip()) - lines.append((len(lines[-1]) * "=")) + title = f"{self.title}".strip() + lines.append(f"**{title}**\n") lines.append("") if self.tgroup is not None: - a = self.tgroup - lines.append(a.to_rst(indent=indent, links=links, base_url=base_url)) + rst_tgroup = self.tgroup.to_rst(indent=indent, links=links, base_url=base_url) + lines.append(rst_tgroup) return "\n".join(lines) @@ -2155,10 +2173,9 @@ def to_rst(self, indent="", links=None, base_url=None, fcache=None): if item.tag in item_needing_links_base_url: entry_item = item.to_rst(indent, links=links, base_url=base_url) else: - entry_item= item.to_rst(indent) + entry_item = item.to_rst(indent) else: entry_item = str(item) - # entry_item = entry_item.replace("*", "\*") items.append(entry_item) @@ -2499,7 +2516,7 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None rst_description = rst_description.replace(f"&{term};", self._terms[term]) description_indent = " " * 4 - if not "* " in rst_description: + if not " * " in rst_description: list_description = self.resized_description( rst_description, max_length, description_indent ) @@ -2866,13 +2883,6 @@ 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"\*") - - # # 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) for key, value in CLEANUP.items(): @@ -2948,8 +2958,6 @@ def term_replacer(match): return "" return self._terms[term] - docstr = re.sub(r"&[\S]*?;", term_replacer, docstr) - # final line by line cleanup lines = [] for line in docstr.splitlines(): @@ -2959,23 +2967,6 @@ def term_replacer(match): continue lines.append(line) - # ensure the hierarchy of the titles - is_equal_sign = False - is_dash_sign = False - i = 0 - while i < len(lines): - if lines[i].lstrip().startswith("--"): - if is_dash_sign == False: - is_dash_sign = True - elif lines[i].lstrip().startswith("=="): - if is_equal_sign or is_dash_sign: - lines[i - 1] = "**" + lines[i - 1] + "**" - lines.pop(i) - if is_equal_sign == False: - is_equal_sign = True - - i += 1 - # ensure that lists begin with list-table i = 2 while i < len(lines): @@ -3063,6 +3054,11 @@ def term_replacer(match): docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S? ", "", docstr) docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S?", "", docstr) docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) + docstr = re.sub(r"Caret.+\?", "", docstr) + docstr = docstr.replace("–", "-") + docstr = replace_asterisks(docstr) + docstr = ponctuaction_whitespace(docstr, ".") # Remove extra whitespace before period + docstr = ponctuaction_whitespace(docstr, ",") # Remove extra whitespace before comma if self.is_archived == True: logging.info(f"{self.name} is an archived command.") @@ -3095,11 +3091,12 @@ def py_notes(self, custom_functions: CustomFunctions = None): else: notes = self.notes.to_rst() - if "flat-table" not in notes and ".. code::" not in notes: - notes = resize_length(notes, self._max_length, list=True) - lines.extend(notes) - else: - lines.append(notes) + to_be_resized = re.findall(r"^[^\.\s].+(?=\n)|(?<=\n)([^\.\s].+)(?=\n)", notes) + + for item in to_be_resized: + resized_item = resize_length(item, self._max_length) + notes = notes.replace(item, resized_item) + lines.extend(notes.split("\n")) if custom_functions is not None and ( self.py_name in custom_functions.py_names and self.py_name in custom_functions.py_notes diff --git a/src/pyconverter/xml2py/cli.py b/src/pyconverter/xml2py/cli.py index 84ef760a2..7ced1e969 100644 --- a/src/pyconverter/xml2py/cli.py +++ b/src/pyconverter/xml2py/cli.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/custom_functions.py b/src/pyconverter/xml2py/custom_functions.py index 4c1f645c5..4599d321b 100644 --- a/src/pyconverter/xml2py/custom_functions.py +++ b/src/pyconverter/xml2py/custom_functions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/directory_format.py b/src/pyconverter/xml2py/directory_format.py index c436111eb..1b8b2e98e 100644 --- a/src/pyconverter/xml2py/directory_format.py +++ b/src/pyconverter/xml2py/directory_format.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/download.py b/src/pyconverter/xml2py/download.py index 77ba32d32..1607081a5 100644 --- a/src/pyconverter/xml2py/download.py +++ b/src/pyconverter/xml2py/download.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/formatter.py b/src/pyconverter/xml2py/formatter.py index e6561314c..89fa57ec4 100644 --- a/src/pyconverter/xml2py/formatter.py +++ b/src/pyconverter/xml2py/formatter.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/load_xml_doc.py b/src/pyconverter/xml2py/load_xml_doc.py index da55a2646..2329818de 100644 --- a/src/pyconverter/xml2py/load_xml_doc.py +++ b/src/pyconverter/xml2py/load_xml_doc.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/utils/__init__.py b/src/pyconverter/xml2py/utils/__init__.py index 3661eb9a9..4468b92f9 100644 --- a/src/pyconverter/xml2py/utils/__init__.py +++ b/src/pyconverter/xml2py/utils/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/utils/regex_pattern.py b/src/pyconverter/xml2py/utils/regex_pattern.py index 5bd394775..1908bd195 100644 --- a/src/pyconverter/xml2py/utils/regex_pattern.py +++ b/src/pyconverter/xml2py/utils/regex_pattern.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 4ef35a269..61fa201e8 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/version_variables.py b/src/pyconverter/xml2py/version_variables.py index 173954ec9..31e6c1bad 100644 --- a/src/pyconverter/xml2py/version_variables.py +++ b/src/pyconverter/xml2py/version_variables.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/src/pyconverter/xml2py/writer.py b/src/pyconverter/xml2py/writer.py index 7f1f70a73..aa37aa8e0 100644 --- a/src/pyconverter/xml2py/writer.py +++ b/src/pyconverter/xml2py/writer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/conftest.py b/tests/conftest.py index 6557a2242..d3592b329 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/customized_functions/inquire.py b/tests/customized_functions/inquire.py index e098367f2..ba742711f 100644 --- a/tests/customized_functions/inquire.py +++ b/tests/customized_functions/inquire.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/customized_functions/k.py b/tests/customized_functions/k.py index 4150620cf..e86afded8 100644 --- a/tests/customized_functions/k.py +++ b/tests/customized_functions/k.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/customized_functions/kdist.py b/tests/customized_functions/kdist.py index 6473e526a..63848ba4b 100644 --- a/tests/customized_functions/kdist.py +++ b/tests/customized_functions/kdist.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_cli.py b/tests/test_cli.py index e585338bc..5683df891 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_custom_functions.py b/tests/test_custom_functions.py index 5bec7b4a0..e1b55d29f 100644 --- a/tests/test_custom_functions.py +++ b/tests/test_custom_functions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_directory_format.py b/tests/test_directory_format.py index edc6e8fb3..c1ab1019d 100644 --- a/tests/test_directory_format.py +++ b/tests/test_directory_format.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_load_xml_doc.py b/tests/test_load_xml_doc.py index abc429e55..5de47e852 100644 --- a/tests/test_load_xml_doc.py +++ b/tests/test_load_xml_doc.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_writer.py b/tests/test_writer.py index 1ac6f380e..f159bbc30 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # diff --git a/tests/test_xml_ast.py b/tests/test_xml_ast.py index 439fd0266..f860f299f 100644 --- a/tests/test_xml_ast.py +++ b/tests/test_xml_ast.py @@ -1,4 +1,4 @@ -# Copyright (C) 2024 ANSYS, Inc. and/or its affiliates. +# Copyright (C) 2024 - 2025 ANSYS, Inc. and/or its affiliates. # SPDX-License-Identifier: MIT # # From 84a4fa8814deeb6d621cea9d3811e8ac36a23ab0 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:53:41 +0100 Subject: [PATCH 21/67] fix: removing undefined references --- src/pyconverter/xml2py/ast_tree.py | 5 ++++- src/pyconverter/xml2py/utils/utils.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 5a495c9c3..fff5d7018 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1937,7 +1937,10 @@ def sphinx_cmd(self): if self.py_cmd == self.command: ref = f"``{self.py_cmd}``" else: - ref = f":ref:`{self.py_cmd}`" + if self.py_cmd in NAME_MAP_GLOB.keys(): + ref = f":ref:`{self.py_cmd}`" + else: + ref = f"``{self.py_cmd}``" return ref def to_rst(self, indent="", max_length=100): diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 61fa201e8..63d9f6cdc 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -84,6 +84,7 @@ def create_name_map(meta_command: list[str], yaml_file_path: Path) -> dict: naive_names = [] rules = get_config_data_value(yaml_file_path, "rules") specific_command_mapping = get_config_data_value(yaml_file_path, "specific_command_mapping") + ignored_commands = get_config_data_value(yaml_file_path, "ignored_commands") for ans_name in meta_command: ans_name = ans_name.lower() if not ans_name[0].isalnum(): @@ -97,6 +98,8 @@ def create_name_map(meta_command: list[str], yaml_file_path: Path) -> dict: for ans_name in meta_command: if ans_name in specific_command_mapping: py_name = specific_command_mapping[ans_name] + elif ans_name in ignored_commands: + continue else: lower_name = ans_name.lower() if not lower_name[0].isalnum(): From 9c067f283c9c7e5337f2cc99069d18cd60ecf0cf Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:34:41 +0100 Subject: [PATCH 22/67] fix: remaining link issues --- src/pyconverter/xml2py/ast_tree.py | 46 ++++++++++++--- src/pyconverter/xml2py/load_xml_doc.py | 79 ++++++++++++++++---------- 2 files changed, 87 insertions(+), 38 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index fff5d7018..7f5ea108f 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -406,6 +406,41 @@ def replace_asterisks(initial_text): return text +def replace_terms(text, terms): + """ + Replace terms with their definitions. + + Parameters + ---------- + text : str + Text to replace terms. + + terms : dict + Dictionary containing the terms and their definitions. + + Returns + ------- + str + Text with the terms replaced. + """ + iter = 0 + special_terms = re.findall(r"\&([^\-\;]+)\;", text) + stop = False + while len(special_terms) > 0 and not stop: + iter_special_terms = re.findall(r"\&([^\-\;]+)\;", text) + for term in special_terms: + if term in terms: + text = text.replace(f"&{term};", terms[term]) + # Check if there are still special terms that can be replaced + if iter_special_terms == re.findall(r"\&([^\-\;]+)\;", text): + stop = True + else: + special_terms = iter_special_terms + iter += 1 + + return text + + # ############################################################################ # Element class # ############################################################################ @@ -2511,13 +2546,7 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None 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]) - + rst_description = replace_terms(rst_description, self._terms) description_indent = " " * 4 if not " * " in rst_description: list_description = self.resized_description( @@ -3071,6 +3100,8 @@ def term_replacer(match): + "This command is archived in the latest version of the software.\n" ) + # Final cleanup + docstr = replace_terms(docstr, self._terms) return docstr def py_notes(self, custom_functions: CustomFunctions = None): @@ -3094,6 +3125,7 @@ def py_notes(self, custom_functions: CustomFunctions = None): else: notes = self.notes.to_rst() + notes = replace_terms(notes, self._terms) to_be_resized = re.findall(r"^[^\.\s].+(?=\n)|(?<=\n)([^\.\s].+)(?=\n)", notes) for item in to_be_resized: diff --git a/src/pyconverter/xml2py/load_xml_doc.py b/src/pyconverter/xml2py/load_xml_doc.py index 2329818de..e6ba29484 100644 --- a/src/pyconverter/xml2py/load_xml_doc.py +++ b/src/pyconverter/xml2py/load_xml_doc.py @@ -32,6 +32,46 @@ from tqdm import tqdm +def link_replacer(file, terms, docu_global, links, base_url, fcache): + with open(file, "r") as fid: + text = fid.read() + matches = re.findall(r"ENTITY([\S\s]*?)(?=\n)", text) + for match in matches: + item = ast.Element(fromstring(match)).to_rst( + links=links, base_url=base_url, fcache=fcache + ) + key = item.split()[0] + text = (item.replace(key, "")).strip() + if not text.startswith("'"): + continue + + text = text[1:-2].strip() + + def term_replacer(match): + term = match.group()[1:-1] + if term in docu_global: + _, key, cite_title = docu_global[term] + if key in links: + root_name, root_title, href, text = links[key] + link = f"{base_url}{root_name}/{href}" + link_text = terms.get(cite_title, root_title) + return f"`{link_text} <{link}>`_" + else: + if term not in terms: + return match.group() + return terms[term] + + text = re.sub(r"&[\S]*;", term_replacer, text) + + if key not in terms: + terms[key] = text + + if "angcycsym" in terms: + print(terms["angcycsym"]) + + return terms + + def load_links(link_path: Path) -> dict: """Load all links. @@ -136,7 +176,7 @@ def load_docu_global(term_path: Path) -> dict: targetptrs = re.findall(r'targetptr="(\S*)"', line) targetptr = targetptrs[0] if len(targetptrs) else None - citetitles = re.findall(r"&(\S*);", line) + citetitles = re.findall(r"&(\S*);<\/citetitle>", line) citetitle = citetitles[0] if len(citetitles) else None docu_global[entity_name] = (targetdoc, targetptr, citetitle) @@ -249,37 +289,14 @@ def load_terms( # load manuals manual_path = term_path / "glb" / manual_file if manual_path.is_file(): - with open(manual_path, "r") as fid: - text = fid.read() - matches = re.findall(r"ENTITY([\S\s]*?)`_" - else: - if term not in terms: - return match.group() - return terms[term] - - text = re.sub(r"&[\S]*;", term_replacer, text) + terms = link_replacer(manual_path, terms, docu_global, links, base_url, fcache) + else: + print("WARNING: No file found for defining terms.") - terms[key] = text + # load docu_global + docu_ent = term_path / "glb" / "docu_global.ent" + if docu_ent.is_file(): + terms = link_replacer(docu_ent, terms, docu_global, links, base_url, fcache) else: print("WARNING: No file found for defining terms.") From a4aedb422778127c1a9332703fdcb906b1c6dfe1 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:38:57 +0100 Subject: [PATCH 23/67] fix: warnings --- src/pyconverter/xml2py/load_xml_doc.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pyconverter/xml2py/load_xml_doc.py b/src/pyconverter/xml2py/load_xml_doc.py index e6ba29484..f5768db5c 100644 --- a/src/pyconverter/xml2py/load_xml_doc.py +++ b/src/pyconverter/xml2py/load_xml_doc.py @@ -270,21 +270,21 @@ def load_terms( # TODO: use another file for this. # Manually adding terms value from warnings. - terms["sgr"] = ":math:`\sigma`" - terms["gt"] = ":math:`\sigma`" + terms["sgr"] = r":math:`\sigma`" + terms["gt"] = r":math:`\sigma`" terms["thgr"] = ":math:`<`" terms["phgr"] = ":math:`<`" - terms["ngr"] = ":math:`\phi`" - terms["agr"] = ":math:`\alpha`" - terms["OHgr"] = ":math:`\Omega`" - terms["phis"] = ":math:`\phi`" - terms["thetas"] = ":math:`\theta`" + terms["ngr"] = r":math:`\phi`" + terms["agr"] = r":math:`\alpha`" + terms["OHgr"] = r":math:`\Omega`" + terms["phis"] = r":math:`\phi`" + terms["thetas"] = r":math:`\theta`" # These are supposed to be uploaded automatically from the `character.ent` file terms["#13"] = "#13" terms["#160"] = "nbsp" terms["#215"] = "times" - terms["#934"] = ":math:`\Phi`" + terms["#934"] = r":math:`\Phi`" # load manuals manual_path = term_path / "glb" / manual_file From 6810c5a07a5e754dd85ae54e45f7608f3ca04bc0 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:49:13 +0100 Subject: [PATCH 24/67] fix: ``type_`` or ``class_`` in docstr --- _package/doc/source/conf.py | 4 +++- src/pyconverter/xml2py/ast_tree.py | 18 +----------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/_package/doc/source/conf.py b/_package/doc/source/conf.py index fe68dde86..504c04385 100644 --- a/_package/doc/source/conf.py +++ b/_package/doc/source/conf.py @@ -65,6 +65,8 @@ # "grpc": ("https://grpc.github.io/grpc/python/", None), } +suppress_warning = ["misc.highlighting_failure"] + # Copy button customization --------------------------------------------------- # exclude traditional Python prompts from the copied code copybutton_prompt_text = r">>> ?|\.\.\. " @@ -77,7 +79,7 @@ numpydoc_validation_checks = { "GL06", # Found unknown section "GL07", # Sections are in the wrong order. - "GL08", # The object does not have a docstring + # "GL08", # The object does not have a docstring "GL09", # Deprecation warning should precede extended summary "GL10", # reST directives {directives} must be followed by two colons "SS01", # No summary found diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 7f5ea108f..b10768793 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2535,7 +2535,6 @@ def resized_description( 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'{self.py_arg_name} : {str_types(self.types, " or ")}'] if isinstance(self._description, str): rst_description = self._description else: @@ -2973,22 +2972,7 @@ def pipe_replacer(match): def trail_replacer(match): return match.group().replace("_", "") - docstr = re.sub(r"([^_|^`][A-Za-z0-9]*)_\s", trail_replacer, docstr) - - def term_replacer(match): - term = match.group()[1:-1] - if term in self._docu_global: - _, key, cite_title = self._docu_global[term] - if key in self._links: - root_name, root_title, href, text = self._links[key] - link = f"{self._base_url}{root_name}/{href}" - link_text = self._terms.get(cite_title, root_title) - return f"`{link_text} <{link}>`_" - else: - if term not in self._terms: - warnings.warn(f"term {term} not in terms") - return "" - return self._terms[term] + docstr = re.sub(r"([^_|^`][A-Za-z0-9]*)_\s[^:]", trail_replacer, docstr) # final line by line cleanup lines = [] From 6bbfceef3bd80286419868f9801ce653f0678b0d Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:51:36 +0100 Subject: [PATCH 25/67] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index b10768793..7f42dd16d 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -24,7 +24,6 @@ from pathlib import Path import textwrap from typing import List -import warnings from inflect import engine from lxml.etree import tostring From 8aa5dbded5326b51feee3bcf6fd31a444df53522 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:29:58 +0100 Subject: [PATCH 26/67] fix: remaining line length --- src/pyconverter/xml2py/ast_tree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 7f42dd16d..e7874a359 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3109,7 +3109,8 @@ def py_notes(self, custom_functions: CustomFunctions = None): notes = self.notes.to_rst() notes = replace_terms(notes, self._terms) - to_be_resized = re.findall(r"^[^\.\s].+(?=\n)|(?<=\n)([^\.\s].+)(?=\n)", notes) + + to_be_resized = re.findall(r"^[^\.\s]?.+(?=\n)|(?<=\n)[^\.\s].+(?=\n)", notes) for item in to_be_resized: resized_item = resize_length(item, self._max_length) From 2f6ec26a9cae41dd88a3d34ab0083e4f814839da Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 12:30:27 +0100 Subject: [PATCH 27/67] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index e7874a359..ae9623c13 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3109,7 +3109,7 @@ def py_notes(self, custom_functions: CustomFunctions = None): notes = self.notes.to_rst() notes = replace_terms(notes, self._terms) - + to_be_resized = re.findall(r"^[^\.\s]?.+(?=\n)|(?<=\n)[^\.\s].+(?=\n)", notes) for item in to_be_resized: From bf844536982987f3a6f8da9cd38ba065ab7c4ac7 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:55:43 +0100 Subject: [PATCH 28/67] fix: ``replace_asterisks`` --- src/pyconverter/xml2py/ast_tree.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index ae9623c13..1ceeaf2c0 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -390,13 +390,13 @@ def replace_asterisks(initial_text): # Replace all * with \* text = re.sub( - r"([^\*])(\*)([A-Z]+) ", r"\1" + r"\*" + r"\3 ", initial_text + r"([^\*])(\*)([A-Z]+)(\`|\s)", r"\1" + r"\*" + r"\3\4", initial_text ) # Replace ``*DIM`` configurations into ``\*DIM`` text = re.sub( r"([^\*\s\\]+)(\*)([^\*\s]+)", r"\1\\2\3", text ) # Replace ``fac1*fac2`` configurations into ``fac1\*fac2`` text = re.sub( - r"([^\*])(\*\*)(\*)([A-Z]+)(\*\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + r"([^\*])(\*\*)(\*)(.*?)(\*\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text ) # Replace ``***DIM**`` configurations into ``**\*DIM**`` text = re.sub( r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text @@ -3071,7 +3071,12 @@ def trail_replacer(match): docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) docstr = re.sub(r"Caret.+\?", "", docstr) docstr = docstr.replace("–", "-") - docstr = replace_asterisks(docstr) + if "*IF" in docstr: + print("BEFORE : ", docstr) + docstr = replace_asterisks(docstr) + print("AFTER : ", docstr) + else: + docstr = replace_asterisks(docstr) docstr = ponctuaction_whitespace(docstr, ".") # Remove extra whitespace before period docstr = ponctuaction_whitespace(docstr, ",") # Remove extra whitespace before comma From b5032dca8ebe149a1c4c0e51a190e31260d8aa6e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:14:35 +0100 Subject: [PATCH 29/67] fix: removing prints and sphinx warnings --- _package/doc/source/conf.py | 4 +++- src/pyconverter/xml2py/ast_tree.py | 7 +------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/_package/doc/source/conf.py b/_package/doc/source/conf.py index 504c04385..9488b8e78 100644 --- a/_package/doc/source/conf.py +++ b/_package/doc/source/conf.py @@ -65,7 +65,9 @@ # "grpc": ("https://grpc.github.io/grpc/python/", None), } -suppress_warning = ["misc.highlighting_failure"] +suppress_warnings = [ + "misc.highlighting_failure", # Suppress highlighting failures +] # Copy button customization --------------------------------------------------- # exclude traditional Python prompts from the copied code diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 1ceeaf2c0..15de7b1c7 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3071,12 +3071,7 @@ def trail_replacer(match): docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) docstr = re.sub(r"Caret.+\?", "", docstr) docstr = docstr.replace("–", "-") - if "*IF" in docstr: - print("BEFORE : ", docstr) - docstr = replace_asterisks(docstr) - print("AFTER : ", docstr) - else: - docstr = replace_asterisks(docstr) + docstr = replace_asterisks(docstr) docstr = ponctuaction_whitespace(docstr, ".") # Remove extra whitespace before period docstr = ponctuaction_whitespace(docstr, ",") # Remove extra whitespace before comma From 3553425e0509d73047219fbe2ee2849f5cf63bb3 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:42:33 +0100 Subject: [PATCH 30/67] fix: cleaning code and adding new line for ``ProgramListing.to_rst()`` --- src/pyconverter/xml2py/ast_tree.py | 35 +++++++++++++------------- src/pyconverter/xml2py/load_xml_doc.py | 3 --- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 15de7b1c7..051813555 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1065,7 +1065,7 @@ 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:: 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" + rst_item = header + textwrap.indent(source_code, prefix=indent + " " * 3) + "\n\n" return rst_item @@ -1929,7 +1929,7 @@ def args(self): if isinstance(self.next_elem, Replaceable): cmd_args += str(self.next_elem[0]) # no tail elif len(self.tail) > 1 and self.tail[1] == " ": - # possible not coded as replacable + # possible not coded as replaceable for word in words[1:]: if word.upper() == word or is_numeric(word): if not (word[-1].isalnum() or word[-1].isdigit()): @@ -2994,19 +2994,6 @@ def trail_replacer(match): lines.insert(i + 1, "") i += 1 - # ensure that lists end with a blank line - i = 0 - while i < len(lines): - j = 1 - if lines[i].lstrip().startswith("* -"): - while i + j < len(lines) - 1 and lines[i + j].lstrip().startswith("-"): - j += 1 - if not lines[i + j].lstrip().startswith("* -"): - if i + j == len(lines) - 1: - j += 1 - lines.insert(i + j, "") - i += j - # ensure that two similar links are not in a similar file. i = 0 link = [] @@ -3060,12 +3047,26 @@ def trail_replacer(match): else: lines[i] = lines[i].replace(l, name_link) - docstr = "\n".join(lines) - # remove repeated line breaks while "\n\n\n" in docstr: docstr = docstr.replace("\n\n\n", "\n\n") + lines = docstr.splitlines() + + # ensure that lists end with a blank line + i = 0 + while i < len(lines): + j = 1 + if lines[i].lstrip().startswith("* -"): + while i + j < len(lines) - 1 and lines[i + j].lstrip().startswith("-"): + j += 1 + if not lines[i + j].lstrip().startswith("* -"): + if i + j == len(lines) - 1: + j += 1 + lines.insert(i + j, "") + i += j + docstr = "\n".join(lines) + docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S? ", "", docstr) docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S?", "", docstr) docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) diff --git a/src/pyconverter/xml2py/load_xml_doc.py b/src/pyconverter/xml2py/load_xml_doc.py index f5768db5c..12cb0f42e 100644 --- a/src/pyconverter/xml2py/load_xml_doc.py +++ b/src/pyconverter/xml2py/load_xml_doc.py @@ -66,9 +66,6 @@ def term_replacer(match): if key not in terms: terms[key] = text - if "angcycsym" in terms: - print(terms["angcycsym"]) - return terms From 6d20cba8422a647e53d5d67f715039bef9aef3d7 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:37:15 +0100 Subject: [PATCH 31/67] fix: error in ``py_docstring`` --- src/pyconverter/xml2py/ast_tree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 051813555..9b78e9ce5 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -791,7 +791,6 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No items.append(rst_item) rst_list_item = "\n".join(items) - # rst_list_item = rst_list_item.replace("*", "\*") return rst_list_item @@ -3047,6 +3046,8 @@ def trail_replacer(match): else: lines[i] = lines[i].replace(l, name_link) + docstr = "\n".join(lines) + # remove repeated line breaks while "\n\n\n" in docstr: docstr = docstr.replace("\n\n\n", "\n\n") From 71584a477cddb33da2385f0dee210f7c8dd1a03e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 13:52:45 +0100 Subject: [PATCH 32/67] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 9b78e9ce5..60b4b3f47 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3047,7 +3047,7 @@ def trail_replacer(match): lines[i] = lines[i].replace(l, name_link) docstr = "\n".join(lines) - + # remove repeated line breaks while "\n\n\n" in docstr: docstr = docstr.replace("\n\n\n", "\n\n") From 337627ac53fccfd295292c6c874106ce16dfc9a8 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:30:48 +0100 Subject: [PATCH 33/67] fix: removing italic feature and fixing ``. . .`` characters --- src/pyconverter/xml2py/ast_tree.py | 45 ++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 60b4b3f47..898e69d0d 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -398,9 +398,9 @@ def replace_asterisks(initial_text): text = re.sub( r"([^\*])(\*\*)(\*)(.*?)(\*\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text ) # Replace ``***DIM**`` configurations into ``**\*DIM**`` - text = re.sub( - r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text - ) # Replace ``**DIM*`` configurations into ``*\*DIM*`` + # text = re.sub( + # r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + # ) # TODO: Replace ``**DIM*`` configurations into ``*\*DIM*`` return text @@ -791,6 +791,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No items.append(rst_item) rst_list_item = "\n".join(items) + # rst_list_item = rst_list_item.replace("*", "\*") return rst_list_item @@ -941,15 +942,14 @@ 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.""" content = str(self[0]) if self.role == "bold": - # TODO: this isn't the correct way of making text bold content = f"**{content}** " - elif self.role == "italic": - # TODO: this isn't the correct way of making text itallic - content = f"*{content}* " + # elif self.role == "italic": + # # TODO: this isn't the correct way of making text itallic + # content = f"{content} " # elif self.role == 'var': # content = f"``{self[0]}`` " else: - content = f"{self[0]} " + content = f"{content} " items = [] for item in self[1:]: @@ -1042,12 +1042,13 @@ def content_equals(self): def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" + rst_replaceable = f"``{self.content[0]}`` {self.tail}" if isinstance(self.prev_elem, Command): if any([self.content[0] in arg for arg in self.prev_elem.args]): - return f"{self.tail}" + rst_replaceable = f"{self.tail}" if self.is_equals: - return self.content_equals - return f"``{self.content[0]}`` {self.tail}" + rst_replaceable = self.content_equals + return rst_replaceable class ProgramListing(Element): @@ -1064,7 +1065,22 @@ 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:: 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\n" + source_code = header + textwrap.indent(source_code, prefix=indent + " " * 3) + "\n\n" + + items = [] + + for item in self: + if isinstance(item, Element): + items += item.to_rst(indent=indent, max_length=max_length) + else: # if isinstance(item, str): + item_in_source = re.search(r"\S+", item).group() + if item_in_source and item_in_source in source_code: + items += source_code + else: + items += item + + rst_item = "".join(items) + return rst_item @@ -3046,8 +3062,6 @@ def trail_replacer(match): else: lines[i] = lines[i].replace(l, name_link) - docstr = "\n".join(lines) - # remove repeated line breaks while "\n\n\n" in docstr: docstr = docstr.replace("\n\n\n", "\n\n") @@ -3073,6 +3087,7 @@ def trail_replacer(match): docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) docstr = re.sub(r"Caret.+\?", "", docstr) docstr = docstr.replace("–", "-") + docstr = docstr.replace(". . .", "...") docstr = replace_asterisks(docstr) docstr = ponctuaction_whitespace(docstr, ".") # Remove extra whitespace before period docstr = ponctuaction_whitespace(docstr, ",") # Remove extra whitespace before comma @@ -3112,7 +3127,7 @@ def py_notes(self, custom_functions: CustomFunctions = None): notes = replace_terms(notes, self._terms) - to_be_resized = re.findall(r"^[^\.\s]?.+(?=\n)|(?<=\n)[^\.\s].+(?=\n)", notes) + to_be_resized = re.findall(r"^[^\.\s].+(?=\n)|(?<=\n)[^\.\s].+(?=\n)", notes) for item in to_be_resized: resized_item = resize_length(item, self._max_length) From 13491f4f31779b9eacb15664051e0024c80c69eb Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:31:25 +0100 Subject: [PATCH 34/67] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 898e69d0d..9b4b21991 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1047,7 +1047,7 @@ def to_rst(self, indent="", max_length=100): if any([self.content[0] in arg for arg in self.prev_elem.args]): rst_replaceable = f"{self.tail}" if self.is_equals: - rst_replaceable = self.content_equals + rst_replaceable = self.content_equals return rst_replaceable @@ -1066,21 +1066,21 @@ def to_rst(self, indent="", max_length=100): header = f"\n\n{indent}.. code:: apdl\n\n" source_code = re.sub(r"[^\S\r\n]", " ", self.source) # Remove extra whitespaces source_code = header + textwrap.indent(source_code, prefix=indent + " " * 3) + "\n\n" - + items = [] - + for item in self: if isinstance(item, Element): items += item.to_rst(indent=indent, max_length=max_length) - else: # if isinstance(item, str): + else: # if isinstance(item, str): item_in_source = re.search(r"\S+", item).group() if item_in_source and item_in_source in source_code: items += source_code else: items += item - - rst_item = "".join(items) - + + rst_item = "".join(items) + return rst_item From 291507bf057fc0b45ef1e31a9e2da01cdf32d501 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:54:12 +0100 Subject: [PATCH 35/67] fix: ``.. code::`` elements (indentations and new lines) + removing unknown caracters --- src/pyconverter/xml2py/ast_tree.py | 39 ++++++++++++++++-------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 9b4b21991..0754e0550 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -65,6 +65,9 @@ ",)": ")", "% ``": "``%", # Ansys variable names should be pulled inside literals "`` %": "%``", # same + "\xa0": " ", + "’": "``", + "∗": "*", } PY_ARG_CLEANUP = { @@ -356,7 +359,6 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", str or list Resized text. """ - text = text.replace(" .", ".") while "\n\n\n" in text: text = text.replace("\n\n\n", "\n\n") @@ -685,22 +687,28 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No rst_list = item_lines new_rst_list = [] - for line in rst_list: - new_rst_list.extend( - resize_length( - line, - max_length=max_length, - initial_indent=indent, - subsequent_indent=indent, - list=True, + + if ".. code::" in "\n".join(rst_list): + lines.extend(rst_list) + + else: + for line in rst_list: + new_rst_list.extend( + resize_length( + line, + max_length=max_length, + initial_indent=indent, + subsequent_indent=indent, + list=True, + ) ) - ) - lines.extend(new_rst_list) + lines.extend(new_rst_list) # lists must have at least one line proceeding lines = ["", ""] + lines + [""] - return "\n\n".join(lines) + + return "\n".join(lines) class SimpleList(ItemizedList): @@ -791,7 +799,6 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No items.append(rst_item) rst_list_item = "\n".join(items) - # rst_list_item = rst_list_item.replace("*", "\*") return rst_list_item @@ -1064,9 +1071,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:: apdl\n\n" - source_code = re.sub(r"[^\S\r\n]", " ", self.source) # Remove extra whitespaces - source_code = header + textwrap.indent(source_code, prefix=indent + " " * 3) + "\n\n" - + source_code = header + textwrap.indent(self.source, prefix=indent + " " * 3) + "\n\n" items = [] for item in self: @@ -1080,7 +1085,6 @@ def to_rst(self, indent="", max_length=100): items += item rst_item = "".join(items) - return rst_item @@ -3126,7 +3130,6 @@ def py_notes(self, custom_functions: CustomFunctions = None): notes = self.notes.to_rst() notes = replace_terms(notes, self._terms) - to_be_resized = re.findall(r"^[^\.\s].+(?=\n)|(?<=\n)[^\.\s].+(?=\n)", notes) for item in to_be_resized: From 2a6ca77e03bab3fb932fae091227137e3335c42e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:19:21 +0100 Subject: [PATCH 36/67] fix: references --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 0754e0550..f07e54596 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1990,7 +1990,7 @@ def sphinx_cmd(self): if self.py_cmd == self.command: ref = f"``{self.py_cmd}``" else: - if self.py_cmd in NAME_MAP_GLOB.keys(): + if self.py_cmd in NAME_MAP_GLOB.values(): ref = f":ref:`{self.py_cmd}`" else: ref = f"``{self.py_cmd}``" From 81b5e5428dd1ed04dd2c3f54d35de35ba95d7052 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:31:53 +0100 Subject: [PATCH 37/67] fix: ``py_docstring`` --- 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 f07e54596..037073797 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3066,6 +3066,8 @@ def trail_replacer(match): else: lines[i] = lines[i].replace(l, name_link) + docstr = "\n".join(lines) + # remove repeated line breaks while "\n\n\n" in docstr: docstr = docstr.replace("\n\n\n", "\n\n") From 01a1098fcabdcb96fbd1cc37bc2a8ee989227a1f Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 16:57:25 +0100 Subject: [PATCH 38/67] =?UTF-8?q?fix:=20``ignored=5Fcommands``=20>``specif?= =?UTF-8?q?ic=5Fcommand=5Fmapping``=20and=20``=E2=80=A6``=20caracter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pyconverter/xml2py/ast_tree.py | 1 + src/pyconverter/xml2py/utils/utils.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 037073797..02f9d904d 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -68,6 +68,7 @@ "\xa0": " ", "’": "``", "∗": "*", + "…": "...", } PY_ARG_CLEANUP = { diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 63d9f6cdc..3e8ab32cc 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -96,10 +96,10 @@ def create_name_map(meta_command: list[str], yaml_file_path: Path) -> dict: # second pass for each name for ans_name in meta_command: - if ans_name in specific_command_mapping: - py_name = specific_command_mapping[ans_name] - elif ans_name in ignored_commands: + if ans_name in ignored_commands: continue + elif ans_name in specific_command_mapping: + py_name = specific_command_mapping[ans_name] else: lower_name = ans_name.lower() if not lower_name[0].isalnum(): From 7a754de54491087a71f5516256c4fea25c2a78e9 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 8 Jan 2025 18:15:41 +0100 Subject: [PATCH 39/67] fix: fixing issues from Codacy review --- src/pyconverter/xml2py/ast_tree.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 02f9d904d..a24ce9c5e 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -210,11 +210,8 @@ def to_py_arg_name(name: str) -> str: while len(arg) > 0 and arg[-1] == "_": arg = arg[:-1] - if arg == "type": - arg = "type_" - - elif arg == "class": - arg = "class_" + if arg in ["type", "class", "property", "format", "dir"]: + arg = f"{arg}_" return f"{arg}" @@ -3242,7 +3239,7 @@ def py_source(self, custom_functions=None, indent=""): command += "}" command += '"\n' else: - command = 'command = f"' + self.name + '"\n' + command = f'command = "{self.name}"\n' return_command = "return self.run(command, **kwargs)\n" source = textwrap.indent("".join([command, return_command]), prefix=" " * 4 + indent) From 7c8d791ac717b1e0f0e38c6111d8166a3719c1de Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:11:22 +0100 Subject: [PATCH 40/67] feat: adding warning info at the top of a command --- config.yaml | 44 ++++++++++++++++++++------- src/pyconverter/xml2py/ast_tree.py | 40 +++++++++++++++++++++--- src/pyconverter/xml2py/utils/utils.py | 21 +++++++++++++ src/pyconverter/xml2py/writer.py | 9 ++++-- 4 files changed, 96 insertions(+), 18 deletions(-) diff --git a/config.yaml b/config.yaml index 02dab56b1..129b34ff3 100644 --- a/config.yaml +++ b/config.yaml @@ -20,20 +20,42 @@ specific_command_mapping: "/INQUIRE": inquire ignored_commands: - - "*VWR" - - "*MWR" - - "C***" - - "*CFO" - - "*CRE" - - "*END" - - "/EOF" + # Non-available commands - PyMAPDL user guide - "*ASK" - - "*IF" + - "*VEDIT" + - "/ERASE" + - "ERASE" + - "HELP" + - "HELPDISP" + - "NOERASE" + - "*CYCLE" + - "*DO" + - "*DOWHILE" - "*ELSE" - - "CMAT" - - "*REP" + - "*ELSEIF" + - "*ENDDO" + - "*GO" + - "*IF" + - "*REPEAT" - "*RETURN" - - "LSRE" + - "*DEL" + - "/BATCH" + - "/EOF" + - "UNDO" + # Additional commands to ignore + - "C***" + - "/DIRECTORY" # Defined in ``mapdl_core.py`` + - "*XPL" # Defined in ``mapdl_grpc.py`` + +warnings: + "This command must be run using :func:`non_interactive `\nPlease visit `Unsupported Interactive Commands `_\nfor further information.": + - "*CREATE" + - "CFOPEN" + - "CFCLOSE" + - "*VWRITE" + - "*MWRITE" + - "LSWRITE" + - "LSREAD" specific_classes: diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index a24ce9c5e..6f7b2e06d 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2898,12 +2898,35 @@ def py_signature(self, custom_functions: CustomFunctions, indent="") -> str: arg_sig = ", ".join(args) return f"{indent}def {self.py_name}({arg_sig}, **kwargs):" - def py_docstring(self, custom_functions: CustomFunctions) -> str: - """Python docstring of the command.""" + def py_docstring(self, custom_functions: CustomFunctions, warning_command_dict: None) -> str: + """ + Python docstring of the command. + + Parameters + ---------- + + custom_functions : CustomFunctions + Custom functions object. + + warning_command_dict: dict, optional + Dictionary of commands associated to a list of warnings. + The default is ``None``. + + + """ xml_cmd = f"{self._terms['pn006p']} Command: `{self.name} <{self.url}>`_" + # ``warning_command_dict`` need to be added here + items = [self.short_desc, "", xml_cmd] + if self.name in warning_command_dict.keys(): + warnings_ = warning_command_dict[self.name] + for warning_ in warnings_: + warning_ = textwrap.indent(warning_, " " * 3) + items.extend([f"\n.. warning::\n{warning_}\n"]) + print(items) + if self.default is not None: if self.default.tag in item_needing_links_base_url: items += [""] + textwrap.wrap( @@ -3247,14 +3270,20 @@ def py_source(self, custom_functions=None, indent=""): source = textwrap.indent("".join(custom_functions.py_code[self.py_name]), prefix=indent) return source - def to_python(self, custom_functions=None, indent=""): + def to_python(self, custom_functions=None, warning_command_dict=None, indent=""): """ Return the complete Python definition of the command. Parameters ---------- custom_functions: CustomFunctions, optional - Custom functions to add to the command. The default is ``None``. + Custom functions to add to the command. + The default is ``None``. + + warning_command_dict: dict, optional + Dictionary of commands associated to a list of warnings. + The default is ``None``. + indent: str, optional Indentation of the Python function. The default is ``""``. @@ -3265,7 +3294,8 @@ def to_python(self, custom_functions=None, indent=""): """ docstr = textwrap.indent( - f'r"""{self.py_docstring(custom_functions)}\n"""', prefix=indent + " " * 4 + f'r"""{self.py_docstring(custom_functions, warning_command_dict)}\n"""', + prefix=indent + " " * 4, ) if custom_functions is not None and self.py_name in custom_functions.lib_import: imports = "\n".join(custom_functions.lib_import[self.py_name]) diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 3e8ab32cc..eaf8fdc74 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -64,6 +64,27 @@ def get_config_data_value(yaml_path: Path, value: str) -> Union[str, dict, list, return config_data.get(value) +def get_warning_command_dict(yaml_path: Path) -> dict: + """ + Get the list of commands that will raise a warning. + + Parameters + ---------- + yaml_path: Path + Path object of the YAML file. + """ + warnings_ = get_config_data_value(yaml_path, "warnings") + warning_command_dict = {} + for warning_, command_list in warnings_.items(): + for command in command_list: + try: + warning_command_dict[command].append(warning_) + except KeyError: + warning_command_dict[command] = [warning_] + + return warning_command_dict + + def create_name_map(meta_command: list[str], yaml_file_path: Path) -> dict: """ Create a mapping between the initial command name and the Python function name. diff --git a/src/pyconverter/xml2py/writer.py b/src/pyconverter/xml2py/writer.py index aa37aa8e0..30c2f2e3a 100644 --- a/src/pyconverter/xml2py/writer.py +++ b/src/pyconverter/xml2py/writer.py @@ -35,6 +35,7 @@ create_name_map, get_config_data_value, get_refentry, + get_warning_command_dict, import_handler, ) import regex as re @@ -430,6 +431,8 @@ def write_source( library_path = Path(get_library_path(new_package_path, config_path)) + warning_command_dict = get_warning_command_dict(config_path) + if not library_path.is_dir(): library_path.mkdir(parents=True, exist_ok=True) @@ -440,7 +443,7 @@ def write_source( continue python_name = name_map[initial_command_name] path = library_path / f"{python_name}.py" - python_method = command_obj.to_python(custom_functions) + python_method = command_obj.to_python(custom_functions, warning_command_dict, indent="") try: exec(python_method) with open(path, "w", encoding="utf-8") as fid: @@ -482,7 +485,9 @@ def write_source( class_structure.append(command.py_name) package_structure[module_name][file_name] = [class_name, class_structure] - python_method = command.to_python(custom_functions, indent=4 * " ") + python_method = command.to_python( + custom_functions, warning_command_dict, indent=4 * " " + ) # Check if there are any imports to be added before the function definition. reg_before_def = pat.BEFORE_DEF + f"{command.py_name})" From 26803a9706770287aa1dbd2233b18e38deb93511 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:21:32 +0100 Subject: [PATCH 41/67] fix: removing print --- src/pyconverter/xml2py/ast_tree.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 6f7b2e06d..4ac7d6ffa 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2925,7 +2925,6 @@ def py_docstring(self, custom_functions: CustomFunctions, warning_command_dict: for warning_ in warnings_: warning_ = textwrap.indent(warning_, " " * 3) items.extend([f"\n.. warning::\n{warning_}\n"]) - print(items) if self.default is not None: if self.default.tag in item_needing_links_base_url: From 7a9fd54c5fd524049549a8f03ce96f51797ddce8 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 9 Jan 2025 17:18:35 +0100 Subject: [PATCH 42/67] fix: updating ``config.yaml`` --- config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.yaml b/config.yaml index 129b34ff3..dda4040f3 100644 --- a/config.yaml +++ b/config.yaml @@ -34,6 +34,7 @@ ignored_commands: - "*ELSE" - "*ELSEIF" - "*ENDDO" + - "*ENDIF" - "*GO" - "*IF" - "*REPEAT" From ead0117b16fa9170a629cbe4793fa2ed9204edf5 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:02:27 +0100 Subject: [PATCH 43/67] fix: bullet list warning + ``GuiMenuItem`` rendering --- src/pyconverter/xml2py/ast_tree.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 4ac7d6ffa..06c29530e 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -787,12 +787,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No else: rst_item = item.to_rst(indent=indent, max_length=max_length) else: - rst_item = resize_length( - str(item), - max_length=max_length, - initial_indent=indent, - subsequent_indent=indent, - ) + rst_item = str(item) items.append(rst_item) @@ -1127,13 +1122,13 @@ 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 + subsequent_indent = initial_indent + " " * 2 rst_item = resize_element_list( rst_item, max_length, initial_indent=initial_indent, - subsequent_indent=initial_indent, + subsequent_indent=subsequent_indent, ) - else: initial_indent = indent + " " subsequent_indent = indent + " " * 2 @@ -1366,7 +1361,10 @@ class GuiLabel(Element): class GuiMenuItem(Element): """Provides the GUI menu item element.""" - pass + def to_rst(self, indent="", max_length=100): + """Return a string to enable converting the element to an RST format.""" + gui_rst = f"``{self[0]}`` {self.tail}" + return gui_rst class SuperScript(Element): From 109cbf8574443edd9edf9b57c3a9a9d5fccaa44b Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:12:49 +0100 Subject: [PATCH 44/67] fix: indent issues due to ``,`` --- src/pyconverter/xml2py/ast_tree.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 06c29530e..5c0336f07 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2558,8 +2558,10 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None base_url=base_url, fcache=fcache, ) - rst_description = replace_terms(rst_description, self._terms) + rst_description = ponctuaction_whitespace(rst_description, ".") # Remove extra whitespace before period + rst_description = ponctuaction_whitespace(rst_description, ",") # Remove extra whitespace before comma + description_indent = " " * 4 if not " * " in rst_description: list_description = self.resized_description( From 1ab7d0b6b9061d911a2240ff7bd483138be764c7 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:16:10 +0100 Subject: [PATCH 45/67] fix: pre-commit --- src/pyconverter/xml2py/ast_tree.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 5c0336f07..e3cf1eb7b 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -2559,8 +2559,10 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None fcache=fcache, ) rst_description = replace_terms(rst_description, self._terms) - rst_description = ponctuaction_whitespace(rst_description, ".") # Remove extra whitespace before period - rst_description = ponctuaction_whitespace(rst_description, ",") # Remove extra whitespace before comma + # Remove extra whitespace before period + rst_description = ponctuaction_whitespace(rst_description, ".") + # Remove extra whitespace before comma + rst_description = ponctuaction_whitespace(rst_description, ",") description_indent = " " * 4 if not " * " in rst_description: From a9051e15f16b4c8578fc701a208cf32ad95a92e5 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:52:09 +0100 Subject: [PATCH 46/67] fix: function rendering with ``Replaceable`` --- src/pyconverter/xml2py/ast_tree.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index e3cf1eb7b..a264983bf 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1042,7 +1042,10 @@ def content_equals(self): def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" - rst_replaceable = f"``{self.content[0]}`` {self.tail}" + tail = self.tail + if tail and "*" in tail: + tail = self.tail.replace("*", "\*") + rst_replaceable = f"``{self.content[0]}`` {tail}" if isinstance(self.prev_elem, Command): if any([self.content[0] in arg for arg in self.prev_elem.args]): rst_replaceable = f"{self.tail}" From a562d16179eb60699ce1c72617abc5171ec6a3ca Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:55:29 +0100 Subject: [PATCH 47/67] feat: using ``msg`` and ``commands`` elements in ``config.yaml`` ``warnings`` section --- config.yaml | 21 +++++++++++++-------- src/pyconverter/xml2py/utils/utils.py | 10 ++++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/config.yaml b/config.yaml index dda4040f3..127169fa5 100644 --- a/config.yaml +++ b/config.yaml @@ -49,14 +49,19 @@ ignored_commands: - "*XPL" # Defined in ``mapdl_grpc.py`` warnings: - "This command must be run using :func:`non_interactive `\nPlease visit `Unsupported Interactive Commands `_\nfor further information.": - - "*CREATE" - - "CFOPEN" - - "CFCLOSE" - - "*VWRITE" - - "*MWRITE" - - "LSWRITE" - - "LSREAD" + - msg: 'This command must be run using :func:`non_interactive + + Please visit `Unsupported Interactive Commands `_ + + for further information.' + commands: + - "*CREATE" + - "CFOPEN" + - "CFCLOSE" + - "*VWRITE" + - "*MWRITE" + - "LSWRITE" + - "LSREAD" specific_classes: diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index eaf8fdc74..f04a92ac4 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -75,12 +75,14 @@ def get_warning_command_dict(yaml_path: Path) -> dict: """ warnings_ = get_config_data_value(yaml_path, "warnings") warning_command_dict = {} - for warning_, command_list in warnings_.items(): - for command in command_list: + for warning_ in warnings_: + message = warning_["msg"] + commands = warning_["commands"] + for command in commands: try: - warning_command_dict[command].append(warning_) + warning_command_dict[command].append(message) except KeyError: - warning_command_dict[command] = [warning_] + warning_command_dict[command] = [message] return warning_command_dict From 9ef9831b98a6e911c0ac38266d1dd0d67d652a1d Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:59:57 +0100 Subject: [PATCH 48/67] fix: accept ``*DIM,`` and ``*DIM.`` for adding ``\`` --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index a264983bf..ee8a3cadc 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -390,7 +390,7 @@ def replace_asterisks(initial_text): # Replace all * with \* text = re.sub( - r"([^\*])(\*)([A-Z]+)(\`|\s)", r"\1" + r"\*" + r"\3\4", initial_text + r"([^\*])(\*)([A-Z]+)(\`|\,|\.|\s)", r"\1" + r"\*" + r"\3\4", initial_text ) # Replace ``*DIM`` configurations into ``\*DIM`` text = re.sub( r"([^\*\s\\]+)(\*)([^\*\s]+)", r"\1\\2\3", text From 84c9b06a4aec77b7a4edeba25aad9d4fcb774f61 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:09:29 +0100 Subject: [PATCH 49/67] fix: remaining line length issue --- src/pyconverter/xml2py/ast_tree.py | 33 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index a264983bf..4a1e30c11 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -360,6 +360,11 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", while "\n\n\n" in text: text = text.replace("\n\n\n", "\n\n") + # Remove extra whitespace before period + text = ponctuaction_whitespace(text, ".") + # Remove extra whitespace before comma + text = ponctuaction_whitespace(text, ",") + wrapper = textwrap.TextWrapper( width=max_length, break_long_words=False, @@ -723,12 +728,12 @@ class Member(Element): def ponctuaction_whitespace(text, ponctuation): - pattern = r".\S\h+\{ponctuation}".format(ponctuation=ponctuation) + 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 = character[0] - if assigned_character in ["*", ")"]: + if assigned_character in ["*", ")", "?"]: pattern = r"\{assigned_character}\h+\{ponctuation}".format( assigned_character=assigned_character, ponctuation=ponctuation ) @@ -860,6 +865,7 @@ def __repr__(self): def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=None): """Return a string to enable converting the element to an RST format.""" items = [] + skip_resize = False for item in self: if isinstance(item, Element): if isinstance(item, Variablelist): @@ -873,6 +879,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No fcache=fcache, ) ) + skip_resize = True else: if item.tag in item_needing_all: items.append( @@ -897,16 +904,18 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No else: items.append(item.to_rst(indent=indent, max_length=max_length)) else: - str_item = resize_length( - str(item), - max_length=max_length, - initial_indent=indent, - subsequent_indent=indent, - ) - items.append(str_item) + items.append(str(item)) rst_item = " ".join(items) + "\n\n" + if not skip_resize: + rst_item = resize_length( + rst_item, + max_length=max_length, + initial_indent=indent, + subsequent_indent=indent, + ) + return rst_item @@ -2562,10 +2571,6 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None fcache=fcache, ) rst_description = replace_terms(rst_description, self._terms) - # Remove extra whitespace before period - rst_description = ponctuaction_whitespace(rst_description, ".") - # Remove extra whitespace before comma - rst_description = ponctuaction_whitespace(rst_description, ",") description_indent = " " * 4 if not " * " in rst_description: @@ -3116,7 +3121,7 @@ def trail_replacer(match): docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S? ", "", docstr) docstr = re.sub(r"bgcolor=\S\S\S\S\S\S\S\S\S\S?", "", docstr) docstr = re.sub(r"_cellfont Shading=\S\S\S\S\S\S\S\S", "", docstr) - docstr = re.sub(r"Caret.+\?", "", docstr) + docstr = re.sub(r"Caret.*\?", "", docstr) docstr = docstr.replace("–", "-") docstr = docstr.replace(". . .", "...") docstr = replace_asterisks(docstr) From afda5666d3eb9ab3ba57dbb5eb5aae851c1745e4 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:20:40 +0100 Subject: [PATCH 50/67] fix: minor changes --- src/pyconverter/xml2py/ast_tree.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 5534509f8..8d0577ed5 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1053,7 +1053,7 @@ def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" tail = self.tail if tail and "*" in tail: - tail = self.tail.replace("*", "\*") + tail = self.tail.replace("*", r"\*") rst_replaceable = f"``{self.content[0]}`` {tail}" if isinstance(self.prev_elem, Command): if any([self.content[0] in arg for arg in self.prev_elem.args]): @@ -1578,7 +1578,7 @@ def entityref(self): entityref = entityref.strip() return entityref - def to_rst(self, fcache, indent="", max_length=100): + def to_rst(self, indent="", max_length=100, fcache=None): """Return a string to enable converting the element to an RST format.""" if self.entityref is None: @@ -3235,7 +3235,9 @@ def py_parm(self, custom_functions=None, links=None, base_url=None, fcache=None) lines.append("-" * 10) for argument in arg_desc: lines.extend( - argument.to_py_docstring(self._max_length, links, base_url, fcache) + argument.to_py_docstring( + self._max_length, links=links, base_url=base_url, fcache=fcache + ) ) lines.append("") else: @@ -3244,7 +3246,11 @@ def py_parm(self, custom_functions=None, links=None, base_url=None, fcache=None) elif len(arg_desc) > 0: lines.append("-" * 10) for argument in arg_desc: - lines.extend(argument.to_py_docstring(self._max_length, links, base_url, fcache)) + lines.extend( + argument.to_py_docstring( + self._max_length, links=links, base_url=base_url, fcache=fcache + ) + ) lines.append("") return lines From e2733c200ded847e4dd5b3ea087103ce80b2f16d Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:57:54 +0100 Subject: [PATCH 51/67] feat: using ``uv`` in ``make_package`_doc` --- make_package_doc.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make_package_doc.ps1 b/make_package_doc.ps1 index 7ffa61187..56c63d45e 100644 --- a/make_package_doc.ps1 +++ b/make_package_doc.ps1 @@ -7,10 +7,10 @@ function Green deactivate cd .\package\ -python -m venv .venv +uv venv .venv --seed .\.venv\Scripts\activate Write-Output "A new virtual environment has been created within the package folder." | Green -pip install -e .[doc] +uv pip install -e .[doc] Write-Output "The package has successfully been installed in the virtual environment." | Green Write-Output "The documentation is about to be built." | Green .\doc\make.bat html From ce3001785c4c0fb6e4fa20e473d19530ff0c4f58 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:03:41 +0100 Subject: [PATCH 52/67] fix: remaining indentation issues --- src/pyconverter/xml2py/ast_tree.py | 215 +++++++++++++++++++++-------- 1 file changed, 155 insertions(+), 60 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 8d0577ed5..db3e3787f 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -68,6 +68,7 @@ "\xa0": " ", "’": "``", "∗": "*", + "−": "-", "…": "...", } @@ -84,7 +85,7 @@ # Map XML command to pycommand function NAME_MAP_GLOB = {} -NO_RESIZE_LIST = ["Variablelist"] +NO_RESIZE_LIST = ["Variablelist", "Caution", "XMLWarning", "ProgramListing", "Example"] MISSING_ARGUMENT_DESCRIPTION = """The description of the argument is missing in the Python function. Please, refer to the `command documentation `_ for further information.""" @@ -357,6 +358,7 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", str or list Resized text. """ + while "\n\n\n" in text: text = text.replace("\n\n\n", "\n\n") @@ -391,7 +393,30 @@ def resize_length(text, max_length=100, initial_indent="", subsequent_indent="", return output -def replace_asterisks(initial_text): +def get_fragment_code(initial_text, pattern): + """ + Split the text around a pattern. + + Parameters + ---------- + initial_text : str + Initial text to split. + + pattern : str + Pattern to split the text. + + Returns + ------- + list + List of fragments. + """ + + # Split the text around code blocks + fragments = re.split(pattern, initial_text) + return fragments + + +def replace_asterisks_without_code(initial_text): # Replace all * with \* text = re.sub( @@ -410,6 +435,27 @@ def replace_asterisks(initial_text): return text +def replace_asterisks(initial_text): + + if ".. code::" in initial_text: + # Regex pattern that matches RST code blocks + pattern = r"(\s*\.\. code:: apdl\n\s*(?: +.+\n)+)" + fragments = get_fragment_code(initial_text, pattern) + + # Process non-code fragments + for i in range(len(fragments)): + if not ".. code::" in fragments[i]: + fragments[i] = replace_asterisks_without_code(fragments[i]) + + # Join the fragments and return the result + output = "".join(fragments).strip() + + else: + output = replace_asterisks_without_code(initial_text) + + return output + + def replace_terms(text, terms): """ Replace terms with their definitions. @@ -865,7 +911,6 @@ def __repr__(self): def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=None): """Return a string to enable converting the element to an RST format.""" items = [] - skip_resize = False for item in self: if isinstance(item, Element): if isinstance(item, Variablelist): @@ -879,7 +924,6 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No fcache=fcache, ) ) - skip_resize = True else: if item.tag in item_needing_all: items.append( @@ -904,11 +948,19 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No else: items.append(item.to_rst(indent=indent, max_length=max_length)) else: - items.append(str(item)) + items.append( + resize_length( + str(item), + max_length=max_length, + initial_indent=indent, + subsequent_indent=indent, + ) + ) rst_item = " ".join(items) + "\n\n" - if not skip_resize: + intersection_types = set(NO_RESIZE_LIST).intersection(set(self.children_types)) + if len(intersection_types) == 0 and "* " not in rst_item: rst_item = resize_length( rst_item, max_length=max_length, @@ -919,7 +971,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No return rst_item -class Phrase(Element): +class Phrase(Paragraph): """Provides the phrase element.""" def __repr__(self): @@ -983,13 +1035,22 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None): class Example(Element): """Provides the example element.""" - # def source(self): - # """The program listing of the documentation.""" - # for item in self._content: - # if isinstance(item, ProgramListing): - # return item - # return "" - pass + def to_rst(self, indent="", max_length=100): + rst_example = [] + for item in self: + if isinstance(item, Title): + title = item.to_rst(indent=indent, max_length=max_length) + if not "Command" in item.children_types: + rst_item = f"**{title}**" + else: + rst_item = f"{title}" + elif isinstance(item, Element): + rst_item = item.to_rst(indent=indent, max_length=max_length) + else: + rst_item = str(item) + rst_example.append(rst_item) + + return "\n".join(rst_example) class InformalExample(Element): @@ -1069,9 +1130,12 @@ class ProgramListing(Element): @property def source(self): """Return the source value.""" - if self._element.text is None: - return "\n".join(str(item) for item in self.content) - return self._element.text + text = self._element.text + if "Replaceable" in self.children_types: + text = " ".join([str(item) for item in self]) + elif text is None: + text = "\n".join(str(item) for item in self.content) + return text def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" @@ -1080,7 +1144,10 @@ def to_rst(self, indent="", max_length=100): items = [] for item in self: - if isinstance(item, Element): + # Replaceable elements are handled in source code + if isinstance(item, Replaceable): + pass + elif isinstance(item, Element): items += item.to_rst(indent=indent, max_length=max_length) else: # if isinstance(item, str): item_in_source = re.search(r"\S+", item).group() @@ -1132,8 +1199,8 @@ 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: - initial_indent = indent + " " * 2 + if len(intersection_types) == 0 and "* " not in rst_item: + initial_indent = indent subsequent_indent = initial_indent + " " * 2 rst_item = resize_element_list( rst_item, @@ -1141,6 +1208,8 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No initial_indent=initial_indent, subsequent_indent=subsequent_indent, ) + else: + rst_item = textwrap.indent(rst_item, prefix=indent * 2) else: initial_indent = indent + " " subsequent_indent = indent + " " * 2 @@ -1308,32 +1377,33 @@ def py_text(self, links=None, base_url=None, fcache=None): def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=None): """Return a string to enable converting the element to an RST format.""" - indent += " " * 4 - # if this is a parameter arg - if self.is_arg: - # This is what needs to be modified in order to have the arg class - lines = [f"{self.py_term(links=links, base_url=base_url)}"] - text = self.py_text(links=links, base_url=base_url, fcache=fcache) - text_list = resize_length( - text, - max_length=max_length, - initial_indent=indent, - subsequent_indent=indent, - list=True, - ) - lines.extend(text_list) - return "\n".join(lines) - py_term = self.py_term(links=links, base_url=base_url) py_text = self.py_text(links=links, base_url=base_url, fcache=fcache) if "``" in py_term: py_term = py_term.replace("``", "") + + intersection_types = set(NO_RESIZE_LIST).intersection(self.text.children_types) + if len(intersection_types) == 0 and "* " not in py_text: + py_text = resize_length( + py_text, max_length=max_length, initial_indent=indent, subsequent_indent=indent + ) + + split_py_text = py_text.splitlines() + + if len(split_py_text) > 1: + + first_line = split_py_text[0] + rest_lines = split_py_text[1:] + + rest_lines = textwrap.indent("\n".join(rest_lines), prefix=" " * 2) + + py_text = f"{first_line}\n{rest_lines}" + lines = [f"* ``{py_term}`` - {py_text}"] - text = "\n".join(lines) - # if 'ID number to which this tip belongs' in text: - # breakpoint() - return text + output = "\n".join(lines) + + return output class Term(Element): @@ -1532,7 +1602,7 @@ def to_rst(self, indent="", max_length=100): return self.tail -class UserInput(Element): +class UserInput(ProgramListing): """Provides the user input element.""" pass @@ -1564,6 +1634,7 @@ def to_rst(self, indent="", max_length=100): str(self), max_length=max_length, initial_indent=indent, subsequent_indent=indent ) ) + lines.append("") return "\n".join(lines) @@ -1637,15 +1708,18 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No else: items.append(item.to_rst(indent, max_length=max_length)) else: - items.append( - resize_length( - str(item), - max_length=max_length, - initial_indent=indent, - subsequent_indent=indent, + if "* " not in str(item): + items.append( + resize_length( + str(item), + max_length=max_length, + initial_indent=indent, + subsequent_indent=indent, + ) ) - ) - return "\n\n" + " ".join(items) + "\n\n" + else: + items.append(str(item)) + return "\n\n" + "".join(items) + "\n\n" class RefMeta(Element): @@ -2141,7 +2215,8 @@ class XMLType(Element): pass -class XMLWarning(Element): +class XMLWarning(Caution): + """XML Warning element are handled the same as Caution elements.""" pass @@ -2552,9 +2627,13 @@ def resized_description( if description is None: description = self._description - output = resize_length( - description, max_length, initial_indent=indent, subsequent_indent=indent, list=True - ) + if "* " not in description: + output = resize_length( + description, max_length, initial_indent=indent, subsequent_indent=indent, list=True + ) + else: + output = textwrap.indent(description, indent) + output = output.split("\n") return output @@ -2573,12 +2652,23 @@ def to_py_docstring(self, max_length=100, links=None, base_url=None, fcache=None rst_description = replace_terms(rst_description, self._terms) description_indent = " " * 4 - if not " * " in rst_description: + + if isinstance(self._description, Element): + intersection_types = set(NO_RESIZE_LIST).intersection( + set(self._description.children_types) + ) + if len(intersection_types) == 0 and "* " not in rst_description: + list_description = self.resized_description( + rst_description, max_length, description_indent + ) + else: + rst_description = textwrap.indent(rst_description, description_indent) + list_description = rst_description.split("\n") + elif " * " not in rst_description: list_description = self.resized_description( rst_description, max_length, description_indent ) else: - rst_description = textwrap.indent(rst_description, description_indent) list_description = rst_description.split("\n") docstring = [f'{self.py_arg_name} : {str_types(self.types, " or ")}'] @@ -2839,9 +2929,13 @@ def arg_desc(self) -> List[Argument]: @property def short_desc(self): """Short description of the command.""" + short_desc = "" if self._refname_div is not None: - return self._refname_div.purpose.to_rst(links=self._links, base_url=self._base_url) - return "" + short_desc = self._refname_div.purpose.to_rst( + links=self._links, base_url=self._base_url + ) + short_desc = resize_length(short_desc, self._max_length) + return short_desc @property def _metadata(self): @@ -2933,7 +3027,7 @@ def py_docstring(self, custom_functions: CustomFunctions, warning_command_dict: if self.name in warning_command_dict.keys(): warnings_ = warning_command_dict[self.name] for warning_ in warnings_: - warning_ = textwrap.indent(warning_, " " * 3) + warning_ = textwrap.indent(warning_, " " * 4) items.extend([f"\n.. warning::\n{warning_}\n"]) if self.default is not None: @@ -3132,8 +3226,8 @@ def trail_replacer(match): logging.info(f"{self.name} is an archived command.") docstr = ( docstr - + "\n\n.. warning::\n\n" - + "This command is archived in the latest version of the software.\n" + + "\n\n.. warning::\n" + + " This command is archived in the latest version of the software.\n" ) # Final cleanup @@ -3415,6 +3509,7 @@ def to_rst(self, indent="", max_length=100): "example": Example, "command": Command, "title": Title, + "ttl": Title, "para": Paragraph, "table": Table, "orderedlist": OrderedList, From b404bd431297c9c7174537234a80e188c710d607 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:18:20 +0100 Subject: [PATCH 53/67] fix: example titles --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index db3e3787f..07da9ccc8 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1041,7 +1041,7 @@ def to_rst(self, indent="", max_length=100): if isinstance(item, Title): title = item.to_rst(indent=indent, max_length=max_length) if not "Command" in item.children_types: - rst_item = f"**{title}**" + rst_item = f"**{title}**\n" else: rst_item = f"{title}" elif isinstance(item, Element): From cf2008baf7ef07692c2d45273588798af319569e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:01:40 +0100 Subject: [PATCH 54/67] fix: arguments with no types + accepting multiple line definition in ``CustomFunctions`` --- src/pyconverter/xml2py/custom_functions.py | 19 +++- tests/customized_functions/secmodif.py | 122 +++++++++++++++++++++ 2 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 tests/customized_functions/secmodif.py diff --git a/src/pyconverter/xml2py/custom_functions.py b/src/pyconverter/xml2py/custom_functions.py index 4599d321b..91d584850 100644 --- a/src/pyconverter/xml2py/custom_functions.py +++ b/src/pyconverter/xml2py/custom_functions.py @@ -51,6 +51,7 @@ def get_docstring_lists(filename: str) -> Tuple[list[str], list[str], list[str], with open(filename, "r") as pyfile: lines = pyfile.readlines() bool_def = False + end_def = False bool_param = False bool_return = False bool_notes = False @@ -65,13 +66,20 @@ def get_docstring_lists(filename: str) -> Tuple[list[str], list[str], list[str], list_py_code = [] list_import = [] for line in lines: - if "import" in line and bool_def is False: + if "import " in line and bool_def is False: list_import.append(line) - elif "def" in line and bool_def is False: + # Need to accept the case where the function is defined in multiple lines + elif "def " in line and bool_def is False: bool_def = True + if bool_def and not end_def: + if ")" in line: + end_def = True split_def = line.split(",") for split_arg in split_def: - if "**kwarg" in split_arg: + split_arg = split_arg.strip() + if "**kwarg" in split_arg or split_arg == "self": + break + elif re.search(r"[a-zA-Z0-9_]+", split_arg) is None: break elif ":" in split_arg and "=" in split_arg: find = re.search(r"\w*(?=\:)", split_arg).group() @@ -79,6 +87,11 @@ def get_docstring_lists(filename: str) -> Tuple[list[str], list[str], list[str], elif "=" in split_arg: find = re.search(r"\w*(?=\=)", split_arg).group() list_py_args.append(find) + elif "def " in split_arg: + pass + else: + find = re.search(r"\S+", split_arg).group() + list_py_args.append(find) elif '"""' in line and begin_docstring is False: begin_docstring = True elif '"""' in line and begin_docstring is True: diff --git a/tests/customized_functions/secmodif.py b/tests/customized_functions/secmodif.py new file mode 100644 index 000000000..27b3cbcd5 --- /dev/null +++ b/tests/customized_functions/secmodif.py @@ -0,0 +1,122 @@ +# Copyright (C) 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +def secmodif( + self, + secid, + keyword, + nx="", + ny="", + nz="", + kcn="", + name="", + name_new="", + option="", + dispPnltVal="", + RotPnltVal="", + **kwargs, +): + """Modifies a pretension section + + APDL Command: SECMODIF + + .. warning:: + + This command have specific signature depending on the values of the given + arguments ``keyword``. + + Parameters + ---------- + + secid : int + Unique section number. This number must already be assigned to a + section. + + keyword : str + - If `Keyword = NORM`: + SECMODIF,SECID, NORM, NX, NY, NZ, KCN + - If `Keyword = NAME`: + SECMODIF,SECID, NAME, Name + - If `Keyword = JOIN`: + SECMODIF,SECID, JOIN, Option, dispPnltVal, RotPnltVal + + norm : str + Keyword specifying that the command will modify the pretension + section normal direction. + + nx, ny, nz : str + Specifies the individual normal components to modify. + + kcn : str + Coordinate system number. This can be either 0 (Global Cartesian), + 1 (Global Cylindrical) 2 (Global Spherical), 4 (Working Plane), 5 + (Global Y Axis Cylindrical) or an arbitrary reference number + assigned to a coordinate system. + + name : str + Change the name of the specified pretension section. + + name_new : str + The new name to be assigned to the pretension section. + + join : str + Set command actions to apply to joint sections only. + + option : str + PNLT -- Modify penalty factors for the specified section. + + dispPnltVal : str + Penalty value for displacement-based constraints: + - `> 0` -- The number is used as a scaling factor to scale the + internally calculated penalty values. + - `< 0` -- The absolute value of the number is used as the penalty + factor in calculations. + + RotPnltVal : str + Penalty value for rotation-based constraints. + - `> 0` -- The number is used as a scaling factor to scale the + internally calculated penalty values. + - `< 0` -- The absolute value of the number is used as the penalty + factor in calculations. + + + Notes + ----- + The SECMODIF command either modifies the normal for a specified + pretension section, or changes the name of the specified pretension + surface. + """ + # Sanity checks + if keyword.upper() not in ["NORM", "NAME", "JOIN"]: + raise ValueError(f"The given argument 'keyword' ({keyword}) is not valid.") + + if keyword == "NORM": + cmd = f"SECMODIF, {secid}, NORM, {nx}, {ny}, {nz}, {kcn}" + elif keyword == "NAME": + cmd = f"SECMODIF, {secid}, NAME, {name or nx}, {name_new or ny}" + elif keyword == "JOIN": + cmd = f"SECMODIF, {secid}, JOIN, {option or nx},{dispPnltVal or ny},{RotPnltVal or nz}" + else: + raise ValueError("We couldn't map the arguments given....") + + self.run(cmd, **kwargs) From d8a8fde43965a238bf80a7045cadb47859291df6 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 16 Jan 2025 17:25:29 +0100 Subject: [PATCH 55/67] fix: test --- src/pyconverter/xml2py/ast_tree.py | 2 -- src/pyconverter/xml2py/load_xml_doc.py | 2 +- tests/conftest.py | 7 ++++++- tests/test_writer.py | 6 +++--- tests/test_xml_ast.py | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 07da9ccc8..8f7bedef2 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -3020,8 +3020,6 @@ def py_docstring(self, custom_functions: CustomFunctions, warning_command_dict: """ xml_cmd = f"{self._terms['pn006p']} Command: `{self.name} <{self.url}>`_" - # ``warning_command_dict`` need to be added here - items = [self.short_desc, "", xml_cmd] if self.name in warning_command_dict.keys(): diff --git a/src/pyconverter/xml2py/load_xml_doc.py b/src/pyconverter/xml2py/load_xml_doc.py index 12cb0f42e..68d3dc8c9 100644 --- a/src/pyconverter/xml2py/load_xml_doc.py +++ b/src/pyconverter/xml2py/load_xml_doc.py @@ -109,7 +109,7 @@ def grab_links(linkmap): if targetptr is not None and href is not None: text = "" if linkmap[0].tag == "ttl": - text = str(linkmap[0]) + text = str(linkmap[0]).strip() links[f"{targetptr}"] = (root_name, root_title, href, text) grab_links(linkmap) diff --git a/tests/conftest.py b/tests/conftest.py index d3592b329..a0f81f8e4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,7 +26,7 @@ from pyconverter.xml2py.custom_functions import CustomFunctions import pyconverter.xml2py.directory_format as ff import pyconverter.xml2py.load_xml_doc as lxd -from pyconverter.xml2py.utils.utils import get_config_data_value +from pyconverter.xml2py.utils.utils import get_config_data_value, get_warning_command_dict import pyconverter.xml2py.writer as wrt import pytest @@ -142,3 +142,8 @@ def library_name_structured(config_path): @pytest.fixture def package_structure(command_map, name_map, directory_path, cwd, path_custom_functions): return wrt.write_source(command_map, name_map, directory_path, cwd, path_custom_functions) + + +@pytest.fixture +def warning_command_dict(config_path): + return get_warning_command_dict(config_path) diff --git a/tests/test_writer.py b/tests/test_writer.py index f159bbc30..b91320325 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -26,7 +26,7 @@ import pytest -def test_convert(command_map, custom_functions): +def test_convert(command_map, custom_functions, warning_command_dict): assert command_map["/XFRM"].name == "/XFRM" assert ( "Original command: WRITE\n\nShort Description:\nWrites the radiation matrix file.\n\nFunction signature:\nWRITE," # noqa : E501 @@ -39,9 +39,9 @@ def test_convert(command_map, custom_functions): assert 'def zoom(self, wn: str = "", lab: str = "", x1: str = "", y1: str = "", x2: str = "", y2: str = "", **kwargs):\n r"""Zooms a region of a display window.\n\n' in command_map[ # noqa : E501 "/ZOOM" ].to_python( - custom_functions + custom_functions, warning_command_dict ) - assert "import re" in command_map["K"].to_python(custom_functions) + assert "import re" in command_map["K"].to_python(custom_functions, warning_command_dict) def test_copy_template_package(cwd): diff --git a/tests/test_xml_ast.py b/tests/test_xml_ast.py index f860f299f..9ede05591 100644 --- a/tests/test_xml_ast.py +++ b/tests/test_xml_ast.py @@ -27,7 +27,7 @@ @pytest.mark.parametrize( "initial_name,expected_output", - [("/PREP7", "prep7"), ("*DMAT", "dmat"), ("SORT", "sort"), ("*GO", "stargo")], + [("/PREP7", "prep7"), ("*DMAT", "dmat"), ("SORT", "sort"), ("*VPLOT", "starvplot")], ) def test_py_name(initial_name, expected_output, name_map): assert ast.to_py_name(initial_name, name_map) == expected_output From 88fc6391940cbdad3590b530af220407e4f8d95e Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:39:56 +0100 Subject: [PATCH 56/67] fix: update ``secmodif`` --- tests/customized_functions/secmodif.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/customized_functions/secmodif.py b/tests/customized_functions/secmodif.py index 27b3cbcd5..d2d4b5901 100644 --- a/tests/customized_functions/secmodif.py +++ b/tests/customized_functions/secmodif.py @@ -53,11 +53,11 @@ def secmodif( section. keyword : str - - If `Keyword = NORM`: + * If `Keyword = NORM`: SECMODIF,SECID, NORM, NX, NY, NZ, KCN - - If `Keyword = NAME`: + * If `Keyword = NAME`: SECMODIF,SECID, NAME, Name - - If `Keyword = JOIN`: + * If `Keyword = JOIN`: SECMODIF,SECID, JOIN, Option, dispPnltVal, RotPnltVal norm : str From 5c39b0c90ee1a324c1c30e2f44a17997bda3443f Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:49:27 +0100 Subject: [PATCH 57/67] fix: adding logs in the code source writter --- src/pyconverter/xml2py/utils/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index f04a92ac4..16c3ce6bc 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import logging from pathlib import Path from typing import Tuple, Union @@ -84,6 +85,12 @@ def get_warning_command_dict(yaml_path: Path) -> dict: except KeyError: warning_command_dict[command] = [message] + if warning_command_dict == {}: + logging.info("No warning commands found in the YAML file.") + + else: + logging.info("Warning commands found in the YAML file.") + return warning_command_dict From f07681f4f5d3e7bacdef2d8ea571d5a440c577d9 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:35:06 +0100 Subject: [PATCH 58/67] fix: showing logger.info --- src/pyconverter/xml2py/ast_tree.py | 13 +++++++------ src/pyconverter/xml2py/utils/utils.py | 7 +++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 8f7bedef2..25bb1d034 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -40,7 +40,8 @@ else: pass -logging.getLogger("py_asciimath.utils").setLevel("CRITICAL") +logger = logging.getLogger("py_asciimath.utils") +logger.setLevel(logging.INFO) # common statements used within the docs to avoid duplication @@ -874,7 +875,7 @@ 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.""" key = f"{self.targetptr}" if (links or base_url) is None: - logging.error("ERROR in the links or the base_url definitions - OLink class.") + logger.error("ERROR in the links or the base_url definitions - OLink class.") if key in links: root_name, root_title, href, text = links[key] link = f"{base_url}{root_name}/{href}" @@ -1572,7 +1573,7 @@ def __repr__(self): 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.""" if (links or base_url) is None: - logging.error( + logger.error( "ERROR exists in the links or the 'base_url' definitions in the 'Link' class." ) tail = " ".join([str(item) for item in self]) @@ -2559,10 +2560,10 @@ def multiple_args(self): split_name[k + 1] ) if name_iter_prev != name_iter_next: - logging.warning( + logger.warning( f"The argument name is not consistent: {name_iter_prev} != {name_iter_next}" # noqa : E501 ) - logging.info( + logger.info( "Applying the longest name for the argument list as it's probably coming from a typography." # noqa : E501 ) if len(name_iter_prev) > len(name_iter_next): @@ -3221,7 +3222,7 @@ def trail_replacer(match): docstr = ponctuaction_whitespace(docstr, ",") # Remove extra whitespace before comma if self.is_archived == True: - logging.info(f"{self.name} is an archived command.") + logger.info(f"{self.name} is an archived command.") docstr = ( docstr + "\n\n.. warning::\n" diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 16c3ce6bc..4bd8e8ffb 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -27,6 +27,9 @@ from lxml.html import fromstring import yaml +logger = logging.getLogger("py_asciimath.utils") +logger.setLevel(logging.INFO) + def parse_yaml(yaml_path: Path) -> dict: """ @@ -86,10 +89,10 @@ def get_warning_command_dict(yaml_path: Path) -> dict: warning_command_dict[command] = [message] if warning_command_dict == {}: - logging.info("No warning commands found in the YAML file.") + logger.info("No warning commands found in the YAML file.") else: - logging.info("Warning commands found in the YAML file.") + logger.info("Warning commands found in the YAML file.") return warning_command_dict From f965bbf6bf0cca508a3522a12e9ecc8bd4eb1cda Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:55:20 +0100 Subject: [PATCH 59/67] fix: ``warnings`` as an option --- src/pyconverter/xml2py/utils/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pyconverter/xml2py/utils/utils.py b/src/pyconverter/xml2py/utils/utils.py index 4bd8e8ffb..12aa8fcc3 100644 --- a/src/pyconverter/xml2py/utils/utils.py +++ b/src/pyconverter/xml2py/utils/utils.py @@ -78,6 +78,9 @@ def get_warning_command_dict(yaml_path: Path) -> dict: Path object of the YAML file. """ warnings_ = get_config_data_value(yaml_path, "warnings") + if warnings_ is None: + logger.info("No warning commands found in the YAML file.") + return {} warning_command_dict = {} for warning_ in warnings_: message = warning_["msg"] From 9dcc6a65556c4c04a5912c734a726a73ef1232bf Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Fri, 17 Jan 2025 18:32:26 +0100 Subject: [PATCH 60/67] fix: error related to Union types in CustomFunctions --- src/pyconverter/xml2py/custom_functions.py | 5 +- tests/customized_functions/enorm.py | 87 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/customized_functions/enorm.py diff --git a/src/pyconverter/xml2py/custom_functions.py b/src/pyconverter/xml2py/custom_functions.py index 91d584850..44b4a8f04 100644 --- a/src/pyconverter/xml2py/custom_functions.py +++ b/src/pyconverter/xml2py/custom_functions.py @@ -74,12 +74,15 @@ def get_docstring_lists(filename: str) -> Tuple[list[str], list[str], list[str], if bool_def and not end_def: if ")" in line: end_def = True + # TODO: Union types are ignored for now + if re.search(r"\[(\w+, )+\w+\]", line) is not None: + line = re.sub(r"\[(\w+, )+\w+\]", "", line) split_def = line.split(",") for split_arg in split_def: split_arg = split_arg.strip() if "**kwarg" in split_arg or split_arg == "self": break - elif re.search(r"[a-zA-Z0-9_]+", split_arg) is None: + elif re.search(r"\w+", split_arg) is None: break elif ":" in split_arg and "=" in split_arg: find = re.search(r"\w*(?=\:)", split_arg).group() diff --git a/tests/customized_functions/enorm.py b/tests/customized_functions/enorm.py new file mode 100644 index 000000000..64e82e02d --- /dev/null +++ b/tests/customized_functions/enorm.py @@ -0,0 +1,87 @@ +# Copyright (C) 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from typing import Optional, Union + + +def enorm(self, enum: Union[str, int] = "", **kwargs) -> Optional[str]: + """Reorients shell element normals or line element node connectivity. + + APDL Command: ENORM + + Parameters + ---------- + enum: str, int + Element number having the normal direction that the + reoriented elements are to match. + + Notes + ----- + Reorients shell elements so that their outward normals are + consistent with that of a specified element. ENORM can also be + used to reorder nodal connectivity of line elements so that + their nodal ordering is consistent with that of a specified + element. + + For shell elements, the operation reorients the element by + reversing and shifting the node connectivity pattern. For + example, for a 4-node shell element, the nodes in positions I, + J, K and L of the original element are placed in positions J, + I, L and K of the reoriented element. All 3-D shell elements + in the selected set are considered for reorientation, and no + element is reoriented more than once during the + operation. Only shell elements adjacent to the lateral (side) + faces are considered. + + The command reorients the shell element normals on the same + panel as the specified shell element. A panel is the geometry + defined by a subset of shell elements bounded by free edges or + T-junctions (anywhere three or more shell edges share common + nodes). + + Reorientation progresses within the selected set until either + of the following conditions is true: + + - The edge of the model is reached. + + - More than two elements (whether selected or unselected) are + adjacent to a lateral face. + + In situations where unselected elements might undesirably + cause case b to control, consider using ENSYM,0,,0,ALL instead + of ENORM. It is recommended that reoriented elements be + displayed and graphically reviewed. + + You cannot use the ENORM command to change the normal + direction of any element that has a body or surface load. We + recommend that you apply all of your loads only after ensuring + that the element normal directions are acceptable. + + Real constant values are not reoriented and may be invalidated + by an element reversal. + + Examples + -------- + >>> mapdl.enorm(1) + + """ + return self.run(f"ENORM,{enum}", **kwargs) From 7fd2887628d736aee974afb8b6f30b57e2b20629 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:24:36 +0100 Subject: [PATCH 61/67] fix: union and multiple lines in CustomFunctioins + fix links in ``py_term`` --- src/pyconverter/xml2py/ast_tree.py | 5 +- src/pyconverter/xml2py/custom_functions.py | 7 +++ tests/customized_functions/cmlist.py | 60 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/customized_functions/cmlist.py diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 25bb1d034..769c23787 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1383,6 +1383,9 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No if "``" in py_term: py_term = py_term.replace("``", "") + + if re.finditer(r"`.+`_", py_term) is None: + py_term = f"``{py_term}``" intersection_types = set(NO_RESIZE_LIST).intersection(self.text.children_types) if len(intersection_types) == 0 and "* " not in py_text: @@ -1401,7 +1404,7 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No py_text = f"{first_line}\n{rest_lines}" - lines = [f"* ``{py_term}`` - {py_text}"] + lines = [f"* {py_term} - {py_text}"] output = "\n".join(lines) return output diff --git a/src/pyconverter/xml2py/custom_functions.py b/src/pyconverter/xml2py/custom_functions.py index 44b4a8f04..b84c8290f 100644 --- a/src/pyconverter/xml2py/custom_functions.py +++ b/src/pyconverter/xml2py/custom_functions.py @@ -74,9 +74,16 @@ def get_docstring_lists(filename: str) -> Tuple[list[str], list[str], list[str], if bool_def and not end_def: if ")" in line: end_def = True + line = re.search(r".+(?=\))", line) + if line is not None: + line = line.group() + else: + continue # TODO: Union types are ignored for now if re.search(r"\[(\w+, )+\w+\]", line) is not None: line = re.sub(r"\[(\w+, )+\w+\]", "", line) + elif re.search(r'\[("\w*", )+"\w*"\]', line): + line = re.sub(r'\[("\w*", )+"\w*"\]', "", line) split_def = line.split(",") for split_arg in split_def: split_arg = split_arg.strip() diff --git a/tests/customized_functions/cmlist.py b/tests/customized_functions/cmlist.py new file mode 100644 index 000000000..15a1e70a1 --- /dev/null +++ b/tests/customized_functions/cmlist.py @@ -0,0 +1,60 @@ +from typing import Any, Dict, Literal + +def cmlist( + self, + name: str = "", + key: str = "", + entity: Literal["VOLU", "AREA", "LINE", "KP", "ELEM", "NODE", ""] = "", + **kwargs: Dict[Any, Any], +) -> None: + """Lists the contents of a component or assembly. + + APDL Command: CMLIST + + Parameters + ---------- + name + Name of the component or assembly to be listed (if blank, list all + selected components and assemblies). If Name is specified, then + Entity is ignored. + + key + Expansion key: + + 0 + Do not list individual entities in the component. + + 1 or EXPA + List individual entities in the component. + + entity + If Name is blank, then the following entity types can be specified: + + VOLU + List the volume components only. + + AREA + List the area components only. + + LINE + List the line components only. + + KP + List the keypoint components only + + ELEM + List the element components only. + + NODE + List the node components only. + + Notes + ----- + This command is valid in any processor. For components, it lists the + type of geometric entity. For assemblies, it lists the components + and/or assemblies that make up the assembly. + + Examples of possible usage: + """ + command = f"CMLIST,{name},{key},{entity}" + return self.run(command, **kwargs) \ No newline at end of file From 7b467033d86d61c9ea88b58216e389cf8a6a1cdf Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:03:45 +0100 Subject: [PATCH 62/67] fix: ``py_term`` and args with ``blank`` --- src/pyconverter/xml2py/ast_tree.py | 6 +++--- tests/customized_functions/cmlist.py | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 769c23787..6e2898dbf 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1322,7 +1322,7 @@ def py_term(self, links=None, base_url=None): arg = self.term.to_rst().replace("--", "").strip() # sanity check - if "blank" in arg.lower(): + if arg.lower() == "blank": arg = "" if not is_numeric(arg): @@ -1383,8 +1383,8 @@ def to_rst(self, indent="", max_length=100, links=None, base_url=None, fcache=No if "``" in py_term: py_term = py_term.replace("``", "") - - if re.finditer(r"`.+`_", py_term) is None: + + if re.search(r"`.+`_", py_term) is None: py_term = f"``{py_term}``" intersection_types = set(NO_RESIZE_LIST).intersection(self.text.children_types) diff --git a/tests/customized_functions/cmlist.py b/tests/customized_functions/cmlist.py index 15a1e70a1..10d8767c3 100644 --- a/tests/customized_functions/cmlist.py +++ b/tests/customized_functions/cmlist.py @@ -1,5 +1,28 @@ +# Copyright (C) 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + from typing import Any, Dict, Literal + def cmlist( self, name: str = "", @@ -57,4 +80,4 @@ def cmlist( Examples of possible usage: """ command = f"CMLIST,{name},{key},{entity}" - return self.run(command, **kwargs) \ No newline at end of file + return self.run(command, **kwargs) From 39e7d187dfbb5a9be13f44e3ab61ceacda0fdf12 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:26:37 +0100 Subject: [PATCH 63/67] feat: using rst ``:file:`` feature --- src/pyconverter/xml2py/ast_tree.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 6e2898dbf..8e7f0ee55 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -852,7 +852,10 @@ class FileName(Element): def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" - return f"``{self[0]}`` {self.tail}" + content = self[0] + if "*" in content: + content = content.replace("*", r"\*") + return f":file:``{content}`` {self.tail}" class OLink(Element): From ef112f8596517b8735cc41425a4abbf95264fb73 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:51:42 +0100 Subject: [PATCH 64/67] fix: ``warning`` command needs an extra line --- src/pyconverter/xml2py/ast_tree.py | 6 +++--- tests/customized_functions/inquire.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 8e7f0ee55..bf86e9de7 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -1634,7 +1634,7 @@ class Caution(Element): def to_rst(self, indent="", max_length=100): """Return a string to enable converting the element to an RST format.""" - lines = ["", "", ".. warning::"] + lines = ["", "", ".. warning::", ""] indent = indent + " " * 4 lines.append( resize_length( @@ -3033,7 +3033,7 @@ def py_docstring(self, custom_functions: CustomFunctions, warning_command_dict: warnings_ = warning_command_dict[self.name] for warning_ in warnings_: warning_ = textwrap.indent(warning_, " " * 4) - items.extend([f"\n.. warning::\n{warning_}\n"]) + items.extend([f"\n.. warning::\n\n{warning_}\n"]) if self.default is not None: if self.default.tag in item_needing_links_base_url: @@ -3231,7 +3231,7 @@ def trail_replacer(match): logger.info(f"{self.name} is an archived command.") docstr = ( docstr - + "\n\n.. warning::\n" + + "\n\n.. warning::\n\n" + " This command is archived in the latest version of the software.\n" ) diff --git a/tests/customized_functions/inquire.py b/tests/customized_functions/inquire.py index ba742711f..9df17df83 100644 --- a/tests/customized_functions/inquire.py +++ b/tests/customized_functions/inquire.py @@ -56,6 +56,7 @@ def inquire(self, strarray="", func="", arg1="", arg2=""): The ``/INQUIRE`` command is valid in any processor. .. warning:: + Take note that from version 0.60.4 and later, the command behaviour has been changed. Previously, the ``StrArray`` argument was omitted. For example: From ae1c0340d09577ebe85fcdba573871ea6c2e60ce Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:27:11 +0100 Subject: [PATCH 65/67] fix: ``:file:`` role --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index bf86e9de7..6dca7ae18 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -855,7 +855,7 @@ def to_rst(self, indent="", max_length=100): content = self[0] if "*" in content: content = content.replace("*", r"\*") - return f":file:``{content}`` {self.tail}" + return f":file:`{content}` {self.tail}" class OLink(Element): From f88d87a086e3aacee120497a3a54170df215b735 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:10:01 +0100 Subject: [PATCH 66/67] fix: arg names in ``to_py_arg_name`` --- src/pyconverter/xml2py/ast_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 6dca7ae18..7b8526e02 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -212,7 +212,7 @@ def to_py_arg_name(name: str) -> str: while len(arg) > 0 and arg[-1] == "_": arg = arg[:-1] - if arg in ["type", "class", "property", "format", "dir"]: + if arg in ["type", "class", "property", "format", "dir", "set", "iter"]: arg = f"{arg}_" return f"{arg}" From 07b9cd7c0b7fe8491218c72ff172fa1dd5426844 Mon Sep 17 00:00:00 2001 From: Camille <78221213+clatapie@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:16:49 +0100 Subject: [PATCH 67/67] maint: cleaning code using ``regex_pattern.py`` --- src/pyconverter/xml2py/ast_tree.py | 19 ++++++------------- src/pyconverter/xml2py/utils/regex_pattern.py | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/pyconverter/xml2py/ast_tree.py b/src/pyconverter/xml2py/ast_tree.py index 7b8526e02..1ee425ab4 100644 --- a/src/pyconverter/xml2py/ast_tree.py +++ b/src/pyconverter/xml2py/ast_tree.py @@ -29,6 +29,7 @@ from lxml.etree import tostring from lxml.html import fromstring from pyconverter.xml2py.custom_functions import CustomFunctions +import pyconverter.xml2py.utils.regex_pattern as regp from pyconverter.xml2py.utils.utils import is_numeric, split_trail_alpha import regex as re @@ -421,16 +422,16 @@ def replace_asterisks_without_code(initial_text): # Replace all * with \* text = re.sub( - r"([^\*])(\*)([A-Z]+)(\`|\,|\.|\s)", r"\1" + r"\*" + r"\3\4", initial_text + regp.GET_STAR_COMMANDS, regp.REPLACE_STAR_COMMANDS, initial_text ) # Replace ``*DIM`` configurations into ``\*DIM`` text = re.sub( - r"([^\*\s\\]+)(\*)([^\*\s]+)", r"\1\\2\3", text + regp.GET_STAR_FUNCTIONS, regp.REPLACE_STAR_FUNCTIONS, text ) # Replace ``fac1*fac2`` configurations into ``fac1\*fac2`` text = re.sub( - r"([^\*])(\*\*)(\*)(.*?)(\*\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + regp.GET_BOLD_COMMANDS, regp.REPLACE_BOLD_COMMANDS, text ) # Replace ``***DIM**`` configurations into ``**\*DIM**`` # text = re.sub( - # r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])", r"\1\2" + r"\*" + r"\4\5\6", text + # regp.GET_ITALIC_COMMANDS, regp.REPLACE_ITALIC_COMMANDS, text # ) # TODO: Replace ``**DIM*`` configurations into ``*\*DIM*`` return text @@ -440,8 +441,7 @@ def replace_asterisks(initial_text): if ".. code::" in initial_text: # Regex pattern that matches RST code blocks - pattern = r"(\s*\.\. code:: apdl\n\s*(?: +.+\n)+)" - fragments = get_fragment_code(initial_text, pattern) + fragments = get_fragment_code(initial_text, regp.GET_CODE_BLOCK) # Process non-code fragments for i in range(len(fragments)): @@ -3097,13 +3097,6 @@ def arg_replacer(match): return f":func:`{func}({py_args}) <{self._autogenerated_directory_name}.{func}>` {tail}" - # command replacement with args - docstr = re.sub( - r"[a-z0-9]*?(\s*,+\s*[A-Za-z0-9%`]*)+", - arg_replacer, - docstr, - ) - def cmd_replacer(match): func = match.group().replace("", "").replace("", "") return f":func:`{func}() <{self._autogenerated_directory_name}.{func}>`" diff --git a/src/pyconverter/xml2py/utils/regex_pattern.py b/src/pyconverter/xml2py/utils/regex_pattern.py index 1908bd195..f5e7adf9f 100644 --- a/src/pyconverter/xml2py/utils/regex_pattern.py +++ b/src/pyconverter/xml2py/utils/regex_pattern.py @@ -20,10 +20,19 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +GET_BOLD_COMMANDS = r"([^\*])(\*\*)(\*)(.*?)(\*\*)([^\*])" BEFORE_DEF = r"[\s\S]*?(?=def " -GET_GROUP = r"(?<=&)(.*?)(?=;)" GET_CLASSNAME = r"(\S+)(?=:)" +GET_CODE_BLOCK = r"(\s*\.\. code:: apdl\n\s*(?: +.+\n)+)" +GET_GROUP = r"(?<=&)(.*?)(?=;)" +# Not used, can be added in case of complications with imports in the future +GET_IMPORTS = r"(?:(?:from [a-zA-Z0-9_.]* import [a-zA-Z0-9_.]* as [a-zA-Z0-9_.]*)|(?:from [a-zA-Z0-9_.]* import [a-zA-Z0-9_.]*)|(?:import [a-zA-Z0-9_.]* as [a-zA-Z0-9_.]*)|(?:import [a-zA-Z0-9_.]*)\s)" # noqa: E501 +GET_ITALIC_COMMANDS = r"([^\*])(\*)(\*)([A-Z]+)(\*)([^\*])" # TODO: Not supported yet +GET_STAR_COMMANDS = r"([^\*])(\*)([A-Z]+)(\`|\,|\.|\s)" +GET_STAR_FUNCTIONS = r"([^\*\s\\]+)(\*)([^\*\s]+)" GET_TYPENAME_1OPT = r"(?<=:)(.*)" GET_TYPENAME_2OPT = r"(?<=:)(.*?)(?=[A-Z][A-Z])" -# Not used for now, can be added in case of complications with imports in the future -GET_IMPORTS = r"(?:(?:from [a-zA-Z0-9_.]* import [a-zA-Z0-9_.]* as [a-zA-Z0-9_.]*)|(?:from [a-zA-Z0-9_.]* import [a-zA-Z0-9_.]*)|(?:import [a-zA-Z0-9_.]* as [a-zA-Z0-9_.]*)|(?:import [a-zA-Z0-9_.]*)\s)" # noqa: E501 +REPLACE_BOLD_COMMANDS = r"\1\2" + r"\*" + r"\4\5\6" +REPLACE_ITALIC_COMMANDS = r"\1\2" + r"\*" + r"\4\5\6" # TODO: Not supported yet +REPLACE_STAR_COMMANDS = r"\1" + r"\*" + r"\3\4" +REPLACE_STAR_FUNCTIONS = r"\1\\2\3"