diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d302093b..5485d7b3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,7 +31,7 @@ jobs: run: | pip install build python -m build . - pip install -r docs/requirements.txt + pip install .[doc] - name: Check documentation build run: sphinx-build -E -b html docs dist/docs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e88a6880..bf50388f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,12 +24,16 @@ jobs: # Support different GA Mac environmnets - python-version: "3.9" os: "macos-13" + numpy_ver: "latest" - python-version: "3.10" os: "macos-13" + numpy_ver: "latest" - python-version: "3.11" os: "macos-latest" + numpy_ver: "latest" - python-version: "3.12" os: "macos-latest" + numpy_ver: "latest" # NEP29 compliance settings - python-version: "3.10" numpy_ver: "1.25" diff --git a/.zenodo.json b/.zenodo.json index 15317e3f..49051aa2 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,28 +1,28 @@ { "license": { "id": "MIT" - }, + }, "notes": "When referencing this package, please cite both the package DOI and the Apex Coordinates journal article: Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.", "references": [ "Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.", "Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and geoelectricity, 47(2), 191–212, doi:10.5636/jgg.47.191." - ], + ], "keywords": [ "Magnetic Apex Coordinates", "Quasi Dipole Coordinates", - "MLT", - "Magnetic Local Time", - "Conversion", - "Coordinate Conversion", - "Converting", - "Ionosphere", - "Magnetic Field", - "Space Physics", + "MLT", + "Magnetic Local Time", + "Conversion", + "Coordinate Conversion", + "Converting", + "Ionosphere", + "Magnetic Field", + "Space Physics", "Heliophysics" - ], + ], "creators": [ { - "orcid": "0000-0002-8043-0953", + "orcid": "0000-0002-8043-0953", "name": "van der Meeren, Christer" }, { @@ -30,8 +30,8 @@ "name": "Laundal, Karl M." }, { - "orcid": "0000-0001-8875-9326", - "affiliation": "Naval Research Laboratory", + "orcid": "0000-0001-8875-9326", + "affiliation": "Naval Research Laboratory", "name": "Burrell, Angeline G." }, { @@ -55,6 +55,11 @@ "orcid": "0000-0001-9741-4063", "affiliation": "GFZ German Research Centre for Geosciences", "name": "Michaelis, Ingo" + }, + { + "orcid": "0000-0001-8321-6074", + "affiliation": "Goddard Space Flight Center", + "name": "Klenzing, Jeff" } ] } diff --git a/AUTHORS.rst b/AUTHORS.rst index 97a80dea..37791a04 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -12,10 +12,14 @@ This python wrapper is made by: * Ashton Reimer * Achim Morschhauser * Ingo Michaelis +* Jeff Klenzing Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified apex coordinates are defined by Richmond [1995] [2]_. The code uses -IGRF-12 with coefficients valid through 2020 [Thébault et al., 2015] [3]_. +IGRF-14 with coefficients valid through 2030. A special issue on IGRF-14 is +currently accepting +`submissions `_. A reference +for IGRF-12 is [Thébault et al., 2015] [3]_. .. [1] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e79124ea..632a8e26 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,13 +2,18 @@ Changelog ========= -2.1.0 (2024-12-XX) +2.1.0 (2025-01-07) ------------------ * Adapted codebase to read IRGF coefficients from a file (updated to IGRF-14) -* Updated docs to reflect missing command-line executable * Updated package to be compliant and installable with numpy 2.0+ * Added tests for Python 3.12 and NEP29 * Fixed link to logo in the README +* Updated pyproject.toml to include most metadata instead of setup.cfg +* Added a citation section to the docs +* Fixed the command-line executable +* Updated code to address deprecation warnings around np.float64 use +* Updated code to remove use of datetime `utcnow` +* Updated meson build requirements to include ninja for Windows builds instead of python-dev-tools 2.0.2 (2024-11-12) ------------------ diff --git a/MANIFEST.in b/MANIFEST.in index ed533e15..d5274d60 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,7 +2,7 @@ graft docs graft fortranapex graft .github -recursive-include apexpy *.txt meson.build +recursive-include apexpy *.txt apexsh.dat meson.build include *.rst include *.md diff --git a/README.rst b/README.rst index e12808f5..47e59293 100644 --- a/README.rst +++ b/README.rst @@ -143,5 +143,5 @@ Badges .. |doi| image:: https://www.zenodo.org/badge/doi/10.5281/zenodo.4585641.svg :target: https://doi.org/10.5281/zenodo.1214206 -.. |logo| image:: https://github.com/aburrell/apexpy/blob/main/docs/apexpy.png +.. |logo| image:: https://github.com/aburrell/apexpy/blob/main/docs/apexpy.png?raw=true :alt: ApexPy logo: yellow magnetic field lines surrounding the Earth's surface, which is blue diff --git a/apexpy/__init__.py b/apexpy/__init__.py index 1895213f..942918db 100644 --- a/apexpy/__init__.py +++ b/apexpy/__init__.py @@ -1,3 +1,5 @@ +"""Conversion functions between geodetic and apex magnetic coordinates.""" +from importlib import metadata from sys import stderr # Below try..catch required for autodoc to work on readthedocs @@ -12,5 +14,9 @@ from apexpy import helpers # noqa F401 # Define the global variables -__version__ = "2.0.1" +try: + __version__ = metadata.version('apexpy') +except metadata.PackageNotFoundError: + # Windows installation is not finding the version automatically + __version__ = "2.1.0" __all__ = ['Apex', 'fortranapex', 'helpers', 'ApexHeightError'] diff --git a/apexpy/__main__.py b/apexpy/__main__.py index 45b5e2e0..24f8a0d2 100644 --- a/apexpy/__main__.py +++ b/apexpy/__main__.py @@ -63,8 +63,12 @@ def main(): lats, lons = apex_obj.convert(arg_array[:, 0], arg_array[:, 1], args.source, args.dest, args.height, datetime=in_time) - # Save the output to a file - np.savetxt(args.file_out, np.column_stack((lats, lons)), fmt='%.8f') + # Save the output to a file. Use the name for non-stdout inputs + if args.file_out.name.lower().find('stdout') >= 0: + fout_name = args.file_out + else: + fout_name = args.file_out.name + np.savetxt(fout_name, np.column_stack((lats, lons)), fmt='%.8f') return diff --git a/apexpy/apex.py b/apexpy/apex.py index 494acad7..37b3e362 100644 --- a/apexpy/apex.py +++ b/apexpy/apex.py @@ -101,8 +101,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): # If datafile is not specified, use the package default, otherwise # check that the provided file exists if datafile is None: - datafile = str(resources.path(__package__, - 'apexsh.dat').__enter__()) + datafile = os.path.join(resources.files(__package__), 'apexsh.dat') else: if not os.path.isfile(datafile): raise IOError('Data file does not exist: {}'.format(datafile)) @@ -120,8 +119,8 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self.fortranlib = fortranlib # Set the IGRF coefficient text file name - self.igrf_fn = str(resources.path(__package__, - 'igrf14coeffs.txt').__enter__()) + self.igrf_fn = os.path.join(resources.files(__package__), + 'igrf14coeffs.txt') # Update the Fortran epoch using the year defined above self.set_epoch(self.year) @@ -565,7 +564,10 @@ def geo2apex(self, glat, glon, height): alat[alat == -9999] = np.nan # If array is returned, dtype is object, so convert to float - return np.float64(alat), np.float64(alon) + alat = helpers.set_array_float(alat) + alon = helpers.set_array_float(alon) + + return alat, alon def apex2geo(self, alat, alon, height, precision=1e-10): """Converts modified apex to geodetic coordinates. @@ -634,7 +636,10 @@ def geo2qd(self, glat, glon, height): qlat, qlon = self._geo2qd(glat, glon, height) # If array is returned, dtype is object, so convert to float - return np.float64(qlat), np.float64(qlon) + qlat = helpers.set_array_float(qlat) + qlon = helpers.set_array_float(qlon) + + return qlat, qlon def qd2geo(self, qlat, qlon, height, precision=1e-10): """Converts quasi-dipole to geodetic coordinates. @@ -674,7 +679,11 @@ def qd2geo(self, qlat, qlon, height, precision=1e-10): glat, glon, error = self._qd2geo(qlat, qlon, height, precision) # If array is returned, dtype is object, so convert to float - return np.float64(glat), np.float64(glon), np.float64(error) + glat = helpers.set_array_float(glat) + glon = helpers.set_array_float(glon) + error = helpers.set_array_float(error) + + return glat, glon, error def apex2qd(self, alat, alon, height): """Converts modified apex to quasi-dipole coordinates. @@ -706,7 +715,10 @@ def apex2qd(self, alat, alon, height): qlat, qlon = self._apex2qd(alat, alon, height) # If array is returned, the dtype is object, so convert to float - return np.float64(qlat), np.float64(qlon) + qlat = helpers.set_array_float(qlat) + qlon = helpers.set_array_float(qlon) + + return qlat, qlon def qd2apex(self, qlat, qlon, height): """Converts quasi-dipole to modified apex coordinates. @@ -737,7 +749,10 @@ def qd2apex(self, qlat, qlon, height): alat, alon = self._qd2apex(qlat, qlon, height) # If array is returned, the dtype is object, so convert to float - return np.float64(alat), np.float64(alon) + alat = helpers.set_array_float(alat) + alon = helpers.set_array_float(alon) + + return alat, alon def mlon2mlt(self, mlon, dtime, ssheight=318550): """Computes the magnetic local time at the specified magnetic longitude @@ -777,8 +792,11 @@ def mlon2mlt(self, mlon, dtime, ssheight=318550): _, ssalon = self.geo2apex(ssglat, ssglon, ssheight) # Calculate the magnetic local time (0-24 h range) from apex longitude. - # np.float64 will ensure lists are converted to arrays - mlt = (180 + np.float64(mlon) - ssalon) / 15 % 24 + # Ensure lists are converted to arrays + mlt = (180 + np.asarray(mlon) - ssalon) / 15 % 24 + + if mlt.shape == (): + mlt = np.float64(mlt) return mlt @@ -818,8 +836,11 @@ def mlt2mlon(self, mlt, dtime, ssheight=318550): _, ssalon = self.geo2apex(ssglat, ssglon, ssheight) # Calculate the magnetic longitude (0-360 h range) from MLT. - # np.float64 will ensure lists are converted to arrays - mlon = (15 * np.float64(mlt) - 180 + ssalon + 360) % 360 + # Ensure lists are converted to arrays + mlon = (15 * np.asarray(mlt) - 180 + ssalon + 360) % 360 + + if mlon.shape == (): + mlon = np.float64(mlon) return mlon @@ -1130,10 +1151,10 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): k_unit = np.array([0, 0, 1], dtype=np.float64).reshape((3, 1)) # Calculate the remaining quasi-dipole base vectors - g1 = ((self.RE + np.float64(height)) + g1 = ((self.RE + np.asarray(height)) / (self.RE + self.refh)) ** (3 / 2) * d1 / F_scalar g2 = -1.0 / (2.0 * F_scalar * np.tan(np.radians(qlat))) * ( - k_unit + ((self.RE + np.float64(height)) + k_unit + ((self.RE + np.asarray(height)) / (self.RE + self.refh)) * d2 / cos_mag_inc) g3 = k_unit * F_scalar f3 = np.cross(g1.T, g2.T).T @@ -1154,7 +1175,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): return out def get_apex(self, lat, height=None): - """ Calculate apex height + """Calculate the apex height along a field line. Parameters ---------- @@ -1264,7 +1285,9 @@ def get_babs(self, glat, glon, height): babs = self._get_babs(glat, glon, height) # If array is returned, the dtype is object, so convert to float - return np.float64(babs) + babs = helpers.set_array_float(babs) + + return babs def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): """Returns the magnetic field vectors in apex coordinates. diff --git a/apexpy/helpers.py b/apexpy/helpers.py index e67cac5e..ca60a2c8 100644 --- a/apexpy/helpers.py +++ b/apexpy/helpers.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - """This module contains helper functions used by :class:`~apexpy.Apex`.""" import datetime as dt @@ -7,6 +6,30 @@ import time +def set_array_float(in_val): + """Set array data type to float. + + Parameters + ---------- + in_val : any + Input value, only modified if it is a np.ndarray + + Returns + ------- + out_val : any + Output value, if `in_val` was an array, `out_val` will be an array of + type `np.float64`. + + """ + + if isinstance(in_val, np.ndarray): + out_val = in_val.astype(np.float64) + else: + out_val = in_val + + return out_val + + def checklat(lat, name='lat'): """Makes sure the latitude is inside [-90, 90], clipping close values (tolerance 1e-4). diff --git a/apexpy/tests/test_fortranapex.py b/apexpy/tests/test_fortranapex.py index 33fc97de..d3c503ca 100644 --- a/apexpy/tests/test_fortranapex.py +++ b/apexpy/tests/test_fortranapex.py @@ -14,6 +14,7 @@ from numpy.testing import assert_allclose from importlib import resources +import os import pytest import apexpy @@ -25,7 +26,7 @@ class TestFortranApex(object): def setup_method(self): """Initialize each test.""" - datafile = str(resources.path(apexpy, 'apexsh.dat').__enter__()) + datafile = os.path.join(resources.files(apexpy), 'apexsh.dat') fa.loadapxsh(datafile, 2000) # Set the inputs diff --git a/apexpy/tests/test_helpers.py b/apexpy/tests/test_helpers.py index b270810f..68ded850 100644 --- a/apexpy/tests/test_helpers.py +++ b/apexpy/tests/test_helpers.py @@ -61,13 +61,54 @@ def teardown_method(self): del self.in_shape, self.calc_val, self.test_val def eval_output(self, rtol=1e-7, atol=0.0): - """Evaluate the values and shape of the calculated and expected output. - """ + """Evaluate the values and shape of the calced and expected output.""" np.testing.assert_allclose(self.calc_val, self.test_val, rtol=rtol, atol=atol) assert np.asarray(self.calc_val).shape == self.in_shape return + @pytest.mark.parametrize('val', [90, np.nan, None, [20.0], True]) + def test_check_set_array_float_no_modify(self, val): + """Test `set_array_float` with inputs that won't be modified. + + Parameters + ---------- + val : any but np.array + Value without a 'dtype' attribute + + """ + self.calc_val = helpers.set_array_float(val) + + if val is None: + assert self.calc_val is None + elif np.isnan(val): + assert np.isnan(self.calc_val) + else: + assert self.calc_val == val + return + + @pytest.mark.parametrize('dtype', [int, float, bool, object]) + def test_check_set_array_float_success(self, dtype): + """Test `set_array_float` modifies array inputs. + + Parameters + ---------- + dtype : dtype + Data type to use when creating input array + + """ + self.in_shape = (2,) + self.calc_val = helpers.set_array_float( + np.ones(shape=self.in_shape, dtype=dtype)) + + # Test that the output dtype is as expected + assert self.calc_val.dtype == np.float64 + + # Ensure values are unity + self.test_val = np.ones(shape=self.in_shape, dtype=np.float64) + self.eval_output() + return + @pytest.mark.parametrize('lat', [90, 0, -90, np.nan]) def test_checklat_scalar(self, lat): """Test good latitude check with scalars. diff --git a/docs/api.rst b/docs/api.rst index cc867bc3..26cab95c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,5 +1,5 @@ -Reference -========= +Package Structure +================= The :py:class:`apexpy.Apex` class is used for all the main functionality (converting between coordinate systems, field line mapping, and calculating @@ -30,11 +30,9 @@ Command-line interface .. highlight:: none When you install this package you used to get a command called ``apexpy``, which -is an interface to the :py:meth:`~apexpy.Apex.convert` method. This interface is -currently missing (see Issue -`#113 `_). Until this issue is -addressed, you may follow the below examples by prefacing the command with -``python -m``, as shown below. +is an interface to the :py:meth:`~apexpy.Apex.convert` method. If this +interface is missing (may be caused by the installation path not being a part +of your path), you may use ``python -m apexpy`` instead. See the documentation for this method for a more thorough explanation of arguments and behaviour. @@ -43,7 +41,7 @@ You can get help on the command by running ``python -m apexpy -h``. .. code:: - $ python -m apexpy -h + $ apexpy -h usage: apexpy [-h] [--height HEIGHT] [--refh REFH] [-i FILE_IN] [-o FILE_OUT] SOURCE DEST DATE diff --git a/docs/authors.rst b/docs/authors.rst index e122f914..a887eee3 100644 --- a/docs/authors.rst +++ b/docs/authors.rst @@ -1 +1,3 @@ +.. _authors: + .. include:: ../AUTHORS.rst diff --git a/docs/citing.rst b/docs/citing.rst new file mode 100644 index 00000000..9c3aad4d --- /dev/null +++ b/docs/citing.rst @@ -0,0 +1,29 @@ +Citations +========= + +When referring to this software package, please be sure to include the package +and specify which version you used ``_. +Note that this DOI will always point to the latest version of the code. A list +of DOIs for all versions can be found at the Zenodo page above. The version can +by found by printing :py:attr:`apexpy.__version__`. We also recommend citing a +reference to the coordinate system you use. More relevant references can be +found in :ref:`authors`. + +Example for citation in BibTex for a generalized version: + +.. code:: + + @misc{apexpy, + author = {van der Meeren, Christer and + Laundal, Karl M. and + Burrell, Angeline G. and + Lamarche, Leslie and + Starr, Gregory and + Reimer, Ashton and + Morschhauser, Achim and + Michaelis, Ingo}, + title = {ApexPy vX.Y.Z}, + year = 2024, + doi = {10.5281/zenodo.1214206}, + url = {https://github.com/aburrell/apexpy} + } diff --git a/docs/conf.py b/docs/conf.py index 2994989d..7df53ae2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- - +"""Configuration for apexpy documentation.""" import json import os -import re +from pyproject_parser import PyProject extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -13,22 +13,18 @@ 'sphinx.ext.extlinks', 'autoapi.extension'] -# Define common elements +# General information about the project. +info = PyProject.load("../pyproject.toml") +# Define common elements source_suffix = '.rst' master_doc = 'index' project = 'ApexPy' -year = '2022' +year = '2024' zenodo = json.loads(open('../.zenodo.json').read()) author = ' and '.join([zcreator['name'] for zcreator in zenodo['creators']]) copyright = ', '.join([year, author]) - -# Get version number from __init__.py -regex = r"(?<=__version__..\s)\S+" -with open('../apexpy/__init__.py', 'r') as fin: - text = fin.read() -match = re.findall(regex, text) -version = release = match[0].strip("'") +version = release = info.project['version'].base_version # Configure autoapi autoapi_type = 'python' diff --git a/docs/index.rst b/docs/index.rst index 7357dcb7..f7733ea3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -12,6 +12,7 @@ Contents contributing maintenance authors + citing changelog Indices and tables diff --git a/docs/requirements.txt b/docs/requirements.txt index 470e42f3..7abacb21 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ +pyproject_parser sphinx>=1.3 sphinx-autoapi sphinx_rtd_theme diff --git a/meson.build b/meson.build index 97351f7b..0c616536 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'apexpy', 'c', # Note that the git commit hash cannot be added dynamically here - version: '2.0.2', + version: '2.1.0', license: 'MIT', meson_version: '>= 0.63.0', default_options: [ diff --git a/pyproject.toml b/pyproject.toml index 7ae99480..80eec3b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,12 +5,13 @@ requires = [ "meson-python>=0.12.0", "setuptools<60.0", # Do not increase, 60.0 enables vendored distutils "Cython>=0.29.21", - "python-dev-tools", + "ninja", "numpy" ] [project] name = "apexpy" +version = "2.1.0" license = {file = "LICENSE"} description = "A Python wrapper for Apex coordinates" maintainers = [ @@ -56,10 +57,9 @@ classifiers = [ "Operating System :: Unix", "Operating System :: MacOS", ] -dynamic = ['version'] -[tool.project.scripts] -apexpy = {reference = 'apexpy.__main__:main', type = 'console'} +[project.scripts] +apexpy = 'apexpy.__main__:main' [project.optional-dependencies] test = [ @@ -69,10 +69,31 @@ test = [ "pytest-cov", "pytest-xdist" ] -doc = ["sphinx>=1.3", "sphinx-rtd-theme"] +doc = [ + "pyproject_parser", + "sphinx>=1.3", + "sphinx-autoapi", + "sphinx-rtd-theme" +] [project.urls] source = "https://github.com/aburrell/apexpy" documentation = "https://apexpy.readthedocs.io/en/latest/" tracker = "https://github.com/aburrell/apexpy/issues" download = "https://github.com/aburrell/apexpy/releases" + +[tool.setuptools.package-data] +mypkg = [ + "apexsh.dat", + "igrf14coeffs.txt" +] + +[tool.coverage.run] +branch = true +relative_files = true +parallel = true +include = "*/site-packages/apexpy/*" + +[tool.coverage.report] +show_missing = true +precision = 2 diff --git a/setup.cfg b/setup.cfg index abe424c1..f9ef6760 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,68 +1,6 @@ [metadata] name = apexpy -version = 2.0.2 -license = MIT -description = "A Python wrapper for Apex coordinates" -long_description = file: README.rst, CHANGELOG.rst -long_description_content_type = text/x-rst -url = https://github.com/aburrell/apexpy -keywords = - apex - modified apex - quasi-dipole - quasi dipole - coordinates - magnetic coordinates - mlt - magnetic local time - conversion - converting -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: OSI Approved :: MIT License - Operating System :: Unix - Operating System :: POSIX - Operating System :: Microsoft :: Windows - Operating System :: MacOS :: MacOS X - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Programming Language :: Python :: Implementation :: CPython - Topic :: Scientific/Engineering :: Physics - Topic :: Utilities - -[options] -zip_safe = False -install_requires = numpy -include_package_data = True -include_entry_points = True - -[options.entry_points] -console_scripts = - apexpy = apexpy.__main__:main - -[options.package_data] -apexpy = - apexsh.dat - igrf14coeffs.txt - -[aliases] -release = register clean --all sdist - -[coverage:run] -branch = True -relative_files = True -parallel = True -include = */site-packages/apexpy/* - -[coverage:report] -show_missing = True -precision = 2 -omit = *migrations* +version = 2.1.0 [flake8] max-line-length = 80