files: .. _api:
  
  API Reference
  =============
  
  High-level functions for GMT modules
  ------------------------------------
  
  Each GMT module (``gmt pscoast``, ``gmt psbasemap``, etc.) is wrapped by a
  function in the ``gmt`` top-level module.
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.figure
      gmt.pscoast
  
  
  Additional utility functions:
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.test
  
  
  Low-level wrappers for the GMT C API
  ------------------------------------
  
  The GMT C API is accessed using ctypes_. The ``gmt.clib`` module offers
  functions and classes that wrap the C API with a pythonic interface.
  
  Most interactions with the C API are done through the
  :func:`gmt.clib.call_module` function.
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.clib.call_module
      gmt.clib.create_session
      gmt.clib.destroy_session
      gmt.clib.load_libgmt
  
  
  .. _ctypes: https://docs.python.org/3/library/ctypes.html
  
files: doc/api.rst
files: .. _api:
  
  API Reference
  =============
  
  High-level functions for GMT modules
  ------------------------------------
  
  Each GMT module (``gmt pscoas``, ``gmt grdgradient``, etc.) is wrapped by a
  function in the ``gmt`` top-level module.
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.begin
      gmt.end
      gmt.figure
      gmt.pscoast
  
  
  Additional utility functions:
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.test
  
  
  Low-level wrappers for the GMT C API
  ------------------------------------
  
  The GMT C API is accessed using ctypes_. The ``gmt.clib`` module offers
  functions and classes that wrap the C API with a pythonic interface.
  
  Most interactions with the C API are done through the
  :func:`gmt.clib.call_module` function.
  
  .. autosummary::
      :toctree: api/
      :template: function.rst
  
      gmt.clib.call_module
      gmt.clib.create_session
      gmt.clib.destroy_session
      gmt.clib.load_libgmt
  
  
  .. _ctypes: https://docs.python.org/3/library/ctypes.html
  
files: doc/api.rst
files: """
  GMT Python interface
  """
  from ._version import get_versions
  
  # Import modules to make the high-level GMT Python API
  from .ps_modules import pscoast
  from .session_management import figure, GMTSession
  
  
  # Get the version number through versioneer
  __version__ = get_versions()['version']
  # Delete the function so that it doesn't appear in the public API
  del get_versions
  
  # Start our global modern mode session. It calls "gmt.begin" when started and
  # "gmt.end" when deleted.
  _GLOBAL_SESSION = GMTSession()
  # Delete the class so that it doesn't appear in the public API
  del GMTSession
  
  
  def test(doctest=True, verbose=True, coverage=False, figures=True):
      """
      Run the test suite.
  
      Uses `py.test <http://pytest.org/>`__ to discover and run the tests. If you
      haven't already, you can install it with `conda
      <http://conda.pydata.org/>`__ or `pip <https://pip.pypa.io/en/stable/>`__.
  
      Parameters
      ----------
  
      doctest : bool
          If ``True``, will run the doctests as well (code examples that start
          with a ``>>>`` in the docs).
      verbose : bool
          If ``True``, will print extra information during the test run.
      coverage : bool
          If ``True``, will run test coverage analysis on the code as well.
          Requires ``pytest-cov``.
      figures : bool
          If ``True``, will test generated figures against saved baseline
          figures.  Requires ``pytest-mpl`` and ``matplotlib``.
  
      Raises
      ------
  
      AssertionError
          If pytest returns a non-zero error code indicating that some tests have
          failed.
  
      """
      import pytest
      args = []
      if verbose:
          args.append('-vv')
      if coverage:
          args.append('--cov=gmt')
          args.append('--cov-report=term-missing')
      if doctest:
          args.append('--doctest-modules')
      if figures:
          args.append('--mpl')
      args.append('--pyargs')
      args.append('gmt')
      status = pytest.main(args)
      assert status == 0, "Some tests have failed."
  
files: gmt/__init__.py
files: """
  GMT Python interface
  """
  from ._version import get_versions
  
  # Import modules to make the high-level GMT Python API
  from .ps_modules import pscoast
  from .session_management import begin, end, figure
  
  
  __version__ = get_versions()['version']
  del get_versions
  
  
  def test(doctest=True, verbose=True, coverage=False, figures=True):
      """
      Run the test suite.
  
      Uses `py.test <http://pytest.org/>`__ to discover and run the tests. If you
      haven't already, you can install it with `conda
      <http://conda.pydata.org/>`__ or `pip <https://pip.pypa.io/en/stable/>`__.
  
      Parameters
      ----------
  
      doctest : bool
          If ``True``, will run the doctests as well (code examples that start
          with a ``>>>`` in the docs).
      verbose : bool
          If ``True``, will print extra information during the test run.
      coverage : bool
          If ``True``, will run test coverage analysis on the code as well.
          Requires ``pytest-cov``.
      figures : bool
          If ``True``, will test generated figures against saved baseline
          figures.  Requires ``pytest-mpl`` and ``matplotlib``.
  
      Raises
      ------
  
      AssertionError
          If pytest returns a non-zero error code indicating that some tests have
          failed.
  
      """
      import pytest
      args = []
      if verbose:
          args.append('-vv')
      if coverage:
          args.append('--cov=gmt')
          args.append('--cov-report=term-missing')
      if doctest:
          args.append('--doctest-modules')
      if figures:
          args.append('--mpl')
      args.append('--pyargs')
      args.append('gmt')
      status = pytest.main(args)
      assert status == 0, "Some tests have failed."
  
