-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow passing arguments containing spaces into pygmt functions #1487
Conversation
Modifying build_arg_string function to replace blank space characters with octal code 040, and added a doctest to check various combinations with single and double quotes included.
Doesn't work yet, as the filename will contain the 040 octal code, but committing to have the diff available for review.
Summary of changed imagesThis is an auto-generated report of images that have changed on the DVC remote
Image diff(s)Added images
Modified images
Report last updated at commit a526d45 |
869b43a
to
d81b80b
Compare
pygmt/tests/test_figure.py
Outdated
with GMTTempFile(prefix="pygmt-filename with spaces", suffix=".png") as imgfile: | ||
fig.savefig(imgfile.name) | ||
assert r"\040" not in os.path.abspath(imgfile.name) | ||
assert os.path.exists(imgfile.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A problem with this PR is that running fig.savefig
with filenames that have spaces (introduced in #1116) will produce an output like "pygmt-filename\040with\040spaces.png". This test currently passes still, but it is a false positive, because Python's os.path.exists
reads "pygmt-filename\040with\040spaces.png" as ""pygmt-filename with spaces.png"".
Any ideas how to improve the test and/or implementation of psconvert
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've manually worked around the problem in 83c8c3b so filenames with spaces will be saved as usual (without \040
in the name).
@weiji14 I'm assuming this won't be done by Friday? Should I bump it to a later version? |
Yes, the bugfix here needs a thorough review and more tests I think, plus I haven't figured out how to resolve #1487 (comment). So let's move it to v0.6.0. |
Also added a regression test for FORMAT_DATE_MAP="o dd".
@@ -55,7 +55,7 @@ def __init__(self, **kwargs): | |||
self.old_defaults[key] = lib.get_default(key) | |||
|
|||
# call gmt set to change GMT defaults | |||
arg_str = " ".join([f"{key}={value}" for key, value in kwargs.items()]) | |||
arg_str = " ".join([f'{key}="{value}"' for key, value in kwargs.items()]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've fixed the FORMAT_DATE_MAP="o dd"
issue reported in the forum by wrapping the arguments in double quotes here in commit c29e632. Didn't use \040
because o\040dd
doesn't work. The problem with this solution is that the workaround suggested in the forum (use FORMAT_DATE_MAP='"o dd"'
) will break in PyGMT v0.6.0, but I think that's acceptable since that workaround isn't intuitive anyway.
So that fig.savefig won't insert `\040` characters when saving filenames with spaces. Resolves problem mentioned in https://github.com/GenericMappingTools/pygmt/pull/1487/files#r703116544
pygmt/figure.py
Outdated
# Manually handle prefix -F argument so spaces aren't converted to \040 | ||
# by build_arg_string function. For more information, see | ||
# https://github.com/GenericMappingTools/pygmt/pull/1487 | ||
prefix = kwargs.pop("F") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a check before this that prefix
is provided so that the user doesn't get a KeyError here? Or you could raise GMTInvalidInput in an except statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Looking at the docs at https://docs.generic-mapping-tools.org/6.3/psconvert.html, it reads like prefix
/-F
is optional, but when I tried running psconvert
without the -F
option, it throws an error: psconvert [ERROR]: Modern GMT mode requires the -F option
. See https://github.com/GenericMappingTools/gmt/blob/adb244afa51ca7246cc051080c9d47193087d6c2/src/psconvert.c#L1041-L1042
So since PyGMT is modern mode only, it should be fine to keep this line intact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs say "If no input files are given, will convert the current active figure (see pygmt.figure). In this case, an output name must be given using parameter prefix." How does one provide other input files than the current figure using pygmt.Figure.psconvert?
If we chose not to support operations analogous to psconvert -Tg test.ps
(which uses the same prefix as the original file name), I think we should make prefix
required in the function signature or raise an invalid input exception to avoid the traceback below. I don't think it's obvious to that the error is caused by not using prefix
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does one provide other input files than the current figure using pygmt.Figure.psconvert?
We can't, because it has not been implemented yet. The current fig.psconvert
is a method of the pygmt.Figure()
class and is tied to the fig
, and can only 'psconvert' the active figure.
If we chose not to support operations analogous to
psconvert -Tg test.ps
(which uses the same prefix as the original file name), I think we should makeprefix
required in the function signature or raise an invalid input exception to avoid the traceback below. I don't think it's obvious to that the error is caused by not usingprefix
.
Ok, I've added a try-except block in 801ba01 to raise a GMTInvalidInput if prefix
/-F
is not given.
I tried for a while to break it. Nice work on this complicated issue! The two issues that I came across relate to the Outgrid parameterThe following will produce a filename with spaces using the main branch but the filename contains "\040" with this branch:
Outfile parameterFile outputs with spaces still require using the trick of wrapping double quotes in single quotes (this could be addressed separately from this PR).
|
Thanks for testing this! I still have a nagging feeling that there's a random edge case that will break this, but we'll find out once this gets released into the wild 😈
Right, fixing this would require a workaround like the one applied to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still a couple issues to work out as mentioned in https://github.com/GenericMappingTools/pygmt/pull/1487/files#r825371307 and #1487 (comment), but this is a big improvement that would benefit users in v0.6.0.
The one thing that makes little sense to me is shown in the following example:
As you can see, I have to use |
I just tested all three examples and only |
Ah, you're right. Good to know that I can still use I'm reading how GMT parses a long text string https://github.com/GenericMappingTools/gmt/blob/434ed1848a1e8886931694b2de4c352e4c6cddae/src/gmt_parse.c#L600, it seems it ignores double quotes (that's why |
if key != "J": # non-projection parameters | ||
_value = str(kwargs[key]).replace(" ", r"\040") | ||
else: | ||
# special handling if key == "J" (projection) | ||
# remove any spaces in PROJ4 string | ||
_value = str(kwargs[key]).replace(" ", "") | ||
gmt_args.append(rf"-{key}{_value}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know almost nothing about PROJ4, but does a PROJ4 string always contains +proj=xxx
or EPSG
? If yes, then we can simplify the if-test to something like:
if key != "J" or (key == "J" and "+proj" not in value and "EPSG" not in value):
_value = str(kwargs[key]).replace(" ", r"\040")
else:
# special handling if -J + PROJ4 string
# remove any spaces in PROJ4 string
_value = str(kwargs[key]).replace(" ", "")
gmt_args.append(rf"-{key}{_value}")
The motivation is, since PROJ4 is not commonly used, it seems a waste of time to do call the replace
method for GMT-style -J.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if all PROJ-strings have either +proj=xxx
or EPSG
(see https://proj.org/operations/projections/index.html), but they probably do? I know that there's +init=EPSG:xxx
which kinda works when I tried here, but that's actually deprecated syntax (see https://pyproj4.github.io/pyproj/stable/gotchas.html#init-auth-auth-code-should-be-replaced-with-auth-auth-code) and probably not something we want to allow.
Also unsure if 2 extra if-checks saves time, considering that either way, a str.replace
still needs to happen for the space character - either to \040
or to nothing ``.
Co-Authored-By: Dongdong Tian <[email protected]>
Thanks team! I'll merge this in and close that almost 4 year old #247 bug!! |
…icMappingTools#1487) * Replace spaces in arguments with octal code 040 Modifying build_arg_string function to replace blank space characters with octal code 040, and added a doctest to check various combinations with single and double quotes included. * Remove workarounds for spaces in fig.subplot's autolabel and title args Supersedes workaround for subplot's autolabel (-A) and title (-T) parameters in a9d167d, 4126c16, and eadb847. * Remove workaround for spaces in fig.text's -F argument * Remove double quotes around legend label test examples * Edit test_rose_no_sectors to remove single quotes from title * Remove workaround for spaces in fig.psconvert prefix Doesn't work yet, as the filename will contain the 040 octal code, but committing to have the diff available for review. * Ensure spaces in pygmt.config arguments can work Also added a regression test for FORMAT_DATE_MAP="o dd". * Manually handle prefix -F in psconvert So that fig.savefig won't insert `\040` characters when saving filenames with spaces. Resolves problem mentioned in https://github.com/GenericMappingTools/pygmt/pull/1487/files#r703116544 * Handle PROJ4 strings with spaces Instead of converting spaces to \040 in proj4 strings, just remove them directly. Added parametrized unit tests to basemap and grdproject to check that it works. * Use Modifier Letter Colon instead of regular colon to fix WIndows tests Adapted from https://stackoverflow.com/questions/10386344/how-to-get-a-file-in-windows-with-a-colon-in-the-filename/25477235#25477235. * Try using underscore instead of Modifier Letter Colon * Raise GMTInvalidInput if no prefix argument is passed to psconvert Co-authored-by: Dongdong Tian <[email protected]>
Description of proposed changes
To allow for spaces in arguments to PyGMT functions, this pull request modifies the
) with the equivalent octal code (
build_arg_string
function inpygmt/helpers/utils.py
to replace space characters (\040
). This enables users to do this:Note that no nested double quotes (
'"some word"'
) are needed anymore!I have also removed some hacky workarounds that allowed for text inputs with spaces into:
subplot
'sautolabel
(-A) andtitle
(-T) parameters - workaround added in Convert booleans arguments in build_arg_string, not in kwargs_to_strings #1125 and Wrap subplot using with statement #822text
's-F
parameterpsconvert
'sprefix
(-F) parameter - workaround added in Let Figure.savefig support filenames with spaces #1116Also updated some unit tests for
fig.legend
andfig.rose
that had used arguments like'"Some text"'
.Special handling of functions where spaces can't just be converted to
\040
:pygmt.config(FORMAT_DATE_MAP="o dd")
works (c29e632)fig.savefig()
saves filenames with spaces properly (Let Figure.savefig support filenames with spaces #1116) without\040
octal code characters in the filename (83c8c3b)projection="+proj4=+longlat +ellipsoid=someplanet"
) work, xref -JEPSG:4326 not writing CRS to netcdf in surface #655 (comment) (3ec7727)Fixes #247 🎉 🎉 🎉
Reminders
make format
andmake check
to make sure the code follows the style guide.doc/api/index.rst
.Slash Commands
You can write slash commands (
/command
) in the first line of a comment to performspecific operations. Supported slash commands are:
/format
: automatically format and lint the code/test-gmt-dev
: run full tests on the latest GMT development version