GRASS 8 Programmer's Manual 8.6.0dev(2026)-ddeab64dbf
Loading...
Searching...
No Matches
parser_md_python.c
Go to the documentation of this file.
1/*!
2 \file lib/gis/parser_md_python.c
3
4 \brief GIS Library - Argument parsing functions (Markdown output - Python)
5
6 (C) 2025 by the GRASS Development Team
7
8 This program is free software under the GNU General Public License
9 (>=v2). Read the file COPYING that comes with GRASS for details.
10
11 \author Vaclav Petras
12 */
13#include <stdbool.h>
14#include <stdio.h>
15#include <string.h>
16
17#include <grass/gis.h>
18#include <grass/glocale.h>
19
20#include "parser_local_proto.h"
21
22static void print_python_short_flag(FILE *file, const char *key,
23 const char *label, const char *description,
24 const char *indent);
25static void print_python_long_flag(FILE *file, const char *key,
26 const char *label, const char *description,
27 const char *indent);
28static void print_python_option(FILE *file, const struct Option *opt,
29 const char *indent, bool tools_api);
30static void print_python_example(FILE *file, const char *python_function,
31 const char *output_format_default,
32 const char *indent, bool tools_api);
33static void print_python_tuple(FILE *file, const char *type, int num_items);
34
35void print_python_short_flag(FILE *file, const char *key, const char *label,
36 const char *description, const char *indent)
37{
38 fprintf(file, "%s", indent);
40 fprintf(file, "**%s**", key);
42 fprintf(file, "\n");
43 if (label != NULL) {
44 fprintf(file, "%s", indent);
48 fprintf(file, "\n");
49 }
50 if (description != NULL) {
51 fprintf(file, "%s", indent);
53 G__md_print_escaped(file, description, indent);
54 }
55}
56
57void print_python_long_flag(FILE *file, const char *key, const char *label,
58 const char *description, const char *indent)
59{
60 fprintf(file, "%s**%s** : bool, *optional*", indent, key);
62 fprintf(file, "\n");
63 if (label != NULL) {
64 fprintf(file, "%s", indent);
68 fprintf(file, "\n");
69 }
70 if (description != NULL) {
71 fprintf(file, "%s", indent);
73 G__md_print_escaped(file, description, indent);
75 fprintf(file, "\n");
76 }
77 fprintf(file, "%s", indent);
79 const char *flag_default = "*None*";
80 fprintf(file, "Default: %s", flag_default);
81}
82
83void print_python_tuple(FILE *file, const char *type, int num_items)
84{
85 fprintf(file, "tuple[%s", type);
86 for (int i = 1; i < num_items; i++) {
87 fprintf(file, ", %s", type);
88 }
89 fprintf(file, "]");
90}
91
92void print_python_option(FILE *file, const struct Option *opt,
93 const char *indent, bool tools_api)
94{
95 const char *type;
96
97 switch (opt->type) {
98 case TYPE_INTEGER:
99 type = "int";
100 break;
101 case TYPE_DOUBLE:
102 type = "float";
103 break;
104 case TYPE_STRING:
105 type = "str";
106 break;
107 default:
108 type = "str";
109 break;
110 }
111
112 char age[KEYLENGTH];
113 char element[KEYLENGTH];
115 if (opt->gisprompt) {
117 if (tools_api && !opt->multiple && opt->type == TYPE_STRING) {
118 if (G_strncasecmp("old", age, 3) == 0 &&
119 G_strncasecmp("file", element, 4) == 0) {
120 type = "str | io.StringIO";
121 }
122 if (G_strncasecmp("old", age, 3) == 0 &&
123 G_strncasecmp("cell", element, 4) == 0) {
124 type = "str | np.ndarray";
125 }
126 if (G_strncasecmp("new", age, 3) == 0 &&
127 G_strncasecmp("cell", element, 4) == 0) {
128 type = "str | type(np.ndarray) | type(np.array) | "
129 "type(gs.array.array)";
130 }
131 }
132 }
133
134 fprintf(file, "%s**%s** : ", indent, opt->key);
136 if (opt->multiple) {
137 if (tuple_items) {
138 fprintf(file, "list[");
139 print_python_tuple(file, type, tuple_items);
140 fprintf(file, "] | ");
141 print_python_tuple(file, type, tuple_items);
142 fprintf(file, " | list[%s] | str", type);
143 }
144 else {
145 if (strcmp(type, "str")) {
146 // If it is not a string, we also show it can be a string
147 // because that may be more relevant to show that for
148 // lists due to examples (it is possible for single value as
149 // well).
150 fprintf(file, "%s | list[%s] | str", type, type);
151 }
152 else {
153 fprintf(file, "%s | list[%s]", type, type);
154 }
155 }
156 }
157 else if (tuple_items) {
158 print_python_tuple(file, type, tuple_items);
159 fprintf(file, " | list[%s] | str", type);
160 }
161 else {
162 fprintf(file, "%s", type);
163 }
164 if (opt->required) {
165 fprintf(file, ", *required*");
166 }
167 else {
168 fprintf(file, ", *optional*");
169 }
170
172 fprintf(file, "\n");
173 if (opt->label) {
174 fprintf(file, "%s", indent);
177 }
178 if (opt->description) {
179 if (opt->label) {
181 fprintf(file, "\n");
182 }
183 fprintf(file, "%s", indent);
185 G__md_print_escaped(file, opt->description, indent);
186 }
187 if (opt->gisprompt || opt->key_desc) {
189 fprintf(file, "\n");
190 fprintf(file, "%s", indent);
192 fprintf(file, "%s: ", _("Used as"));
193 }
194 if (opt->gisprompt) {
195 if (strcmp(age, "new") == 0)
196 fprintf(file, "output, ");
197 else if (strcmp(age, "old") == 0)
198 fprintf(file, "input, ");
199 // While element more strictly expresses how the value will be
200 // used given that the parser may read that information, desc
201 // is meant as a user-facing representation of the same
202 // information.
204 }
205 if (opt->gisprompt && opt->key_desc) {
206 fprintf(file, ", ");
207 }
208 if (opt->key_desc) {
209 fprintf(file, "*%s*", opt->key_desc);
210 }
211
212 if (opt->options) {
214 fprintf(file, "\n");
215 fprintf(file, "%s", indent);
217 fprintf(file, "%s: *", _("Allowed values"));
219 fprintf(file, "*");
220 }
221
222 if (opt->descs) {
223 int i = 0;
224
225 while (opt->opts[i]) {
226 if (opt->descs[i]) {
228 fprintf(file, "\n");
229 fprintf(file, "%s", indent);
230 char *thumbnails = NULL;
231 if (opt->gisprompt) {
232 if (strcmp(opt->gisprompt, "old,colortable,colortable") ==
233 0)
234 thumbnails = "colortables";
235 else if (strcmp(opt->gisprompt, "old,barscale,barscale") ==
236 0)
237 thumbnails = "barscales";
238 else if (strcmp(opt->gisprompt,
239 "old,northarrow,northarrow") == 0)
240 thumbnails = "northarrows";
241
242 if (thumbnails) {
244 fprintf(file, "![%s](%s/%s.png) ", opt->opts[i],
245 thumbnails, opt->opts[i]);
246 }
247 else {
249 }
250 }
252 fprintf(file, "**");
254 fprintf(file, "**: ");
255 G__md_print_escaped(file, opt->descs[i], indent);
256 }
257 i++;
258 }
259 }
260
261 if (opt->def && opt->def[0] != '\0') {
263 fprintf(file, "\n");
264 fprintf(file, "%s", indent);
266 fprintf(file, "%s:", _("Default"));
267 fprintf(file, " *");
269 fprintf(file, "*");
270 }
271}
272
273void print_python_example(FILE *file, const char *python_function,
274 const char *output_format_default, const char *indent,
275 bool tools_api)
276{
277 fprintf(file, "\n%sExample:\n", indent);
278
279 fprintf(file, "\n%s```python\n", indent);
280 bool first_parameter_printed = false;
281 if (tools_api) {
282 char *tool_name = G_store(st->pgm_name);
283 G_strchg(tool_name, '.', '_');
284 fprintf(file, "%stools = Tools()\n", indent);
285 fprintf(file, "%stools.%s(", indent, tool_name);
287 }
288 else {
289 fprintf(file, "%sgs.%s(\"%s\"", indent, python_function, st->pgm_name);
291 }
292
293 const struct Option *first_required_rule_option =
295 const struct Option *opt = NULL;
296 const char *type;
297
298 if (st->n_opts) {
299 opt = &st->first_option;
300
301 while (opt != NULL) {
302 if (opt->key_desc != NULL)
303 type = opt->key_desc;
304 else
305 switch (opt->type) {
306 case TYPE_INTEGER:
307 type = "integer";
308 break;
309 case TYPE_DOUBLE:
310 type = "float";
311 break;
312 case TYPE_STRING:
313 type = "string";
314 break;
315 default:
316 type = "string";
317 break;
318 }
319 if (opt->required || first_required_rule_option == opt ||
320 (strcmp(opt->key, "format") == 0 && output_format_default)) {
322 fprintf(file, ", ");
323 }
324 fprintf(file, "%s=", opt->key);
325
326 char *value = NULL;
327 if (opt->answer) {
328 value = G_store(opt->answer);
329 }
330 else if (opt->options && opt->type == TYPE_STRING) {
331 // Get example value from allowed values, but only for
332 // strings because numbers may have ranges and we don't
333 // want to print a range.
334 // Get allowed values as tokens.
335 char **tokens;
336 char delm[2];
337 delm[0] = ',';
338 delm[1] = '\0';
339 tokens = G_tokenize(opt->options, delm);
340 // We are interested in the first allowed value.
341 if (tokens[0]) {
342 G_chop(tokens[0]);
343 value = G_store(tokens[0]);
344 }
346 }
347
348 if (output_format_default && strcmp(opt->key, "format") == 0) {
350 }
351 else if (value) {
352 if (opt->type == TYPE_INTEGER || opt->type == TYPE_DOUBLE) {
353 fprintf(file, "%s", value);
354 }
355 else {
356 fprintf(file, "\"%s\"", value);
357 }
358 }
359 else {
360 if (opt->type == TYPE_INTEGER) {
361 fprintf(file, "0");
362 }
363 else if (opt->type == TYPE_DOUBLE) {
364 fprintf(file, "0.0");
365 }
366 else {
367 fprintf(file, "\"%s\"", type);
368 }
369 }
371 G_free(value);
372 }
373 opt = opt->next_opt;
374 }
375 }
376 fprintf(file, ")\n%s```\n", indent);
377}
378
380 bool tools_api)
381{
382 struct Option *opt;
383 struct Flag *flag;
384 int new_prompt = 0;
385 bool output_format_option = false;
386 const char *output_format_default = NULL;
387 bool shell_eval_flag = false;
388 const char *python_function = NULL;
389
391
392 if (st->n_opts) {
393 opt = &st->first_option;
394 while (opt != NULL) {
395 if (strcmp(opt->key, "format") == 0) {
396 if (opt->options) {
397 int i = 0;
398 while (opt->opts[i]) {
399 if (strcmp(opt->opts[i], "csv") == 0)
400 output_format_default = "csv";
401 if (strcmp(opt->opts[i], "json") == 0) {
402 output_format_default = "json";
403 break;
404 }
405 i++;
406 }
407 }
410 }
411 break;
412 }
413 opt = opt->next_opt;
414 }
415 }
416 if (st->n_flags) {
417 flag = &st->first_flag;
418 while (st->n_flags && flag != NULL) {
419 if (flag->key == 'g') {
420 shell_eval_flag = true;
421 break;
422 }
423 flag = flag->next_flag;
424 }
425 }
426 bool first_parameter_printed = false;
427 if (tools_api) {
428 char *tool_name = G_store(st->pgm_name);
429 G_strchg(tool_name, '.', '_');
430 fprintf(file, "%s*grass.tools.Tools.%s*(", indent, tool_name);
432 }
433 else {
435 python_function = "parse_command";
436 // We know this can be parsed, but we don't detect just plain
437 // text output to use read_command because we can't distinguish
438 // between plain text outputs and modifications of data.
439 }
440 else {
441 python_function = "run_command";
442 }
443 fprintf(file, "%s*grass.script.%s*(\"***%s***\",", indent,
444 python_function, st->pgm_name);
445 fprintf(file, "\n");
447 }
448
449 if (st->n_opts) {
450 opt = &st->first_option;
451
452 while (opt != NULL) {
454 fprintf(file, "%s ", indent);
455 }
456 if (!opt->required && !opt->answer) {
457 fprintf(file, "**%s**=*None*", opt->key);
458 }
459 else {
460 fprintf(file, "**%s**", opt->key);
461 }
462 if (opt->answer) {
463 fprintf(file, "=");
465 if (!tuple_items &&
466 (opt->type == TYPE_INTEGER || opt->type == TYPE_DOUBLE)) {
467 fprintf(file, "*");
469 fprintf(file, "*");
470 }
471 else {
472 fprintf(file, "*\"");
474 fprintf(file, "\"*");
475 }
476 }
477 fprintf(file, ",\n");
479 opt = opt->next_opt;
480 }
481 }
482
483 if (st->n_flags) {
484 flag = &st->first_flag;
485 fprintf(file, "%s **flags**=*None*,\n", indent);
486 }
487
488 const char *flag_default = "*None*";
489 if (new_prompt)
490 fprintf(file, "%s **overwrite**=%s,\n", indent, flag_default);
491
492 fprintf(file, "%s **verbose**=%s,\n", indent, flag_default);
493 fprintf(file, "%s **quiet**=%s,\n", indent, flag_default);
494 fprintf(file, "%s **superquiet**=%s)\n", indent, flag_default);
495
496 print_python_example(file, python_function, output_format_default, indent,
497 tools_api);
498 if (tools_api) {
500 "\n%sThis grass.tools API is experimental in version 8.5 "
501 "and expected to be stable in version 8.6.\n",
502 indent);
503 }
504}
505
507 bool tools_api)
508{
509 struct Option *opt;
510 struct Flag *flag;
511 int new_prompt = 0;
512
514
515 // Options (key-value parameters)
516 if (st->n_opts) {
517 opt = &st->first_option;
518 while (opt != NULL) {
519 print_python_option(file, opt, indent, tools_api);
520 opt = opt->next_opt;
522 fprintf(file, "\n");
523 }
524 }
525
526 // Short (one-letter) flags and tool-specific long flags
527 if (st->n_flags) {
528 fprintf(file, "%s**flags** : str, *optional*", indent);
530 fprintf(file, "\n");
531 fprintf(file, "%s", indent);
533 fprintf(file, "Allowed values: ");
534 flag = &st->first_flag;
535 while (st->n_flags && flag != NULL) {
536 fprintf(file, "*%s*", &flag->key);
537 flag = flag->next_flag;
538 if (flag != NULL)
539 fprintf(file, ", ");
540 }
542 fprintf(file, "\n");
543 flag = &st->first_flag;
544 while (st->n_flags && flag != NULL) {
545 print_python_short_flag(file, &flag->key, flag->label,
546 flag->description, indent);
548 fprintf(file, "\n");
549 flag = flag->next_flag;
550 }
551 }
552 if (new_prompt) {
553 print_python_long_flag(
554 file, "overwrite", NULL,
555 _("Allow output files to overwrite existing files"), indent);
557 fprintf(file, "\n");
558 }
559 // Pre-defined long flags
560 print_python_long_flag(file, "verbose", NULL, _("Verbose module output"),
561 indent);
563 fprintf(file, "\n");
564 print_python_long_flag(file, "quiet", NULL, _("Quiet module output"),
565 indent);
567 fprintf(file, "\n");
568 print_python_long_flag(file, "superquiet", NULL,
569 _("Very quiet module output"), indent);
571 fprintf(file, "\n");
572
573 if (!tools_api)
574 return;
575
576 fprintf(file, "\n%sReturns:\n\n", indent);
577
578 bool outputs_arrays = false;
579 char age[KEYLENGTH];
580 char element[KEYLENGTH];
582 if (st->n_opts) {
583 opt = &st->first_option;
584 while (opt != NULL) {
585 if (opt->gisprompt) {
586 G__split_gisprompt(opt->gisprompt, age, element,
588 if (tools_api && !opt->multiple && opt->type == TYPE_STRING) {
589 if (G_strncasecmp("new", age, 3) == 0 &&
590 G_strncasecmp("cell", element, 4) == 0) {
591 outputs_arrays = true;
592 }
593 }
594 }
595 opt = opt->next_opt;
596 }
597 }
598
599 fprintf(file, "%s**result** : ", indent);
600 fprintf(file, "grass.tools.support.ToolResult");
601 if (outputs_arrays) {
602 fprintf(file, " | np.ndarray | tuple[np.ndarray]");
603 }
604 fprintf(file, " | None");
606 fprintf(file, "\n%s", indent);
607 fprintf(file, "If the tool produces text as standard output, a "
608 "*ToolResult* object will be returned. "
609 "Otherwise, `None` will be returned.");
610 if (outputs_arrays) {
611 fprintf(file, " If an array type (e.g., *np.ndarray*) is used for one "
612 "of the raster outputs, "
613 "the result will be an array and will have the shape "
614 "corresponding to the computational region. "
615 "If an array type is used for more than one raster "
616 "output, the result will be a tuple of arrays.");
617 }
618 fprintf(file, "\n");
619
620 fprintf(file, "\n%sRaises:\n\n", indent);
622 "%s*grass.tools.ToolError*: When the tool ended with an error.\n",
623 indent);
624}
#define NULL
Definition ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition gis/token.c:47
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition gis/token.c:197
char * G_strchg(char *, char, char)
Replace all occurrences of character in string bug with new.
Definition strings.c:160
char * G_chop(char *)
Chop leading and trailing white spaces.
Definition strings.c:332
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:87
int G_strncasecmp(const char *, const char *, int)
String compare ignoring case (upper or lower) - limited number of characters.
Definition strings.c:69
#define TYPE_STRING
Definition gis.h:191
#define TYPE_INTEGER
Definition gis.h:189
#define TYPE_DOUBLE
Definition gis.h:190
#define _(str)
Definition glocale.h:10
#define file
int G__uses_new_gisprompt(void)
Definition parser.c:892
struct state * st
Definition parser.c:104
void G__split_gisprompt(const char *gisprompt, char *age, char *element, char *desc)
Definition parser.c:1775
const struct Option * G__first_required_option_from_rules(void)
void G__md_print_escaped(FILE *f, const char *str, const char *indent)
int G__option_num_tuple_items(const struct Option *opt)
Get number of tuple items if option is a tuple.
void G__md_print_escaped_for_options(FILE *f, const char *str)
void G__md_print_python_long_version(FILE *file, const char *indent, bool tools_api)
void G__md_print_python_short_version(FILE *file, const char *indent, bool tools_api)
Structure that stores flag info.
Definition gis.h:594
Structure that stores option information.
Definition gis.h:563
int type
Definition gis.h:565