Skip to content

Commit fc1e165

Browse files
committed
Add options and flags
1 parent 51b6e03 commit fc1e165

4 files changed

Lines changed: 819 additions & 0 deletions

File tree

astro.config.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ export default defineConfig({
8484
label: 'Flying Tips',
8585
slug: 'flying-tips'
8686
},
87+
{
88+
label: 'Options & Flags',
89+
slug: 'options-flags'
90+
},
8791
{
8892
label: 'Frequently Asked Questions',
8993
slug: 'faq'

scripts/options-flags.template.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Arguments
3+
template: doc
4+
---
5+
6+
This is the complete list of arguments available in [ODM](https://github.com/WebODM/ODM). Note that some of these will not be visible from [WebODM](https://github.com/WebODM/WebODM).
7+
8+
$arguments

scripts/update_options_flags.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/python3
2+
3+
import argparse, os, urllib.request, ast, sys
4+
from string import Template
5+
6+
SCRIPT_DIR = os.path.dirname(__file__)
7+
DEFAULT_URL = "https://raw.githubusercontent.com/WebODM/ODM/master/opendm/config.py"
8+
9+
parser = argparse.ArgumentParser(description='Extract ODM arguments and generate a Markdown reference page.')
10+
parser.add_argument('input', type=str, nargs='?',
11+
default=DEFAULT_URL,
12+
help='URL to ODM\'s config.py')
13+
args = parser.parse_args()
14+
15+
url = args.input
16+
outfile = os.path.join(SCRIPT_DIR, "..", "src", "content", "docs", "options-flags.md")
17+
tmplfile = os.path.join(SCRIPT_DIR, "options-flags.template.md")
18+
19+
print("Fetching %s ..." % url)
20+
res = urllib.request.urlopen(url)
21+
config_file = res.read().decode('utf-8')
22+
23+
options = {}
24+
args_map = {}
25+
26+
class ArgumentParserStub(argparse.ArgumentParser):
27+
def add_argument(self, *args, **kwargs):
28+
argparse.ArgumentParser.add_argument(self, *args, **kwargs)
29+
options[args[0]] = {}
30+
args_map[args[0]] = args[1:]
31+
32+
for name, value in kwargs.items():
33+
options[args[0]][str(name)] = str(value)
34+
35+
def add_mutually_exclusive_group(self):
36+
return ArgumentParserStub()
37+
38+
# Voodoo! :)
39+
# Parse AST, extract assignments and function definitions,
40+
# execute in current scope to populate options via the stub parser.
41+
root = ast.parse(config_file)
42+
new_body = []
43+
for stmt in root.body:
44+
if hasattr(stmt, 'targets'): # Assignments
45+
new_body.append(stmt)
46+
elif hasattr(stmt, 'name'): # Functions
47+
new_body.append(stmt)
48+
49+
root.body = new_body
50+
exec(compile(root, filename="<ast>", mode="exec"))
51+
52+
# Misc variables needed to get config() to run
53+
__version__ = '?'
54+
class context:
55+
root_path = ''
56+
num_cores = 4
57+
settings_path = ''
58+
class io:
59+
def path_or_json_string_to_dict(s):
60+
return s
61+
def path_or_json_string(s):
62+
return s
63+
class log:
64+
def ODM_ERROR(s):
65+
pass
66+
def ODM_WARNING(s):
67+
pass
68+
def ODM_INFO(s):
69+
pass
70+
71+
config(["--project-path", "/bogus", "name"], parser=ArgumentParserStub())
72+
73+
# --- helpers ---
74+
75+
def get_opt_name(opt):
76+
opt_name = opt
77+
arg_map = args_map[opt]
78+
if len(arg_map) > 0:
79+
opt_name = max(arg_map + (opt_name, ), key=len)
80+
return opt_name.replace("--", "")
81+
82+
def get_opt_descr(opt):
83+
h = options[opt].get('help', '')
84+
if not h:
85+
return ''
86+
h = h.replace("\n", "\n\n")
87+
# Remove default/choices template markers — we show them separately
88+
h = h.replace("Can be one of: %(choices)s.", "")
89+
h = h.replace("Can be one of: %(choices)s", "")
90+
h = h.replace('%(choices)s', options[opt].get('choices', ''))
91+
92+
# Remove some tags
93+
# Escape HTML tags
94+
import re
95+
h = re.sub(r'<[^>]+>', lambda m: '`' + m.group(0) + '`', h)
96+
97+
# Strip trailing "Default: %(default)s" (with optional period) so we
98+
# don't duplicate the separate **Default:** line we emit below.
99+
100+
h = re.sub(r'\s*Default:\s*%\(default\)s\.?\s*$', '', h)
101+
h = h.replace('%(default)s', '`' + options[opt].get('default', '') + '`')
102+
103+
# Collapse multiple spaces
104+
h = re.sub(r' +', ' ', h).strip()
105+
return h
106+
107+
def get_opt_default(opt):
108+
res = options[opt].get('default', '')
109+
if res == "==SUPPRESS==":
110+
res = ""
111+
return res
112+
113+
def get_opt_choices(opt):
114+
raw = options[opt].get('choices', options[opt].get('metavar', ''))
115+
return raw.replace('[', '').replace(']', '').replace(',', ' | ').replace('\'', '')
116+
117+
# --- build markdown ---
118+
119+
print("Found %s ODM options" % len(options))
120+
121+
if len(options) == 0:
122+
print("No options found")
123+
sys.exit(1)
124+
125+
keys = list(options.keys())
126+
keys.sort(key=lambda a: a.replace("-", ""))
127+
128+
sections = ""
129+
for opt in keys:
130+
name = get_opt_name(opt)
131+
descr = get_opt_descr(opt)
132+
default = get_opt_default(opt)
133+
choices = get_opt_choices(opt)
134+
135+
section = "## %s\n\n" % name
136+
if descr:
137+
section += "%s\n\n" % descr
138+
if choices:
139+
section += "**Options:** `%s`\n\n" % choices
140+
if default:
141+
section += "**Default:** `%s`\n\n" % default
142+
143+
sections += section
144+
145+
with open(tmplfile) as f:
146+
tmpl = Template(f.read())
147+
148+
with open(outfile, "w") as f:
149+
f.write(tmpl.substitute(arguments=sections))
150+
151+
print("Wrote %s" % outfile)

0 commit comments

Comments
 (0)