diff --git a/doc/conf.py b/doc/conf.py index fb47eab1d19..22c973073fb 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -150,7 +150,14 @@ html_theme_options = {} repository = "GenericMappingTools/pygmt" repository_url = "https://github.com/GenericMappingTools/pygmt" -commit_link = f'{ __commit__[:8] }' +if __commit__: + commit_link = ( + f'{ __commit__[:8] }' + ) +else: + commit_link = ( + f'{ __version__ }' + ) html_context = { "menu_links": [ ( diff --git a/pygmt/__init__.py b/pygmt/__init__.py index 2aeb76d10a6..033a21f631b 100644 --- a/pygmt/__init__.py +++ b/pygmt/__init__.py @@ -47,7 +47,7 @@ # Get semantic version through setuptools-scm __version__ = f'v{get_distribution("pygmt").version}' # e.g. v0.1.2.dev3+g0ab3cd78 -__commit__ = __version__.split("+g")[-1] # 0ab3cd78 +__commit__ = __version__.split("+g")[-1] if "+g" in __version__ else "" # 0ab3cd78 # Start our global modern mode session _begin() diff --git a/pygmt/datasets/earth_relief.py b/pygmt/datasets/earth_relief.py index 1a949cc38f4..1dc288164ce 100644 --- a/pygmt/datasets/earth_relief.py +++ b/pygmt/datasets/earth_relief.py @@ -10,7 +10,7 @@ from pygmt.src import grdcut, which -@kwargs_to_strings(convert_bools=False, region="sequence") +@kwargs_to_strings(region="sequence") def load_earth_relief(resolution="01d", region=None, registration=None, use_srtm=False): r""" Load Earth relief grids (topography and bathymetry) in various resolutions. diff --git a/pygmt/helpers/decorators.py b/pygmt/helpers/decorators.py index d050cf96ffb..dcd26723142 100644 --- a/pygmt/helpers/decorators.py +++ b/pygmt/helpers/decorators.py @@ -302,14 +302,14 @@ def new_module(*args, **kwargs): return alias_decorator -def kwargs_to_strings(convert_bools=True, **conversions): +def kwargs_to_strings(**conversions): """ Decorator to convert given keyword arguments to strings. The strings are what GMT expects from command line arguments. - Converts all boolean arguments by default. Transforms ``True`` into ``''`` - (empty string) and removes the argument from ``kwargs`` if ``False``. + Boolean arguments and None are not converted and will be processed in the + ``build_arg_string`` function. You can also specify other conversions to specific arguments. @@ -323,9 +323,6 @@ def kwargs_to_strings(convert_bools=True, **conversions): Parameters ---------- - convert_bools : bool - If ``True``, convert all boolean arguments to strings using the rules - specified above. If ``False``, leave them as they are. conversions : keyword arguments Keyword arguments specifying other kinds of conversions that should be performed. The keyword is the name of the argument and the value is the @@ -356,16 +353,18 @@ def kwargs_to_strings(convert_bools=True, **conversions): >>> module(R="5/6/7/8") {'R': '5/6/7/8'} >>> module(P=True) - {'P': ''} + {'P': True} >>> module(P=False) - {} + {'P': False} + >>> module(P=None) + {'P': None} >>> module(i=[1, 2]) {'i': '1,2'} >>> module(files=["data1.txt", "data2.txt"]) {'files': 'data1.txt data2.txt'} >>> # Other non-boolean arguments are passed along as they are >>> module(123, bla=(1, 2, 3), foo=True, A=False, i=(5, 6)) - {'bla': (1, 2, 3), 'foo': '', 'i': '5,6'} + {'A': False, 'bla': (1, 2, 3), 'foo': True, 'i': '5,6'} args: 123 >>> import datetime >>> module( @@ -416,8 +415,6 @@ def new_module(*args, **kwargs): """ New module instance that converts the arguments first. """ - if convert_bools: - kwargs = remove_bools(kwargs) for arg, fmt in conversions.items(): if arg in kwargs: value = kwargs[arg] @@ -442,30 +439,3 @@ def new_module(*args, **kwargs): return new_module return converter - - -def remove_bools(kwargs): - """ - Remove booleans from arguments. - - If ``True``, replace it with an empty string. If ``False``, completely - remove the entry from the argument list. - - Parameters - ---------- - kwargs : dict - Dictionary with the keyword arguments. - - Returns - ------- - new_kwargs : dict - A copy of `kwargs` with the booleans parsed. - """ - new_kwargs = {} - for arg, value in kwargs.items(): - if isinstance(value, bool): - if value: - new_kwargs[arg] = "" - else: - new_kwargs[arg] = value - return new_kwargs diff --git a/pygmt/helpers/utils.py b/pygmt/helpers/utils.py index 749ebe63e5b..c25b751146f 100644 --- a/pygmt/helpers/utils.py +++ b/pygmt/helpers/utils.py @@ -106,7 +106,8 @@ def build_arg_string(kwargs): Transform keyword arguments into a GMT argument string. Make sure all arguments have been previously converted to a string - representation using the ``kwargs_to_strings`` decorator. + 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 argument. For example, the kwargs entry ``'B': ['xa', @@ -128,10 +129,20 @@ def build_arg_string(kwargs): >>> print( ... build_arg_string( - ... dict(R="1/2/3/4", J="X4i", P="", E=200, X=None, Y=None) + ... dict( + ... A=True, + ... B=False, + ... E=200, + ... J="X4c", + ... P="", + ... R="1/2/3/4", + ... X=None, + ... Y=None, + ... Z=0, + ... ) ... ) ... ) - -E200 -JX4i -P -R1/2/3/4 + -A -E200 -JX4c -P -R1/2/3/4 -Z0 >>> print( ... build_arg_string( ... dict( @@ -142,20 +153,22 @@ def build_arg_string(kwargs): ... ) ... ) ... ) - -Bxaf -Byaf -BWSen -I1/1p,blue -I2/0.25p,blue -JX4i -R1/2/3/4 + -BWSen -Bxaf -Byaf -I1/1p,blue -I2/0.25p,blue -JX4i -R1/2/3/4 """ - sorted_args = [] - for key in sorted(kwargs): + gmt_args = [] + # Exclude arguments that are None and False + filtered_kwargs = { + k: v for k, v in kwargs.items() if (v is not None and v is not False) + } + for key in filtered_kwargs: if is_nonstr_iter(kwargs[key]): for value in kwargs[key]: - sorted_args.append("-{}{}".format(key, value)) - elif kwargs[key] is None: # arguments like -XNone are invalid - continue + gmt_args.append(f"-{key}{value}") + elif kwargs[key] is True: + gmt_args.append(f"-{key}") else: - sorted_args.append("-{}{}".format(key, kwargs[key])) - - arg_str = " ".join(sorted_args) - return arg_str + gmt_args.append(f"-{key}{kwargs[key]}") + return " ".join(sorted(gmt_args)) def is_nonstr_iter(value): diff --git a/pygmt/src/subplot.py b/pygmt/src/subplot.py index fdf11856bf1..d290bf0a112 100644 --- a/pygmt/src/subplot.py +++ b/pygmt/src/subplot.py @@ -148,8 +148,9 @@ def subplot(self, nrows=1, ncols=1, **kwargs): {XY} """ kwargs = self._preprocess(**kwargs) # pylint: disable=protected-access - # allow for spaces in string with needing double quotes - kwargs["A"] = f'"{kwargs.get("A")}"' if kwargs.get("A") is not None else None + # allow for spaces in string without needing double quotes + if isinstance(kwargs.get("A"), str): + kwargs["A"] = f'"{kwargs.get("A")}"' kwargs["T"] = f'"{kwargs.get("T")}"' if kwargs.get("T") else None if nrows < 1 or ncols < 1: diff --git a/pygmt/src/text.py b/pygmt/src/text.py index 4c7b5421410..9db7d35bac4 100644 --- a/pygmt/src/text.py +++ b/pygmt/src/text.py @@ -177,13 +177,23 @@ def text_( ) ): kwargs.update({"F": ""}) - if angle is not None and isinstance(angle, (int, float, str)): + + if angle is True: + kwargs["F"] += "+a" + elif isinstance(angle, (int, float, str)): kwargs["F"] += f"+a{str(angle)}" - if font is not None and isinstance(font, str): + + if font is True: + kwargs["F"] += "+f" + elif isinstance(font, str): kwargs["F"] += f"+f{font}" - if justify is not None and isinstance(justify, str): + + if justify is True: + kwargs["F"] += "+j" + elif isinstance(justify, str): kwargs["F"] += f"+j{justify}" - if position is not None and isinstance(position, str): + + if isinstance(position, str): kwargs["F"] += f'+c{position}+t"{text}"' extra_arrays = [] diff --git a/pygmt/tests/test_helpers.py b/pygmt/tests/test_helpers.py index 5f33291798f..f2ac45c9e35 100644 --- a/pygmt/tests/test_helpers.py +++ b/pygmt/tests/test_helpers.py @@ -50,23 +50,6 @@ def test_kwargs_to_strings_fails(): kwargs_to_strings(bla="blablabla") -def test_kwargs_to_strings_no_bools(): - """ - Test that not converting bools works. - """ - - @kwargs_to_strings(convert_bools=False) - def my_module(**kwargs): - """ - Function that does nothing. - """ - return kwargs - - # The module should return the exact same arguments it was given - args = dict(P=True, A=False, R="1/2/3/4") - assert my_module(**args) == args - - def test_gmttempfile(): """ Check that file is really created and deleted.