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