From 3b80a54a78df0c9d65b570aa8608fa6931653f66 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Thu, 21 Mar 2024 17:24:53 +0800 Subject: [PATCH 1/5] Add function build_arg_list for building arguments list from keyword dictionaries --- pygmt/helpers/__init__.py | 1 + pygmt/helpers/decorators.py | 2 +- pygmt/helpers/utils.py | 102 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/pygmt/helpers/__init__.py b/pygmt/helpers/__init__.py index d0356798d97..128b1e31a18 100644 --- a/pygmt/helpers/__init__.py +++ b/pygmt/helpers/__init__.py @@ -16,6 +16,7 @@ ) from pygmt.helpers.utils import ( args_in_kwargs, + build_arg_list, build_arg_string, data_kind, is_nonstr_iter, diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index 28041911d23..c6f19693781 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -622,7 +622,7 @@ def kwargs_to_strings(**conversions): The strings are what GMT expects from command line arguments. Boolean arguments and None are not converted and will be processed in the - ``build_arg_string`` function. + ``build_arg_list`` function. You can also specify other conversions to specific arguments. diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 75514d2077d..7b8fe8123f2 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -315,6 +315,108 @@ def non_ascii_to_octal(argstr): return argstr.translate(str.maketrans(mapping)) +def build_arg_list( + kwdict: dict, + confdict: dict | None = None, + infile: str | pathlib.PurePath | list[str | pathlib.PurePath] | None = None, + outfile: str | pathlib.PurePath | None = None, +) -> list[str]: + r""" + Convert keyword dictionaries and input/output files into a list of GMT arguments. + + Make sure all values in ``kwdict`` have been previously converted to a string + representation using the ``kwargs_to_strings`` decorator. The only exceptions are + ``True``, ``False`` and ``None``. + + Any lists or tuples left will be interpreted as multiple entries for the same + command line option. For example, the kwargs entry ``"B": ["xa", "yaf"]`` will be + converted to ``["-Bxa", "-Byaf"]``. + + Parameters + ---------- + kwdict + A dictionary containing parsed keyword arguments. + confdict + A dictionary containing configurable GMT parameters. + infile + The input file or a list of input files. + outfile + The output file. + + Returns + ------- + args + The list of command line arguments that will be passed to GMT modules. The + keyword arguments are sorted alphabetically, followed by GMT configuration + key-value pairs, with optional input file(s) at the beginning and optional + output file at the end. + + Examples + -------- + >>> build_arg_list(dict(A=True, B=False, C=None, D=0, E=200, F="", G="1/2/3/4")) + ['-A', '-D0', '-E200', '-F', '-G1/2/3/4'] + >>> build_arg_list(dict(A="1/2/3/4", B=["xaf", "yaf", "WSen"], C=("1p", "2p"))) + ['-A1/2/3/4', '-BWSen', '-Bxaf', '-Byaf', '-C1p', '-C2p'] + >>> print( + ... build_arg_list( + ... dict( + ... B=["af", "WSne+tBlank Space"], + ... F='+t"Empty Spaces"', + ... l="'Void Space'", + ... ) + ... ) + ... ) + ['-BWSne+tBlank Space', '-Baf', '-F+t"Empty Spaces"', "-l'Void Space'"] + >>> print( + ... build_arg_list( + ... dict(A="0", B=True, C="rainbow"), + ... confdict=dict(FORMAT_DATE_MAP="o dd"), + ... infile="input.txt", + ... outfile="output.txt", + ... ) + ... ) + ['input.txt', '-A0', '-B', '-Crainbow', '--FORMAT_DATE_MAP=o dd', '->output.txt'] + >>> print( + ... build_arg_list( + ... dict(A="0", B=True), + ... confdict=dict(FORMAT_DATE_MAP="o dd"), + ... infile=["f1.txt", "f2.txt"], + ... outfile="out.txt", + ... ) + ... ) + ['f1.txt', 'f2.txt', '-A0', '-B', '--FORMAT_DATE_MAP=o dd', '->out.txt'] + >>> print(build_arg_list(dict(R="1/2/3/4", J="X4i", watre=True))) + Traceback (most recent call last): + ... + pygmt.exceptions.GMTInvalidInput: Unrecognized parameter 'watre'. + """ + gmt_args = [] + for key, value in kwdict.items(): + if len(key) > 2: # Raise an exception for unrecognized options + raise GMTInvalidInput(f"Unrecognized parameter '{key}'.") + if value is None or value is False: # Exclude arguments that are None and False + pass + elif value is True: + gmt_args.append(f"-{key}") + elif is_nonstr_iter(value): + gmt_args.extend(non_ascii_to_octal(f"-{key}{_value}") for _value in value) + else: + gmt_args.append(non_ascii_to_octal(f"-{key}{value}")) + gmt_args = sorted(gmt_args) + + if confdict: + gmt_args.extend(f"--{key}={value}" for key, value in confdict.items()) + + if infile: # infile can be a single file or a list of files + if isinstance(infile, str | pathlib.PurePath): + gmt_args = [str(infile), *gmt_args] + else: + gmt_args = [str(_file) for _file in infile] + gmt_args + if outfile: + gmt_args.append("->" + str(outfile)) + return gmt_args + + def build_arg_string(kwdict, confdict=None, infile=None, outfile=None): r""" Convert keyword dictionaries and input/output files into a GMT argument string. From 05ab2fc77ab616551cd2233d92f5c05e508652d2 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 1 Apr 2024 07:50:32 +0800 Subject: [PATCH 2/5] Update pygmt/helpers/utils.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Yvonne Fröhlich <94163266+yvonnefroehlich@users.noreply.github.com> --- pygmt/helpers/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 7b8fe8123f2..7ab377b37d7 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -394,7 +394,7 @@ def build_arg_list( for key, value in kwdict.items(): if len(key) > 2: # Raise an exception for unrecognized options raise GMTInvalidInput(f"Unrecognized parameter '{key}'.") - if value is None or value is False: # Exclude arguments that are None and False + if value is None or value is False: # Exclude arguments that are None or False pass elif value is True: gmt_args.append(f"-{key}") From e6fcad89e4c7236452d62497a3b53f79306a7e2b Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Sun, 7 Apr 2024 20:05:59 +0800 Subject: [PATCH 3/5] Minor update for the outfile --- pygmt/helpers/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 7ab377b37d7..e8ac102f245 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -413,7 +413,7 @@ def build_arg_list( else: gmt_args = [str(_file) for _file in infile] + gmt_args if outfile: - gmt_args.append("->" + str(outfile)) + gmt_args.append(f"->{outfile}") return gmt_args From 4559e1d7d38c023cd502a7b7a1802e0e5a7a4080 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 8 Apr 2024 09:53:52 +0800 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- pygmt/helpers/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index e8ac102f245..0e36381a473 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -316,9 +316,9 @@ def non_ascii_to_octal(argstr): def build_arg_list( - kwdict: dict, - confdict: dict | None = None, - infile: str | pathlib.PurePath | list[str | pathlib.PurePath] | None = None, + kwdict: dict[str, Any], + confdict: dict[str, str] | None = None, + infile: str | pathlib.PurePath | Sequence[str | pathlib.PurePath] | None = None, outfile: str | pathlib.PurePath | None = None, ) -> list[str]: r""" @@ -328,8 +328,8 @@ def build_arg_list( representation using the ``kwargs_to_strings`` decorator. The only exceptions are ``True``, ``False`` and ``None``. - Any lists or tuples left will be interpreted as multiple entries for the same - command line option. For example, the kwargs entry ``"B": ["xa", "yaf"]`` will be + Any remaining lists or tuples will be interpreted as multiple entries for the same + parameter. For example, the kwargs entry ``"B": ["xa", "yaf"]`` will be converted to ``["-Bxa", "-Byaf"]``. Parameters From 914cb46e21264efe092281a05d6dfa4a7f1489c4 Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 8 Apr 2024 10:37:19 +0800 Subject: [PATCH 5/5] Fix typing issues --- pygmt/helpers/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 0e36381a473..204519c3cf2 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -11,7 +11,8 @@ import sys import time import webbrowser -from collections.abc import Iterable +from collections.abc import Iterable, Sequence +from typing import Any import xarray as xr from pygmt.exceptions import GMTInvalidInput