files: gmt/__init__.py
files: """
  Session management modules: begin, end, figure, clean
  """
  from . import clib
  
  
  def begin():
      """
      Initiate a new GMT modern mode session.
  
      Used in combination with :func:`gmt.end`.
  
      Only meant to be used once for creating the global session.
  
      """
      prefix = 'gmt-python-session'
      clib.call_module('begin', prefix)
  
  
  def end():
      """
      Terminate GMT modern mode session and optionally produce the figure files.
  
      Called after :func:`gmt.begin` and all commands that you want included in a
      session. Will finalize any PostScript plots that were made in the
      background, convert them to the desired format (specified in
      ``gmt.begin``), and bring the figures to the working directory.
  
      """
      clib.call_module('end', '')
  
  
  def figure():
      """
      Start a new figure.
  
      All plotting commands run afterward will append to this figure.
  
      Unlike the command-line version (``gmt figure``), this function does not
      trigger the generation of a figure file. An explicit call to
      :func:`gmt.savefig` or :func:`gmt.psconvert` must be made in order to get a
      file.
  
      """
      prefix = 'gmt-python-figure'
      # Passing format '-' tells gmt.end to not produce any files.
      fmt = '-'
      clib.call_module('figure', '{} {}'.format(prefix, fmt))
  
  
  class GMTSession():
      """
      Placeholder for an active modern mode session.
  
      Calls ``begin`` and ``figure`` when created. Calls ``end`` when destroyed
      so that the tmp files are cleaned.
  
      The call to ``figure`` is necessary because the default behavior in Python
      is to not generate figure files unless explicitly commanded by
      ``psconvert`` or ``savefig``. The call starts a new figure with the format
      ``-`` which indicates that ``end`` should skip processing that figure.
  
      """
  
      def __init__(self):
          self.is_active = False
          self.begin()
  
      def begin(self):
          """
          Starts a modern mode session by calling ``begin`` and ``figure``.
  
          Sets the attribute ``_is_active`` to ``True`` to indicate that there
          is an active session.
          """
          assert not self.is_active, \
              "Session is already active. Can't start two simultaneous sessions"
          begin()
          figure()
          self.is_active = True
  
      def end(self):
          """
          End the current session.
          """
          assert self.is_active, "Can't end an inactive session."
          end()
          self.is_active = False
  
      def restart(self):
          """
          End the current session (if it's active) and start a new one.
          """
          if self.is_active:
              self.end()
          self.begin()
  
files: gmt/session_management.py
files: """
  Session management modules: begin, end, figure, clean
  """
  from . import clib
  
  
  def begin():
      """
      Initiate a new GMT modern mode session.
  
      Used in combination with :func:`gmt.end`.
  
      Only meant to be used once for creating the global session.
  
      """
      prefix = 'gmt-python-session'
      clib.call_module('begin', prefix)
  
  
  def end():
      """
      Terminate GMT modern mode session and optionally produce the figure files.
  
      Called after :func:`gmt.begin` and all commands that you want included in a
      session. Will finalize any PostScript plots that were made in the
      background, convert them to the desired format (specified in
      ``gmt.begin``), and bring the figures to the working directory.
  
      """
      clib.call_module('end', '')
  
  
  def figure():
      """
      Start a new figure.
  
      All plotting commands run afterward will append to this figure.
  
      Unlike the command-line version (``gmt figure``), this function does not
      trigger the generation of a figure file. An explicit call to
      :func:`gmt.savefig` or :func:`gmt.psconvert` must be made in order to get a
      file.
  
      """
      prefix = 'gmt-python-figure'
      # Passing format '-' tells gmt.end to not produce any files.
      fmt = '-'
      clib.call_module('figure', '{} {}'.format(prefix, fmt))
  
