Skip to content
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

Refactor the gmt.clib package #210

Merged
merged 9 commits into from
Jul 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
TESTDIR=tmp-test-dir-with-unique-name
PYTEST_ARGS=--doctest-modules -v --pyargs
PYTEST_COV_ARGS=--cov-config=../.coveragerc --cov-report=term-missing
CHECK_FILES=gmt setup.py
FORMAT_FILES=gmt setup.py doc/conf.py
LINT_FILES=gmt setup.py

help:
@echo "Commands:"
Expand All @@ -28,18 +29,19 @@ test:
coverage:
# Run a tmp folder to make sure the tests are run on the installed version
mkdir -p $(TESTDIR)
cd $(TESTDIR); python -c "import gmt; gmt.print_libgmt_info()"
@echo ""
@cd $(TESTDIR); python -c "import gmt; gmt.print_clib_info()"
@echo ""
cd $(TESTDIR); pytest $(PYTEST_COV_ARGS) --cov=gmt $(PYTEST_ARGS) gmt
cp $(TESTDIR)/.coverage* .
rm -r $(TESTDIR)

format:
black $(CHECK_FILES)
black $(FORMAT_FILES)

check:
black --check $(CHECK_FILES)
pylint $(CHECK_FILES)
black --check $(FORMAT_FILES)
pylint $(LINT_FILES)

clean:
find . -name "*.pyc" -exec rm -v {} \;
Expand Down
53 changes: 33 additions & 20 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Miscellaneous

which
test
print_libgmt_info
print_clib_info


Datasets
Expand Down Expand Up @@ -100,37 +100,50 @@ All custom exceptions are derived from :class:`gmt.exceptions.GMTError`.
GMT C API
---------

The :mod:`gmt.clib` package is a wrapper for the GMT C API built using
`ctypes <https://docs.python.org/3/library/ctypes.html>`__.
Most calls to the C API happen through the :class:`gmt.clib.LibGMT` class.
The :mod:`gmt.clib` package is a wrapper for the GMT C API built using :mod:`ctypes`.
Most calls to the C API happen through the :class:`gmt.clib.Session` class.

.. autosummary::
:toctree: generated

clib.LibGMT
clib.Session

Main methods (this is what the rest of the library uses):
`GMT modules <http://gmt.soest.hawaii.edu/doc/latest/#man-pages>`__ are executed through
the :meth:`~gmt.clib.Session.call_module` method:

.. autosummary::
:toctree: generated

clib.LibGMT.call_module
clib.LibGMT.grid_to_vfile
clib.LibGMT.vectors_to_vfile
clib.LibGMT.matrix_to_vfile
clib.LibGMT.extract_region
clib.Session.call_module

Passing memory blocks between Python variables (:class:`numpy.ndarray`,
:class:`pandas.Series`, and :class:`xarray.DataArray`) and GMT happens through *virtual
files*. These methods are context managers that automate the conversion of Python
variables to GMT virtual files:

.. autosummary::
:toctree: generated

clib.Session.virtualfile_from_matrix
clib.Session.virtualfile_from_vectors
clib.Session.virtualfile_from_grid


Low level access (these are mostly used by the :mod:`gmt.clib` package):

.. autosummary::
:toctree: generated

clib.LibGMT.create_session
clib.LibGMT.destroy_session
clib.LibGMT.get_constant
clib.LibGMT.get_default
clib.LibGMT.create_data
clib.LibGMT.open_virtual_file
clib.LibGMT.put_matrix
clib.LibGMT.put_vector
clib.LibGMT.write_data
clib.Session.create
clib.Session.destroy
clib.Session.__getitem__
clib.Session.__enter__
clib.Session.__exit__
clib.Session.get_default
clib.Session.create_data
clib.Session.put_matrix
clib.Session.put_vector
clib.Session.write_data
clib.Session.open_virtual_file
clib.Session.extract_region
clib.Session.get_libgmt_func
105 changes: 65 additions & 40 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,101 @@
from gmt import __version__, __commit__

extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.doctest',
'sphinx.ext.viewcode',
'sphinx.ext.extlinks',
'numpydoc',
'nbsphinx',
'gmt.sphinxext.gmtplot',
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.coverage",
"sphinx.ext.mathjax",
"sphinx.ext.doctest",
"sphinx.ext.viewcode",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"numpydoc",
"nbsphinx",
"gmt.sphinxext.gmtplot",
]

# Autosummary pages will be generated by sphinx-autogen instead of sphinx-build
autosummary_generate = False

numpydoc_class_members_toctree = False

