Skip to content

Commit 81a30a8

Browse files
authored
Refactor the gmt.clib package (#210)
A much needed refactor of the `gmt.clib` package. * Renames `LibGMT` to `Session` because that is what it actually is (the GMT API session). * Renames `clib.core` to `clib.session` and break up `clib.utils` into `clib.loading` (functions for loading libgmt) and `clib.conversion` (functions that handle the ctypes conversions from numpy, etc). * Renames the virtual file creating methods from `*_to_vfile` to `virtualfile_from_*` * The `create` and `destroy` methods now set the session pointer (`Session.session_pointer`), which simplifies the `__enter__` and `__exit__` methods. * `Session.get_constant` is now implemented as `Session.__getitem__` so getting a constant is now much shorter: `self["GMT_IS_MATRIX"]`. * The constant values are stored as module variables instead of in the class. * Re-enables intersphinx.
1 parent 757670d commit 81a30a8

15 files changed

+840
-795
lines changed

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
TESTDIR=tmp-test-dir-with-unique-name
44
PYTEST_ARGS=--doctest-modules -v --pyargs
55
PYTEST_COV_ARGS=--cov-config=../.coveragerc --cov-report=term-missing
6-
CHECK_FILES=gmt setup.py
6+
FORMAT_FILES=gmt setup.py doc/conf.py
7+
LINT_FILES=gmt setup.py
78

89
help:
910
@echo "Commands:"
@@ -28,18 +29,19 @@ test:
2829
coverage:
2930
# Run a tmp folder to make sure the tests are run on the installed version
3031
mkdir -p $(TESTDIR)
31-
cd $(TESTDIR); python -c "import gmt; gmt.print_libgmt_info()"
32+
@echo ""
33+
@cd $(TESTDIR); python -c "import gmt; gmt.print_clib_info()"
3234
@echo ""
3335
cd $(TESTDIR); pytest $(PYTEST_COV_ARGS) --cov=gmt $(PYTEST_ARGS) gmt
3436
cp $(TESTDIR)/.coverage* .
3537
rm -r $(TESTDIR)
3638

3739
format:
38-
black $(CHECK_FILES)
40+
black $(FORMAT_FILES)
3941

4042
check:
41-
black --check $(CHECK_FILES)
42-
pylint $(CHECK_FILES)
43+
black --check $(FORMAT_FILES)
44+
pylint $(LINT_FILES)
4345

4446
clean:
4547
find . -name "*.pyc" -exec rm -v {} \;

doc/api/index.rst

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Miscellaneous
6262

6363
which
6464
test
65-
print_libgmt_info
65+
print_clib_info
6666

6767

6868
Datasets
@@ -100,37 +100,50 @@ All custom exceptions are derived from :class:`gmt.exceptions.GMTError`.
100100
GMT C API
101101
---------
102102

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

107106
.. autosummary::
108107
:toctree: generated
109108

110-
clib.LibGMT
109+
clib.Session
111110

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

114114
.. autosummary::
115115
:toctree: generated
116116

117-
clib.LibGMT.call_module
118-
clib.LibGMT.grid_to_vfile
119-
clib.LibGMT.vectors_to_vfile
120-
clib.LibGMT.matrix_to_vfile
121-
clib.LibGMT.extract_region
117+
clib.Session.call_module
118+
119+
Passing memory blocks between Python variables (:class:`numpy.ndarray`,
120+
:class:`pandas.Series`, and :class:`xarray.DataArray`) and GMT happens through *virtual
121+
files*. These methods are context managers that automate the conversion of Python
122+
variables to GMT virtual files:
123+
124+
.. autosummary::
125+
:toctree: generated
126+
127+
clib.Session.virtualfile_from_matrix
128+
clib.Session.virtualfile_from_vectors
129+
clib.Session.virtualfile_from_grid
130+
122131

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

125134
.. autosummary::
126135
:toctree: generated
127136

128-
clib.LibGMT.create_session
129-
clib.LibGMT.destroy_session
130-
clib.LibGMT.get_constant
131-
clib.LibGMT.get_default
132-
clib.LibGMT.create_data
133-
clib.LibGMT.open_virtual_file
134-
clib.LibGMT.put_matrix
135-
clib.LibGMT.put_vector
136-
clib.LibGMT.write_data
137+
clib.Session.create
138+
clib.Session.destroy
139+
clib.Session.__getitem__
140+
clib.Session.__enter__
141+
clib.Session.__exit__
142+
clib.Session.get_default
143+
clib.Session.create_data
144+
clib.Session.put_matrix
145+
clib.Session.put_vector
146+
clib.Session.write_data
147+
clib.Session.open_virtual_file
148+
clib.Session.extract_region
149+
clib.Session.get_libgmt_func

doc/conf.py

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,76 +11,101 @@
1111
from gmt import __version__, __commit__
1212

1313
extensions = [
14-
'sphinx.ext.autodoc',
15-
'sphinx.ext.autosummary',
16-
'sphinx.ext.coverage',
17-
'sphinx.ext.mathjax',
18-
'sphinx.ext.doctest',
19-
'sphinx.ext.viewcode',
20-
'sphinx.ext.extlinks',
21-
'numpydoc',
22-
'nbsphinx',
23-
'gmt.sphinxext.gmtplot',
14+
"sphinx.ext.autodoc",
15+
"sphinx.ext.autosummary",
16+
"sphinx.ext.coverage",
17+
"sphinx.ext.mathjax",
18+
"sphinx.ext.doctest",
19+
"sphinx.ext.viewcode",
20+
"sphinx.ext.extlinks",
21+
"sphinx.ext.intersphinx",
22+
"numpydoc",
23+
"nbsphinx",
24+
"gmt.sphinxext.gmtplot",
2425
]
2526

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

2930
numpydoc_class_members_toctree = False
3031

32+
# intersphinx configuration
33+
intersphinx_mapping = {
34+
"python": ("https://docs.python.org/3/", None),
35+
"numpy": ("https://docs.scipy.org/doc/numpy/", None),
36+
"pandas": ("http://pandas.pydata.org/pandas-docs/stable/", None),
37+
"xarray": ("http://xarray.pydata.org/en/stable/", None),
38+
}
39+
3140
# Sphinx project configuration
32-
templates_path = ['_templates']
33-
exclude_patterns = ['_build', '**.ipynb_checkpoints']
34-
source_suffix = '.rst'
41+
templates_path = ["_templates"]
42+
exclude_patterns = ["_build", "**.ipynb_checkpoints"]
43+
source_suffix = ".rst"
3544
# The encoding of source files.
36-
source_encoding = 'utf-8-sig'
37-
master_doc = 'index'
45+
source_encoding = "utf-8-sig"
46+
master_doc = "index"
3847

3948
# General information about the project
4049
year = datetime.date.today().year
41-
project = u'GMT/Python'
42-
copyright = u'2017-2018, Leonardo Uieda and Paul Wessel'
43-
if len(__version__.split('+')) > 1 or __version__ == 'unknown':
44-
version = 'dev'
50+
project = u"GMT/Python"
51+
copyright = u"2017-2018, Leonardo Uieda and Paul Wessel"
52+
if len(__version__.split("+")) > 1 or __version__ == "unknown":
53+
version = "dev"
4554
else:
4655
version = __version__
4756

4857
# These enable substitutions using |variable| in the rst files
4958
rst_epilog = """
5059
.. |year| replace:: {year}
51-
""".format(year=year)
60+
""".format(
61+
year=year
62+
)
5263

53-
html_last_updated_fmt = '%b %d, %Y'
54-
html_title = 'GMT/Python'
55-
html_short_title = 'GMT/Python'
56-
html_logo = '_static/gmt-python-logo.png'
57-
html_favicon = '_static/favicon.png'
58-
html_static_path = ['_static']
59-
html_extra_path = ['.nojekyll', 'CNAME']
60-
pygments_style = 'default'
64+
html_last_updated_fmt = "%b %d, %Y"
65+
html_title = "GMT/Python"
66+
html_short_title = "GMT/Python"
67+
html_logo = "_static/gmt-python-logo.png"
68+
html_favicon = "_static/favicon.png"
69+
html_static_path = ["_static"]
70+
html_extra_path = [".nojekyll", "CNAME"]
71+
pygments_style = "default"
6172
add_function_parentheses = False
6273
html_show_sourcelink = False
6374
html_show_sphinx = True
6475
html_show_copyright = True
6576

6677
# Theme config
6778
html_theme = "sphinx_rtd_theme"
68-
html_theme_options = {
69-
}
79+
html_theme_options = {}
7080
html_context = {
71-
'menu_links': [
72-
('<i class="fa fa-play fa-fw"></i> Try it online!', 'http://try.gmtpython.xyz'),
73-
('<i class="fa fa-github fa-fw"></i> Source Code', 'https://github.com/GenericMappingTools/gmt-python'),
74-
('<i class="fa fa-users fa-fw"></i> Contributing', 'https://github.com/GenericMappingTools/gmt-python/blob/master/CONTRIBUTING.md'),
75-
('<i class="fa fa-book fa-fw"></i> Code of Conduct', 'https://github.com/GenericMappingTools/gmt-python/blob/master/CODE_OF_CONDUCT.md'),
76-
('<i class="fa fa-gavel fa-fw"></i> License', 'https://github.com/GenericMappingTools/gmt-python/blob/master/LICENSE.txt'),
77-
('<i class="fa fa-comment fa-fw"></i> Contact', 'https://gitter.im/GenericMappingTools/gmt-python'),
81+
"menu_links": [
82+
('<i class="fa fa-play fa-fw"></i> Try it online!', "http://try.gmtpython.xyz"),
83+
(
84+
'<i class="fa fa-github fa-fw"></i> Source Code',
85+
"https://github.com/GenericMappingTools/gmt-python",
86+
),
87+
(
88+
'<i class="fa fa-users fa-fw"></i> Contributing',
89+
"https://github.com/GenericMappingTools/gmt-python/blob/master/CONTRIBUTING.md",
90+
),
91+
(
92+
'<i class="fa fa-book fa-fw"></i> Code of Conduct',
93+
"https://github.com/GenericMappingTools/gmt-python/blob/master/CODE_OF_CONDUCT.md",
94+
),
95+
(
96+
'<i class="fa fa-gavel fa-fw"></i> License',
97+
"https://github.com/GenericMappingTools/gmt-python/blob/master/LICENSE.txt",
98+
),
99+
(
100+
'<i class="fa fa-comment fa-fw"></i> Contact',
101+
"https://gitter.im/GenericMappingTools/gmt-python",
102+
),
78103
],
79104
# Custom variables to enable "Improve this page"" and "Download notebook"
80105
# links
81-
'doc_path': 'doc',
82-
'github_repo': 'GenericMappingTools/gmt-python',
83-
'github_version': 'master',
106+
"doc_path": "doc",
107+
"github_repo": "GenericMappingTools/gmt-python",
108+
"github_version": "master",
84109
}
85110

86111
# Load the custom CSS files (needs sphinx >= 1.6 for this to work)

doc/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Project Goals
6666

6767
* Make GMT more accessible to new users.
6868
* Build a Pythonic API for GMT.
69-
* Interface with the GMT C API directly using :py:mod:`ctypes` (no system calls).
69+
* Interface with the GMT C API directly using :mod:`ctypes` (no system calls).
7070
* Support for rich display in the `Jupyter notebook <http://jupyter.org/>`__.
7171
* Integration with the Scipy stack: :class:`numpy.ndarray` or :class:`pandas.DataFrame`
7272
for data tables and :class:`xarray.DataArray` for grids.

gmt/__init__.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,19 @@
2727
_atexit.register(_end)
2828

2929

30-
def print_libgmt_info():
30+
def print_clib_info():
3131
"""
32-
Print information about the currently loaded GMT shared library.
32+
Print information about the GMT shared library that we can find.
3333
34-
Includes the GMT version, default values for parameters, the path to the
35-
``libgmt`` shared library, and GMT directories.
34+
Includes the GMT version, default values for parameters, the path to the ``libgmt``
35+
shared library, and GMT directories.
3636
"""
37-
import shutil
38-
from .clib import LibGMT
39-
40-
columns = shutil.get_terminal_size().columns
41-
title = "Currently loaded libgmt"
42-
left = (columns - len(title) - 2) // 2
43-
right = left + (columns - (2 * left + len(title) + 2))
44-
header = " ".join(["=" * left, title, "=" * right])
45-
46-
with LibGMT() as lib:
47-
lines = [header]
48-
for key in sorted(lib.info):
49-
lines.append("{}: {}".format(key, lib.info[key]))
37+
from .clib import Session
38+
39+
lines = ["Loaded libgmt:"]
40+
with Session() as ses:
41+
for key in sorted(ses.info):
42+
lines.append(" {}: {}".format(key, ses.info[key]))
5043
print("\n".join(lines))
5144

5245

@@ -83,7 +76,7 @@ def test(doctest=True, verbose=True, coverage=False, figures=True):
8376
"""
8477
import pytest
8578

86-
print_libgmt_info()
79+
print_clib_info()
8780

8881
args = []
8982
if verbose:

gmt/base_plotting.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Base class with plot generating commands.
33
Does not define any special non-GMT methods (savefig, show, etc).
44
"""
5-
from .clib import LibGMT
5+
from .clib import Session
66
from .exceptions import GMTInvalidInput
77
from .helpers import (
88
build_arg_string,
@@ -122,7 +122,7 @@ def coast(self, **kwargs):
122122
123123
"""
124124
kwargs = self._preprocess(**kwargs)
125-
with LibGMT() as lib:
125+
with Session() as lib:
126126
lib.call_module("coast", build_arg_string(kwargs))
127127

128128
@fmt_docstring
@@ -146,11 +146,11 @@ def grdimage(self, grid, **kwargs):
146146
"""
147147
kwargs = self._preprocess(**kwargs)
148148
kind = data_kind(grid, None, None)
149-
with LibGMT() as lib:
149+
with Session() as lib:
150150
if kind == "file":
151151
file_context = dummy_context(grid)
152152
elif kind == "grid":
153-
file_context = lib.grid_to_vfile(grid)
153+
file_context = lib.virtualfile_from_grid(grid)
154154
else:
155155
raise GMTInvalidInput("Unrecognized data type: {}".format(type(grid)))
156156
with file_context as fname:
@@ -257,14 +257,14 @@ def plot(self, x=None, y=None, data=None, sizes=None, direction=None, **kwargs):
257257
)
258258
extra_arrays.append(sizes)
259259

260-
with LibGMT() as lib:
260+
with Session() as lib:
261261
# Choose how data will be passed in to the module
262262
if kind == "file":
263263
file_context = dummy_context(data)
264264
elif kind == "matrix":
265-
file_context = lib.matrix_to_vfile(data)
265+
file_context = lib.virtualfile_from_matrix(data)
266266
elif kind == "vectors":
267-
file_context = lib.vectors_to_vfile(x, y, *extra_arrays)
267+
file_context = lib.virtualfile_from_vectors(x, y, *extra_arrays)
268268

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

322322
@fmt_docstring
@@ -351,5 +351,5 @@ def logo(self, **kwargs):
351351
kwargs = self._preprocess(**kwargs)
352352
if "D" not in kwargs:
353353
raise GMTInvalidInput("Option D must be specified.")
354-
with LibGMT() as lib:
354+
with Session() as lib:
355355
lib.call_module("logo", build_arg_string(kwargs))

gmt/clib/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""
22
Low-level wrapper for the GMT C API.
33
4-
The :class:`gmt.clib.LibGMT` class wraps the GMT C shared library (``libgmt``)
4+
The :class:`gmt.clib.Session` class wraps the GMT C shared library (``libgmt``)
55
with a pythonic interface.
66
Access to the C library is done through :py:mod:`ctypes`.
77
88
"""
9-
from .core import LibGMT
9+
from .session import Session

0 commit comments

Comments
 (0)