import ast import datetime import subprocess version = open("../debian/changelog").readlines()[0].split()[1].strip("()") today = datetime.datetime.now().strftime("%d/%m/%Y") def get_current_commit(): p = subprocess.Popen( "git rev-parse --verify HEAD", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = p.communicate() current_commit = stdout.strip().decode("utf-8") return current_commit current_commit = get_current_commit() def print_config_panel_docs(): fname = "../src/utils/configpanel.py" content = open(fname).read() # NB: This magic is because we want to be able to run this script outside of a YunoHost context, # in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports... tree = ast.parse(content) ConfigPanelClasses = reversed( [ c for c in tree.body if isinstance(c, ast.ClassDef) and c.name in {"SectionModel", "PanelModel", "ConfigPanelModel"} ] ) print("## Configuration panel structure") for c in ConfigPanelClasses: doc = ast.get_docstring(c) print("") print(f"### {c.name.replace('Model', '')}") print("") print(doc) print("") print("---") def print_form_doc(): fname = "../src/utils/form.py" content = open(fname).read() # NB: This magic is because we want to be able to run this script outside of a YunoHost context, # in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports... tree = ast.parse(content) OptionClasses = [ c for c in tree.body if isinstance(c, ast.ClassDef) and c.name.endswith("Option") ] OptionDocString = {} print("## List of all option types") for c in OptionClasses: if not isinstance(c.body[0], ast.Expr): continue option_type = None if c.name in {"BaseOption", "BaseInputOption"}: option_type = c.name elif c.body[1].target.id == "type": option_type = c.body[1].value.attr generaltype = ( c.bases[0].id.replace("Option", "").replace("Base", "").lower() if c.bases else None ) docstring = ast.get_docstring(c) if docstring: if "#### Properties" not in docstring: docstring += """ #### Properties - [common properties](#common-properties)""" OptionDocString[option_type] = { "doc": docstring, "generaltype": generaltype, } # Dirty hack to have "BaseOption" as first and "BaseInputOption" as 2nd in list base = OptionDocString.pop("BaseOption") baseinput = OptionDocString.pop("BaseInputOption") OptionDocString2 = { "BaseOption": base, "BaseInputOption": baseinput, } OptionDocString2.update(OptionDocString) for option_type, infos in OptionDocString2.items(): if option_type == "display_text": # display_text is kind of legacy x_x continue print("") if option_type == "BaseOption": print("### Common properties") elif option_type == "BaseInputOption": print("### Common inputs properties") else: print( f"### `{option_type}`" + (f" ({infos['generaltype']})" if infos["generaltype"] else "") ) print("") print(infos["doc"]) print("") print("---") print( rf"""--- title: Technical details for config panel structure and form option types template: docs taxonomy: category: docs routes: default: '/dev/forms' --- Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{current_commit}/doc/generate_options_doc.py) on {today} (YunoHost version {version}) ## Glossary You may encounter some named types which are used for simplicity. - `Translation`: a translated property - used for properties: `ask`, `help` and `Pattern.error` - a `dict` with locales as keys and translations as values: ```toml ask.en = "The text in english" ask.fr = "Le texte en français" ``` It is not currently possible for translators to translate those string in weblate. - a single `str` for a single english default string ```toml help = "The text in english" ``` - `JSExpression`: a `str` JS expression to be evaluated to `true` or `false`: - used for properties: `visible` and `enabled` - operators availables: `==`, `!=`, `>`, `>=`, `<`, `<=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%` and `match()` - `Binding`: bind a value to a file/property/variable/getter/setter/validator - save the value in `settings.yaml` when not defined - nothing at all with `"null"` - a custom getter/setter/validator with `"null"` + a function starting with `get__`, `set__`, `validate__` in `scripts/config` - a variable/property in a file with `:__FINALPATH__/my_file.php` - a whole file with `__FINALPATH__/my_file.php` - `Pattern`: a `dict` with a regex to match the value against and an error message ```toml pattern.regexp = '^[A-F]\d\d$' pattern.error = "Provide a room number such as F12: one uppercase and 2 numbers" # or with translated error pattern.error.en = "Provide a room number such as F12: one uppercase and 2 numbers" pattern.error.fr = "Entrez un numéro de salle comme F12: une lettre majuscule et deux chiffres." ``` - IMPORTANT: your `pattern.regexp` should be between simple quote, not double. """ ) print_config_panel_docs() print_form_doc()