files: gmt/session_management.py
files: """
  Test the wrappers for the C API.
  """
  import os
  
  from ..clib import create_session, destroy_session, call_module, load_libgmt
  
  
  TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
  
  
  def test_load_libgmt():
      "Test that loading libgmt works and doesn't crash."
      libgmt = load_libgmt()
      assert hasattr(libgmt, 'GMT_Create_Session')
  
  
  def test_clib_session_management():
      "Test that create and destroy session are called without errors"
      session1 = create_session()
      assert session1 is not None
      session2 = create_session()
      assert session2 is not None
      assert session2 != session1
      destroy_session(session1)
      destroy_session(session2)
  
  
  def test_call_module():
      "Run a psbasemap call to see if the module works"
      data_fname = os.path.join(TEST_DATA_DIR, 'points.txt')
      out_fname = 'test_call_module.txt'
      call_module('gmtinfo', '{} -C ->{}'.format(data_fname, out_fname))
      assert os.path.exists(out_fname)
      with open(out_fname) as out_file:
          output = out_file.read().strip().replace('\t', ' ')
          assert output == '11.5309 61.7074 -2.9289 7.8648 0.1412 0.9338'
      os.remove(out_fname)
  
files: gmt/tests/test_clib.py
files: """
  Test the wrappers for the C API.
  """
  import os
  
  from ..clib import create_session, destroy_session, call_module, load_libgmt
  
  
  def test_load_libgmt():
      "Test that loading libgmt works and doesn't crash."
      libgmt = load_libgmt()
      assert hasattr(libgmt, 'GMT_Create_Session')
  
  
  def test_clib_session_management():
      "Test that create and destroy session are called without errors"
      session1 = create_session()
      assert session1 is not None
      session2 = create_session()
      assert session2 is not None
      assert session2 != session1
      destroy_session(session1)
      destroy_session(session2)
  
  
  def test_call_module():
      "Run a psbasemap call to see if the module works"
      module = 'psbasemap'
      args = '-R10/70/-3/8 -JX4i/3i -Ba -P ->tmp.ps'
      call_module(module, args)
      assert os.path.exists('tmp.ps')
      os.remove('tmp.ps')
      # Not the most ideal test. Just check if no segfaults or exceptions occur.
  
files: gmt/tests/test_clib.py
files: """
  Test the session management modules.
  """
  import os
  
  from pytest import raises
  
  from .. import figure, _GLOBAL_SESSION
  from ..session_management import begin, end, GMTSession
  from ..clib import call_module
  
  
  def test_begin_end():
      """"
      Run a command inside a begin-end modern mode block.
      First, end the global session. When finished, restart it.
      """
      _GLOBAL_SESSION.end()
      begin()
      call_module('psbasemap', '-R10/70/-3/8 -JX4i/3i -Ba -P')
      end()
      _GLOBAL_SESSION.restart()
      assert os.path.exists('gmt-python-session.pdf')
      os.remove('gmt-python-session.pdf')
  
  
  def test_session_figure():
      """
      Run a figure command and check that no file is be generated by gmt.end
  
      Need to end the global session before doing this.
      """
      _GLOBAL_SESSION.end()
      begin()
      figure()
      call_module('psbasemap', '-R10/70/-3/8 -JX4i/3i -Ba -P')
      end()
      _GLOBAL_SESSION.restart()
      assert not os.path.exists('gmt-python-figure.pdf')
  
  
  def test_gmtsession_begin_error():
      """
      Check that an error is raised when trying to start session without ending
      it first.
      """
      _GLOBAL_SESSION.end()
      session = GMTSession()
      with raises(AssertionError):
          session.begin()
      session.end()
      _GLOBAL_SESSION.restart()
  
  
  def test_gmtsession_restart():
      """
      Check that a session can be restarted without crashes.
      Restart should kill current session and begin a new one.
      There should be no way to begin a session without ending it first.
      """
      _GLOBAL_SESSION.end()
      session = GMTSession()
      assert session.is_active
      # Should work when session is active
      session.restart()
      assert session.is_active
      # And also when it isn't
      session.end()
      assert not session.is_active
      session.restart()
      assert session.is_active
      session.end()
      _GLOBAL_SESSION.restart()
  
  
  def test_gmtsession_error_end():
      """
      Should raise an error when calling end twice in a row.
      """
      _GLOBAL_SESSION.end()
      session = GMTSession()
      assert session.is_active
      session.end()
      assert not session.is_active
      with raises(AssertionError):
          session.end()
      _GLOBAL_SESSION.restart()
  
files: gmt/tests/test_session_management.py
files: """
  Test the session management modules.
  """
  import os
  
  from .. import begin, end, figure
  from ..clib import call_module
  
  
  def test_session():
      """"
      Run a command inside a begin-end modern mode block.
      """
      begin()
      call_module('psbasemap', '-R10/70/-3/8 -JX4i/3i -Ba -P')
      end()
      assert os.path.exists('gmt-python-session.pdf')
      os.remove('gmt-python-session.pdf')
  
  
  def test_session_figure():
      """
      Run a figure command inside a begin-end modern mode block.
      No file should be generated.
      """
      begin()
      figure()
      call_module('psbasemap', '-R10/70/-3/8 -JX4i/3i -Ba -P')
      end()
      assert not os.path.exists('gmt-python-figure.pdf')
  
files: gmt/tests/test_session_management.py