From 1d9e72a819fc10628a96cf1f6e7600a515b01e9b Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 2 Sep 2020 23:05:21 +1200 Subject: [PATCH 01/19] Implement tempfile_from_buffer for io.StringIO inputs --- pygmt/helpers/__init__.py | 2 +- pygmt/helpers/tempfile.py | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/pygmt/helpers/__init__.py b/pygmt/helpers/__init__.py index b8a6958816d..95dc48078bb 100644 --- a/pygmt/helpers/__init__.py +++ b/pygmt/helpers/__init__.py @@ -2,7 +2,7 @@ Functions, classes, decorators, and context managers to help wrap GMT modules. """ from .decorators import fmt_docstring, use_alias, kwargs_to_strings -from .tempfile import GMTTempFile, unique_name +from .tempfile import GMTTempFile, tempfile_from_buffer, unique_name from .utils import ( data_kind, dummy_context, diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index a17293eb460..df8e85112e8 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -2,7 +2,9 @@ Utilities for dealing with temporary file management. """ import os +import shutil import uuid +from contextlib import contextmanager from tempfile import NamedTemporaryFile import numpy as np @@ -105,3 +107,49 @@ def loadtxt(self, **kwargs): """ return np.loadtxt(self.name, **kwargs) + + +@contextmanager +def tempfile_from_buffer(buf): + """ + Store an io.StringIO buffer stream inside a temporary text file. + + Use the temporary file name to pass in data in your string buffer to a GMT + module. + + Context manager (use in a ``with`` block). Yields the temporary file name + that you can pass as an argument to a GMT module call. Closes the + temporary file upon exit of the ``with`` block. + + Parameters + ---------- + buf : io.StringIO + The in-memory text stream buffer that will be included in the temporary + file. + + Yields + ------ + fname : str + The name of temporary file. Pass this as a file name argument to a GMT + module. + + Examples + -------- + + >>> import io + >>> from pygmt.helpers import tempfile_from_buffer + >>> from pygmt import info + >>> data = np.arange(0, 6, 0.5).reshape((4, 3)) + >>> buf = io.StringIO() + >>> np.savetxt(fname=buf, X=data, fmt="%.1f") + >>> with tempfile_from_buffer(buf=buf) as fname: + ... result = info(fname, per_column=True) + ... print(result.strip()) + 0 4.5 0.5 5 1 5.5 + """ + with GMTTempFile() as tmpfile: + buf.seek(0) # Change stream position back to start + with open(file=tmpfile.name, mode="w") as fdst: + shutil.copyfileobj(fsrc=buf, fdst=fdst) + + yield tmpfile.name From e8cb8b69ba31c7ab6ee74c89b2b7c637a843e1df Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 11:01:53 +1200 Subject: [PATCH 02/19] Allow passing in kwargs to tempfile_from_buffer --- pygmt/helpers/tempfile.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index df8e85112e8..1c7c3073397 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -110,7 +110,7 @@ def loadtxt(self, **kwargs): @contextmanager -def tempfile_from_buffer(buf): +def tempfile_from_buffer(buf, **kwargs): """ Store an io.StringIO buffer stream inside a temporary text file. @@ -126,6 +126,8 @@ def tempfile_from_buffer(buf): buf : io.StringIO The in-memory text stream buffer that will be included in the temporary file. + kwargs : dict + Keyword arguments passed to the underlying GMTTempFile constructor. Yields ------ @@ -147,7 +149,7 @@ def tempfile_from_buffer(buf): ... print(result.strip()) 0 4.5 0.5 5 1 5.5 """ - with GMTTempFile() as tmpfile: + with GMTTempFile(**kwargs) as tmpfile: buf.seek(0) # Change stream position back to start with open(file=tmpfile.name, mode="w") as fdst: shutil.copyfileobj(fsrc=buf, fdst=fdst) From a64f47fe692c4a09bb10dde21af807d739bc4b13 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 11:02:18 +1200 Subject: [PATCH 03/19] Mock X2SYS_HOME in a temporary X2SYS_TMP instead of current working dir --- pygmt/tests/test_x2sys_cross.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 5d3ced83aa2..2665def26ed 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -3,6 +3,7 @@ Tests for x2sys_cross """ import os +import shutil from tempfile import TemporaryDirectory import numpy as np @@ -19,10 +20,14 @@ @pytest.fixture(name="mock_x2sys_home") def fixture_mock_x2sys_home(monkeypatch): """ - Set the X2SYS_HOME environment variable to the current working directory - for the test session + Set the X2SYS_HOME environment variable to "X2SYS_TMP" in the current + working directory for the test session """ - monkeypatch.setenv("X2SYS_HOME", os.getcwd()) + x2sys_home = os.path.join(os.getcwd(), "X2SYS_TMP") + os.makedirs(name=x2sys_home, exist_ok=True) + monkeypatch.setenv("X2SYS_HOME", x2sys_home) + yield x2sys_home + shutil.rmtree(x2sys_home) # cleanup X2SYS_TMP directory after tests @pytest.fixture(scope="module", name="tracks") @@ -39,7 +44,7 @@ def test_x2sys_cross_input_file_output_file(mock_x2sys_home): Run x2sys_cross by passing in a filename, and output internal crossovers to an ASCII txt file """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) outfile = os.path.join(tmpdir, "tmp_coe.txt") @@ -59,7 +64,7 @@ def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home): Run x2sys_cross by passing in a filename, and output internal crossovers to a pandas.DataFrame """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i", verbose="i") @@ -79,7 +84,7 @@ def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): to a pandas.DataFrame. Not actually implemented yet, wait for https://github.com/GenericMappingTools/gmt/issues/3717 """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) @@ -102,7 +107,7 @@ def test_x2sys_cross_input_two_filenames(mock_x2sys_home): Run x2sys_cross by passing in two filenames, and output external crossovers to a pandas.DataFrame """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) @@ -142,7 +147,7 @@ def test_x2sys_cross_region_interpolation_numpoints(mock_x2sys_home): Test that x2sys_cross's region (R), interpolation (l) and numpoints (W) arguments work. """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross( @@ -165,7 +170,7 @@ def test_x2sys_cross_trackvalues(mock_x2sys_home): """ Test that x2sys_cross's trackvalues (Z) argument work. """ - with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, trackvalues=True) From 9437850f6bd2f6240e98b8f9a1ca6b5a8d36df91 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 11:08:29 +1200 Subject: [PATCH 04/19] Allow passing in pandas dataframes to x2sys_cross Implemented by storing pandas.DataFrame data in a temporary file and passing this intermediate file to x2sys_cross. Need to do some regex file parsing to get the right file extension (suffix) for this to work. --- pygmt/tests/test_x2sys_cross.py | 29 ++++++++++++------------ pygmt/x2sys.py | 39 ++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 2665def26ed..d54b11dc961 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -36,7 +36,8 @@ def fixture_tracks(): Load track data from the sample bathymetry file """ dataframe = load_sample_bathymetry() - return [dataframe.query(expr="bathymetry > -20")] # reduce size of dataset + dataframe.columns = ["x", "y", "z"] # longitude, latitude, bathymetry + return [dataframe.query(expr="z > -20")] # reduce size of dataset def test_x2sys_cross_input_file_output_file(mock_x2sys_home): @@ -81,25 +82,23 @@ def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home): def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): """ Run x2sys_cross by passing in one dataframe, and output external crossovers - to a pandas.DataFrame. Not actually implemented yet, wait for - https://github.com/GenericMappingTools/gmt/issues/3717 + to a pandas.DataFrame. """ with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) - x2sys_init(tag=tag, fmtfile="xyz", force=True) + x2sys_init(tag=tag, fmtfile="xyz", suffix="tsv", force=True) - with pytest.raises(NotImplementedError): - _ = x2sys_cross(tracks=tracks, tag=tag, coe="i", verbose="i") + output = x2sys_cross(tracks=tracks, tag=tag, coe="i", verbose="i") - # assert isinstance(output, pd.DataFrame) - # assert output.shape == (4, 12) - # columns = list(output.columns) - # assert columns[:6] == ["x", "y", "t_1", "t_2", "dist_1", "dist_2"] - # assert columns[6:] == ["head_1","head_2","vel_1","vel_2","z_X","z_M"] - # assert output.dtypes["t_1"].type == np.datetime64 - # assert output.dtypes["t_2"].type == np.datetime64 + assert isinstance(output, pd.DataFrame) + assert output.shape == (14, 12) + columns = list(output.columns) + assert columns[:6] == ["x", "y", "i_1", "i_2", "dist_1", "dist_2"] + assert columns[6:] == ["head_1", "head_2", "vel_1", "vel_2", "z_X", "z_M"] + assert output.dtypes["i_1"].type == np.object_ # np.datetime64 + assert output.dtypes["i_2"].type == np.object_ # np.datetime64 - # return output + return output def test_x2sys_cross_input_two_filenames(mock_x2sys_home): @@ -136,7 +135,7 @@ def test_x2sys_cross_invalid_tracks_input_type(tracks): Run x2sys_cross using tracks input that is not a pandas.DataFrame (matrix) or str (file) type, which would raise a GMTInvalidInput error. """ - invalid_tracks = tracks[0].to_xarray().bathymetry + invalid_tracks = tracks[0].to_xarray().z assert data_kind(invalid_tracks) == "grid" with pytest.raises(GMTInvalidInput): x2sys_cross(tracks=[invalid_tracks]) diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index 4f55b23f3dc..7b7d12f900e 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -2,6 +2,9 @@ GMT supplementary X2SYS module for crossover analysis. """ import contextlib +import io +import os +import re import pandas as pd @@ -14,6 +17,7 @@ dummy_context, fmt_docstring, kwargs_to_strings, + tempfile_from_buffer, use_alias, ) @@ -263,8 +267,31 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): if kind == "file": file_contexts.append(dummy_context(track)) elif kind == "matrix": - raise NotImplementedError(f"{type(track)} inputs are not supported yet") - # file_contexts.append(lib.virtualfile_from_matrix(track.values)) + # find suffix (-E) of trackfiles used (e.g. xyz, csv, etc) from + # $X2SYS_HOME/TAGNAME/TAGNAME.tag file using regex search + try: + with open( + os.path.join( + os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag" + ), + "r", + ) as tagfile: + suffix = re.search( + pattern=r"-E(\S*)", # match file extension after -E + string=tagfile.readlines()[-1], # e.g. "-Dxyz -Exyz -I1/1" + ).group(1) + except AttributeError: # 'NoneType' object has no attribute 'group' + suffix = kwargs["T"] # tempfile suffix will be same as TAG name + + # Save pandas DataFrame data to io.StringIO stream buffer + buf = io.StringIO() + track.to_csv( + path_or_buf=buf, + sep="\t", + index=False, + date_format="%Y-%m-%dT%H:%M:%S.%fZ", + ) + file_contexts.append(tempfile_from_buffer(buf=buf, suffix=f".{suffix}")) else: raise GMTInvalidInput(f"Unrecognized data type: {type(track)}") @@ -278,8 +305,8 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if outfile isn't set, return pd.DataFrame - # Read the tab-separated ASCII table - table = pd.read_csv( + # Read the tab-separated ASCII table into pandas.DataFrame + result = pd.read_csv( tmpfile.name, sep="\t", header=2, # Column names are on 2nd row @@ -287,7 +314,9 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): parse_dates=[2, 3], # Datetimes on 3rd and 4th column ) # Remove the "# " from "# x" in the first column - result = table.rename(columns={table.columns[0]: table.columns[0][2:]}) + result = result.rename( + columns={result.columns[0]: result.columns[0][2:]} + ) elif outfile != tmpfile.name: # if outfile is set, output in outfile only result = None From c32870c37b14a1bb940150bf7c34ca640a919b7b Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 11:53:51 +1200 Subject: [PATCH 05/19] Change working directory to inside X2SYS_TMP during test session So that the tests will pass on macOS and Windows too. --- pygmt/tests/test_x2sys_cross.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index d54b11dc961..6a879e46a6e 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -20,12 +20,14 @@ @pytest.fixture(name="mock_x2sys_home") def fixture_mock_x2sys_home(monkeypatch): """ - Set the X2SYS_HOME environment variable to "X2SYS_TMP" in the current - working directory for the test session + Set X2SYS_HOME environment variable to "X2SYS_TMP" in the current + working directory, and change working directory to it for the duration of + the test session """ x2sys_home = os.path.join(os.getcwd(), "X2SYS_TMP") os.makedirs(name=x2sys_home, exist_ok=True) monkeypatch.setenv("X2SYS_HOME", x2sys_home) + monkeypatch.chdir(x2sys_home) yield x2sys_home shutil.rmtree(x2sys_home) # cleanup X2SYS_TMP directory after tests From 01c5ee54bb026ff8c1037e89d5b0fc0712be9212 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 17:06:18 +1200 Subject: [PATCH 06/19] Remove some info level verbose messages from x2sys_cross --- pygmt/tests/test_x2sys_cross.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 6a879e46a6e..8f084776262 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -52,7 +52,7 @@ def test_x2sys_cross_input_file_output_file(mock_x2sys_home): x2sys_init(tag=tag, fmtfile="xyz", force=True) outfile = os.path.join(tmpdir, "tmp_coe.txt") output = x2sys_cross( - tracks=["@tut_ship.xyz"], tag=tag, coe="i", outfile=outfile, verbose="i" + tracks=["@tut_ship.xyz"], tag=tag, coe="i", outfile=outfile ) assert output is None # check that output is None since outfile is set @@ -70,7 +70,7 @@ def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home): with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) - output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i", verbose="i") + output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i") assert isinstance(output, pd.DataFrame) assert output.shape == (14294, 12) @@ -118,9 +118,7 @@ def test_x2sys_cross_input_two_filenames(mock_x2sys_home): with open(os.path.join(os.getcwd(), f"track_{i}.xyz"), mode="w") as fname: np.savetxt(fname=fname, X=np.random.rand(10, 3)) - output = x2sys_cross( - tracks=["track_0.xyz", "track_1.xyz"], tag=tag, coe="e", verbose="i" - ) + output = x2sys_cross(tracks=["track_0.xyz", "track_1.xyz"], tag=tag, coe="e") assert isinstance(output, pd.DataFrame) assert output.shape == (24, 12) From 4132ff5994907b9fb7fd181d1721b3bc8f65ad3e Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 11:01:53 +1200 Subject: [PATCH 07/19] Revert "Allow passing in kwargs to tempfile_from_buffer" This reverts commit e8cb8b69ba31c7ab6ee74c89b2b7c637a843e1df. --- pygmt/helpers/tempfile.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index 1c7c3073397..df8e85112e8 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -110,7 +110,7 @@ def loadtxt(self, **kwargs): @contextmanager -def tempfile_from_buffer(buf, **kwargs): +def tempfile_from_buffer(buf): """ Store an io.StringIO buffer stream inside a temporary text file. @@ -126,8 +126,6 @@ def tempfile_from_buffer(buf, **kwargs): buf : io.StringIO The in-memory text stream buffer that will be included in the temporary file. - kwargs : dict - Keyword arguments passed to the underlying GMTTempFile constructor. Yields ------ @@ -149,7 +147,7 @@ def tempfile_from_buffer(buf, **kwargs): ... print(result.strip()) 0 4.5 0.5 5 1 5.5 """ - with GMTTempFile(**kwargs) as tmpfile: + with GMTTempFile() as tmpfile: buf.seek(0) # Change stream position back to start with open(file=tmpfile.name, mode="w") as fdst: shutil.copyfileobj(fsrc=buf, fdst=fdst) From 2c41ca4a6272520a80c9377abb43a5202d1d6222 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 2 Sep 2020 23:05:21 +1200 Subject: [PATCH 08/19] Revert "Implement tempfile_from_buffer for io.StringIO inputs" This reverts commit 1d9e72a819fc10628a96cf1f6e7600a515b01e9b. --- pygmt/helpers/__init__.py | 2 +- pygmt/helpers/tempfile.py | 48 --------------------------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/pygmt/helpers/__init__.py b/pygmt/helpers/__init__.py index 95dc48078bb..b8a6958816d 100644 --- a/pygmt/helpers/__init__.py +++ b/pygmt/helpers/__init__.py @@ -2,7 +2,7 @@ Functions, classes, decorators, and context managers to help wrap GMT modules. """ from .decorators import fmt_docstring, use_alias, kwargs_to_strings -from .tempfile import GMTTempFile, tempfile_from_buffer, unique_name +from .tempfile import GMTTempFile, unique_name from .utils import ( data_kind, dummy_context, diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index df8e85112e8..a17293eb460 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -2,9 +2,7 @@ Utilities for dealing with temporary file management. """ import os -import shutil import uuid -from contextlib import contextmanager from tempfile import NamedTemporaryFile import numpy as np @@ -107,49 +105,3 @@ def loadtxt(self, **kwargs): """ return np.loadtxt(self.name, **kwargs) - - -@contextmanager -def tempfile_from_buffer(buf): - """ - Store an io.StringIO buffer stream inside a temporary text file. - - Use the temporary file name to pass in data in your string buffer to a GMT - module. - - Context manager (use in a ``with`` block). Yields the temporary file name - that you can pass as an argument to a GMT module call. Closes the - temporary file upon exit of the ``with`` block. - - Parameters - ---------- - buf : io.StringIO - The in-memory text stream buffer that will be included in the temporary - file. - - Yields - ------ - fname : str - The name of temporary file. Pass this as a file name argument to a GMT - module. - - Examples - -------- - - >>> import io - >>> from pygmt.helpers import tempfile_from_buffer - >>> from pygmt import info - >>> data = np.arange(0, 6, 0.5).reshape((4, 3)) - >>> buf = io.StringIO() - >>> np.savetxt(fname=buf, X=data, fmt="%.1f") - >>> with tempfile_from_buffer(buf=buf) as fname: - ... result = info(fname, per_column=True) - ... print(result.strip()) - 0 4.5 0.5 5 1 5.5 - """ - with GMTTempFile() as tmpfile: - buf.seek(0) # Change stream position back to start - with open(file=tmpfile.name, mode="w") as fdst: - shutil.copyfileobj(fsrc=buf, fdst=fdst) - - yield tmpfile.name From aa3a5216b4ca9e64ac09633991f20bec1bbc01b5 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 22:01:10 +1200 Subject: [PATCH 09/19] Use tempfile_from_dftrack instead of tempfile_from_buffer --- pygmt/x2sys.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index 7b7d12f900e..e0192d85766 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -2,7 +2,6 @@ GMT supplementary X2SYS module for crossover analysis. """ import contextlib -import io import os import re @@ -17,11 +16,27 @@ dummy_context, fmt_docstring, kwargs_to_strings, - tempfile_from_buffer, use_alias, ) +@contextlib.contextmanager +def tempfile_from_dftrack(track, suffix): + """ + Saves a pandas.DataFrame track table to a temporary tab-separated ASCII + text file with a suffix (e.g. 'xyz'). + """ + with GMTTempFile(suffix=suffix) as tmpfile: + track.to_csv( + path_or_buf=tmpfile.name, + sep="\t", + index=False, + date_format="%Y-%m-%dT%H:%M:%S.%fZ", + ) + + yield tmpfile.name + + @fmt_docstring @use_alias( D="fmtfile", @@ -283,15 +298,8 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): except AttributeError: # 'NoneType' object has no attribute 'group' suffix = kwargs["T"] # tempfile suffix will be same as TAG name - # Save pandas DataFrame data to io.StringIO stream buffer - buf = io.StringIO() - track.to_csv( - path_or_buf=buf, - sep="\t", - index=False, - date_format="%Y-%m-%dT%H:%M:%S.%fZ", - ) - file_contexts.append(tempfile_from_buffer(buf=buf, suffix=f".{suffix}")) + # Save pandas.DataFrame track data to temporary file + file_contexts.append(tempfile_from_dftrack(track=track, suffix=suffix)) else: raise GMTInvalidInput(f"Unrecognized data type: {type(track)}") From fcd6cfecf25379847c5ccf18477dc19a7a82eba6 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 22:18:20 +1200 Subject: [PATCH 10/19] Try closing the file stream before writing Because Windows (and macOS?) might not support opening same temporary file twice. --- pygmt/helpers/tempfile.py | 1 + pygmt/x2sys.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index a17293eb460..059915a24e2 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -61,6 +61,7 @@ def __init__(self, prefix="pygmt-", suffix=".txt"): args = dict(prefix=prefix, suffix=suffix, delete=False) with NamedTemporaryFile(**args) as tmpfile: self.name = tmpfile.name + self.close = tmpfile.close def __enter__(self): return self diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index e0192d85766..a7728113529 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -27,6 +27,7 @@ def tempfile_from_dftrack(track, suffix): text file with a suffix (e.g. 'xyz'). """ with GMTTempFile(suffix=suffix) as tmpfile: + tmpfile.close() # close the file stream track.to_csv( path_or_buf=tmpfile.name, sep="\t", From 17a2f3ec062797c605e9df0c28434490392232f4 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 22:49:17 +1200 Subject: [PATCH 11/19] Try different way of closing the tmpfile generated from dataframe --- pygmt/x2sys.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index a7728113529..0d5a6f6c484 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -26,16 +26,17 @@ def tempfile_from_dftrack(track, suffix): Saves a pandas.DataFrame track table to a temporary tab-separated ASCII text file with a suffix (e.g. 'xyz'). """ - with GMTTempFile(suffix=suffix) as tmpfile: - tmpfile.close() # close the file stream + try: + tmpfile = GMTTempFile(suffix=suffix) track.to_csv( path_or_buf=tmpfile.name, sep="\t", index=False, date_format="%Y-%m-%dT%H:%M:%S.%fZ", ) - yield tmpfile.name + finally: + tmpfile.close() # close the file stream @fmt_docstring From 144642953a7027b54db3c2a8d784a90d9780c507 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 23:41:51 +1200 Subject: [PATCH 12/19] Don't use GMTTempFile, just generate random filename and write to it --- pygmt/helpers/tempfile.py | 1 - pygmt/x2sys.py | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pygmt/helpers/tempfile.py b/pygmt/helpers/tempfile.py index 059915a24e2..a17293eb460 100644 --- a/pygmt/helpers/tempfile.py +++ b/pygmt/helpers/tempfile.py @@ -61,7 +61,6 @@ def __init__(self, prefix="pygmt-", suffix=".txt"): args = dict(prefix=prefix, suffix=suffix, delete=False) with NamedTemporaryFile(**args) as tmpfile: self.name = tmpfile.name - self.close = tmpfile.close def __enter__(self): return self diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index 0d5a6f6c484..ff9863127de 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -16,6 +16,7 @@ dummy_context, fmt_docstring, kwargs_to_strings, + unique_name, use_alias, ) @@ -27,16 +28,16 @@ def tempfile_from_dftrack(track, suffix): text file with a suffix (e.g. 'xyz'). """ try: - tmpfile = GMTTempFile(suffix=suffix) + tmpfilename = f"track-{unique_name()[:7]}.{suffix}" track.to_csv( - path_or_buf=tmpfile.name, + path_or_buf=tmpfilename, sep="\t", index=False, date_format="%Y-%m-%dT%H:%M:%S.%fZ", ) - yield tmpfile.name + yield tmpfilename finally: - tmpfile.close() # close the file stream + os.remove(tmpfilename) @fmt_docstring From a2b08be2b241f598816e50b9e8082cfd38c400ca Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 00:12:21 +1200 Subject: [PATCH 13/19] Reduce git diff and make Windows tests pass by ignoring permission error --- pygmt/tests/test_x2sys_cross.py | 2 +- pygmt/x2sys.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 8f084776262..25a0079326f 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -29,7 +29,7 @@ def fixture_mock_x2sys_home(monkeypatch): monkeypatch.setenv("X2SYS_HOME", x2sys_home) monkeypatch.chdir(x2sys_home) yield x2sys_home - shutil.rmtree(x2sys_home) # cleanup X2SYS_TMP directory after tests + shutil.rmtree(path=x2sys_home, ignore_errors=True) @pytest.fixture(scope="module", name="tracks") diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index ff9863127de..5f783ccc3ec 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -291,8 +291,7 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): with open( os.path.join( os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag" - ), - "r", + ) ) as tagfile: suffix = re.search( pattern=r"-E(\S*)", # match file extension after -E @@ -316,8 +315,8 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): # Read temporary csv output to a pandas table if outfile == tmpfile.name: # if outfile isn't set, return pd.DataFrame - # Read the tab-separated ASCII table into pandas.DataFrame - result = pd.read_csv( + # Read the tab-separated ASCII table + table = pd.read_csv( tmpfile.name, sep="\t", header=2, # Column names are on 2nd row @@ -325,9 +324,7 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): parse_dates=[2, 3], # Datetimes on 3rd and 4th column ) # Remove the "# " from "# x" in the first column - result = result.rename( - columns={result.columns[0]: result.columns[0][2:]} - ) + result = table.rename(columns={table.columns[0]: table.columns[0][2:]}) elif outfile != tmpfile.name: # if outfile is set, output in outfile only result = None From 8efb415bde67351338160d2c882e3a8e60dc5cad Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 09:55:58 +1200 Subject: [PATCH 14/19] Revert "Mock X2SYS_HOME in a temporary X2SYS_TMP instead of current working dir" This reverts commit a64f47fe692c4a09bb10dde21af807d739bc4b13. --- pygmt/tests/test_x2sys_cross.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 25a0079326f..eef19073d51 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -3,7 +3,6 @@ Tests for x2sys_cross """ import os -import shutil from tempfile import TemporaryDirectory import numpy as np @@ -20,16 +19,10 @@ @pytest.fixture(name="mock_x2sys_home") def fixture_mock_x2sys_home(monkeypatch): """ - Set X2SYS_HOME environment variable to "X2SYS_TMP" in the current - working directory, and change working directory to it for the duration of - the test session + Set the X2SYS_HOME environment variable to the current working directory + for the test session """ - x2sys_home = os.path.join(os.getcwd(), "X2SYS_TMP") - os.makedirs(name=x2sys_home, exist_ok=True) - monkeypatch.setenv("X2SYS_HOME", x2sys_home) - monkeypatch.chdir(x2sys_home) - yield x2sys_home - shutil.rmtree(path=x2sys_home, ignore_errors=True) + monkeypatch.setenv("X2SYS_HOME", os.getcwd()) @pytest.fixture(scope="module", name="tracks") @@ -47,7 +40,7 @@ def test_x2sys_cross_input_file_output_file(mock_x2sys_home): Run x2sys_cross by passing in a filename, and output internal crossovers to an ASCII txt file """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) outfile = os.path.join(tmpdir, "tmp_coe.txt") @@ -67,7 +60,7 @@ def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home): Run x2sys_cross by passing in a filename, and output internal crossovers to a pandas.DataFrame """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i") @@ -86,7 +79,7 @@ def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): Run x2sys_cross by passing in one dataframe, and output external crossovers to a pandas.DataFrame. """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", suffix="tsv", force=True) @@ -108,7 +101,7 @@ def test_x2sys_cross_input_two_filenames(mock_x2sys_home): Run x2sys_cross by passing in two filenames, and output external crossovers to a pandas.DataFrame """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) @@ -146,7 +139,7 @@ def test_x2sys_cross_region_interpolation_numpoints(mock_x2sys_home): Test that x2sys_cross's region (R), interpolation (l) and numpoints (W) arguments work. """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross( @@ -169,7 +162,7 @@ def test_x2sys_cross_trackvalues(mock_x2sys_home): """ Test that x2sys_cross's trackvalues (Z) argument work. """ - with TemporaryDirectory(prefix="X2SYS", dir=mock_x2sys_home) as tmpdir: + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, trackvalues=True) From 1b658c7f3940f0c61bc06b69655c08bb0f3c802c Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Wed, 9 Sep 2020 17:06:18 +1200 Subject: [PATCH 15/19] Revert "Remove some info level verbose messages from x2sys_cross" This reverts commit 01c5ee54bb026ff8c1037e89d5b0fc0712be9212. --- pygmt/tests/test_x2sys_cross.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index eef19073d51..3940e18b3ff 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -45,7 +45,7 @@ def test_x2sys_cross_input_file_output_file(mock_x2sys_home): x2sys_init(tag=tag, fmtfile="xyz", force=True) outfile = os.path.join(tmpdir, "tmp_coe.txt") output = x2sys_cross( - tracks=["@tut_ship.xyz"], tag=tag, coe="i", outfile=outfile + tracks=["@tut_ship.xyz"], tag=tag, coe="i", outfile=outfile, verbose="i" ) assert output is None # check that output is None since outfile is set @@ -63,7 +63,7 @@ def test_x2sys_cross_input_file_output_dataframe(mock_x2sys_home): with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) x2sys_init(tag=tag, fmtfile="xyz", force=True) - output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i") + output = x2sys_cross(tracks=["@tut_ship.xyz"], tag=tag, coe="i", verbose="i") assert isinstance(output, pd.DataFrame) assert output.shape == (14294, 12) @@ -111,7 +111,9 @@ def test_x2sys_cross_input_two_filenames(mock_x2sys_home): with open(os.path.join(os.getcwd(), f"track_{i}.xyz"), mode="w") as fname: np.savetxt(fname=fname, X=np.random.rand(10, 3)) - output = x2sys_cross(tracks=["track_0.xyz", "track_1.xyz"], tag=tag, coe="e") + output = x2sys_cross( + tracks=["track_0.xyz", "track_1.xyz"], tag=tag, coe="e", verbose="i" + ) assert isinstance(output, pd.DataFrame) assert output.shape == (24, 12) From 8430dd7666161db6438b11786b85b3f8367e93ae Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 11:31:44 +1200 Subject: [PATCH 16/19] Better suffix finder for dataframe input into x2sys_cross --- pygmt/tests/test_x2sys_cross.py | 6 +++--- pygmt/x2sys.py | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index 3940e18b3ff..bca657faa99 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -81,7 +81,7 @@ def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): """ with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: tag = os.path.basename(tmpdir) - x2sys_init(tag=tag, fmtfile="xyz", suffix="tsv", force=True) + x2sys_init(tag=tag, fmtfile="xyz", force=True) output = x2sys_cross(tracks=tracks, tag=tag, coe="i", verbose="i") @@ -90,8 +90,8 @@ def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): columns = list(output.columns) assert columns[:6] == ["x", "y", "i_1", "i_2", "dist_1", "dist_2"] assert columns[6:] == ["head_1", "head_2", "vel_1", "vel_2", "z_X", "z_M"] - assert output.dtypes["i_1"].type == np.object_ # np.datetime64 - assert output.dtypes["i_2"].type == np.object_ # np.datetime64 + assert output.dtypes["i_1"].type == np.object_ + assert output.dtypes["i_2"].type == np.object_ return output diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index 5f783ccc3ec..fdbb026b231 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -4,6 +4,7 @@ import contextlib import os import re +from pathlib import Path import pandas as pd @@ -287,18 +288,18 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): elif kind == "matrix": # find suffix (-E) of trackfiles used (e.g. xyz, csv, etc) from # $X2SYS_HOME/TAGNAME/TAGNAME.tag file using regex search + lastline = ( + Path(os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag") + .read_text() + .strip() + .split("\n")[-1] + ) # e.g. "-Dxyz -Etsv -I1/1" try: - with open( - os.path.join( - os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag" - ) - ) as tagfile: - suffix = re.search( - pattern=r"-E(\S*)", # match file extension after -E - string=tagfile.readlines()[-1], # e.g. "-Dxyz -Exyz -I1/1" - ).group(1) + # 1st try to match file extension after -E + suffix = re.search(pattern=r"-E(\S*)", string=lastline).group(1) except AttributeError: # 'NoneType' object has no attribute 'group' - suffix = kwargs["T"] # tempfile suffix will be same as TAG name + # 2nd try to match file extension after -D + suffix = re.search(pattern=r"-D(\S*)", string=lastline).group(1) # Save pandas.DataFrame track data to temporary file file_contexts.append(tempfile_from_dftrack(track=track, suffix=suffix)) From b93a7aa10819382acaa50ac293e18531a5e47f41 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 11:33:56 +1200 Subject: [PATCH 17/19] Test input two pandas dataframes to x2sys_cross with time column --- pygmt/tests/test_x2sys_cross.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pygmt/tests/test_x2sys_cross.py b/pygmt/tests/test_x2sys_cross.py index bca657faa99..50577b738df 100644 --- a/pygmt/tests/test_x2sys_cross.py +++ b/pygmt/tests/test_x2sys_cross.py @@ -96,6 +96,40 @@ def test_x2sys_cross_input_dataframe_output_dataframe(mock_x2sys_home, tracks): return output +def test_x2sys_cross_input_two_dataframes(mock_x2sys_home): + """ + Run x2sys_cross by passing in two pandas.DataFrame tables with a time + column, and output external crossovers to a pandas.DataFrame + """ + with TemporaryDirectory(prefix="X2SYS", dir=os.getcwd()) as tmpdir: + tag = os.path.basename(tmpdir) + x2sys_init( + tag=tag, fmtfile="xyz", suffix="xyzt", units=["de", "se"], force=True + ) + + # Add a time row to the x2sys fmtfile + with open(file=os.path.join(tmpdir, "xyz.fmt"), mode="a") as fmtfile: + fmtfile.write("time\ta\tN\t0\t1\t0\t%g\n") + + # Create pandas.DataFrame track tables + tracks = [] + for i in range(2): + np.random.seed(seed=i) + track = pd.DataFrame(data=np.random.rand(10, 3), columns=("x", "y", "z")) + track["time"] = pd.date_range(start=f"2020-{i}1-01", periods=10, freq="ms") + tracks.append(track) + + output = x2sys_cross(tracks=tracks, tag=tag, coe="e", verbose="i") + + assert isinstance(output, pd.DataFrame) + assert output.shape == (30, 12) + columns = list(output.columns) + assert columns[:6] == ["x", "y", "t_1", "t_2", "dist_1", "dist_2"] + assert columns[6:] == ["head_1", "head_2", "vel_1", "vel_2", "z_X", "z_M"] + assert output.dtypes["t_1"].type == np.datetime64 + assert output.dtypes["t_2"].type == np.datetime64 + + def test_x2sys_cross_input_two_filenames(mock_x2sys_home): """ Run x2sys_cross by passing in two filenames, and output external crossovers From 5338f3720a111cd1abbcc3352a5c9f2985ada430 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 15:27:45 +1200 Subject: [PATCH 18/19] Refactor to find suffix without using regex Also rename 'result' to 'table' to prevent pylint complaining about R0914: Too many local variables (16/15) (too-many-locals) --- pygmt/x2sys.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index fdbb026b231..696dc324a23 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -3,7 +3,6 @@ """ import contextlib import os -import re from pathlib import Path import pandas as pd @@ -287,19 +286,16 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): file_contexts.append(dummy_context(track)) elif kind == "matrix": # find suffix (-E) of trackfiles used (e.g. xyz, csv, etc) from - # $X2SYS_HOME/TAGNAME/TAGNAME.tag file using regex search + # $X2SYS_HOME/TAGNAME/TAGNAME.tag file lastline = ( Path(os.environ["X2SYS_HOME"], kwargs["T"], f"{kwargs['T']}.tag") .read_text() .strip() .split("\n")[-1] ) # e.g. "-Dxyz -Etsv -I1/1" - try: - # 1st try to match file extension after -E - suffix = re.search(pattern=r"-E(\S*)", string=lastline).group(1) - except AttributeError: # 'NoneType' object has no attribute 'group' - # 2nd try to match file extension after -D - suffix = re.search(pattern=r"-D(\S*)", string=lastline).group(1) + for item in sorted(lastline.split()): # sort list alphabetically + if item.startswith(("-E", "-D")): # prefer -Etsv over -Dxyz + suffix = item[2:] # e.g. tsv (1st choice) or xyz (2nd choice) # Save pandas.DataFrame track data to temporary file file_contexts.append(tempfile_from_dftrack(track=track, suffix=suffix)) @@ -325,8 +321,8 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): parse_dates=[2, 3], # Datetimes on 3rd and 4th column ) # Remove the "# " from "# x" in the first column - result = table.rename(columns={table.columns[0]: table.columns[0][2:]}) + table = table.rename(columns={table.columns[0]: table.columns[0][2:]}) elif outfile != tmpfile.name: # if outfile is set, output in outfile only - result = None + table = None - return result + return table From bb223639d11a482ad7001f121456260cb1002474 Mon Sep 17 00:00:00 2001 From: Wei Ji Date: Thu, 10 Sep 2020 16:45:11 +1200 Subject: [PATCH 19/19] Improve docstring of x2sys_cross and tempfile_from_dftrack --- pygmt/x2sys.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pygmt/x2sys.py b/pygmt/x2sys.py index 696dc324a23..22294c3e791 100644 --- a/pygmt/x2sys.py +++ b/pygmt/x2sys.py @@ -24,8 +24,23 @@ @contextlib.contextmanager def tempfile_from_dftrack(track, suffix): """ - Saves a pandas.DataFrame track table to a temporary tab-separated ASCII - text file with a suffix (e.g. 'xyz'). + Saves pandas.DataFrame track table to a temporary tab-separated ASCII text + file with a unique name (to prevent clashes when running x2sys_cross), + adding a suffix extension to the end. + + Parameters + ---------- + track : pandas.DataFrame + A table holding track data with coordinate (x, y) or (lon, lat) values, + and (optionally) time (t). + suffix : str + File extension, e.g. xyz, tsv, etc. + + Yields + ------ + tmpfilename : str + A temporary tab-separated value file with a unique name holding the + track data. E.g. 'track-1a2b3c4.tsv'. """ try: tmpfilename = f"track-{unique_name()[:7]}.{suffix}" @@ -180,9 +195,10 @@ def x2sys_cross(tracks=None, outfile=None, **kwargs): Parameters ---------- - tracks : str or list + tracks : pandas.DataFrame or str or list A table or a list of tables with (x, y) or (lon, lat) values in the - first two columns. Supported formats are ASCII, native binary, or + first two columns. Track(s) can be provided as pandas DataFrame tables + or file names. Supported file formats are ASCII, native binary, or COARDS netCDF 1-D data. More columns may also be present. If the filenames are missing their file extension, we will append the