# intersphinx configuration
intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None),
"numpy": ("https://docs.scipy.org/doc/numpy/", None),
"pandas": ("http://pandas.pydata.org/pandas-docs/stable/", None),
"xarray": ("http://xarray.pydata.org/en/stable/", None),
}

# Sphinx project configuration
templates_path = ['_templates']
exclude_patterns = ['_build', '**.ipynb_checkpoints']
source_suffix = '.rst'
templates_path = ["_templates"]
exclude_patterns = ["_build", "**.ipynb_checkpoints"]
source_suffix = ".rst"
# The encoding of source files.
source_encoding = 'utf-8-sig'
master_doc = 'index'
source_encoding = "utf-8-sig"
master_doc = "index"

# General information about the project
year = datetime.date.today().year
project = u'GMT/Python'
copyright = u'2017-2018, Leonardo Uieda and Paul Wessel'
if len(__version__.split('+')) > 1 or __version__ == 'unknown':
version = 'dev'
project = u"GMT/Python"
copyright = u"2017-2018, Leonardo Uieda and Paul Wessel"
if len(__version__.split("+")) > 1 or __version__ == "unknown":
version = "dev"
else:
version = __version__

# These enable substitutions using |variable| in the rst files
rst_epilog = """
.. |year| replace:: {year}
""".format(year=year)
""".format(
year=year
)

html_last_updated_fmt = '%b %d, %Y'
html_title = 'GMT/Python'
html_short_title = 'GMT/Python'
html_logo = '_static/gmt-python-logo.png'
html_favicon = '_static/favicon.png'
html_static_path = ['_static']
html_extra_path = ['.nojekyll', 'CNAME']
pygments_style = 'default'
html_last_updated_fmt = "%b %d, %Y"
html_title = "GMT/Python"
html_short_title = "GMT/Python"
html_logo = "_static/gmt-python-logo.png"
html_favicon = "_static/favicon.png"
html_static_path = ["_static"]
html_extra_path = [".nojekyll", "CNAME"]
pygments_style = "default"
add_function_parentheses = False
html_show_sourcelink = False
html_show_sphinx = True
html_show_copyright = True

# Theme config
html_theme = "sphinx_rtd_theme"
html_theme_options = {
}
html_theme_options = {}
html_context = {
'menu_links': [
('<i class="fa fa-play fa-fw"></i> Try it online!', 'http://try.gmtpython.xyz'),
('<i class="fa fa-github fa-fw"></i> Source Code', 'https://github.com/GenericMappingTools/gmt-python'),
('<i class="fa fa-users fa-fw"></i> Contributing', 'https://github.com/GenericMappingTools/gmt-python/blob/master/CONTRIBUTING.md'),
('<i class="fa fa-book fa-fw"></i> Code of Conduct', 'https://github.com/GenericMappingTools/gmt-python/blob/master/CODE_OF_CONDUCT.md'),
('<i class="fa fa-gavel fa-fw"></i> License', 'https://github.com/GenericMappingTools/gmt-python/blob/master/LICENSE.txt'),
('<i class="fa fa-comment fa-fw"></i> Contact', 'https://gitter.im/GenericMappingTools/gmt-python'),
"menu_links": [
('<i class="fa fa-play fa-fw"></i> Try it online!', "http://try.gmtpython.xyz"),
(
'<i class="fa fa-github fa-fw"></i> Source Code',
"https://github.com/GenericMappingTools/gmt-python",
),
(
'<i class="fa fa-users fa-fw"></i> Contributing',
"https://github.com/GenericMappingTools/gmt-python/blob/master/CONTRIBUTING.md",
),
(
'<i class="fa fa-book fa-fw"></i> Code of Conduct',
"https://github.com/GenericMappingTools/gmt-python/blob/master/CODE_OF_CONDUCT.md",
),
(
'<i class="fa fa-gavel fa-fw"></i> License',
"https://github.com/GenericMappingTools/gmt-python/blob/master/LICENSE.txt",
),
(
'<i class="fa fa-comment fa-fw"></i> Contact',
"https://gitter.im/GenericMappingTools/gmt-python",
),
],
# Custom variables to enable "Improve this page"" and "Download notebook"
# links
'doc_path': 'doc',
'github_repo': 'GenericMappingTools/gmt-python',
'github_version': 'master',
"doc_path": "doc",
"github_repo": "GenericMappingTools/gmt-python",
"github_version": "master",
}

# Load the custom CSS files (needs sphinx >= 1.6 for this to work)
Expand Down
2 changes: 1 addition & 1 deletion doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Project Goals

* Make GMT more accessible to new users.
* Build a Pythonic API for GMT.
* Interface with the GMT C API directly using :py:mod:`ctypes` (no system calls).
* Interface with the GMT C API directly using :mod:`ctypes` (no system calls).
* Support for rich display in the `Jupyter notebook <http://jupyter.org/>`__.
* Integration with the Scipy stack: :class:`numpy.ndarray` or :class:`pandas.DataFrame`
for data tables and :class:`xarray.DataArray` for grids.
Expand Down
29 changes: 11 additions & 18 deletions gmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,19 @@
_atexit.register(_end)


def print_libgmt_info():
def print_clib_info():
"""
Print information about the currently loaded GMT shared library.
Print information about the GMT shared library that we can find.

Includes the GMT version, default values for parameters, the path to the
``libgmt`` shared library, and GMT directories.
Includes the GMT version, default values for parameters, the path to the ``libgmt``
shared library, and GMT directories.
"""
import shutil
from .clib import LibGMT

columns = shutil.get_terminal_size().columns
title = "Currently loaded libgmt"
left = (columns - len(title) - 2) // 2
right = left + (columns - (2 * left + len(title) + 2))
header = " ".join(["=" * left, title, "=" * right])

with LibGMT() as lib:
lines = [header]
for key in sorted(lib.info):
lines.append("{}: {}".format(key, lib.info[key]))
from .clib import Session

lines = ["Loaded libgmt:"]
with Session() as ses:
for key in sorted(ses.info):
lines.append(" {}: {}".format(key, ses.info[key]))
print("\n".join(lines))


Expand Down Expand Up @@ -83,7 +76,7 @@ def test(doctest=True, verbose=True, coverage=False, figures=True):
"""
import pytest

print_libgmt_info()
print_clib_info()

args = []
if verbose:
Expand Down
18 changes: 9 additions & 9 deletions gmt/base_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Base class with plot generating commands.
Does not define any special non-GMT methods (savefig, show, etc).
"""
from .clib import LibGMT
from .clib import Session
from .exceptions import GMTInvalidInput
from .helpers import (
build_arg_string,
Expand Down Expand Up @@ -122,7 +122,7 @@ def coast(self, **kwargs):

"""
kwargs = self._preprocess(**kwargs)
with LibGMT() as lib:
with Session() as lib:
lib.call_module("coast", build_arg_string(kwargs))

@fmt_docstring
Expand All @@ -146,11 +146,11 @@ def grdimage(self, grid, **kwargs):
"""
kwargs = self._preprocess(**kwargs)
kind = data_kind(grid, None, None)
with LibGMT() as lib:
with Session() as lib:
if kind == "file":
file_context = dummy_context(grid)
elif kind == "grid":
file_context = lib.grid_to_vfile(grid)
file_context = lib.virtualfile_from_grid(grid)
else:
raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid)))
with file_context as fname:
Expand Down Expand Up @@ -257,14 +257,14 @@ def plot(self, x=None, y=None, data=None, sizes=None, direction=None, **kwargs):
)
extra_arrays.append(sizes)

with LibGMT() as lib:
with Session() as lib:
# Choose how data will be passed in to the module
if kind == "file":
file_context = dummy_context(data)
elif kind == "matrix":
file_context = lib.matrix_to_vfile(data)
file_context = lib.virtualfile_from_matrix(data)
elif kind == "vectors":
file_context = lib.vectors_to_vfile(x, y, *extra_arrays)
file_context = lib.virtualfile_from_vectors(x, y, *extra_arrays)

with file_context as fname:
arg_str = " ".join([fname, build_arg_string(kwargs)])
Expand Down Expand Up @@ -316,7 +316,7 @@ def basemap(self, **kwargs):
raise GMTInvalidInput("At least one of B, L, or T must be specified.")
if "D" in kwargs and "F" not in kwargs:
raise GMTInvalidInput("Option D requires F to be specified as well.")
with LibGMT() as lib:
with Session() as lib:
lib.call_module("basemap", build_arg_string(kwargs))

@fmt_docstring
Expand Down Expand Up @@ -351,5 +351,5 @@ def logo(self, **kwargs):
kwargs = self._preprocess(**kwargs)
if "D" not in kwargs:
raise GMTInvalidInput("Option D must be specified.")
with LibGMT() as lib:
with Session() as lib:
lib.call_module("logo", build_arg_string(kwargs))
4 changes: 2 additions & 2 deletions gmt/clib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
Low-level wrapper for the GMT C API.

The :class:`gmt.clib.LibGMT` class wraps the GMT C shared library (``libgmt``)
The :class:`gmt.clib.Session` class wraps the GMT C shared library (``libgmt``)
with a pythonic interface.
Access to the C library is done through :py:mod:`ctypes`.

"""
from .core import LibGMT
from .session import Session
Loading