diff --git a/.cirrus.yml b/.cirrus.yml
index babb679d..9c280254 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -22,8 +22,21 @@ env:
CFUNITS_CACHE_BUILD: "0"
# Base environment conda packages to be installed
MAMBA_CACHE_PACKAGES: "pip conda-lock"
- # Increment the build number to force new pip cache upload.
- PIP_CACHE_BUILD: "0"
+
+
+#
+# Linting
+#
+lint_task:
+ auto_cancellation: true
+ container:
+ image: python:latest
+ cpu: 2
+ memory: 4G
+ name: "${CIRRUS_OS}: linting"
+ lint_script:
+ - python -m pip install --retries 3 pre-commit
+ - pre-commit run --all-files
#
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 4f76e098..00000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-cf_units/_version.py export-subst
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..f08cf31b
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,37 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: "v4.0.1"
+ hooks:
+ # Prevent giant files from being committed.
+ - id: check-added-large-files
+ # Check whether files parse as valid Python.
+ - id: check-ast
+ # Check for file name conflicts on case-insensitive filesytems.
+ - id: check-case-conflict
+ # Check for files that contain merge conflict strings.
+ - id: check-merge-conflict
+ # Check for debugger imports and py37+ `breakpoint()` calls in Python source.
+ - id: debug-statements
+ # Don't commit to main branch.
+ - id: no-commit-to-branch
+- repo: https://github.com/psf/black
+ rev: "21.6b0"
+ hooks:
+ - id: black
+ # Force black to run on whole repo, using settings from pyproject.toml
+ pass_filenames: false
+ args: ["--config=./pyproject.toml", "--check", "."]
+- repo: https://github.com/PyCQA/flake8
+ rev: "3.9.2"
+ hooks:
+ # Run flake8.
+ - id: flake8
+ args: ["--config=./setup.cfg"]
+- repo: https://github.com/pycqa/isort
+ rev: "5.8.0"
+ hooks:
+ - id: isort
+ name: isort
+ args: ["--filter-files", "--check"]
diff --git a/CHANGES b/CHANGES
index 4599955c..0de1676e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
This file is no longer updated and is provided for historical purposes only.
-Please see docs/cf_units/src/whatsnew/ for a changelog.
+Please see the Git history for a record of changes.
Release 1.4 (14 June 2013)
diff --git a/INSTALL b/INSTALL
index 36856436..82b4b925 100644
--- a/INSTALL
+++ b/INSTALL
@@ -26,68 +26,38 @@ Installing from source
The latest cf-units source release is available from
https://github.com/SciTools/cf-units/releases.
-cf-units makes use of cftime, cython, numpy, setuptools and udunits2.
-These dependencies must be in place before you can successfully install
-cf-units. Once you have satisfied the requirements detailed below,
-extract the cf-units source package, change to the new directory, and enter::
- python setup.py install
+Build and runtime requirements
+==============================
+See the ``requirements/`` directory for the external packages you will need to
+have installed before installing and running cf-units.
-For non-standard locations, additional build lib & include paths
-can be provided as per-usual at build_ext phase::
+The recommended way to provision requirements is using Conda::
- python setup.py build_ext -I/path/to/include -L/path/to/lib
- python setup.py install
+ conda env create -f requirements/cf-units.yml
+All requirements except ``udunits2`` are also available individually via PyPI::
-Build and runtime requirements
-==============================
-These are external packages which you will need to have installed before
-installing and running cf-units.
+ pip install numpy
-Many of these packages are available in Linux package managers
+Many of the packages are also available in Linux package managers
such as aptitude and yum. For example, it may be possible to install
Numpy using::
apt-get install python-numpy
-If you are installing dependencies with a package manager on Linux,
-you may need to install the development packages (look for a "-dev"
-postfix) in addition to the core packages.
-
-python 2.7 or later (https://www.python.org/)
- cf-units is compatible with Python 2 and Python 3.
-
-cftime 1.0.0 or later (https://unidata.github.io/cftime/)
- Python package for decoding time units conforming to
- Climate and Forecasting (CF) netCDF conventions.
-cython (https://pypi.org/project/Cython/)
- Optimising static compiler for Python and the extended Cython
- programming language.
+Installing cf-units
+===================
+Once you have satisfied the requirements detailed above, extract the cf-units
+source package, change to the new directory, and enter::
-numpy 1.14 or later (https://pypi.org/project/numpy/)
- Python package for scientific computing including a powerful N-dimensional
- array object.
+ pip install .
-udunits2 2.2.26 or later (https://www.unidata.ucar.edu/downloads/udunits/index.jsp)
- C library for units of physical quantities.
-
-setuptools (https://pypi.org/project/setuptools/)
- Python package for installing/removing python packages.
-
-
-Optional
-''''''''
-These packages are required for the full cf-units test suite to run.
-
-pep8 1.4.6* (https://pypi.org/project/pep8/)
- Python package for software testing.
-
-pytest (https://pypi.org/project/pytest/)
- Python testing framework.
+For non-standard locations, additional build lib & include paths
+can be provided as per-usual at ``build_ext`` phase::
-* Those packages have been tested with a specific build.
+ pip install --global-option=build_ext --global-option="-I/path/to/include2 --global-option="-L/path/to/lib" .
Custom site configuration
diff --git a/MANIFEST.in b/MANIFEST.in
index 47741d99..dd50b8e4 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,3 +4,4 @@ recursive-include cf_units *.py *.pxd *.pyx *.c
include cf_units/etc/site.cfg.template
include CHANGES COPYING COPYING.LESSER INSTALL
exclude cf_units/etc/site.cfg
+graft requirements
diff --git a/README.md b/README.md
index 861f266e..126e1ce8 100644
--- a/README.md
+++ b/README.md
@@ -1,59 +1,40 @@
-
-
-
-Units of measure as defined by the Climate and Forecast (CF) metadata
-conventions.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+# [cf-units](https://scitools.org.uk/cf-units/docs/latest/)
+
+#### Units of measure as defined by the Climate and Forecast (CF) metadata conventions.
+
+[comment]: # (https://shields.io/ is a good source of these)
+[](https://cirrus-ci.com/github/SciTools/cf-units)
+[](https://codecov.io/gh/SciTools/cf-units)
+[](https://results.pre-commit.ci/latest/github/SciTools/cf-units/master)
+,
+[](https://anaconda.org/conda-forge/cf-units)
+[](https://pypi.org/project/cf-units/)
+[](https://github.com/SciTools/cf-units/releases)
+[](https://doi.org/10.5281/zenodo.3723086)
+,
+[](https://github.com/psf/black)
+[](https://github.com/PyCQA/flake8)
+[](https://pycqa.github.io/isort/)
+,
+[](COPYING)
+[](https://github.com/SciTools/cf-units/graphs/contributors)
+[](https://github.com/SciTools/cf-units/commits/master)
# Table of contents
-
-
-
+ $ markdownlint README.md)
- [Overview](#overview)
- [Example](#example)
- [Get in touch](#get-in-touch)
- [Credits, copyright and license](#credits-copyright-and-license)
-
-
## Overview
Units of measure as required by the Climate and Forecast (CF) metadata
@@ -81,7 +62,6 @@ Documentation can be found at .
[submit a GitHub issue](https://github.com/SciTools/cf-units/issues).
- Suggest features: see our [contributing guide](.github/CONTRIBUTING.md).
-
## Credits, copyright and license
cf-units is developed collaboratively under the SciTools umbrella.
diff --git a/cf_units/__init__.py b/cf_units/__init__.py
index 12089112..1da01f6d 100644
--- a/cf_units/__init__.py
+++ b/cf_units/__init__.py
@@ -14,49 +14,56 @@
"""
-from contextlib import contextmanager
import copy
import os.path
import sys
+from contextlib import contextmanager
import cftime
import numpy as np
-from . import config
-from .util import _OrderedHashable, approx_equal
-
from cf_units import _udunits2 as _ud
-from cf_units._udunits2 import (UT_ASCII, UT_ISO_8859_1, UT_LATIN1, UT_UTF8,
- UT_NAMES, UT_DEFINITION)
+from cf_units._udunits2 import (
+ UT_ASCII,
+ UT_DEFINITION,
+ UT_ISO_8859_1,
+ UT_LATIN1,
+ UT_NAMES,
+ UT_UTF8,
+)
+from . import config
from ._version import version as __version__ # noqa: F401
+from .util import _OrderedHashable, approx_equal
-__all__ = ['CALENDAR_STANDARD',
- 'CALENDAR_GREGORIAN',
- 'CALENDAR_PROLEPTIC_GREGORIAN',
- 'CALENDAR_NO_LEAP',
- 'CALENDAR_JULIAN',
- 'CALENDAR_ALL_LEAP',
- 'CALENDAR_365_DAY',
- 'CALENDAR_366_DAY',
- 'CALENDAR_360_DAY',
- 'CALENDARS',
- 'CALENDAR_ALIASES',
- 'UT_NAMES',
- 'UT_DEFINITION',
- 'UT_ASCII',
- 'FLOAT32',
- 'FLOAT64',
- 'is_time',
- 'is_vertical',
- 'Unit',
- 'date2num',
- 'decode_time',
- 'encode_clock',
- 'encode_date',
- 'encode_time',
- 'num2date',
- 'suppress_errors']
+__all__ = [
+ "CALENDAR_STANDARD",
+ "CALENDAR_GREGORIAN",
+ "CALENDAR_PROLEPTIC_GREGORIAN",
+ "CALENDAR_NO_LEAP",
+ "CALENDAR_JULIAN",
+ "CALENDAR_ALL_LEAP",
+ "CALENDAR_365_DAY",
+ "CALENDAR_366_DAY",
+ "CALENDAR_360_DAY",
+ "CALENDARS",
+ "CALENDAR_ALIASES",
+ "UT_NAMES",
+ "UT_DEFINITION",
+ "UT_ASCII",
+ "FLOAT32",
+ "FLOAT64",
+ "is_time",
+ "is_vertical",
+ "Unit",
+ "date2num",
+ "decode_time",
+ "encode_clock",
+ "encode_date",
+ "encode_time",
+ "num2date",
+ "suppress_errors",
+]
########################################################################
@@ -68,52 +75,66 @@
#
# default constants
#
-EPOCH = '1970-01-01 00:00:00'
-_UNKNOWN_UNIT_STRING = 'unknown'
-_UNKNOWN_UNIT_SYMBOL = '?'
-_UNKNOWN_UNIT = [_UNKNOWN_UNIT_STRING, _UNKNOWN_UNIT_SYMBOL, '???', '']
-_NO_UNIT_STRING = 'no_unit'
-_NO_UNIT_SYMBOL = '-'
-_NO_UNIT = [_NO_UNIT_STRING, _NO_UNIT_SYMBOL, 'no unit', 'no-unit', 'nounit']
-_UNIT_DIMENSIONLESS = '1'
-_OP_SINCE = ' since '
+EPOCH = "1970-01-01 00:00:00"
+_UNKNOWN_UNIT_STRING = "unknown"
+_UNKNOWN_UNIT_SYMBOL = "?"
+_UNKNOWN_UNIT = [_UNKNOWN_UNIT_STRING, _UNKNOWN_UNIT_SYMBOL, "???", ""]
+_NO_UNIT_STRING = "no_unit"
+_NO_UNIT_SYMBOL = "-"
+_NO_UNIT = [_NO_UNIT_STRING, _NO_UNIT_SYMBOL, "no unit", "no-unit", "nounit"]
+_UNIT_DIMENSIONLESS = "1"
+_OP_SINCE = " since "
_CATEGORY_UNKNOWN, _CATEGORY_NO_UNIT, _CATEGORY_UDUNIT = range(3)
#
# libudunits2 constants
#
-UT_FORMATS = [UT_ASCII, UT_ISO_8859_1, UT_LATIN1, UT_UTF8, UT_NAMES,
- UT_DEFINITION]
+UT_FORMATS = [
+ UT_ASCII,
+ UT_ISO_8859_1,
+ UT_LATIN1,
+ UT_UTF8,
+ UT_NAMES,
+ UT_DEFINITION,
+]
#
# cftime constants
#
-CALENDAR_STANDARD = 'standard'
-CALENDAR_GREGORIAN = 'gregorian'
-CALENDAR_PROLEPTIC_GREGORIAN = 'proleptic_gregorian'
-CALENDAR_NO_LEAP = 'noleap'
-CALENDAR_JULIAN = 'julian'
-CALENDAR_ALL_LEAP = 'all_leap'
-CALENDAR_365_DAY = '365_day'
-CALENDAR_366_DAY = '366_day'
-CALENDAR_360_DAY = '360_day'
+CALENDAR_STANDARD = "standard"
+CALENDAR_GREGORIAN = "gregorian"
+CALENDAR_PROLEPTIC_GREGORIAN = "proleptic_gregorian"
+CALENDAR_NO_LEAP = "noleap"
+CALENDAR_JULIAN = "julian"
+CALENDAR_ALL_LEAP = "all_leap"
+CALENDAR_365_DAY = "365_day"
+CALENDAR_366_DAY = "366_day"
+CALENDAR_360_DAY = "360_day"
#: The calendars recognised by cf_units.
#: These are accessible as strings, or as constants in the form
#: ``cf_units.CALENDAR_{ calendar_name.upper() }``. For example,
#: ``cf_units.CALENDAR_NO_LEAP`` and ``cf_units.CALENDAR_366_DAY``.
-CALENDARS = [CALENDAR_STANDARD, CALENDAR_GREGORIAN,
- CALENDAR_PROLEPTIC_GREGORIAN, CALENDAR_NO_LEAP, CALENDAR_JULIAN,
- CALENDAR_ALL_LEAP, CALENDAR_365_DAY, CALENDAR_366_DAY,
- CALENDAR_360_DAY]
+CALENDARS = [
+ CALENDAR_STANDARD,
+ CALENDAR_GREGORIAN,
+ CALENDAR_PROLEPTIC_GREGORIAN,
+ CALENDAR_NO_LEAP,
+ CALENDAR_JULIAN,
+ CALENDAR_ALL_LEAP,
+ CALENDAR_365_DAY,
+ CALENDAR_366_DAY,
+ CALENDAR_360_DAY,
+]
#: Where calendars have multiple names, we map the alias to the
#: definitive form.
CALENDAR_ALIASES = {
CALENDAR_STANDARD: CALENDAR_GREGORIAN,
CALENDAR_NO_LEAP: CALENDAR_365_DAY,
- CALENDAR_ALL_LEAP: CALENDAR_366_DAY}
+ CALENDAR_ALL_LEAP: CALENDAR_366_DAY,
+}
#
@@ -129,16 +150,16 @@
########################################################################
# Convenience dictionary for the Unit convert method.
-_cv_convert_scalar = {FLOAT32: _ud.convert_float,
- FLOAT64: _ud.convert_double}
-_cv_convert_array = {FLOAT32: _ud.convert_floats,
- FLOAT64: _ud.convert_doubles}
+_cv_convert_scalar = {FLOAT32: _ud.convert_float, FLOAT64: _ud.convert_double}
+_cv_convert_array = {FLOAT32: _ud.convert_floats, FLOAT64: _ud.convert_doubles}
# Map of ut_encodings to encoding strings
-_encoding_lookup = {UT_ASCII: 'ascii',
- UT_ISO_8859_1: 'iso_8859_1',
- UT_LATIN1: 'latin1',
- UT_UTF8: 'utf-8'}
+_encoding_lookup = {
+ UT_ASCII: "ascii",
+ UT_ISO_8859_1: "iso_8859_1",
+ UT_LATIN1: "latin1",
+ UT_UTF8: "utf-8",
+}
@contextmanager
@@ -166,16 +187,20 @@ def suppress_errors():
_ud_system = _ud.read_xml()
except _ud.UdunitsError:
_alt_xml_path = config.get_option(
- 'System', 'udunits2_xml_path',
- default=os.path.join(sys.prefix, 'share', 'udunits',
- 'udunits2.xml'))
+ "System",
+ "udunits2_xml_path",
+ default=os.path.join(
+ sys.prefix, "share", "udunits", "udunits2.xml"
+ ),
+ )
try:
_ud_system = _ud.read_xml(_alt_xml_path.encode())
except _ud.UdunitsError as e:
- error_msg = ': "%s"' % e.error_msg() if e.errnum else ''
+ error_msg = ': "%s"' % e.error_msg() if e.errnum else ""
raise OSError(
- '[%s] Failed to open UDUNITS-2 XML unit database%s'
- % (e.status_msg(), error_msg))
+ "[%s] Failed to open UDUNITS-2 XML unit database%s"
+ % (e.status_msg(), error_msg)
+ )
########################################################################
@@ -184,6 +209,7 @@ def suppress_errors():
#
########################################################################
+
def encode_time(year, month, day, hour, minute, second):
"""
Return date/clock time encoded as a double precision value.
@@ -382,9 +408,13 @@ def _discard_microsecond(date):
# Create date objects of the same type returned by cftime.num2date()
# (either datetime.datetime or cftime.datetime), discarding the
# microseconds
- dates = np.array([d and d.__class__(d.year, d.month, d.day,
- d.hour, d.minute, d.second)
- for d in dates])
+ dates = np.array(
+ [
+ d
+ and d.__class__(d.year, d.month, d.day, d.hour, d.minute, d.second)
+ for d in dates
+ ]
+ )
result = dates[0] if shape == () else dates.reshape(shape)
return result
@@ -456,11 +486,13 @@ def num2date(time_value, unit, calendar, only_use_cftime_datetimes=True):
unit_string = unit_string.replace("epoch", EPOCH)
unit_inst = Unit(unit_string, calendar=calendar)
return unit_inst.num2date(
- time_value, only_use_cftime_datetimes=only_use_cftime_datetimes)
+ time_value, only_use_cftime_datetimes=only_use_cftime_datetimes
+ )
-def _num2date_to_nearest_second(time_value, unit,
- only_use_cftime_datetimes=True):
+def _num2date_to_nearest_second(
+ time_value, unit, only_use_cftime_datetimes=True
+):
"""
Return datetime encoding of numeric time value with respect to the given
time reference units, with a resolution of 1 second.
@@ -493,12 +525,15 @@ def _num2date_to_nearest_second(time_value, unit,
# arbitrarily. It is probably not possible to replicate that behaviour with
# later versions, if one wished to do so for the sake of consistency.
cftime_unit = unit.cftime_unit
- time_units = cftime_unit.split(' ')[0]
- has_half_seconds = np.logical_and(time_units == 'seconds',
- time_values % 1. == 0.5)
+ time_units = cftime_unit.split(" ")[0]
+ has_half_seconds = np.logical_and(
+ time_units == "seconds", time_values % 1.0 == 0.5
+ )
num2date_kwargs = dict(
- units=cftime_unit, calendar=unit.calendar,
- only_use_cftime_datetimes=only_use_cftime_datetimes)
+ units=cftime_unit,
+ calendar=unit.calendar,
+ only_use_cftime_datetimes=only_use_cftime_datetimes,
+ )
dates = cftime.num2date(time_values, **num2date_kwargs)
try:
# We can assume all or none of the dates have a microsecond attribute
@@ -508,10 +543,11 @@ def _num2date_to_nearest_second(time_value, unit,
round_mask = np.logical_or(has_half_seconds, microseconds != 0)
ceil_mask = np.logical_or(has_half_seconds, microseconds >= 500000)
if time_values[ceil_mask].size > 0:
- useconds = Unit('second')
+ useconds = Unit("second")
second_frac = useconds.convert(0.75, time_units)
dates[ceil_mask] = cftime.num2date(
- time_values[ceil_mask] + second_frac, **num2date_kwargs)
+ time_values[ceil_mask] + second_frac, **num2date_kwargs
+ )
dates[round_mask] = _discard_microsecond(dates[round_mask])
result = dates[0] if shape == () else dates.reshape(shape)
return result
@@ -539,7 +575,7 @@ def as_unit(unit):
if result is None:
# Typically unit is a string, however we cater for other types of
# 'unit' (e.g. iris.unit.Unit).
- result = Unit(unit, calendar=getattr(unit, 'calendar', None))
+ result = Unit(unit, calendar=getattr(unit, "calendar", None))
if use_cache:
_CACHE[unit] = result
return result
@@ -601,10 +637,11 @@ def _ud_value_error(ud_err, message):
ud_msg = ud_err.error_msg()
if ud_msg:
- message = u'{}: {}'.format(message, ud_msg)
+ message = "{}: {}".format(message, ud_msg)
- message = u'[{status}] {message}'.format(
- status=ud_err.status_msg(), message=message)
+ message = "[{status}] {message}".format(
+ status=ud_err.status_msg(), message=message
+ )
return ValueError(message)
@@ -624,6 +661,7 @@ class Unit(_OrderedHashable):
This class also supports time and calendar defintion and manipulation.
"""
+
def _init_from_tuple(self, values):
# Implements the required interface for an _OrderedHashable.
# This will also ensure a Unit._init(*Unit.names) method exists.
@@ -646,28 +684,30 @@ def __lt__(self, other):
# Prevent attribute updates
def __setattr__(self, name, value):
- raise AttributeError('Instances of %s are immutable' %
- type(self).__name__)
+ raise AttributeError(
+ "Instances of %s are immutable" % type(self).__name__
+ )
def __delattr__(self, name):
- raise AttributeError('Instances of %s are immutable' %
- type(self).__name__)
+ raise AttributeError(
+ "Instances of %s are immutable" % type(self).__name__
+ )
# Declare the attribute names relevant to the ordered and hashable
# behaviour.
- _names = ('category', 'ut_unit', 'calendar', 'origin')
+ _names = ("category", "ut_unit", "calendar", "origin")
category = None
- 'Is this an unknown unit, a no-unit, or a UDUNITS-2 unit.'
+ "Is this an unknown unit, a no-unit, or a UDUNITS-2 unit."
ut_unit = None
- 'Reference to the quantity defining the UDUNITS-2 unit.'
+ "Reference to the quantity defining the UDUNITS-2 unit."
calendar = None
- 'Represents the unit calendar name, see cf_units.CALENDARS'
+ "Represents the unit calendar name, see cf_units.CALENDARS"
origin = None
- 'The original string used to create this unit.'
+ "The original string used to create this unit."
__slots__ = ()
@@ -728,12 +768,12 @@ def __init__(self, unit, calendar=None):
encoding = UT_UTF8
if unit is None:
- unit = ''
+ unit = ""
unit = str(unit).strip()
- if unit.lower().endswith(' utc'):
- unit = unit[:unit.lower().rfind(' utc')]
+ if unit.lower().endswith(" utc"):
+ unit = unit[: unit.lower().rfind(" utc")]
if unit.endswith(" since epoch"):
unit = unit.replace("epoch", EPOCH)
@@ -755,10 +795,11 @@ def __init__(self, unit, calendar=None):
category = _CATEGORY_UDUNIT
str_unit = unit
try:
- ut_unit = _ud.parse(_ud_system, unit.encode('utf8'), encoding)
+ ut_unit = _ud.parse(_ud_system, unit.encode("utf8"), encoding)
except _ud.UdunitsError as exception:
value_error = _ud_value_error(
- exception, u'Failed to parse unit "{}"'.format(str_unit))
+ exception, 'Failed to parse unit "{}"'.format(str_unit)
+ )
raise value_error from None
if _OP_SINCE in unit.lower():
if calendar is None:
@@ -768,18 +809,24 @@ def __init__(self, unit, calendar=None):
if calendar_ in CALENDAR_ALIASES:
calendar_ = CALENDAR_ALIASES[calendar_]
if calendar_ not in CALENDARS:
- msg = '{!r} is an unsupported calendar.'
+ msg = "{!r} is an unsupported calendar."
raise ValueError(msg.format(calendar))
else:
- msg = 'Expected string-like calendar argument, got {!r}.'
+ msg = "Expected string-like calendar argument, got {!r}."
raise TypeError(msg.format(type(calendar)))
# Call the OrderedHashable's init.
- self._init(category, ut_unit, calendar_, unit,)
+ self._init(
+ category,
+ ut_unit,
+ calendar_,
+ unit,
+ )
@classmethod
- def _new_from_existing_ut(cls, category, ut_unit,
- calendar=None, origin=None):
+ def _new_from_existing_ut(
+ cls, category, ut_unit, calendar=None, origin=None
+ ):
# Short-circuit __init__ if we know what we are doing and already
# have a UT handle.
unit = cls.__new__(cls)
@@ -798,14 +845,14 @@ def _new_from_existing_ut(cls, category, ut_unit,
def __getstate__(self):
# state capture method for Pickle.dump()
# - return the instance data needed to reconstruct a Unit value
- return {'unit_text': self.origin, 'calendar': self.calendar}
+ return {"unit_text": self.origin, "calendar": self.calendar}
def __setstate__(self, state):
# object reconstruction method for Pickle.load()
# intercept the Pickle.load() operation and call own __init__ again
# - this is to ensure a valid ut_unit attribute (as these
# handles aren't persistent)
- self.__init__(state['unit_text'], calendar=state['calendar'])
+ self.__init__(state["unit_text"], calendar=state["calendar"])
def __copy__(self):
return self
@@ -834,7 +881,7 @@ def is_time(self):
if self.is_unknown() or self.is_no_unit():
result = False
else:
- day = _ud.get_unit_by_name(_ud_system, b'day')
+ day = _ud.get_unit_by_name(_ud_system, b"day")
result = _ud.are_convertible(self.ut_unit, day)
return result
@@ -860,10 +907,10 @@ def is_vertical(self):
if self.is_unknown() or self.is_no_unit():
result = False
else:
- bar = _ud.get_unit_by_name(_ud_system, b'bar')
+ bar = _ud.get_unit_by_name(_ud_system, b"bar")
result = _ud.are_convertible(self.ut_unit, bar)
if not result:
- meter = _ud.get_unit_by_name(_ud_system, b'meter')
+ meter = _ud.get_unit_by_name(_ud_system, b"meter")
result = _ud.are_convertible(self.ut_unit, meter)
return result
@@ -913,10 +960,11 @@ def is_long_time_interval(self):
"""
result = False
- long_time_intervals = ['year', 'month']
+ long_time_intervals = ["year", "month"]
if self.is_time_reference():
- result = any(interval in self.origin
- for interval in long_time_intervals)
+ result = any(
+ interval in self.origin for interval in long_time_intervals
+ )
return result
def title(self, value):
@@ -941,9 +989,9 @@ def title(self, value):
"""
if self.is_time_reference():
dt = self.num2date(value)
- result = dt.strftime('%Y-%m-%d %H:%M:%S')
+ result = dt.strftime("%Y-%m-%d %H:%M:%S")
else:
- result = '%s %s' % (str(value), self)
+ result = "%s %s" % (str(value), self)
return result
@property
@@ -968,9 +1016,9 @@ def modulus(self):
"""
- if self == 'radians':
+ if self == "radians":
result = np.pi * 2
- elif self == 'degrees':
+ elif self == "degrees":
result = 360.0
else:
result = None
@@ -997,12 +1045,17 @@ def is_convertible(self, other):
"""
other = as_unit(other)
- if self.is_unknown() or self.is_no_unit() or other.is_unknown() or \
- other.is_no_unit():
+ if (
+ self.is_unknown()
+ or self.is_no_unit()
+ or other.is_unknown()
+ or other.is_no_unit()
+ ):
result = False
else:
- result = (self.calendar == other.calendar and
- _ud.are_convertible(self.ut_unit, other.ut_unit))
+ result = self.calendar == other.calendar and _ud.are_convertible(
+ self.ut_unit, other.ut_unit
+ )
return result
def is_dimensionless(self):
@@ -1023,8 +1076,9 @@ def is_dimensionless(self):
True
"""
- return (self.category == _CATEGORY_UDUNIT and
- bool(_ud.is_dimensionless(self.ut_unit)))
+ return self.category == _CATEGORY_UDUNIT and bool(
+ _ud.is_dimensionless(self.ut_unit)
+ )
def is_unknown(self):
"""
@@ -1118,8 +1172,9 @@ def format(self, option=None):
option = [option]
for i in option:
bitmask |= i
- encoding = bitmask & \
- (UT_ASCII | UT_ISO_8859_1 | UT_LATIN1 | UT_UTF8)
+ encoding = bitmask & (
+ UT_ASCII | UT_ISO_8859_1 | UT_LATIN1 | UT_UTF8
+ )
encoding_str = _encoding_lookup[encoding]
result = _ud.format(self.ut_unit, bitmask)
@@ -1225,13 +1280,15 @@ def offset_by_time(self, origin):
"""
if not isinstance(origin, (float, (int,))):
- raise TypeError('a numeric type for the origin argument is'
- ' required')
+ raise TypeError(
+ "a numeric type for the origin argument is" " required"
+ )
try:
ut_unit = _ud.offset_by_time(self.ut_unit, origin)
except _ud.UdunitsError as exception:
value_error = _ud_value_error(
- exception, 'Failed to offset {!r}'.format(self))
+ exception, "Failed to offset {!r}".format(self)
+ )
raise value_error from None
calendar = None
return Unit._new_from_existing_ut(_CATEGORY_UDUNIT, ut_unit, calendar)
@@ -1259,7 +1316,8 @@ def invert(self):
else:
ut_unit = _ud.invert(self.ut_unit)
result = Unit._new_from_existing_ut(
- _CATEGORY_UDUNIT, ut_unit, calendar=None)
+ _CATEGORY_UDUNIT, ut_unit, calendar=None
+ )
return result
def root(self, root):
@@ -1286,14 +1344,14 @@ def root(self, root):
"""
if round(root) != root:
- raise TypeError('An integer for the root argument is required')
+ raise TypeError("An integer for the root argument is required")
if self.is_unknown():
result = self
elif self.is_no_unit():
raise ValueError("Cannot take the root of a 'no-unit'.")
else:
# only update the unit if it is not scalar
- if self == Unit('1'):
+ if self == Unit("1"):
result = self
else:
try:
@@ -1301,11 +1359,13 @@ def root(self, root):
except _ud.UdunitsError as exception:
value_error = _ud_value_error(
exception,
- 'Failed to take the root of {!r}'.format(self))
+ "Failed to take the root of {!r}".format(self),
+ )
raise value_error from None
calendar = None
result = Unit._new_from_existing_ut(
- _CATEGORY_UDUNIT, ut_unit, calendar)
+ _CATEGORY_UDUNIT, ut_unit, calendar
+ )
return result
def log(self, base):
@@ -1336,17 +1396,20 @@ def log(self, base):
try:
ut_unit = _ud.log(base, self.ut_unit)
except TypeError:
- raise TypeError('A numeric type for the base argument is '
- ' required')
+ raise TypeError(
+ "A numeric type for the base argument is " " required"
+ )
except _ud.UdunitsError as exception:
value_err = _ud_value_error(
exception,
- 'Failed to calculate logorithmic base '
- 'of {!r}'.format(self))
+ "Failed to calculate logorithmic base "
+ "of {!r}".format(self),
+ )
raise value_err from None
calendar = None
result = Unit._new_from_existing_ut(
- _CATEGORY_UDUNIT, ut_unit, calendar)
+ _CATEGORY_UDUNIT, ut_unit, calendar
+ )
return result
def __str__(self):
@@ -1385,7 +1448,8 @@ def __repr__(self):
result = "{}('{}')".format(self.__class__.__name__, self)
else:
result = "{}('{}', calendar='{}')".format(
- self.__class__.__name__, self, self.calendar)
+ self.__class__.__name__, self, self.calendar
+ )
return result
def _offset_common(self, offset):
@@ -1400,7 +1464,8 @@ def _offset_common(self, offset):
result = NotImplemented
else:
result = Unit._new_from_existing_ut(
- _CATEGORY_UDUNIT, ut_unit, calendar=None)
+ _CATEGORY_UDUNIT, ut_unit, calendar=None
+ )
return result
def __add__(self, other):
@@ -1434,11 +1499,13 @@ def _op_common(self, other, op_func):
except _ud.UdunitsError as exception:
value_err = _ud_value_error(
exception,
- 'Failed to {} {!r} by {!r}'.format(op_label, self, other))
+ "Failed to {} {!r} by {!r}".format(op_label, self, other),
+ )
raise value_err from None
calendar = None
result = Unit._new_from_existing_ut(
- _CATEGORY_UDUNIT, ut_unit, calendar)
+ _CATEGORY_UDUNIT, ut_unit, calendar
+ )
return result
def __rmul__(self, other):
@@ -1553,14 +1620,15 @@ def __pow__(self, power):
try:
power = float(power)
except ValueError:
- raise TypeError('A numeric value is required for the power'
- ' argument.')
+ raise TypeError(
+ "A numeric value is required for the power" " argument."
+ )
if self.is_unknown():
result = self
elif self.is_no_unit():
raise ValueError("Cannot raise the power of a 'no-unit'.")
- elif self == Unit('1'):
+ elif self == Unit("1"):
# 1 ** N -> 1
result = self
else:
@@ -1570,14 +1638,14 @@ def __pow__(self, power):
# root.
if not approx_equal(power, 0.0) and abs(power) < 1:
if not approx_equal(1 / power, round(1 / power)):
- raise ValueError('Cannot raise a unit by a decimal.')
+ raise ValueError("Cannot raise a unit by a decimal.")
root = int(round(1 / power))
result = self.root(root)
else:
# Failing that, check for powers which are (very nearly) simple
# integer values.
if not approx_equal(power, round(power)):
- msg = 'Cannot raise a unit by a decimal (got %s).' % power
+ msg = "Cannot raise a unit by a decimal (got %s)." % power
raise ValueError(msg)
power = int(round(power))
@@ -1586,7 +1654,8 @@ def __pow__(self, power):
except _ud.UdunitsError as exception:
value_err = _ud_value_error(
exception,
- 'Failed to raise the power of {!r}'.format(self))
+ "Failed to raise the power of {!r}".format(self),
+ )
raise value_err from None
result = Unit._new_from_existing_ut(_CATEGORY_UDUNIT, ut_unit)
return result
@@ -1717,26 +1786,31 @@ def convert(self, value, other, ctype=FLOAT64, inplace=False):
result = copy.deepcopy(value)
# Use cftime for converting reference times that are not using a
# gregorian calendar as it handles these and udunits does not.
- if self.is_time_reference() \
- and self.calendar != CALENDAR_GREGORIAN:
+ if (
+ self.is_time_reference()
+ and self.calendar != CALENDAR_GREGORIAN
+ ):
result_datetimes = cftime.num2date(
- result, self.cftime_unit, self.calendar)
+ result, self.cftime_unit, self.calendar
+ )
result = cftime.date2num(
- result_datetimes, other.cftime_unit, other.calendar)
- convert_type = (
- isinstance(value, np.ndarray)
- and np.issubsctype(value.dtype, np.floating)
+ result_datetimes, other.cftime_unit, other.calendar
)
+ convert_type = isinstance(
+ value, np.ndarray
+ ) and np.issubsctype(value.dtype, np.floating)
if convert_type:
result = result.astype(value.dtype)
else:
try:
- ut_converter = _ud.get_converter(self.ut_unit,
- other.ut_unit)
+ ut_converter = _ud.get_converter(
+ self.ut_unit, other.ut_unit
+ )
except _ud.UdunitsError as exception:
value_err = _ud_value_error(
exception,
- 'Failed to convert {!r} to {!r}'.format(self, other))
+ "Failed to convert {!r} to {!r}".format(self, other),
+ )
raise value_err from None
if isinstance(result, np.ndarray):
# Can only handle array of np.float32 or np.float64 so
@@ -1747,47 +1821,54 @@ def convert(self, value, other, ctype=FLOAT64, inplace=False):
# Convert arrays with explicit endianness to native
# endianness: udunits seems to be tripped up by arrays
# with endianness other than native.
- if result.dtype.byteorder != '=':
+ if result.dtype.byteorder != "=":
if inplace:
raise ValueError(
- 'Unable to convert non-native byte ordered '
- 'array in-place. Consider byte-swapping '
- 'first.')
+ "Unable to convert non-native byte ordered "
+ "array in-place. Consider byte-swapping "
+ "first."
+ )
else:
result = result.astype(result.dtype.type)
# Strict type check of numpy array.
if result.dtype.type not in (np.float32, np.float64):
raise TypeError(
- "Expect a numpy array of '%s' or '%s'" %
- np.float32, np.float64)
+ "Expect a numpy array of '%s' or '%s'"
+ % np.float32,
+ np.float64,
+ )
ctype = result.dtype.type
# Utilise global convenience dictionary
# _cv_convert_array to convert our array in 1d form
- result_tmp = result.ravel(order='A')
+ result_tmp = result.ravel(order="A")
# Do the actual conversion.
_cv_convert_array[ctype](
- ut_converter, result_tmp, result_tmp)
+ ut_converter, result_tmp, result_tmp
+ )
# If result_tmp was a copy, not a view (i.e. not C
# contiguous), copy the data back to the original.
if not np.shares_memory(result, result_tmp):
result_tmp = result_tmp.reshape(
- result.shape, order='A')
+ result.shape, order="A"
+ )
if isinstance(result, np.ma.MaskedArray):
result.data[...] = result_tmp
else:
result[...] = result_tmp
else:
if ctype not in _cv_convert_scalar:
- raise ValueError('Invalid target type. Can only '
- 'convert to float or double.')
+ raise ValueError(
+ "Invalid target type. Can only "
+ "convert to float or double."
+ )
# Utilise global convenience dictionary
# _cv_convert_scalar
- result = _cv_convert_scalar[ctype](ut_converter,
- result)
+ result = _cv_convert_scalar[ctype](ut_converter, result)
return result
else:
- raise ValueError("Unable to convert from '%r' to '%r'." %
- (self, other))
+ raise ValueError(
+ "Unable to convert from '%r' to '%r'." % (self, other)
+ )
@property
def cftime_unit(self):
@@ -1797,13 +1878,15 @@ def cftime_unit(self):
"""
if self.calendar is None:
- raise ValueError('Unit has undefined calendar')
+ raise ValueError("Unit has undefined calendar")
# `cftime` cannot parse long time intervals ("months" or "years").
if self.is_long_time_interval():
- interval = self.origin.split(' ')[0]
- emsg = ('Time units with interval of "months", "years" '
- '(or singular of these) cannot be processed, got "{!s}".')
+ interval = self.origin.split(" ")[0]
+ emsg = (
+ 'Time units with interval of "months", "years" '
+ '(or singular of these) cannot be processed, got "{!s}".'
+ )
raise ValueError(emsg.format(interval))
#
@@ -1899,5 +1982,7 @@ def num2date(self, time_value, only_use_cftime_datetimes=True):
"""
return _num2date_to_nearest_second(
- time_value, self,
- only_use_cftime_datetimes=only_use_cftime_datetimes)
+ time_value,
+ self,
+ only_use_cftime_datetimes=only_use_cftime_datetimes,
+ )
diff --git a/cf_units/_udunits2.pyx b/cf_units/_udunits2.pyx
index 14d38c27..7778b386 100644
--- a/cf_units/_udunits2.pyx
+++ b/cf_units/_udunits2.pyx
@@ -16,8 +16,8 @@ See also: `UDUNITS-2
# cython: nonecheck=True
import numpy as np
-cimport numpy as np
+cimport numpy as np
from libc cimport errno, string
diff --git a/cf_units/_udunits2_parser/__init__.py b/cf_units/_udunits2_parser/__init__.py
index ceedb743..482e3a66 100644
--- a/cf_units/_udunits2_parser/__init__.py
+++ b/cf_units/_udunits2_parser/__init__.py
@@ -6,25 +6,26 @@
import unicodedata
-from antlr4 import InputStream, CommonTokenStream
+from antlr4 import CommonTokenStream, InputStream
from antlr4.error.ErrorListener import ErrorListener
+from . import graph
from .parser.udunits2Lexer import udunits2Lexer
from .parser.udunits2Parser import udunits2Parser
from .parser.udunits2ParserVisitor import udunits2ParserVisitor
-from . import graph
-
# Dictionary mapping token rule id to token name.
-TOKEN_ID_NAMES = {getattr(udunits2Lexer, rule, None): rule
- for rule in udunits2Lexer.ruleNames}
+TOKEN_ID_NAMES = {
+ getattr(udunits2Lexer, rule, None): rule
+ for rule in udunits2Lexer.ruleNames
+}
def handle_UNICODE_EXPONENT(string):
# Convert unicode to compatibility form, replacing unicode minus with
# ascii minus (which is actually a less good version
# of unicode minus).
- normd = unicodedata.normalize('NFKC', string).replace('−', '-')
+ normd = unicodedata.normalize("NFKC", string).replace("−", "-")
return int(normd)
@@ -33,28 +34,29 @@ class UnitParseVisitor(udunits2ParserVisitor):
A visitor which converts the parse tree into an abstract expression graph.
"""
+
#: A dictionary mapping lexer TOKEN names to the action that should be
#: taken on them when visited. For full context of what is allowed, see
#: visitTerminal.
TERM_HANDLERS = {
- 'CLOSE_PAREN': None,
- 'DATE': str,
- 'DIVIDE': graph.Operand('/'), # Drop context, such as " per ".
- 'E_POWER': str,
- 'FLOAT': graph.Number, # Preserve precision as str.
- 'HOUR_MINUTE_SECOND': str,
- 'HOUR_MINUTE': str,
- 'ID': graph.Identifier,
- 'INT': lambda c: graph.Number(int(c)),
- 'MULTIPLY': graph.Operand('*'),
- 'OPEN_PAREN': None,
- 'PERIOD': str,
- 'RAISE': graph.Operand,
- 'TIMESTAMP': graph.Timestamp,
- 'SIGNED_INT': lambda c: graph.Number(int(c)),
- 'SHIFT_OP': None,
- 'WS': None,
- 'UNICODE_EXPONENT': handle_UNICODE_EXPONENT,
+ "CLOSE_PAREN": None,
+ "DATE": str,
+ "DIVIDE": graph.Operand("/"), # Drop context, such as " per ".
+ "E_POWER": str,
+ "FLOAT": graph.Number, # Preserve precision as str.
+ "HOUR_MINUTE_SECOND": str,
+ "HOUR_MINUTE": str,
+ "ID": graph.Identifier,
+ "INT": lambda c: graph.Number(int(c)),
+ "MULTIPLY": graph.Operand("*"),
+ "OPEN_PAREN": None,
+ "PERIOD": str,
+ "RAISE": graph.Operand,
+ "TIMESTAMP": graph.Timestamp,
+ "SIGNED_INT": lambda c: graph.Number(int(c)),
+ "SHIFT_OP": None,
+ "WS": None,
+ "UNICODE_EXPONENT": handle_UNICODE_EXPONENT,
}
def defaultResult(self):
@@ -116,7 +118,7 @@ def visitProduct(self, ctx):
# e.g. 1*2/3*4*5 = 1*(2/(3*(4*5)))
for node in nodes[:-1][::-1]:
if isinstance(node, graph.Operand):
- if node.content == '/':
+ if node.content == "/":
op_type = graph.Divide
else:
op_type = graph.Multiply
@@ -152,7 +154,7 @@ def visitShift_spec(self, ctx):
def visitUnit_spec(self, ctx):
node = self.visitChildren(ctx)
if not node:
- node = graph.Terminal('')
+ node = graph.Terminal("")
return node
@@ -161,13 +163,14 @@ class SyntaxErrorRaiser(ErrorListener):
Turn any parse errors into sensible SyntaxErrors.
"""
+
def __init__(self, unit_string):
self.unit_string = unit_string
super(ErrorListener, self).__init__()
def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e):
# https://stackoverflow.com/a/36367357/741316
- context = ("inline", line, column+2, "'{}'".format(self.unit_string))
+ context = ("inline", line, column + 2, "'{}'".format(self.unit_string))
syntax_error = SyntaxError(msg, context)
raise syntax_error from None
@@ -186,7 +189,7 @@ def _debug_tokens(unit_string):
parser.unit_spec()
for token in stream.tokens:
- if token.text == '':
+ if token.text == "":
continue
token_type_idx = token.type
rule = TOKEN_ID_NAMES[token_type_idx]
diff --git a/cf_units/_udunits2_parser/compile.py b/cf_units/_udunits2_parser/compile.py
index 3cea8c7a..cd2a6bc5 100644
--- a/cf_units/_udunits2_parser/compile.py
+++ b/cf_units/_udunits2_parser/compile.py
@@ -17,42 +17,43 @@
"""
import collections
-from pathlib import Path
import re
import subprocess
import urllib.request
+from pathlib import Path
try:
import jinja2
except ImportError:
- raise ImportError('Jinja2 needed to compile the grammar.')
+ raise ImportError("Jinja2 needed to compile the grammar.")
-JAR_NAME = 'antlr-4.7.2-complete.jar'
-JAR_URL = f'https://www.antlr.org/download/{JAR_NAME}'
+JAR_NAME = "antlr-4.7.2-complete.jar"
+JAR_URL = f"https://www.antlr.org/download/{JAR_NAME}"
HERE = Path(__file__).resolve().parent
JAR = HERE / JAR_NAME
-LEXER = HERE / 'parser' / 'udunits2Lexer.g4'
-PARSER = HERE / 'udunits2Parser.g4'
+LEXER = HERE / "parser" / "udunits2Lexer.g4"
+PARSER = HERE / "udunits2Parser.g4"
def expand_lexer(source, target):
- MODE_P = re.compile(r'mode ([A-Z_]+)\;')
- TOKEN_P = re.compile(r'([A-Z_]+) ?\:.*')
+ MODE_P = re.compile(r"mode ([A-Z_]+)\;")
+ TOKEN_P = re.compile(r"([A-Z_]+) ?\:.*")
- with open(source, 'r') as fh:
+ with open(source, "r") as fh:
content = fh.read()
- template = jinja2.Environment(
- loader=jinja2.BaseLoader).from_string(content)
+ template = jinja2.Environment(loader=jinja2.BaseLoader).from_string(
+ content
+ )
- current_mode = 'DEFAULT_MODE'
+ current_mode = "DEFAULT_MODE"
tokens = collections.defaultdict(list)
- for line in content.split('\n'):
+ for line in content.split("\n"):
mode_g = MODE_P.match(line)
if mode_g:
current_mode = mode_g.group(1)
@@ -62,33 +63,50 @@ def expand_lexer(source, target):
tokens[current_mode].append(token_g.group(1))
new_content = template.render(tokens=tokens)
- with open(target, 'w') as fh:
+ with open(target, "w") as fh:
fh.write(new_content)
def main():
if not JAR.exists():
- print(f'Downloading {JAR_NAME}...')
+ print(f"Downloading {JAR_NAME}...")
urllib.request.urlretrieve(JAR_URL, str(JAR))
- print('Expanding lexer...')
- expand_lexer(LEXER.parent.parent / (LEXER.name + '.jinja'), str(LEXER))
+ print("Expanding lexer...")
+ expand_lexer(LEXER.parent.parent / (LEXER.name + ".jinja"), str(LEXER))
- print('Compiling lexer...')
+ print("Compiling lexer...")
subprocess.run(
- ['java', '-jar', str(JAR), '-Dlanguage=Python3',
- str(LEXER), '-o', 'parser'],
- check=True)
-
- print('Compiling parser...')
+ [
+ "java",
+ "-jar",
+ str(JAR),
+ "-Dlanguage=Python3",
+ str(LEXER),
+ "-o",
+ "parser",
+ ],
+ check=True,
+ )
+
+ print("Compiling parser...")
subprocess.run(
- ['java', '-jar', str(JAR), '-Dlanguage=Python3',
- '-no-listener', '-visitor',
- str(PARSER), '-o', 'parser'],
- check=True)
-
- print('Done.')
-
-
-if __name__ == '__main__':
+ [
+ "java",
+ "-jar",
+ str(JAR),
+ "-Dlanguage=Python3",
+ "-no-listener",
+ "-visitor",
+ str(PARSER),
+ "-o",
+ "parser",
+ ],
+ check=True,
+ )
+
+ print("Done.")
+
+
+if __name__ == "__main__":
main()
diff --git a/cf_units/_udunits2_parser/graph.py b/cf_units/_udunits2_parser/graph.py
index e6b4c270..ba0bd34f 100644
--- a/cf_units/_udunits2_parser/graph.py
+++ b/cf_units/_udunits2_parser/graph.py
@@ -10,6 +10,7 @@ class Node:
Represents a node in an expression graph.
"""
+
def __init__(self, **kwargs):
self._attrs = kwargs
@@ -27,13 +28,13 @@ def __getattr__(self, name):
def _repr_ctx(self):
# Return a dictionary that is useful for passing to string.format.
- kwargs = ', '.join(
- '{}={!r}'.format(key, value)
- for key, value in self._attrs.items())
+ kwargs = ", ".join(
+ "{}={!r}".format(key, value) for key, value in self._attrs.items()
+ )
return dict(cls_name=self.__class__.__name__, kwargs=kwargs)
def __repr__(self):
- return '{cls_name}({kwargs})'.format(**self._repr_ctx())
+ return "{cls_name}({kwargs})".format(**self._repr_ctx())
class Terminal(Node):
@@ -41,6 +42,7 @@ class Terminal(Node):
A generic terminal node in an expression graph.
"""
+
def __init__(self, content):
super().__init__(content=content)
@@ -48,7 +50,7 @@ def children(self):
return []
def __str__(self):
- return '{}'.format(self.content)
+ return "{}".format(self.content)
class Operand(Terminal):
@@ -61,6 +63,7 @@ class Number(Terminal):
class Identifier(Terminal):
"""The unit itself (e.g. meters, m, km and π)"""
+
pass
@@ -71,17 +74,17 @@ def __init__(self, lhs, rhs):
class Raise(BinaryOp):
def __str__(self):
- return f'{self.lhs}^{self.rhs}'
+ return f"{self.lhs}^{self.rhs}"
class Multiply(BinaryOp):
def __str__(self):
- return f'{self.lhs}·{self.rhs}'
+ return f"{self.lhs}·{self.rhs}"
class Divide(BinaryOp):
def __str__(self):
- return f'{self.lhs}/{self.rhs}'
+ return f"{self.lhs}/{self.rhs}"
class Shift(Node):
@@ -90,7 +93,7 @@ def __init__(self, unit, shift_from):
super().__init__(unit=unit, shift_from=shift_from)
def __str__(self):
- return f'({self.unit} @ {self.shift_from})'
+ return f"({self.unit} @ {self.shift_from})"
class Timestamp(Terminal):
@@ -112,9 +115,10 @@ class Visitor:
of an expression graph.
"""
+
def visit(self, node):
"""Visit a node."""
- method = 'visit_' + node.__class__.__name__
+ method = "visit_" + node.__class__.__name__
visitor = getattr(self, method, self.generic_visit)
return visitor(node)
diff --git a/cf_units/_udunits2_parser/parser/udunits2Lexer.py b/cf_units/_udunits2_parser/parser/udunits2Lexer.py
index ce86bd13..308b1b1b 100644
--- a/cf_units/_udunits2_parser/parser/udunits2Lexer.py
+++ b/cf_units/_udunits2_parser/parser/udunits2Lexer.py
@@ -1,9 +1,9 @@
# Generated from /Users/pelson/dev/scitools/cf-units/cf_units/_udunits2_parser/parser/udunits2Lexer.g4 by ANTLR 4.7.2
-from antlr4 import *
+import sys
from io import StringIO
from typing.io import TextIO
-import sys
+from antlr4 import *
def serializedATN():
@@ -14,8 +14,8 @@ def serializedATN():
buf.write("\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22")
buf.write("\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27")
buf.write("\4\30\t\30\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35")
- buf.write("\t\35\4\36\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4")
- buf.write("$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t")
+ buf.write('\t\35\4\36\t\36\4\37\t\37\4 \t \4!\t!\4"\t"\4#\t#\4')
+ buf.write("$\t$\4%\t%\4&\t&\4'\t'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t")
buf.write(",\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63")
buf.write("\t\63\4\64\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\4")
buf.write("9\t9\4:\t:\4;\t;\4<\t<\4=\t=\4>\t>\4?\t?\4@\t@\4A\tA\4")
@@ -46,10 +46,10 @@ def serializedATN():
buf.write("\35\3\35\3\35\5\35\u0172\n\35\3\36\5\36\u0175\n\36\3\36")
buf.write("\3\36\3\36\3\36\3\36\3\36\3\36\5\36\u017e\n\36\3\37\3")
buf.write("\37\5\37\u0182\n\37\3\37\5\37\u0185\n\37\3\37\5\37\u0188")
- buf.write("\n\37\3 \3 \3 \3 \3 \3 \3!\3!\3!\3!\3\"\3\"\3#\3#\3#\3")
+ buf.write('\n\37\3 \3 \3 \3 \3 \3 \3!\3!\3!\3!\3"\3"\3#\3#\3#\3')
buf.write("#\3#\3#\5#\u019c\n#\3$\3$\3$\5$\u01a1\n$\5$\u01a3\n$\3")
buf.write("%\3%\3%\5%\u01a8\n%\5%\u01aa\n%\3%\3%\3%\3&\3&\3&\3&\3")
- buf.write("\'\3\'\3\'\3\'\3(\3(\3(\3(\3)\3)\3)\3)\3*\3*\3*\3*\3+")
+ buf.write("'\3'\3'\3'\3(\3(\3(\3(\3)\3)\3)\3)\3*\3*\3*\3*\3+")
buf.write("\3+\3+\3+\3,\3,\3,\3,\3-\3-\3-\3-\3.\3.\3.\3.\3/\3/\3")
buf.write("/\3/\3\60\3\60\3\60\3\60\3\61\3\61\3\61\3\61\3\62\3\62")
buf.write("\3\62\3\62\3\63\3\63\3\63\3\63\3\64\3\64\3\64\3\64\3\65")
@@ -62,7 +62,7 @@ def serializedATN():
buf.write("J\3J\3J\3K\3K\3K\3K\3K\3L\3L\3L\3L\3L\3M\3M\3M\3M\3M\3")
buf.write("N\3N\3N\3N\3N\3O\3O\3O\3O\3O\2\2P\5\3\7\4\t\2\13\5\r\6")
buf.write("\17\7\21\b\23\t\25\n\27\13\31\2\33\f\35\r\37\16!\17#\20")
- buf.write("%\21\'\22)\23+\24-\25/\26\61\27\63\2\65\2\67\29\2;\2=")
+ buf.write("%\21'\22)\23+\24-\25/\26\61\27\63\2\65\2\67\29\2;\2=")
buf.write("\2?\2A\30C\31E\32G\33I\2K\34M\35O\2Q\2S\2U\2W\2Y\2[\2")
buf.write("]\2_\2a\2c\2e\2g\2i\2k\2m\2o\2q\2s\2u\2w\2y\2{\2}\2\177")
buf.write("\2\u0081\2\u0083\2\u0085\2\u0087\2\u0089\2\u008b\2\u008d")
@@ -71,11 +71,11 @@ def serializedATN():
buf.write("g\6\2\u00b4\u00b5\u00bb\u00bb\u2072\u2072\u2076\u207d")
buf.write("\5\2C\\aac|\13\2\u0082\u0082\u00af\u00af\u00b2\u00b2\u00b7")
buf.write("\u00b7\u00c2\u00d8\u00da\u00f8\u00fa\u0101\u03ab\u03ab")
- buf.write("\u03c2\u03c2\3\2\"\"\2\u0292\2\5\3\2\2\2\2\7\3\2\2\2\2")
+ buf.write('\u03c2\u03c2\3\2""\2\u0292\2\5\3\2\2\2\2\7\3\2\2\2\2')
buf.write("\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23")
buf.write("\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\33\3\2\2\2\2\35\3")
buf.write("\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2")
- buf.write("\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2")
+ buf.write("'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2")
buf.write("\3\61\3\2\2\2\3A\3\2\2\2\3C\3\2\2\2\3E\3\2\2\2\3G\3\2")
buf.write("\2\2\3K\3\2\2\2\3M\3\2\2\2\3O\3\2\2\2\3Q\3\2\2\2\3S\3")
buf.write("\2\2\2\3U\3\2\2\2\3W\3\2\2\2\3Y\3\2\2\2\3[\3\2\2\2\3]")
@@ -92,7 +92,7 @@ def serializedATN():
buf.write("\2\2\21\u00ca\3\2\2\2\23\u00cc\3\2\2\2\25\u00ce\3\2\2")
buf.write("\2\27\u00d1\3\2\2\2\31\u00d7\3\2\2\2\33\u00d9\3\2\2\2")
buf.write("\35\u00ef\3\2\2\2\37\u0103\3\2\2\2!\u0108\3\2\2\2#\u010f")
- buf.write("\3\2\2\2%\u011a\3\2\2\2\'\u011c\3\2\2\2)\u0134\3\2\2\2")
+ buf.write("\3\2\2\2%\u011a\3\2\2\2'\u011c\3\2\2\2)\u0134\3\2\2\2")
buf.write("+\u013a\3\2\2\2-\u013c\3\2\2\2/\u013e\3\2\2\2\61\u0147")
buf.write("\3\2\2\2\63\u014b\3\2\2\2\65\u0156\3\2\2\2\67\u0159\3")
buf.write("\2\2\29\u0160\3\2\2\2;\u0171\3\2\2\2=\u017d\3\2\2\2?\u017f")
@@ -119,10 +119,10 @@ def serializedATN():
buf.write("\3\2\2\2\u00ae\f\3\2\2\2\u00af\u00b1\5-\26\2\u00b0\u00af")
buf.write("\3\2\2\2\u00b1\u00b4\3\2\2\2\u00b2\u00b0\3\2\2\2\u00b2")
buf.write("\u00b3\3\2\2\2\u00b3\u00c0\3\2\2\2\u00b4\u00b2\3\2\2\2")
- buf.write("\u00b5\u00c1\7\61\2\2\u00b6\u00b7\7\"\2\2\u00b7\u00b8")
+ buf.write('\u00b5\u00c1\7\61\2\2\u00b6\u00b7\7"\2\2\u00b7\u00b8')
buf.write("\7R\2\2\u00b8\u00b9\7G\2\2\u00b9\u00ba\7T\2\2\u00ba\u00c1")
- buf.write("\7\"\2\2\u00bb\u00bc\7\"\2\2\u00bc\u00bd\7r\2\2\u00bd")
- buf.write("\u00be\7g\2\2\u00be\u00bf\7t\2\2\u00bf\u00c1\7\"\2\2\u00c0")
+ buf.write('\7"\2\2\u00bb\u00bc\7"\2\2\u00bc\u00bd\7r\2\2\u00bd')
+ buf.write('\u00be\7g\2\2\u00be\u00bf\7t\2\2\u00bf\u00c1\7"\2\2\u00c0')
buf.write("\u00b5\3\2\2\2\u00c0\u00b6\3\2\2\2\u00c0\u00bb\3\2\2\2")
buf.write("\u00c1\u00c5\3\2\2\2\u00c2\u00c4\5-\26\2\u00c3\u00c2\3")
buf.write("\2\2\2\u00c4\u00c7\3\2\2\2\u00c5\u00c3\3\2\2\2\u00c5\u00c6")
@@ -155,7 +155,7 @@ def serializedATN():
buf.write("\2\2\2\u0104\u0105\3\2\2\2\u0105\u0106\b\17\2\2\u0106")
buf.write(" \3\2\2\2\u0107\u0109\t\4\2\2\u0108\u0107\3\2\2\2\u0109")
buf.write("\u010a\3\2\2\2\u010a\u0108\3\2\2\2\u010a\u010b\3\2\2\2")
- buf.write("\u010b\"\3\2\2\2\u010c\u0110\7`\2\2\u010d\u010e\7,\2\2")
+ buf.write('\u010b"\3\2\2\2\u010c\u0110\7`\2\2\u010d\u010e\7,\2\2')
buf.write("\u010e\u0110\7,\2\2\u010f\u010c\3\2\2\2\u010f\u010d\3")
buf.write("\2\2\2\u0110$\3\2\2\2\u0111\u0112\7n\2\2\u0112\u0113\7")
buf.write("q\2\2\u0113\u011b\7i\2\2\u0114\u0115\7n\2\2\u0115\u011b")
@@ -227,7 +227,7 @@ def serializedATN():
buf.write("\u01aa\u01ab\3\2\2\2\u01ab\u01ac\7V\2\2\u01ac\u01ad\5")
buf.write("I$\2\u01adL\3\2\2\2\u01ae\u01af\5G#\2\u01af\u01b0\7V\2")
buf.write("\2\u01b0\u01b1\5I$\2\u01b1N\3\2\2\2\u01b2\u01b3\5\5\2")
- buf.write("\2\u01b3\u01b4\3\2\2\2\u01b4\u01b5\b\'\4\2\u01b5P\3\2")
+ buf.write("\2\u01b3\u01b4\3\2\2\2\u01b4\u01b5\b'\4\2\u01b5P\3\2")
buf.write("\2\2\u01b6\u01b7\5\7\3\2\u01b7\u01b8\3\2\2\2\u01b8\u01b9")
buf.write("\b(\5\2\u01b9R\3\2\2\2\u01ba\u01bb\5\13\5\2\u01bb\u01bc")
buf.write("\3\2\2\2\u01bc\u01bd\b)\6\2\u01bdT\3\2\2\2\u01be\u01bf")
@@ -247,7 +247,7 @@ def serializedATN():
buf.write("\2\2\2\u01e4\u01e5\b\63\20\2\u01e5h\3\2\2\2\u01e6\u01e7")
buf.write("\5#\21\2\u01e7\u01e8\3\2\2\2\u01e8\u01e9\b\64\21\2\u01e9")
buf.write("j\3\2\2\2\u01ea\u01eb\5%\22\2\u01eb\u01ec\3\2\2\2\u01ec")
- buf.write("\u01ed\b\65\22\2\u01edl\3\2\2\2\u01ee\u01ef\5\'\23\2\u01ef")
+ buf.write("\u01ed\b\65\22\2\u01edl\3\2\2\2\u01ee\u01ef\5'\23\2\u01ef")
buf.write("\u01f0\3\2\2\2\u01f0\u01f1\b\66\23\2\u01f1n\3\2\2\2\u01f2")
buf.write("\u01f3\5)\24\2\u01f3\u01f4\3\2\2\2\u01f4\u01f5\b\67\24")
buf.write("\2\u01f5p\3\2\2\2\u01f6\u01f7\5+\25\2\u01f7\u01f8\3\2")
@@ -284,7 +284,7 @@ def serializedATN():
buf.write("\bI\21\2\u024b\u024c\bI\30\2\u024c\u0094\3\2\2\2\u024d")
buf.write("\u024e\5%\22\2\u024e\u024f\3\2\2\2\u024f\u0250\bJ\22\2")
buf.write("\u0250\u0251\bJ\30\2\u0251\u0096\3\2\2\2\u0252\u0253\5")
- buf.write("\'\23\2\u0253\u0254\3\2\2\2\u0254\u0255\bK\23\2\u0255")
+ buf.write("'\23\2\u0253\u0254\3\2\2\2\u0254\u0255\bK\23\2\u0255")
buf.write("\u0256\bK\30\2\u0256\u0098\3\2\2\2\u0257\u0258\5)\24\2")
buf.write("\u0258\u0259\3\2\2\2\u0259\u025a\bL\24\2\u025a\u025b\b")
buf.write("L\30\2\u025b\u009a\3\2\2\2\u025c\u025d\5+\25\2\u025d\u025e")
@@ -308,7 +308,7 @@ class udunits2Lexer(Lexer):
atn = ATNDeserializer().deserialize(serializedATN())
- decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
+ decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)]
SHIFT_MODE = 1
ID_SEEN = 2
@@ -341,49 +341,131 @@ class udunits2Lexer(Lexer):
TIMESTAMP = 26
DT_T_CLOCK = 27
- channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ]
+ channelNames = ["DEFAULT_TOKEN_CHANNEL", "HIDDEN"]
- modeNames = [ "DEFAULT_MODE", "SHIFT_MODE", "ID_SEEN" ]
+ modeNames = ["DEFAULT_MODE", "SHIFT_MODE", "ID_SEEN"]
- literalNames = [ "",
- "'+'", "'.'", "'('", "')'", "':'", "'-'" ]
+ literalNames = ["", "'+'", "'.'", "'('", "')'", "':'", "'-'"]
- symbolicNames = [ "",
- "SIGNED_INT", "PLUS", "MULTIPLY", "DIVIDE", "PERIOD", "OPEN_PAREN",
- "CLOSE_PAREN", "SEMICOLON", "INT", "E_POWER", "FLOAT", "SHIFT_OP",
- "UNICODE_EXPONENT", "RAISE", "LOG", "LOGREF", "ID", "LATIN_SUBSET",
- "WS", "ERRORCHARACTER", "TIMEZONE", "HOUR_MINUTE_SECOND", "HOUR_MINUTE",
- "M_MINUS", "DATE", "TIMESTAMP", "DT_T_CLOCK" ]
+ symbolicNames = [
+ "",
+ "SIGNED_INT",
+ "PLUS",
+ "MULTIPLY",
+ "DIVIDE",
+ "PERIOD",
+ "OPEN_PAREN",
+ "CLOSE_PAREN",
+ "SEMICOLON",
+ "INT",
+ "E_POWER",
+ "FLOAT",
+ "SHIFT_OP",
+ "UNICODE_EXPONENT",
+ "RAISE",
+ "LOG",
+ "LOGREF",
+ "ID",
+ "LATIN_SUBSET",
+ "WS",
+ "ERRORCHARACTER",
+ "TIMEZONE",
+ "HOUR_MINUTE_SECOND",
+ "HOUR_MINUTE",
+ "M_MINUS",
+ "DATE",
+ "TIMESTAMP",
+ "DT_T_CLOCK",
+ ]
- ruleNames = [ "SIGNED_INT", "PLUS", "MINUS", "MULTIPLY", "DIVIDE", "PERIOD",
- "OPEN_PAREN", "CLOSE_PAREN", "SEMICOLON", "INT", "ANY_INT",
- "E_POWER", "FLOAT", "SHIFT_OP", "UNICODE_EXPONENT", "RAISE",
- "LOG", "LOGREF", "ID", "LATIN_SUBSET", "WS", "ERRORCHARACTER",
- "TIMEZONE", "SIGN", "HOUR", "MINUTE", "SECOND", "MONTH",
- "DAY", "YEAR", "HOUR_MINUTE_SECOND", "HOUR_MINUTE", "M_MINUS",
- "DATE", "CLOCK", "TIMESTAMP", "DT_T_CLOCK", "SHIFT_MODE_SIGNED_INT",
- "SHIFT_MODE_PLUS", "SHIFT_MODE_MULTIPLY", "SHIFT_MODE_DIVIDE",
- "SHIFT_MODE_PERIOD", "SHIFT_MODE_OPEN_PAREN", "SHIFT_MODE_CLOSE_PAREN",
- "SHIFT_MODE_SEMICOLON", "SHIFT_MODE_INT", "SHIFT_MODE_E_POWER",
- "SHIFT_MODE_FLOAT", "SHIFT_MODE_SHIFT_OP", "SHIFT_MODE_UNICODE_EXPONENT",
- "SHIFT_MODE_RAISE", "SHIFT_MODE_LOG", "SHIFT_MODE_LOGREF",
- "SHIFT_MODE_ID", "SHIFT_MODE_LATIN_SUBSET", "SHIFT_MODE_WS",
- "SHIFT_MODE_ERRORCHARACTER", "ID_SEEN_SIGNED_INT", "EXTRA_MULTIPLY",
- "ID_SEEN_AUTO_SIGNED_INT", "ID_SEEN_AUTO_PLUS", "ID_SEEN_AUTO_MULTIPLY",
- "ID_SEEN_AUTO_DIVIDE", "ID_SEEN_AUTO_PERIOD", "ID_SEEN_AUTO_OPEN_PAREN",
- "ID_SEEN_AUTO_CLOSE_PAREN", "ID_SEEN_AUTO_SEMICOLON",
- "ID_SEEN_AUTO_INT", "ID_SEEN_AUTO_E_POWER", "ID_SEEN_AUTO_SHIFT_OP",
- "ID_SEEN_AUTO_UNICODE_EXPONENT", "ID_SEEN_AUTO_RAISE",
- "ID_SEEN_AUTO_LOG", "ID_SEEN_AUTO_LOGREF", "ID_SEEN_AUTO_ID",
- "ID_SEEN_AUTO_LATIN_SUBSET", "ID_SEEN_AUTO_WS", "ID_SEEN_AUTO_ERRORCHARACTER" ]
+ ruleNames = [
+ "SIGNED_INT",
+ "PLUS",
+ "MINUS",
+ "MULTIPLY",
+ "DIVIDE",
+ "PERIOD",
+ "OPEN_PAREN",
+ "CLOSE_PAREN",
+ "SEMICOLON",
+ "INT",
+ "ANY_INT",
+ "E_POWER",
+ "FLOAT",
+ "SHIFT_OP",
+ "UNICODE_EXPONENT",
+ "RAISE",
+ "LOG",
+ "LOGREF",
+ "ID",
+ "LATIN_SUBSET",
+ "WS",
+ "ERRORCHARACTER",
+ "TIMEZONE",
+ "SIGN",
+ "HOUR",
+ "MINUTE",
+ "SECOND",
+ "MONTH",
+ "DAY",
+ "YEAR",
+ "HOUR_MINUTE_SECOND",
+ "HOUR_MINUTE",
+ "M_MINUS",
+ "DATE",
+ "CLOCK",
+ "TIMESTAMP",
+ "DT_T_CLOCK",
+ "SHIFT_MODE_SIGNED_INT",
+ "SHIFT_MODE_PLUS",
+ "SHIFT_MODE_MULTIPLY",
+ "SHIFT_MODE_DIVIDE",
+ "SHIFT_MODE_PERIOD",
+ "SHIFT_MODE_OPEN_PAREN",
+ "SHIFT_MODE_CLOSE_PAREN",
+ "SHIFT_MODE_SEMICOLON",
+ "SHIFT_MODE_INT",
+ "SHIFT_MODE_E_POWER",
+ "SHIFT_MODE_FLOAT",
+ "SHIFT_MODE_SHIFT_OP",
+ "SHIFT_MODE_UNICODE_EXPONENT",
+ "SHIFT_MODE_RAISE",
+ "SHIFT_MODE_LOG",
+ "SHIFT_MODE_LOGREF",
+ "SHIFT_MODE_ID",
+ "SHIFT_MODE_LATIN_SUBSET",
+ "SHIFT_MODE_WS",
+ "SHIFT_MODE_ERRORCHARACTER",
+ "ID_SEEN_SIGNED_INT",
+ "EXTRA_MULTIPLY",
+ "ID_SEEN_AUTO_SIGNED_INT",
+ "ID_SEEN_AUTO_PLUS",
+ "ID_SEEN_AUTO_MULTIPLY",
+ "ID_SEEN_AUTO_DIVIDE",
+ "ID_SEEN_AUTO_PERIOD",
+ "ID_SEEN_AUTO_OPEN_PAREN",
+ "ID_SEEN_AUTO_CLOSE_PAREN",
+ "ID_SEEN_AUTO_SEMICOLON",
+ "ID_SEEN_AUTO_INT",
+ "ID_SEEN_AUTO_E_POWER",
+ "ID_SEEN_AUTO_SHIFT_OP",
+ "ID_SEEN_AUTO_UNICODE_EXPONENT",
+ "ID_SEEN_AUTO_RAISE",
+ "ID_SEEN_AUTO_LOG",
+ "ID_SEEN_AUTO_LOGREF",
+ "ID_SEEN_AUTO_ID",
+ "ID_SEEN_AUTO_LATIN_SUBSET",
+ "ID_SEEN_AUTO_WS",
+ "ID_SEEN_AUTO_ERRORCHARACTER",
+ ]
grammarFileName = "udunits2Lexer.g4"
- def __init__(self, input=None, output:TextIO = sys.stdout):
+ def __init__(self, input=None, output: TextIO = sys.stdout):
super().__init__(input, output)
self.checkVersion("4.7.2")
- self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
+ self._interp = LexerATNSimulator(
+ self, self.atn, self.decisionsToDFA, PredictionContextCache()
+ )
self._actions = None
self._predicates = None
-
-
diff --git a/cf_units/_udunits2_parser/parser/udunits2Parser.py b/cf_units/_udunits2_parser/parser/udunits2Parser.py
index 47e1a8ea..46bc98aa 100644
--- a/cf_units/_udunits2_parser/parser/udunits2Parser.py
+++ b/cf_units/_udunits2_parser/parser/udunits2Parser.py
@@ -1,9 +1,10 @@
# Generated from /Users/pelson/dev/scitools/cf-units/cf_units/_udunits2_parser/udunits2Parser.g4 by ANTLR 4.7.2
# encoding: utf-8
-from antlr4 import *
+import sys
from io import StringIO
from typing.io import TextIO
-import sys
+
+from antlr4 import *
def serializedATN():
@@ -26,9 +27,9 @@ def serializedATN():
buf.write("\2\24\u0081\3\2\2\2\26\30\5\4\3\2\27\26\3\2\2\2\27\30")
buf.write("\3\2\2\2\30\31\3\2\2\2\31\32\7\2\2\3\32\3\3\2\2\2\33\61")
buf.write("\5\6\4\2\34\36\5\6\4\2\35\37\7\25\2\2\36\35\3\2\2\2\36")
- buf.write("\37\3\2\2\2\37 \3\2\2\2 \"\7\16\2\2!#\7\25\2\2\"!\3\2")
- buf.write("\2\2\"#\3\2\2\2#$\3\2\2\2$%\5\16\b\2%\61\3\2\2\2&(\5\6")
- buf.write("\4\2\')\7\25\2\2(\'\3\2\2\2()\3\2\2\2)*\3\2\2\2*,\7\16")
+ buf.write('\37\3\2\2\2\37 \3\2\2\2 "\7\16\2\2!#\7\25\2\2"!\3\2')
+ buf.write('\2\2"#\3\2\2\2#$\3\2\2\2$%\5\16\b\2%\61\3\2\2\2&(\5\6')
+ buf.write("\4\2')\7\25\2\2('\3\2\2\2()\3\2\2\2)*\3\2\2\2*,\7\16")
buf.write("\2\2+-\7\25\2\2,+\3\2\2\2,-\3\2\2\2-.\3\2\2\2./\5\20\t")
buf.write("\2/\61\3\2\2\2\60\33\3\2\2\2\60\34\3\2\2\2\60&\3\2\2\2")
buf.write("\61\5\3\2\2\2\62\63\b\4\1\2\63\64\5\b\5\2\64F\3\2\2\2")
@@ -52,33 +53,78 @@ def serializedATN():
buf.write("\21\3\2\2\2z~\7\30\2\2{~\7\31\2\2|~\5\f\7\2}z\3\2\2\2")
buf.write("}{\3\2\2\2}|\3\2\2\2~\23\3\2\2\2\177\u0082\7\31\2\2\u0080")
buf.write("\u0082\5\f\7\2\u0081\177\3\2\2\2\u0081\u0080\3\2\2\2\u0082")
- buf.write("\25\3\2\2\2\26\27\36\"(,\60ADFT\\bgknsvx}\u0081")
+ buf.write('\25\3\2\2\2\26\27\36"(,\60ADFT\\bgknsvx}\u0081')
return buf.getvalue()
-class udunits2Parser ( Parser ):
+class udunits2Parser(Parser):
grammarFileName = "udunits2Parser.g4"
atn = ATNDeserializer().deserialize(serializedATN())
- decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
+ decisionsToDFA = [DFA(ds, i) for i, ds in enumerate(atn.decisionToState)]
sharedContextCache = PredictionContextCache()
- literalNames = [ "", "", "'+'", "", "",
- "'.'", "'('", "')'", "':'", "", "",
- "", "", "", "",
- "", "", "", "",
- "", "", "", "",
- "", "'-'" ]
-
- symbolicNames = [ "", "SIGNED_INT", "PLUS", "MULTIPLY", "DIVIDE",
- "PERIOD", "OPEN_PAREN", "CLOSE_PAREN", "SEMICOLON",
- "INT", "E_POWER", "FLOAT", "SHIFT_OP", "UNICODE_EXPONENT",
- "RAISE", "LOG", "LOGREF", "ID", "LATIN_SUBSET", "WS",
- "ERRORCHARACTER", "TIMEZONE", "HOUR_MINUTE_SECOND",
- "HOUR_MINUTE", "M_MINUS", "DATE", "TIMESTAMP", "DT_T_CLOCK" ]
+ literalNames = [
+ "",
+ "",
+ "'+'",
+ "",
+ "",
+ "'.'",
+ "'('",
+ "')'",
+ "':'",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "'-'",
+ ]
+
+ symbolicNames = [
+ "",
+ "SIGNED_INT",
+ "PLUS",
+ "MULTIPLY",
+ "DIVIDE",
+ "PERIOD",
+ "OPEN_PAREN",
+ "CLOSE_PAREN",
+ "SEMICOLON",
+ "INT",
+ "E_POWER",
+ "FLOAT",
+ "SHIFT_OP",
+ "UNICODE_EXPONENT",
+ "RAISE",
+ "LOG",
+ "LOGREF",
+ "ID",
+ "LATIN_SUBSET",
+ "WS",
+ "ERRORCHARACTER",
+ "TIMEZONE",
+ "HOUR_MINUTE_SECOND",
+ "HOUR_MINUTE",
+ "M_MINUS",
+ "DATE",
+ "TIMESTAMP",
+ "DT_T_CLOCK",
+ ]
RULE_unit_spec = 0
RULE_shift_spec = 1
@@ -91,50 +137,63 @@ class udunits2Parser ( Parser ):
RULE_signed_clock = 8
RULE_timezone_offset = 9
- ruleNames = [ "unit_spec", "shift_spec", "product", "power", "basic_spec",
- "integer", "number", "timestamp", "signed_clock", "timezone_offset" ]
+ ruleNames = [
+ "unit_spec",
+ "shift_spec",
+ "product",
+ "power",
+ "basic_spec",
+ "integer",
+ "number",
+ "timestamp",
+ "signed_clock",
+ "timezone_offset",
+ ]
EOF = Token.EOF
- SIGNED_INT=1
- PLUS=2
- MULTIPLY=3
- DIVIDE=4
- PERIOD=5
- OPEN_PAREN=6
- CLOSE_PAREN=7
- SEMICOLON=8
- INT=9
- E_POWER=10
- FLOAT=11
- SHIFT_OP=12
- UNICODE_EXPONENT=13
- RAISE=14
- LOG=15
- LOGREF=16
- ID=17
- LATIN_SUBSET=18
- WS=19
- ERRORCHARACTER=20
- TIMEZONE=21
- HOUR_MINUTE_SECOND=22
- HOUR_MINUTE=23
- M_MINUS=24
- DATE=25
- TIMESTAMP=26
- DT_T_CLOCK=27
-
- def __init__(self, input:TokenStream, output:TextIO = sys.stdout):
+ SIGNED_INT = 1
+ PLUS = 2
+ MULTIPLY = 3
+ DIVIDE = 4
+ PERIOD = 5
+ OPEN_PAREN = 6
+ CLOSE_PAREN = 7
+ SEMICOLON = 8
+ INT = 9
+ E_POWER = 10
+ FLOAT = 11
+ SHIFT_OP = 12
+ UNICODE_EXPONENT = 13
+ RAISE = 14
+ LOG = 15
+ LOGREF = 16
+ ID = 17
+ LATIN_SUBSET = 18
+ WS = 19
+ ERRORCHARACTER = 20
+ TIMEZONE = 21
+ HOUR_MINUTE_SECOND = 22
+ HOUR_MINUTE = 23
+ M_MINUS = 24
+ DATE = 25
+ TIMESTAMP = 26
+ DT_T_CLOCK = 27
+
+ def __init__(self, input: TokenStream, output: TextIO = sys.stdout):
super().__init__(input, output)
self.checkVersion("4.7.2")
- self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache)
+ self._interp = ParserATNSimulator(
+ self, self.atn, self.decisionsToDFA, self.sharedContextCache
+ )
self._predicates = None
-
-
-
class Unit_specContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -142,36 +201,42 @@ def EOF(self):
return self.getToken(udunits2Parser.EOF, 0)
def shift_spec(self):
- return self.getTypedRuleContext(udunits2Parser.Shift_specContext,0)
-
+ return self.getTypedRuleContext(
+ udunits2Parser.Shift_specContext, 0
+ )
def getRuleIndex(self):
return udunits2Parser.RULE_unit_spec
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitUnit_spec" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitUnit_spec"):
return visitor.visitUnit_spec(self)
else:
return visitor.visitChildren(self)
-
-
-
def unit_spec(self):
localctx = udunits2Parser.Unit_specContext(self, self._ctx, self.state)
self.enterRule(localctx, 0, self.RULE_unit_spec)
- self._la = 0 # Token type
+ self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
self.state = 21
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << udunits2Parser.SIGNED_INT) | (1 << udunits2Parser.OPEN_PAREN) | (1 << udunits2Parser.INT) | (1 << udunits2Parser.FLOAT) | (1 << udunits2Parser.ID))) != 0):
+ if ((_la) & ~0x3F) == 0 and (
+ (1 << _la)
+ & (
+ (1 << udunits2Parser.SIGNED_INT)
+ | (1 << udunits2Parser.OPEN_PAREN)
+ | (1 << udunits2Parser.INT)
+ | (1 << udunits2Parser.FLOAT)
+ | (1 << udunits2Parser.ID)
+ )
+ ) != 0:
self.state = 20
self.shift_spec()
-
self.state = 23
self.match(udunits2Parser.EOF)
except RecognitionException as re:
@@ -182,55 +247,54 @@ def unit_spec(self):
self.exitRule()
return localctx
-
class Shift_specContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
def product(self):
- return self.getTypedRuleContext(udunits2Parser.ProductContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.ProductContext, 0)
def SHIFT_OP(self):
return self.getToken(udunits2Parser.SHIFT_OP, 0)
def number(self):
- return self.getTypedRuleContext(udunits2Parser.NumberContext,0)
+ return self.getTypedRuleContext(udunits2Parser.NumberContext, 0)
-
- def WS(self, i:int=None):
+ def WS(self, i: int = None):
if i is None:
return self.getTokens(udunits2Parser.WS)
else:
return self.getToken(udunits2Parser.WS, i)
def timestamp(self):
- return self.getTypedRuleContext(udunits2Parser.TimestampContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.TimestampContext, 0)
def getRuleIndex(self):
return udunits2Parser.RULE_shift_spec
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitShift_spec" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitShift_spec"):
return visitor.visitShift_spec(self)
else:
return visitor.visitChildren(self)
-
-
-
def shift_spec(self):
- localctx = udunits2Parser.Shift_specContext(self, self._ctx, self.state)
+ localctx = udunits2Parser.Shift_specContext(
+ self, self._ctx, self.state
+ )
self.enterRule(localctx, 2, self.RULE_shift_spec)
- self._la = 0 # Token type
+ self._la = 0 # Token type
try:
self.state = 46
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,5,self._ctx)
+ la_ = self._interp.adaptivePredict(self._input, 5, self._ctx)
if la_ == 1:
self.enterOuterAlt(localctx, 1)
self.state = 25
@@ -244,21 +308,19 @@ def shift_spec(self):
self.state = 28
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 27
self.match(udunits2Parser.WS)
-
self.state = 30
self.match(udunits2Parser.SHIFT_OP)
self.state = 32
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 31
self.match(udunits2Parser.WS)
-
self.state = 34
self.number()
pass
@@ -270,26 +332,23 @@ def shift_spec(self):
self.state = 38
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 37
self.match(udunits2Parser.WS)
-
self.state = 40
self.match(udunits2Parser.SHIFT_OP)
self.state = 42
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 41
self.match(udunits2Parser.WS)
-
self.state = 44
self.timestamp()
pass
-
except RecognitionException as re:
localctx.exception = re
self._errHandler.reportError(self, re)
@@ -298,20 +357,21 @@ def shift_spec(self):
self.exitRule()
return localctx
-
class ProductContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
def power(self):
- return self.getTypedRuleContext(udunits2Parser.PowerContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.PowerContext, 0)
def product(self):
- return self.getTypedRuleContext(udunits2Parser.ProductContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.ProductContext, 0)
def MULTIPLY(self):
return self.getToken(udunits2Parser.MULTIPLY, 0)
@@ -319,7 +379,7 @@ def MULTIPLY(self):
def DIVIDE(self):
return self.getToken(udunits2Parser.DIVIDE, 0)
- def WS(self, i:int=None):
+ def WS(self, i: int = None):
if i is None:
return self.getTokens(udunits2Parser.WS)
else:
@@ -328,22 +388,20 @@ def WS(self, i:int=None):
def getRuleIndex(self):
return udunits2Parser.RULE_product
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitProduct" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitProduct"):
return visitor.visitProduct(self)
else:
return visitor.visitChildren(self)
-
-
- def product(self, _p:int=0):
+ def product(self, _p: int = 0):
_parentctx = self._ctx
_parentState = self.state
localctx = udunits2Parser.ProductContext(self, self._ctx, _parentState)
_prevctx = localctx
_startState = 4
self.enterRecursionRule(localctx, 4, self.RULE_product, _p)
- self._la = 0 # Token type
+ self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
self.state = 49
@@ -351,33 +409,53 @@ def product(self, _p:int=0):
self._ctx.stop = self._input.LT(-1)
self.state = 68
self._errHandler.sync(self)
- _alt = self._interp.adaptivePredict(self._input,8,self._ctx)
- while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER:
- if _alt==1:
+ _alt = self._interp.adaptivePredict(self._input, 8, self._ctx)
+ while _alt != 2 and _alt != ATN.INVALID_ALT_NUMBER:
+ if _alt == 1:
if self._parseListeners is not None:
self.triggerExitRuleEvent()
_prevctx = localctx
self.state = 66
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,7,self._ctx)
+ la_ = self._interp.adaptivePredict(
+ self._input, 7, self._ctx
+ )
if la_ == 1:
- localctx = udunits2Parser.ProductContext(self, _parentctx, _parentState)
- self.pushNewRecursionContext(localctx, _startState, self.RULE_product)
+ localctx = udunits2Parser.ProductContext(
+ self, _parentctx, _parentState
+ )
+ self.pushNewRecursionContext(
+ localctx, _startState, self.RULE_product
+ )
self.state = 51
if not self.precpred(self._ctx, 4):
- from antlr4.error.Errors import FailedPredicateException
- raise FailedPredicateException(self, "self.precpred(self._ctx, 4)")
+ from antlr4.error.Errors import (
+ FailedPredicateException,
+ )
+
+ raise FailedPredicateException(
+ self, "self.precpred(self._ctx, 4)"
+ )
self.state = 52
self.power()
pass
elif la_ == 2:
- localctx = udunits2Parser.ProductContext(self, _parentctx, _parentState)
- self.pushNewRecursionContext(localctx, _startState, self.RULE_product)
+ localctx = udunits2Parser.ProductContext(
+ self, _parentctx, _parentState
+ )
+ self.pushNewRecursionContext(
+ localctx, _startState, self.RULE_product
+ )
self.state = 53
if not self.precpred(self._ctx, 3):
- from antlr4.error.Errors import FailedPredicateException
- raise FailedPredicateException(self, "self.precpred(self._ctx, 3)")
+ from antlr4.error.Errors import (
+ FailedPredicateException,
+ )
+
+ raise FailedPredicateException(
+ self, "self.precpred(self._ctx, 3)"
+ )
self.state = 54
self.match(udunits2Parser.MULTIPLY)
self.state = 55
@@ -385,12 +463,21 @@ def product(self, _p:int=0):
pass
elif la_ == 3:
- localctx = udunits2Parser.ProductContext(self, _parentctx, _parentState)
- self.pushNewRecursionContext(localctx, _startState, self.RULE_product)
+ localctx = udunits2Parser.ProductContext(
+ self, _parentctx, _parentState
+ )
+ self.pushNewRecursionContext(
+ localctx, _startState, self.RULE_product
+ )
self.state = 56
if not self.precpred(self._ctx, 2):
- from antlr4.error.Errors import FailedPredicateException
- raise FailedPredicateException(self, "self.precpred(self._ctx, 2)")
+ from antlr4.error.Errors import (
+ FailedPredicateException,
+ )
+
+ raise FailedPredicateException(
+ self, "self.precpred(self._ctx, 2)"
+ )
self.state = 57
self.match(udunits2Parser.DIVIDE)
self.state = 58
@@ -398,32 +485,40 @@ def product(self, _p:int=0):
pass
elif la_ == 4:
- localctx = udunits2Parser.ProductContext(self, _parentctx, _parentState)
- self.pushNewRecursionContext(localctx, _startState, self.RULE_product)
+ localctx = udunits2Parser.ProductContext(
+ self, _parentctx, _parentState
+ )
+ self.pushNewRecursionContext(
+ localctx, _startState, self.RULE_product
+ )
self.state = 59
if not self.precpred(self._ctx, 1):
- from antlr4.error.Errors import FailedPredicateException
- raise FailedPredicateException(self, "self.precpred(self._ctx, 1)")
- self.state = 61
+ from antlr4.error.Errors import (
+ FailedPredicateException,
+ )
+
+ raise FailedPredicateException(
+ self, "self.precpred(self._ctx, 1)"
+ )
+ self.state = 61
self._errHandler.sync(self)
_la = self._input.LA(1)
while True:
self.state = 60
self.match(udunits2Parser.WS)
- self.state = 63
+ self.state = 63
self._errHandler.sync(self)
_la = self._input.LA(1)
- if not (_la==udunits2Parser.WS):
+ if not (_la == udunits2Parser.WS):
break
self.state = 65
self.power()
pass
-
self.state = 70
self._errHandler.sync(self)
- _alt = self._interp.adaptivePredict(self._input,8,self._ctx)
+ _alt = self._interp.adaptivePredict(self._input, 8, self._ctx)
except RecognitionException as re:
localctx.exception = re
@@ -433,20 +528,23 @@ def product(self, _p:int=0):
self.unrollRecursionContexts(_parentctx)
return localctx
-
class PowerContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
def basic_spec(self):
- return self.getTypedRuleContext(udunits2Parser.Basic_specContext,0)
-
+ return self.getTypedRuleContext(
+ udunits2Parser.Basic_specContext, 0
+ )
def integer(self):
- return self.getTypedRuleContext(udunits2Parser.IntegerContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.IntegerContext, 0)
def RAISE(self):
return self.getToken(udunits2Parser.RAISE, 0)
@@ -457,15 +555,12 @@ def UNICODE_EXPONENT(self):
def getRuleIndex(self):
return udunits2Parser.RULE_power
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitPower" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitPower"):
return visitor.visitPower(self)
else:
return visitor.visitChildren(self)
-
-
-
def power(self):
localctx = udunits2Parser.PowerContext(self, self._ctx, self.state)
@@ -473,7 +568,7 @@ def power(self):
try:
self.state = 82
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,9,self._ctx)
+ la_ = self._interp.adaptivePredict(self._input, 9, self._ctx)
if la_ == 1:
self.enterOuterAlt(localctx, 1)
self.state = 71
@@ -506,7 +601,6 @@ def power(self):
self.match(udunits2Parser.UNICODE_EXPONENT)
pass
-
except RecognitionException as re:
localctx.exception = re
self._errHandler.reportError(self, re)
@@ -515,10 +609,13 @@ def power(self):
self.exitRule()
return localctx
-
class Basic_specContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -529,31 +626,30 @@ def OPEN_PAREN(self):
return self.getToken(udunits2Parser.OPEN_PAREN, 0)
def shift_spec(self):
- return self.getTypedRuleContext(udunits2Parser.Shift_specContext,0)
-
+ return self.getTypedRuleContext(
+ udunits2Parser.Shift_specContext, 0
+ )
def CLOSE_PAREN(self):
return self.getToken(udunits2Parser.CLOSE_PAREN, 0)
def number(self):
- return self.getTypedRuleContext(udunits2Parser.NumberContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.NumberContext, 0)
def getRuleIndex(self):
return udunits2Parser.RULE_basic_spec
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitBasic_spec" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitBasic_spec"):
return visitor.visitBasic_spec(self)
else:
return visitor.visitChildren(self)
-
-
-
def basic_spec(self):
- localctx = udunits2Parser.Basic_specContext(self, self._ctx, self.state)
+ localctx = udunits2Parser.Basic_specContext(
+ self, self._ctx, self.state
+ )
self.enterRule(localctx, 8, self.RULE_basic_spec)
try:
self.state = 90
@@ -573,7 +669,11 @@ def basic_spec(self):
self.state = 87
self.match(udunits2Parser.CLOSE_PAREN)
pass
- elif token in [udunits2Parser.SIGNED_INT, udunits2Parser.INT, udunits2Parser.FLOAT]:
+ elif token in [
+ udunits2Parser.SIGNED_INT,
+ udunits2Parser.INT,
+ udunits2Parser.FLOAT,
+ ]:
self.enterOuterAlt(localctx, 3)
self.state = 89
self.number()
@@ -589,10 +689,13 @@ def basic_spec(self):
self.exitRule()
return localctx
-
class IntegerContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -605,25 +708,24 @@ def SIGNED_INT(self):
def getRuleIndex(self):
return udunits2Parser.RULE_integer
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitInteger" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitInteger"):
return visitor.visitInteger(self)
else:
return visitor.visitChildren(self)
-
-
-
def integer(self):
localctx = udunits2Parser.IntegerContext(self, self._ctx, self.state)
self.enterRule(localctx, 10, self.RULE_integer)
- self._la = 0 # Token type
+ self._la = 0 # Token type
try:
self.enterOuterAlt(localctx, 1)
self.state = 92
_la = self._input.LA(1)
- if not(_la==udunits2Parser.SIGNED_INT or _la==udunits2Parser.INT):
+ if not (
+ _la == udunits2Parser.SIGNED_INT or _la == udunits2Parser.INT
+ ):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -636,16 +738,18 @@ def integer(self):
self.exitRule()
return localctx
-
class NumberContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
def integer(self):
- return self.getTypedRuleContext(udunits2Parser.IntegerContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.IntegerContext, 0)
def FLOAT(self):
return self.getToken(udunits2Parser.FLOAT, 0)
@@ -653,15 +757,12 @@ def FLOAT(self):
def getRuleIndex(self):
return udunits2Parser.RULE_number
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitNumber" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitNumber"):
return visitor.visitNumber(self)
else:
return visitor.visitChildren(self)
-
-
-
def number(self):
localctx = udunits2Parser.NumberContext(self, self._ctx, self.state)
@@ -691,10 +792,13 @@ def number(self):
self.exitRule()
return localctx
-
class TimestampContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -705,18 +809,20 @@ def INT(self):
return self.getToken(udunits2Parser.INT, 0)
def signed_clock(self):
- return self.getTypedRuleContext(udunits2Parser.Signed_clockContext,0)
+ return self.getTypedRuleContext(
+ udunits2Parser.Signed_clockContext, 0
+ )
-
- def WS(self, i:int=None):
+ def WS(self, i: int = None):
if i is None:
return self.getTokens(udunits2Parser.WS)
else:
return self.getToken(udunits2Parser.WS, i)
def timezone_offset(self):
- return self.getTypedRuleContext(udunits2Parser.Timezone_offsetContext,0)
-
+ return self.getTypedRuleContext(
+ udunits2Parser.Timezone_offsetContext, 0
+ )
def DT_T_CLOCK(self):
return self.getToken(udunits2Parser.DT_T_CLOCK, 0)
@@ -727,29 +833,28 @@ def TIMESTAMP(self):
def getRuleIndex(self):
return udunits2Parser.RULE_timestamp
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitTimestamp" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitTimestamp"):
return visitor.visitTimestamp(self)
else:
return visitor.visitChildren(self)
-
-
-
def timestamp(self):
localctx = udunits2Parser.TimestampContext(self, self._ctx, self.state)
self.enterRule(localctx, 14, self.RULE_timestamp)
- self._la = 0 # Token type
+ self._la = 0 # Token type
try:
self.state = 118
self._errHandler.sync(self)
- la_ = self._interp.adaptivePredict(self._input,17,self._ctx)
+ la_ = self._interp.adaptivePredict(self._input, 17, self._ctx)
if la_ == 1:
self.enterOuterAlt(localctx, 1)
self.state = 98
_la = self._input.LA(1)
- if not(_la==udunits2Parser.INT or _la==udunits2Parser.DATE):
+ if not (
+ _la == udunits2Parser.INT or _la == udunits2Parser.DATE
+ ):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -760,7 +865,9 @@ def timestamp(self):
self.enterOuterAlt(localctx, 2)
self.state = 99
_la = self._input.LA(1)
- if not(_la==udunits2Parser.INT or _la==udunits2Parser.DATE):
+ if not (
+ _la == udunits2Parser.INT or _la == udunits2Parser.DATE
+ ):
self._errHandler.recoverInline(self)
else:
self._errHandler.reportMatch(self)
@@ -768,29 +875,34 @@ def timestamp(self):
self.state = 101
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 100
self.match(udunits2Parser.WS)
-
self.state = 103
self.signed_clock()
self.state = 108
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << udunits2Parser.SIGNED_INT) | (1 << udunits2Parser.INT) | (1 << udunits2Parser.WS) | (1 << udunits2Parser.HOUR_MINUTE))) != 0):
+ if ((_la) & ~0x3F) == 0 and (
+ (1 << _la)
+ & (
+ (1 << udunits2Parser.SIGNED_INT)
+ | (1 << udunits2Parser.INT)
+ | (1 << udunits2Parser.WS)
+ | (1 << udunits2Parser.HOUR_MINUTE)
+ )
+ ) != 0:
self.state = 105
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 104
self.match(udunits2Parser.WS)
-
self.state = 107
self.timezone_offset()
-
pass
elif la_ == 3:
@@ -806,22 +918,26 @@ def timestamp(self):
self.state = 113
self._errHandler.sync(self)
_la = self._input.LA(1)
- if _la==udunits2Parser.WS:
+ if _la == udunits2Parser.WS:
self.state = 112
self.match(udunits2Parser.WS)
-
self.state = 116
self._errHandler.sync(self)
_la = self._input.LA(1)
- if (((_la) & ~0x3f) == 0 and ((1 << _la) & ((1 << udunits2Parser.SIGNED_INT) | (1 << udunits2Parser.INT) | (1 << udunits2Parser.HOUR_MINUTE))) != 0):
+ if ((_la) & ~0x3F) == 0 and (
+ (1 << _la)
+ & (
+ (1 << udunits2Parser.SIGNED_INT)
+ | (1 << udunits2Parser.INT)
+ | (1 << udunits2Parser.HOUR_MINUTE)
+ )
+ ) != 0:
self.state = 115
self.timezone_offset()
-
pass
-
except RecognitionException as re:
localctx.exception = re
self._errHandler.reportError(self, re)
@@ -830,10 +946,13 @@ def timestamp(self):
self.exitRule()
return localctx
-
class Signed_clockContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -844,24 +963,22 @@ def HOUR_MINUTE(self):
return self.getToken(udunits2Parser.HOUR_MINUTE, 0)
def integer(self):
- return self.getTypedRuleContext(udunits2Parser.IntegerContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.IntegerContext, 0)
def getRuleIndex(self):
return udunits2Parser.RULE_signed_clock
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitSigned_clock" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitSigned_clock"):
return visitor.visitSigned_clock(self)
else:
return visitor.visitChildren(self)
-
-
-
def signed_clock(self):
- localctx = udunits2Parser.Signed_clockContext(self, self._ctx, self.state)
+ localctx = udunits2Parser.Signed_clockContext(
+ self, self._ctx, self.state
+ )
self.enterRule(localctx, 16, self.RULE_signed_clock)
try:
self.state = 123
@@ -893,10 +1010,13 @@ def signed_clock(self):
self.exitRule()
return localctx
-
class Timezone_offsetContext(ParserRuleContext):
-
- def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1):
+ def __init__(
+ self,
+ parser,
+ parent: ParserRuleContext = None,
+ invokingState: int = -1,
+ ):
super().__init__(parent, invokingState)
self.parser = parser
@@ -904,24 +1024,22 @@ def HOUR_MINUTE(self):
return self.getToken(udunits2Parser.HOUR_MINUTE, 0)
def integer(self):
- return self.getTypedRuleContext(udunits2Parser.IntegerContext,0)
-
+ return self.getTypedRuleContext(udunits2Parser.IntegerContext, 0)
def getRuleIndex(self):
return udunits2Parser.RULE_timezone_offset
- def accept(self, visitor:ParseTreeVisitor):
- if hasattr( visitor, "visitTimezone_offset" ):
+ def accept(self, visitor: ParseTreeVisitor):
+ if hasattr(visitor, "visitTimezone_offset"):
return visitor.visitTimezone_offset(self)
else:
return visitor.visitChildren(self)
-
-
-
def timezone_offset(self):
- localctx = udunits2Parser.Timezone_offsetContext(self, self._ctx, self.state)
+ localctx = udunits2Parser.Timezone_offsetContext(
+ self, self._ctx, self.state
+ )
self.enterRule(localctx, 18, self.RULE_timezone_offset)
try:
self.state = 127
@@ -948,9 +1066,7 @@ def timezone_offset(self):
self.exitRule()
return localctx
-
-
- def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int):
+ def sempred(self, localctx: RuleContext, ruleIndex: int, predIndex: int):
if self._predicates == None:
self._predicates = dict()
self._predicates[2] = self.product_sempred
@@ -960,23 +1076,15 @@ def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int):
else:
return pred(localctx, predIndex)
- def product_sempred(self, localctx:ProductContext, predIndex:int):
- if predIndex == 0:
- return self.precpred(self._ctx, 4)
-
-
- if predIndex == 1:
- return self.precpred(self._ctx, 3)
-
-
- if predIndex == 2:
- return self.precpred(self._ctx, 2)
-
-
- if predIndex == 3:
- return self.precpred(self._ctx, 1)
-
-
+ def product_sempred(self, localctx: ProductContext, predIndex: int):
+ if predIndex == 0:
+ return self.precpred(self._ctx, 4)
+ if predIndex == 1:
+ return self.precpred(self._ctx, 3)
+ if predIndex == 2:
+ return self.precpred(self._ctx, 2)
+ if predIndex == 3:
+ return self.precpred(self._ctx, 1)
diff --git a/cf_units/_udunits2_parser/parser/udunits2ParserVisitor.py b/cf_units/_udunits2_parser/parser/udunits2ParserVisitor.py
index 338b9a07..b26f9060 100644
--- a/cf_units/_udunits2_parser/parser/udunits2ParserVisitor.py
+++ b/cf_units/_udunits2_parser/parser/udunits2ParserVisitor.py
@@ -1,5 +1,6 @@
# Generated from /Users/pelson/dev/scitools/cf-units/cf_units/_udunits2_parser/udunits2Parser.g4 by ANTLR 4.7.2
from antlr4 import *
+
if __name__ is not None and "." in __name__:
from .udunits2Parser import udunits2Parser
else:
@@ -7,57 +8,48 @@
# This class defines a complete generic visitor for a parse tree produced by udunits2Parser.
+
class udunits2ParserVisitor(ParseTreeVisitor):
# Visit a parse tree produced by udunits2Parser#unit_spec.
- def visitUnit_spec(self, ctx:udunits2Parser.Unit_specContext):
+ def visitUnit_spec(self, ctx: udunits2Parser.Unit_specContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#shift_spec.
- def visitShift_spec(self, ctx:udunits2Parser.Shift_specContext):
+ def visitShift_spec(self, ctx: udunits2Parser.Shift_specContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#product.
- def visitProduct(self, ctx:udunits2Parser.ProductContext):
+ def visitProduct(self, ctx: udunits2Parser.ProductContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#power.
- def visitPower(self, ctx:udunits2Parser.PowerContext):
+ def visitPower(self, ctx: udunits2Parser.PowerContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#basic_spec.
- def visitBasic_spec(self, ctx:udunits2Parser.Basic_specContext):
+ def visitBasic_spec(self, ctx: udunits2Parser.Basic_specContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#integer.
- def visitInteger(self, ctx:udunits2Parser.IntegerContext):
+ def visitInteger(self, ctx: udunits2Parser.IntegerContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#number.
- def visitNumber(self, ctx:udunits2Parser.NumberContext):
+ def visitNumber(self, ctx: udunits2Parser.NumberContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#timestamp.
- def visitTimestamp(self, ctx:udunits2Parser.TimestampContext):
+ def visitTimestamp(self, ctx: udunits2Parser.TimestampContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#signed_clock.
- def visitSigned_clock(self, ctx:udunits2Parser.Signed_clockContext):
+ def visitSigned_clock(self, ctx: udunits2Parser.Signed_clockContext):
return self.visitChildren(ctx)
-
# Visit a parse tree produced by udunits2Parser#timezone_offset.
- def visitTimezone_offset(self, ctx:udunits2Parser.Timezone_offsetContext):
+ def visitTimezone_offset(self, ctx: udunits2Parser.Timezone_offsetContext):
return self.visitChildren(ctx)
-
-del udunits2Parser
\ No newline at end of file
+del udunits2Parser
diff --git a/cf_units/config.py b/cf_units/config.py
index c9b73cc0..888753e8 100644
--- a/cf_units/config.py
+++ b/cf_units/config.py
@@ -6,7 +6,6 @@
import configparser
-
import os.path
import sys
@@ -28,11 +27,11 @@ def get_option(section, option, default=None):
ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
# The full path to the configuration directory of the active cf_units instance.
-CONFIG_PATH = os.path.join(ROOT_PATH, 'etc')
+CONFIG_PATH = os.path.join(ROOT_PATH, "etc")
# Load the optional "site.cfg" file if it exists.
if sys.version_info >= (3, 2):
config = configparser.ConfigParser()
else:
config = configparser.SafeConfigParser()
-config.read([os.path.join(CONFIG_PATH, 'site.cfg')])
+config.read([os.path.join(CONFIG_PATH, "site.cfg")])
diff --git a/cf_units/tests/integration/parse/test_graph.py b/cf_units/tests/integration/parse/test_graph.py
index 28e3a767..7cd65665 100644
--- a/cf_units/tests/integration/parse/test_graph.py
+++ b/cf_units/tests/integration/parse/test_graph.py
@@ -4,58 +4,58 @@
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.
-from cf_units._udunits2_parser import parse
import cf_units._udunits2_parser.graph as g
+from cf_units._udunits2_parser import parse
def test_Node_attributes():
- n = g.Node(a=1, kwarg='two', arbitrary_kwargs=3)
+ n = g.Node(a=1, kwarg="two", arbitrary_kwargs=3)
assert n.a == 1
- assert n.kwarg == 'two'
+ assert n.kwarg == "two"
assert n.arbitrary_kwargs == 3
def test_Node_str():
- n = g.Node(a=1, kwarg='two', arbitrary_kwargs=3)
+ n = g.Node(a=1, kwarg="two", arbitrary_kwargs=3)
assert str(n) == "Node(a=1, kwarg='two', arbitrary_kwargs=3)"
def test_Node_children():
- n = g.Node(a=1, kwarg='two', arbitrary_kwargs=3)
+ n = g.Node(a=1, kwarg="two", arbitrary_kwargs=3)
# Ordered, and consistent.
- assert n.children() == [1, 'two', 3]
+ assert n.children() == [1, "two", 3]
def test_large_graph():
- graph = parse('m2/4.1.2π per second @ 10')
+ graph = parse("m2/4.1.2π per second @ 10")
assert isinstance(graph, g.Shift)
unit, shift_from = graph.children()
assert isinstance(shift_from, g.Number)
- assert str(shift_from) == '10'
+ assert str(shift_from) == "10"
assert isinstance(unit, g.Divide)
lhs, rhs = unit.children()
- assert str(lhs) == 'm^2/4.1·.2·π'
- assert str(rhs) == 'second'
+ assert str(lhs) == "m^2/4.1·.2·π"
+ assert str(rhs) == "second"
assert isinstance(lhs, g.Multiply)
lhs, rhs = lhs.children()
- assert str(lhs) == 'm^2/4.1·.2'
- assert str(rhs) == 'π'
+ assert str(lhs) == "m^2/4.1·.2"
+ assert str(rhs) == "π"
assert isinstance(lhs, g.Multiply)
lhs, rhs = lhs.children()
- assert str(lhs) == 'm^2/4.1'
- assert str(rhs) == '.2'
+ assert str(lhs) == "m^2/4.1"
+ assert str(rhs) == ".2"
assert isinstance(lhs, g.Divide)
lhs, rhs = lhs.children()
- assert str(lhs) == 'm^2'
- assert str(rhs) == '4.1'
+ assert str(lhs) == "m^2"
+ assert str(rhs) == "4.1"
assert isinstance(lhs, g.Raise)
lhs, rhs = lhs.children()
- assert str(lhs) == 'm'
- assert str(rhs) == '2'
+ assert str(lhs) == "m"
+ assert str(rhs) == "2"
diff --git a/cf_units/tests/integration/parse/test_parse.py b/cf_units/tests/integration/parse/test_parse.py
index 11d14dc0..6c1f9391 100644
--- a/cf_units/tests/integration/parse/test_parse.py
+++ b/cf_units/tests/integration/parse/test_parse.py
@@ -11,142 +11,129 @@
import cf_units
from cf_units._udunits2_parser import normalize
-
testdata = [
- '',
- '1',
- '12',
- '1.2',
- '+1',
- '+1.2',
- '-1',
- '-1.2',
- '-1.2e0',
- '2e6',
- '2e-6',
- '2.e-6',
- '.1e2',
- '.1e2.2',
- '2e', # <- TODO: Assert this isn't 2e1, but is infact the unit e *2
- 'm',
- 'meter',
-
+ "",
+ "1",
+ "12",
+ "1.2",
+ "+1",
+ "+1.2",
+ "-1",
+ "-1.2",
+ "-1.2e0",
+ "2e6",
+ "2e-6",
+ "2.e-6",
+ ".1e2",
+ ".1e2.2",
+ "2e", # <- TODO: Assert this isn't 2e1, but is infact the unit e *2
+ "m",
+ "meter",
# Multiplication
- '1 2 3',
- '1 -2 -3',
- '1m',
- '1*m',
- 'm·m',
- '1 m',
- '1 m',
- 'm -1',
- 'm -1.2',
- 'm 1',
- 'm 1.2',
- 'm-+2',
- 'm--4',
- 'm*1*2',
- 'm--2--3',
-
+ "1 2 3",
+ "1 -2 -3",
+ "1m",
+ "1*m",
+ "m·m",
+ "1 m",
+ "1 m",
+ "m -1",
+ "m -1.2",
+ "m 1",
+ "m 1.2",
+ "m-+2",
+ "m--4",
+ "m*1*2",
+ "m--2--3",
# TODO: add some tests with brackets.
- 'm(2.3)',
- 'm(2.3m)',
- '(1.2)(2.4)',
- '(5m(6s-1))',
- '2*3*4/5m/6*7*8',
-
-
- 'm/2',
- 'm1',
- 'm m',
- 'm2',
- 'm+2',
- 'm¹',
- 'm²',
- 'm³',
- '2⁴', # NOTE: Udunits can't do m⁴ for some reason. Bug?
- '2⁵',
- '2⁴²',
- '3⁻²',
- 'm2 s2',
- 'm^2*s^2',
-
- '1-2',
- '1-2-3', # nb. looks a bit like a date, but it isn't!
- 'm-1',
- 'm^2',
- 'm^+2',
- 'm^-1',
- 'm.2', # This is 2*m
- 'm.+2', # 2*m
- 'm.2.4', # This is 2.4 * m
- 'm0.2', # But this is 2 m^0
- 'm2.5', # And this is 5m^2
- 'm2.3.4', # 0.4 * m^2
- 'm--1',
-
+ "m(2.3)",
+ "m(2.3m)",
+ "(1.2)(2.4)",
+ "(5m(6s-1))",
+ "2*3*4/5m/6*7*8",
+ "m/2",
+ "m1",
+ "m m",
+ "m2",
+ "m+2",
+ "m¹",
+ "m²",
+ "m³",
+ "2⁴", # NOTE: Udunits can't do m⁴ for some reason. Bug?
+ "2⁵",
+ "2⁴²",
+ "3⁻²",
+ "m2 s2",
+ "m^2*s^2",
+ "1-2",
+ "1-2-3", # nb. looks a bit like a date, but it isn't!
+ "m-1",
+ "m^2",
+ "m^+2",
+ "m^-1",
+ "m.2", # This is 2*m
+ "m.+2", # 2*m
+ "m.2.4", # This is 2.4 * m
+ "m0.2", # But this is 2 m^0
+ "m2.5", # And this is 5m^2
+ "m2.3.4", # 0.4 * m^2
+ "m--1",
# Division
- 'm per 2',
- 'm per s',
- 'm / 2',
-
+ "m per 2",
+ "m per s",
+ "m / 2",
# Shift
- 'm@10',
- 'm @10',
- 'm @ 10',
- 'm@ 10',
- 'm from2',
- 'm from2e-1',
- '(m @ 10) (s @ 10)',
-
+ "m@10",
+ "m @10",
+ "m @ 10",
+ "m@ 10",
+ "m from2",
+ "m from2e-1",
+ "(m @ 10) (s @ 10)",
# Date shift
- 's from 1990',
- 'minutes since 1990',
- 'hour@1990',
- 'hours from 1990-1',
- 'hours from 1990-1-1',
- 'hours from 1990-1-1 0',
- 'hours from 1990-1-1 0:1:1',
- 'hours from 1990-1-1 0:0:1 +2',
- 's since 1990-1-2+5:2:2',
- 's since 1990-1-2+5:2',
- 's since 1990-1-2 5 6:0', # Undocumented packed_clock format?
- 's since 19900102T5', # Packed format (undocumented?)
- 's since 19900101T190030 +2',
- 's since 199022T1', # UGLY! (bug?).
-
- 's since 1990 +2:0:2.9',
- 's since 1990-2T1',
- 'hours from 1990-1-1 -19:4:2',
- 'hours from 1990-1-1 3+1',
-
- 'seconds from 1990-1-1 0:0:0 +2550',
- 's since 1990-1-2+5:2:2',
- 'hours from 1990-1-1 0:1:60',
- 'hours from 1990-1-1 0:1:62',
-
- '(hours since 1900) (s since 1980)', # Really fruity behaviour.
-
+ "s from 1990",
+ "minutes since 1990",
+ "hour@1990",
+ "hours from 1990-1",
+ "hours from 1990-1-1",
+ "hours from 1990-1-1 0",
+ "hours from 1990-1-1 0:1:1",
+ "hours from 1990-1-1 0:0:1 +2",
+ "s since 1990-1-2+5:2:2",
+ "s since 1990-1-2+5:2",
+ "s since 1990-1-2 5 6:0", # Undocumented packed_clock format?
+ "s since 19900102T5", # Packed format (undocumented?)
+ "s since 19900101T190030 +2",
+ "s since 199022T1", # UGLY! (bug?).
+ "s since 1990 +2:0:2.9",
+ "s since 1990-2T1",
+ "hours from 1990-1-1 -19:4:2",
+ "hours from 1990-1-1 3+1",
+ "seconds from 1990-1-1 0:0:0 +2550",
+ "s since 1990-1-2+5:2:2",
+ "hours from 1990-1-1 0:1:60",
+ "hours from 1990-1-1 0:1:62",
+ "(hours since 1900) (s since 1980)", # Really fruity behaviour.
# Unicode / constants
- 'π',
- 'e',
- '°C',
+ "π",
+ "e",
+ "°C",
]
invalid = [
- '1 * m',
- 'm--m',
- '-m',
- '.1e2.',
- 'm+-1',
- '--1',
- '+-1',
- '--3.1',
- '$',
- '£',
- 'hours from 1990-0-0 0:0:0',
- 'hours since 1900-1 10:12 10:0 1',
- 's since 1990:01:02T1900 +1',
+ "1 * m",
+ "m--m",
+ "-m",
+ ".1e2.",
+ "m+-1",
+ "--1",
+ "+-1",
+ "--3.1",
+ "$",
+ "£",
+ "hours from 1990-0-0 0:0:0",
+ "hours since 1900-1 10:12 10:0 1",
+ "s since 1990:01:02T1900 +1",
]
@@ -167,10 +154,7 @@ def test_normed_units_equivalent(_, unit_str):
assert raw_symbol == parsed_expr_symbol
-udunits_bugs = [
- '2¹²³⁴⁵⁶⁷⁸⁹⁰',
- 'm⁻²'
-]
+udunits_bugs = ["2¹²³⁴⁵⁶⁷⁸⁹⁰", "m⁻²"]
@pytest.mark.parametrize("_, unit_str", enumerate(invalid))
@@ -184,8 +168,9 @@ def test_invalid_units(_, unit_str):
cf_valid = False
# Double check that udunits2 can't parse this.
- assert cf_valid is False, \
- 'Unit {!r} is unexpectedly valid in UDUNITS2'.format(unit_str)
+ assert (
+ cf_valid is False
+ ), "Unit {!r} is unexpectedly valid in UDUNITS2".format(unit_str)
try:
normalize(unit_str)
@@ -194,7 +179,7 @@ def test_invalid_units(_, unit_str):
can_parse = False
# Now confirm that we couldn't parse this either.
- msg = 'Parser unexpectedly able to deal with {}'.format(unit_str)
+ msg = "Parser unexpectedly able to deal with {}".format(unit_str)
assert can_parse is False, msg
@@ -204,21 +189,20 @@ def multi_enumerate(items):
not_udunits = [
- ['foo', 'foo'],
- ['mfrom1', 'mfrom^1'],
- ['m⁴', 'm^4'], # udunits bug.
- ['2¹²³⁴⁵⁶⁷⁸⁹⁰', '2^1234567890'],
-
+ ["foo", "foo"],
+ ["mfrom1", "mfrom^1"],
+ ["m⁴", "m^4"], # udunits bug.
+ ["2¹²³⁴⁵⁶⁷⁸⁹⁰", "2^1234567890"],
# Unicode (subset of the subset).
- ['À'] * 2,
- ['Á'] * 2,
- ['Ö'] * 2,
- ['Ø'] * 2,
- ['ö'] * 2,
- ['ø'] * 2,
- ['ÿ'] * 2,
- ['µ'] * 2,
- ['µ°F·Ω⁻¹', 'µ°F·Ω^-1'],
+ ["À"] * 2,
+ ["Á"] * 2,
+ ["Ö"] * 2,
+ ["Ø"] * 2,
+ ["ö"] * 2,
+ ["ø"] * 2,
+ ["ÿ"] * 2,
+ ["µ"] * 2,
+ ["µ°F·Ω⁻¹", "µ°F·Ω^-1"],
]
@@ -241,19 +225,19 @@ def test_invalid_in_udunits_but_still_parses(_, unit_str, expected):
known_issues = [
# Disabled due to crazy results from UDUNITS.
- ['s since +1990 +2:0:2.9', SyntaxError],
- ['s since -1990 +2:0:2.9', SyntaxError],
-
+ ["s since +1990 +2:0:2.9", SyntaxError],
+ ["s since -1990 +2:0:2.9", SyntaxError],
# The following are not yet implemented.
- ['hours since 2001-12-31 23:59:59.999UTC', SyntaxError],
- ['hours since 2001-12-31 23:59:59.999 Z', SyntaxError],
- ['hours since 2001-12-31 23:59:59.999 GMT', SyntaxError],
- ['0.1 lg(re 1 mW)', SyntaxError],
+ ["hours since 2001-12-31 23:59:59.999UTC", SyntaxError],
+ ["hours since 2001-12-31 23:59:59.999 Z", SyntaxError],
+ ["hours since 2001-12-31 23:59:59.999 GMT", SyntaxError],
+ ["0.1 lg(re 1 mW)", SyntaxError],
]
-@pytest.mark.parametrize("_, unit_str, expected",
- multi_enumerate(known_issues))
+@pytest.mark.parametrize(
+ "_, unit_str, expected", multi_enumerate(known_issues)
+)
def test_known_issues(_, unit_str, expected):
# Unfortunately the grammar is not perfect.
# These are the cases that don't work yet but which do work with udunits.
@@ -274,7 +258,7 @@ def test_syntax_parse_error_quality():
msg = re.escape(r"no viable alternative at input 'm^m' (inline, line 1)")
with pytest.raises(SyntaxError, match=msg) as err:
- normalize('m^m 2s')
+ normalize("m^m 2s")
# The problem is with the m after "^", so make sure the exception is
# pointing at it (including the leading speechmark).
assert err.value.offset == 4
@@ -286,7 +270,7 @@ def test_unknown_symbol_error():
# The × character is explicitly excluded in the UDUNITS2
# implementation. It would make some sense to support it in the
# future though.
- normalize('Thing×Another')
+ normalize("Thing×Another")
# The 7th character (including the speechmark) is the problem, check that
# the exception points at the right location.
# correct location...
@@ -297,10 +281,10 @@ def test_unknown_symbol_error():
not_allowed = [
- 'hours from 1990-1-1 -20:4:18 +2',
- 'm++2',
- 'm s^(-1)',
- 'm per /s',
+ "hours from 1990-1-1 -20:4:18 +2",
+ "m++2",
+ "m s^(-1)",
+ "m per /s",
]
diff --git a/cf_units/tests/integration/test__num2date_to_nearest_second.py b/cf_units/tests/integration/test__num2date_to_nearest_second.py
index 617696b5..f4b12b6b 100644
--- a/cf_units/tests/integration/test__num2date_to_nearest_second.py
+++ b/cf_units/tests/integration/test__num2date_to_nearest_second.py
@@ -5,11 +5,11 @@
# licensing details.
"""Test function :func:`cf_units._num2date_to_nearest_second`."""
-import unittest
import datetime
+import unittest
-import numpy as np
import cftime
+import numpy as np
import cf_units
from cf_units import _num2date_to_nearest_second
@@ -17,15 +17,16 @@
class Test(unittest.TestCase):
def setup_units(self, calendar):
- self.useconds = cf_units.Unit('seconds since 1970-01-01', calendar)
- self.uminutes = cf_units.Unit('minutes since 1970-01-01', calendar)
- self.uhours = cf_units.Unit('hours since 1970-01-01', calendar)
- self.udays = cf_units.Unit('days since 1970-01-01', calendar)
+ self.useconds = cf_units.Unit("seconds since 1970-01-01", calendar)
+ self.uminutes = cf_units.Unit("minutes since 1970-01-01", calendar)
+ self.uhours = cf_units.Unit("hours since 1970-01-01", calendar)
+ self.udays = cf_units.Unit("days since 1970-01-01", calendar)
def check_dates(self, nums, units, expected, only_cftime=True):
for num, unit, exp in zip(nums, units, expected):
res = _num2date_to_nearest_second(
- num, unit, only_use_cftime_datetimes=only_cftime)
+ num, unit, only_use_cftime_datetimes=only_cftime
+ )
self.assertEqual(exp, res)
self.assertIsInstance(res, type(exp))
@@ -38,235 +39,295 @@ def check_timedelta(self, nums, units, expected):
self.assertEqual((delta.days, seconds), exp)
def test_scalar(self):
- unit = cf_units.Unit('seconds since 1970-01-01', 'gregorian')
- num = 5.
+ unit = cf_units.Unit("seconds since 1970-01-01", "gregorian")
+ num = 5.0
exp = datetime.datetime(1970, 1, 1, 0, 0, 5)
res = _num2date_to_nearest_second(num, unit)
self.assertEqual(exp, res)
self.assertIsInstance(res, cftime.datetime)
def test_sequence(self):
- unit = cf_units.Unit('seconds since 1970-01-01', 'gregorian')
- nums = [20., 40., 60., 80, 100.]
- exp = [datetime.datetime(1970, 1, 1, 0, 0, 20),
- datetime.datetime(1970, 1, 1, 0, 0, 40),
- datetime.datetime(1970, 1, 1, 0, 1),
- datetime.datetime(1970, 1, 1, 0, 1, 20),
- datetime.datetime(1970, 1, 1, 0, 1, 40)]
+ unit = cf_units.Unit("seconds since 1970-01-01", "gregorian")
+ nums = [20.0, 40.0, 60.0, 80, 100.0]
+ exp = [
+ datetime.datetime(1970, 1, 1, 0, 0, 20),
+ datetime.datetime(1970, 1, 1, 0, 0, 40),
+ datetime.datetime(1970, 1, 1, 0, 1),
+ datetime.datetime(1970, 1, 1, 0, 1, 20),
+ datetime.datetime(1970, 1, 1, 0, 1, 40),
+ ]
res = _num2date_to_nearest_second(nums, unit)
np.testing.assert_array_equal(exp, res)
def test_multidim_sequence(self):
- unit = cf_units.Unit('seconds since 1970-01-01', 'gregorian')
- nums = [[20., 40., 60.],
- [80, 100., 120.]]
+ unit = cf_units.Unit("seconds since 1970-01-01", "gregorian")
+ nums = [[20.0, 40.0, 60.0], [80, 100.0, 120.0]]
exp_shape = (2, 3)
res = _num2date_to_nearest_second(nums, unit)
self.assertEqual(exp_shape, res.shape)
def test_masked_ndarray(self):
- unit = cf_units.Unit('seconds since 1970-01-01', 'gregorian')
- nums = np.ma.masked_array([20., 40., 60.], [False, True, False])
- exp = [datetime.datetime(1970, 1, 1, 0, 0, 20),
- None,
- datetime.datetime(1970, 1, 1, 0, 1)]
+ unit = cf_units.Unit("seconds since 1970-01-01", "gregorian")
+ nums = np.ma.masked_array([20.0, 40.0, 60.0], [False, True, False])
+ exp = [
+ datetime.datetime(1970, 1, 1, 0, 0, 20),
+ None,
+ datetime.datetime(1970, 1, 1, 0, 1),
+ ]
res = _num2date_to_nearest_second(nums, unit)
np.testing.assert_array_equal(exp, res)
# Gregorian Calendar tests
def test_simple_gregorian_py_datetime_type(self):
- self.setup_units('gregorian')
- nums = [20., 40.,
- 75., 150.,
- 8., 16.,
- 300., 600.]
- units = [self.useconds, self.useconds,
- self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
- expected = [datetime.datetime(1970, 1, 1, 0, 0, 20),
- datetime.datetime(1970, 1, 1, 0, 0, 40),
- datetime.datetime(1970, 1, 1, 1, 15),
- datetime.datetime(1970, 1, 1, 2, 30),
- datetime.datetime(1970, 1, 1, 8),
- datetime.datetime(1970, 1, 1, 16),
- datetime.datetime(1970, 10, 28),
- datetime.datetime(1971, 8, 24)]
+ self.setup_units("gregorian")
+ nums = [20.0, 40.0, 75.0, 150.0, 8.0, 16.0, 300.0, 600.0]
+ units = [
+ self.useconds,
+ self.useconds,
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
+ expected = [
+ datetime.datetime(1970, 1, 1, 0, 0, 20),
+ datetime.datetime(1970, 1, 1, 0, 0, 40),
+ datetime.datetime(1970, 1, 1, 1, 15),
+ datetime.datetime(1970, 1, 1, 2, 30),
+ datetime.datetime(1970, 1, 1, 8),
+ datetime.datetime(1970, 1, 1, 16),
+ datetime.datetime(1970, 10, 28),
+ datetime.datetime(1971, 8, 24),
+ ]
self.check_dates(nums, units, expected, only_cftime=False)
def test_simple_gregorian(self):
- self.setup_units('gregorian')
- nums = [20., 40.,
- 75., 150.,
- 8., 16.,
- 300., 600.]
- units = [self.useconds, self.useconds,
- self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("gregorian")
+ nums = [20.0, 40.0, 75.0, 150.0, 8.0, 16.0, 300.0, 600.0]
+ units = [
+ self.useconds,
+ self.useconds,
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
expected = [
- cftime.datetime(1970, 1, 1, 0, 0, 20, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 40, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 1, 15, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 2, 30, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 8, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 16, calendar='gregorian'),
- cftime.datetime(1970, 10, 28, calendar='gregorian'),
- cftime.datetime(1971, 8, 24, calendar='gregorian')]
+ cftime.datetime(1970, 1, 1, 0, 0, 20, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 40, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 1, 15, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 2, 30, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 8, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 16, calendar="gregorian"),
+ cftime.datetime(1970, 10, 28, calendar="gregorian"),
+ cftime.datetime(1971, 8, 24, calendar="gregorian"),
+ ]
self.check_dates(nums, units, expected)
def test_fractional_gregorian(self):
- self.setup_units('gregorian')
- nums = [5./60., 10./60.,
- 15./60., 30./60.,
- 8./24., 16./24.]
- units = [self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("gregorian")
+ nums = [
+ 5.0 / 60.0,
+ 10.0 / 60.0,
+ 15.0 / 60.0,
+ 30.0 / 60.0,
+ 8.0 / 24.0,
+ 16.0 / 24.0,
+ ]
+ units = [
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
expected = [
- cftime.datetime(1970, 1, 1, 0, 0, 5, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 10, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 15, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 30, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 8, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 16, calendar='gregorian')]
+ cftime.datetime(1970, 1, 1, 0, 0, 5, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 10, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 15, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 30, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 8, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 16, calendar="gregorian"),
+ ]
self.check_dates(nums, units, expected)
def test_fractional_second_gregorian(self):
- self.setup_units('gregorian')
- nums = [0.25, 0.5, 0.75,
- 1.5, 2.5, 3.5, 4.5]
+ self.setup_units("gregorian")
+ nums = [0.25, 0.5, 0.75, 1.5, 2.5, 3.5, 4.5]
units = [self.useconds] * 7
- expected = [cftime.datetime(1970, 1, 1, 0, 0, 0, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 1, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 1, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 2, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 3, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 4, calendar='gregorian'),
- cftime.datetime(1970, 1, 1, 0, 0, 5, calendar='gregorian')]
+ expected = [
+ cftime.datetime(1970, 1, 1, 0, 0, 0, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 1, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 1, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 2, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 3, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 4, calendar="gregorian"),
+ cftime.datetime(1970, 1, 1, 0, 0, 5, calendar="gregorian"),
+ ]
self.check_dates(nums, units, expected)
# 360 day Calendar tests
def test_simple_360_day(self):
- self.setup_units('360_day')
- nums = [20., 40.,
- 75., 150.,
- 8., 16.,
- 300., 600.]
- units = [self.useconds, self.useconds,
- self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("360_day")
+ nums = [20.0, 40.0, 75.0, 150.0, 8.0, 16.0, 300.0, 600.0]
+ units = [
+ self.useconds,
+ self.useconds,
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, nums[0]),
- (0, nums[1]),
- (0, nums[2]*60),
- (0, nums[3]*60),
- (0, nums[4]*60*60),
- (0, nums[5]*60*60),
- (nums[6], 0),
- (nums[7], 0)]
+ expected = [
+ (0, nums[0]),
+ (0, nums[1]),
+ (0, nums[2] * 60),
+ (0, nums[3] * 60),
+ (0, nums[4] * 60 * 60),
+ (0, nums[5] * 60 * 60),
+ (nums[6], 0),
+ (nums[7], 0),
+ ]
self.check_timedelta(nums, units, expected)
def test_fractional_360_day(self):
- self.setup_units('360_day')
- nums = [5./60., 10./60.,
- 15./60., 30./60.,
- 8./24., 16./24.]
- units = [self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("360_day")
+ nums = [
+ 5.0 / 60.0,
+ 10.0 / 60.0,
+ 15.0 / 60.0,
+ 30.0 / 60.0,
+ 8.0 / 24.0,
+ 16.0 / 24.0,
+ ]
+ units = [
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, nums[0]*60),
- (0, nums[1]*60),
- (0, nums[2]*60*60),
- (0, nums[3]*60*60),
- (0, nums[4]*24*60*60),
- (0, nums[5]*24*60*60)]
+ expected = [
+ (0, nums[0] * 60),
+ (0, nums[1] * 60),
+ (0, nums[2] * 60 * 60),
+ (0, nums[3] * 60 * 60),
+ (0, nums[4] * 24 * 60 * 60),
+ (0, nums[5] * 24 * 60 * 60),
+ ]
self.check_timedelta(nums, units, expected)
def test_fractional_second_360_day(self):
- self.setup_units('360_day')
- nums = [0.25, 0.5, 0.75,
- 1.5, 2.5, 3.5, 4.5]
+ self.setup_units("360_day")
+ nums = [0.25, 0.5, 0.75, 1.5, 2.5, 3.5, 4.5]
units = [self.useconds] * 7
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, 0.0), # rounded down to this
- (0, 1.0), # rounded up to this
- (0, 1.0), # rounded up to this
- (0, 2.0), # rounded up to this
- (0, 3.0), # rounded up to this
- (0, 4.0), # rounded up to this
- (0, 5.0)] # rounded up to this
+ expected = [
+ (0, 0.0), # rounded down to this
+ (0, 1.0), # rounded up to this
+ (0, 1.0), # rounded up to this
+ (0, 2.0), # rounded up to this
+ (0, 3.0), # rounded up to this
+ (0, 4.0), # rounded up to this
+ (0, 5.0),
+ ] # rounded up to this
self.check_timedelta(nums, units, expected)
# 365 day Calendar tests
def test_simple_365_day(self):
- self.setup_units('365_day')
- nums = [20., 40.,
- 75., 150.,
- 8., 16.,
- 300., 600.]
- units = [self.useconds, self.useconds,
- self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("365_day")
+ nums = [20.0, 40.0, 75.0, 150.0, 8.0, 16.0, 300.0, 600.0]
+ units = [
+ self.useconds,
+ self.useconds,
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, nums[0]),
- (0, nums[1]),
- (0, nums[2]*60),
- (0, nums[3]*60),
- (0, nums[4]*60*60),
- (0, nums[5]*60*60),
- (nums[6], 0),
- (nums[7], 0)]
+ expected = [
+ (0, nums[0]),
+ (0, nums[1]),
+ (0, nums[2] * 60),
+ (0, nums[3] * 60),
+ (0, nums[4] * 60 * 60),
+ (0, nums[5] * 60 * 60),
+ (nums[6], 0),
+ (nums[7], 0),
+ ]
self.check_timedelta(nums, units, expected)
def test_fractional_365_day(self):
- self.setup_units('365_day')
- nums = [5./60., 10./60.,
- 15./60., 30./60.,
- 8./24., 16./24.]
- units = [self.uminutes, self.uminutes,
- self.uhours, self.uhours,
- self.udays, self.udays]
+ self.setup_units("365_day")
+ nums = [
+ 5.0 / 60.0,
+ 10.0 / 60.0,
+ 15.0 / 60.0,
+ 30.0 / 60.0,
+ 8.0 / 24.0,
+ 16.0 / 24.0,
+ ]
+ units = [
+ self.uminutes,
+ self.uminutes,
+ self.uhours,
+ self.uhours,
+ self.udays,
+ self.udays,
+ ]
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, nums[0]*60),
- (0, nums[1]*60),
- (0, nums[2]*60*60),
- (0, nums[3]*60*60),
- (0, nums[4]*24*60*60),
- (0, nums[5]*24*60*60)]
+ expected = [
+ (0, nums[0] * 60),
+ (0, nums[1] * 60),
+ (0, nums[2] * 60 * 60),
+ (0, nums[3] * 60 * 60),
+ (0, nums[4] * 24 * 60 * 60),
+ (0, nums[5] * 24 * 60 * 60),
+ ]
self.check_timedelta(nums, units, expected)
def test_fractional_second_365_day(self):
- self.setup_units('365_day')
- nums = [0.25, 0.5, 0.75,
- 1.5, 2.5, 3.5, 4.5]
+ self.setup_units("365_day")
+ nums = [0.25, 0.5, 0.75, 1.5, 2.5, 3.5, 4.5]
units = [self.useconds] * 7
# Expected results in (days, seconds) delta from unit epoch.
- expected = [(0, 0.0), # rounded down to this
- (0, 1.0), # rounded up to this
- (0, 1.0), # rounded up to this
- (0, 2.0), # rounded up to this
- (0, 3.0), # rounded up to this
- (0, 4.0), # rounded up to this
- (0, 5.0)] # rounded up to this
+ expected = [
+ (0, 0.0), # rounded down to this
+ (0, 1.0), # rounded up to this
+ (0, 1.0), # rounded up to this
+ (0, 2.0), # rounded up to this
+ (0, 3.0), # rounded up to this
+ (0, 4.0), # rounded up to this
+ (0, 5.0),
+ ] # rounded up to this
self.check_timedelta(nums, units, expected)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/cf_units/tests/integration/test_date2num.py b/cf_units/tests/integration/test_date2num.py
index 9820d585..7905c31e 100644
--- a/cf_units/tests/integration/test_date2num.py
+++ b/cf_units/tests/integration/test_date2num.py
@@ -5,8 +5,8 @@
# licensing details.
"""Test function :func:`cf_units.date2num`."""
-import unittest
import datetime
+import unittest
import numpy as np
@@ -15,53 +15,61 @@
class Test(unittest.TestCase):
def setUp(self):
- self.unit = 'seconds since 1970-01-01'
- self.calendar = 'gregorian'
+ self.unit = "seconds since 1970-01-01"
+ self.calendar = "gregorian"
def test_single(self):
date = datetime.datetime(1970, 1, 1, 0, 0, 5)
- exp = 5.
+ exp = 5.0
res = date2num(date, self.unit, self.calendar)
# num2date won't return an exact value representing the date,
# even if one exists
self.assertAlmostEqual(exp, res, places=4)
def test_sequence(self):
- dates = [datetime.datetime(1970, 1, 1, 0, 0, 20),
- datetime.datetime(1970, 1, 1, 0, 0, 40),
- datetime.datetime(1970, 1, 1, 0, 1),
- datetime.datetime(1970, 1, 1, 0, 1, 20),
- datetime.datetime(1970, 1, 1, 0, 1, 40)]
- exp = [20., 40., 60., 80, 100.]
+ dates = [
+ datetime.datetime(1970, 1, 1, 0, 0, 20),
+ datetime.datetime(1970, 1, 1, 0, 0, 40),
+ datetime.datetime(1970, 1, 1, 0, 1),
+ datetime.datetime(1970, 1, 1, 0, 1, 20),
+ datetime.datetime(1970, 1, 1, 0, 1, 40),
+ ]
+ exp = [20.0, 40.0, 60.0, 80, 100.0]
res = date2num(dates, self.unit, self.calendar)
np.testing.assert_array_almost_equal(exp, res, decimal=4)
def test_multidim_sequence(self):
- dates = [[datetime.datetime(1970, 1, 1, 0, 0, 20),
- datetime.datetime(1970, 1, 1, 0, 0, 40),
- datetime.datetime(1970, 1, 1, 0, 1)],
- [datetime.datetime(1970, 1, 1, 0, 1, 20),
- datetime.datetime(1970, 1, 1, 0, 1, 40),
- datetime.datetime(1970, 1, 1, 0, 2)]]
+ dates = [
+ [
+ datetime.datetime(1970, 1, 1, 0, 0, 20),
+ datetime.datetime(1970, 1, 1, 0, 0, 40),
+ datetime.datetime(1970, 1, 1, 0, 1),
+ ],
+ [
+ datetime.datetime(1970, 1, 1, 0, 1, 20),
+ datetime.datetime(1970, 1, 1, 0, 1, 40),
+ datetime.datetime(1970, 1, 1, 0, 2),
+ ],
+ ]
exp_shape = (2, 3)
res = date2num(dates, self.unit, self.calendar)
self.assertEqual(exp_shape, res.shape)
def test_discard_mircosecond(self):
date = datetime.datetime(1970, 1, 1, 0, 0, 5, 750000)
- exp = 5.
+ exp = 5.0
res = date2num(date, self.unit, self.calendar)
self.assertAlmostEqual(exp, res, places=4)
def test_long_time_interval(self):
# This test should fail with an error that we need to catch properly.
- unit = 'years since 1970-01-01'
+ unit = "years since 1970-01-01"
date = datetime.datetime(1970, 1, 1, 0, 0, 5)
exp_emsg = 'interval of "months", "years" .* got "years".'
with self.assertRaisesRegex(ValueError, exp_emsg):
date2num(date, unit, self.calendar)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/cf_units/tests/test_coding_standards.py b/cf_units/tests/test_coding_standards.py
index a1ebc073..c9689c86 100644
--- a/cf_units/tests/test_coding_standards.py
+++ b/cf_units/tests/test_coding_standards.py
@@ -4,18 +4,15 @@
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.
-from datetime import datetime
-from fnmatch import fnmatch
-from glob import glob
-from itertools import chain
import os
-import re
import subprocess
import unittest
+from datetime import datetime
+from fnmatch import fnmatch
+from glob import glob
import cf_units
-
LICENSE_TEMPLATE = """# Copyright cf-units contributors
#
# This file is part of cf-units and is released under the LGPL license.
@@ -27,12 +24,15 @@
# against Python finding the cf_units package via a symlink.
DIR = os.path.realpath(os.path.dirname(cf_units.__file__))
REPO_DIR = os.path.dirname(DIR)
-DOCS_DIR = os.path.join(REPO_DIR, 'doc')
-DOCS_DIR = cf_units.config.get_option('Resources', 'doc_dir', default=DOCS_DIR)
-exclusion = ['Makefile', 'make.bat', 'build']
-DOCS_DIRS = glob(os.path.join(DOCS_DIR, '*'))
-DOCS_DIRS = [DOC_DIR for DOC_DIR in DOCS_DIRS if os.path.basename(DOC_DIR) not
- in exclusion]
+DOCS_DIR = os.path.join(REPO_DIR, "doc")
+DOCS_DIR = cf_units.config.get_option("Resources", "doc_dir", default=DOCS_DIR)
+exclusion = ["Makefile", "make.bat", "build"]
+DOCS_DIRS = glob(os.path.join(DOCS_DIR, "*"))
+DOCS_DIRS = [
+ DOC_DIR
+ for DOC_DIR in DOCS_DIRS
+ if os.path.basename(DOC_DIR) not in exclusion
+]
class TestLicenseHeaders(unittest.TestCase):
@@ -53,12 +53,12 @@ def whatchanged_parse(whatchanged_output):
for line in whatchanged_output:
if not line.strip():
continue
- elif line.startswith('TIME:'):
+ elif line.startswith("TIME:"):
dt = datetime.fromtimestamp(int(line[5:]))
else:
# Non blank, non date, line -> must be the lines
# containing the file info.
- fname = ' '.join(line.split('\t')[1:])
+ fname = " ".join(line.split("\t")[1:])
yield fname, dt
@staticmethod
@@ -75,16 +75,16 @@ def last_change_by_fname():
"""
# Check the ".git" folder exists at the repo dir.
- if not os.path.isdir(os.path.join(REPO_DIR, '.git')):
- raise ValueError('{} is not a git repository.'.format(REPO_DIR))
+ if not os.path.isdir(os.path.join(REPO_DIR, ".git")):
+ raise ValueError("{} is not a git repository.".format(REPO_DIR))
# Call "git whatchanged" to get the details of all the files and when
# they were last changed.
- output = subprocess.check_output(['git', 'whatchanged',
- "--pretty=TIME:%ct"],
- cwd=REPO_DIR)
- output = str(output.decode('ascii'))
- output = output.split('\n')
+ output = subprocess.check_output(
+ ["git", "whatchanged", "--pretty=TIME:%ct"], cwd=REPO_DIR
+ )
+ output = str(output.decode("ascii"))
+ output = output.split("\n")
res = {}
for fname, dt in TestLicenseHeaders.whatchanged_parse(output):
if fname not in res or dt > res[fname]:
@@ -93,38 +93,44 @@ def last_change_by_fname():
return res
def test_license_headers(self):
- exclude_patterns = ('setup.py',
- 'build/*',
- 'dist/*',
- 'cf_units/_version.py',
- 'cf_units/_udunits2_parser/parser/*'
- )
+ exclude_patterns = (
+ "setup.py",
+ "build/*",
+ "dist/*",
+ "cf_units/_version.py",
+ "cf_units/_udunits2_parser/parser/*",
+ )
try:
last_change_by_fname = self.last_change_by_fname()
except ValueError:
# Caught the case where this is not a git repo.
- return self.skipTest('cf_units installation did not look like a '
- 'git repo.')
+ return self.skipTest(
+ "cf_units installation did not look like a " "git repo."
+ )
failed = False
for fname, last_change in sorted(last_change_by_fname.items()):
full_fname = os.path.join(REPO_DIR, fname)
- if full_fname.endswith('.py') and os.path.isfile(full_fname) and \
- not any(fnmatch(fname, pat) for pat in exclude_patterns):
+ if (
+ full_fname.endswith(".py")
+ and os.path.isfile(full_fname)
+ and not any(fnmatch(fname, pat) for pat in exclude_patterns)
+ ):
with open(full_fname) as fh:
content = fh.read()
if not content.startswith(LICENSE_TEMPLATE):
print(
- f'The file {fname} does not start with the '
- f'required license header.'
+ f"The file {fname} does not start with the "
+ f"required license header."
)
failed = True
if failed:
raise AssertionError(
- 'There were license header failures. See stdout.')
+ "There were license header failures. See stdout."
+ )
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/cf_units/tests/test_tex.py b/cf_units/tests/test_tex.py
index 37fae3fb..667896c1 100644
--- a/cf_units/tests/test_tex.py
+++ b/cf_units/tests/test_tex.py
@@ -4,42 +4,40 @@
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.
-import pytest
-
from cf_units.tex import tex
def test_basic():
- u = 'kg kg-1'
- assert tex(u) == r'{kg}\cdot{{kg}^{-1}}'
+ u = "kg kg-1"
+ assert tex(u) == r"{kg}\cdot{{kg}^{-1}}"
def test_identifier_micro():
- u = 'microW m-2'
- assert tex(u) == r'{{\mu}W}\cdot{{m}^{-2}}'
+ u = "microW m-2"
+ assert tex(u) == r"{{\mu}W}\cdot{{m}^{-2}}"
def test_raise():
- u = 'm^2'
- assert tex(u) == r'{m}^{2}'
+ u = "m^2"
+ assert tex(u) == r"{m}^{2}"
def test_multiply():
- u = 'foo bar'
- assert tex(u) == r'{foo}\cdot{bar}'
+ u = "foo bar"
+ assert tex(u) == r"{foo}\cdot{bar}"
def test_divide():
- u = 'foo per bar'
- assert tex(u) == r'\frac{foo}{bar}'
+ u = "foo per bar"
+ assert tex(u) == r"\frac{foo}{bar}"
def test_shift():
- u = 'foo @ 50'
- assert tex(u) == r'{foo} @ {50}'
+ u = "foo @ 50"
+ assert tex(u) == r"{foo} @ {50}"
def test_complex():
- u = 'microW^2 / (5^-2)π per sec @ 42'
- expected = r'{\frac{{\frac{{{\mu}W}^{2}}{{5}^{-2}}}\cdot{π}}{sec}} @ {42}'
+ u = "microW^2 / (5^-2)π per sec @ 42"
+ expected = r"{\frac{{\frac{{{\mu}W}^{2}}{{5}^{-2}}}\cdot{π}}{sec}} @ {42}"
assert tex(u) == expected
diff --git a/cf_units/tests/test_unit.py b/cf_units/tests/test_unit.py
index 1bf51567..f1d58bcd 100644
--- a/cf_units/tests/test_unit.py
+++ b/cf_units/tests/test_unit.py
@@ -8,540 +8,519 @@
"""
-import unittest
import copy
import datetime as datetime
import operator
import re
-
+import unittest
from operator import truediv
-import numpy as np
import cftime
+import numpy as np
import cf_units as unit
from cf_units import suppress_errors
-
Unit = unit.Unit
class Test_unit__creation(unittest.TestCase):
-
def test_is_valid_unit_string(self):
- unit_strs = [' meter',
- 'meter ',
- ' meter ']
+ unit_strs = [" meter", "meter ", " meter "]
for unit_str in unit_strs:
u = Unit(unit_str)
- self.assertEqual(u.name, 'meter')
+ self.assertEqual(u.name, "meter")
def test_not_valid_unit_str(self):
- with self.assertRaisesRegex(ValueError, 'Failed to parse unit'):
- Unit('wibble')
+ with self.assertRaisesRegex(ValueError, "Failed to parse unit"):
+ Unit("wibble")
def test_calendar(self):
calendar = unit.CALENDAR_365_DAY
- u = Unit('hours since 1970-01-01 00:00:00', calendar=calendar)
+ u = Unit("hours since 1970-01-01 00:00:00", calendar=calendar)
self.assertEqual(u.calendar, calendar)
def test_calendar_alias(self):
calendar = unit.CALENDAR_NO_LEAP
- u = Unit('hours since 1970-01-01 00:00:00', calendar=calendar)
+ u = Unit("hours since 1970-01-01 00:00:00", calendar=calendar)
self.assertEqual(u.calendar, unit.CALENDAR_365_DAY)
def test_no_calendar(self):
- u = Unit('hours since 1970-01-01 00:00:00')
+ u = Unit("hours since 1970-01-01 00:00:00")
self.assertEqual(u.calendar, unit.CALENDAR_GREGORIAN)
def test_unsupported_calendar(self):
- with self.assertRaisesRegex(ValueError, 'unsupported calendar'):
- Unit('hours since 1970-01-01 00:00:00', calendar='wibble')
+ with self.assertRaisesRegex(ValueError, "unsupported calendar"):
+ Unit("hours since 1970-01-01 00:00:00", calendar="wibble")
def test_calendar_w_unicode(self):
calendar = unit.CALENDAR_365_DAY
- u = Unit(u'hours\xb2 hours-1 since epoch', calendar=calendar)
+ u = Unit("hours\xb2 hours-1 since epoch", calendar=calendar)
self.assertEqual(u.calendar, calendar)
- expected = u'hours² hours-1 since 1970-01-01 00:00:00'
+ expected = "hours² hours-1 since 1970-01-01 00:00:00"
self.assertEqual(u.origin, expected)
def test_unicode_valid(self):
# Some unicode characters are allowed.
- u = Unit(u'm²')
- assert u.symbol == 'm2'
+ u = Unit("m²")
+ assert u.symbol == "m2"
def test_py2k_unicode(self):
- u = Unit(u'm\xb2')
- assert u.symbol == 'm2'
+ u = Unit("m\xb2")
+ assert u.symbol == "m2"
def test_unicode_invalid(self):
# Not all unicode characters are allowed.
- msg = r'Failed to parse unit \"ø\"'
+ msg = r"Failed to parse unit \"ø\""
with self.assertRaisesRegex(ValueError, msg):
- Unit('ø')
+ Unit("ø")
class Test_modulus(unittest.TestCase):
-
def test_modulus__degrees(self):
- u = Unit('degrees')
+ u = Unit("degrees")
self.assertEqual(u.modulus, 360.0)
def test_modulus__radians(self):
- u = Unit('radians')
- self.assertEqual(u.modulus, np.pi*2)
+ u = Unit("radians")
+ self.assertEqual(u.modulus, np.pi * 2)
def test_no_modulus(self):
- u = Unit('meter')
+ u = Unit("meter")
self.assertEqual(u.modulus, None)
class Test_is_convertible(unittest.TestCase):
-
def test_convert_distance_to_force(self):
- u = Unit('meter')
- v = Unit('newton')
+ u = Unit("meter")
+ v = Unit("newton")
self.assertFalse(u.is_convertible(v))
def test_convert_distance_units(self):
- u = Unit('meter')
- v = Unit('mile')
+ u = Unit("meter")
+ v = Unit("mile")
self.assertTrue(u.is_convertible(v))
def test_convert_to_unknown(self):
- u = Unit('meter')
- v = Unit('unknown')
+ u = Unit("meter")
+ v = Unit("unknown")
self.assertFalse(u.is_convertible(v))
self.assertFalse(v.is_convertible(u))
def test_convert_to_no_unit(self):
- u = Unit('meter')
- v = Unit('no unit')
+ u = Unit("meter")
+ v = Unit("no unit")
self.assertFalse(u.is_convertible(v))
self.assertFalse(v.is_convertible(u))
def test_convert_unknown_to_no_unit(self):
- u = Unit('unknown')
- v = Unit('no unit')
+ u = Unit("unknown")
+ v = Unit("no unit")
self.assertFalse(u.is_convertible(v))
self.assertFalse(v.is_convertible(u))
class Test_is_dimensionless(unittest.TestCase):
-
def test_dimensionless(self):
- u = Unit('1')
+ u = Unit("1")
self.assertTrue(u.is_dimensionless())
def test_distance_dimensionless(self):
- u = Unit('meter')
+ u = Unit("meter")
self.assertFalse(u.is_dimensionless())
def test_unknown_dimensionless(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_dimensionless())
def test_no_unit_dimensionless(self):
- u = Unit('no unit')
+ u = Unit("no unit")
self.assertFalse(u.is_dimensionless())
class Test_format(unittest.TestCase):
-
def test_basic(self):
- u = Unit('watt')
- self.assertEqual(u.format(), 'W')
+ u = Unit("watt")
+ self.assertEqual(u.format(), "W")
def test_format_ascii(self):
- u = Unit('watt')
- self.assertEqual(u.format(unit.UT_ASCII), 'W')
+ u = Unit("watt")
+ self.assertEqual(u.format(unit.UT_ASCII), "W")
def test_format_ut_names(self):
- u = Unit('watt')
- self.assertEqual(u.format(unit.UT_NAMES), 'watt')
+ u = Unit("watt")
+ self.assertEqual(u.format(unit.UT_NAMES), "watt")
def test_format_unit_definition(self):
- u = Unit('watt')
- self.assertEqual(u.format(unit.UT_DEFINITION), 'm2.kg.s-3')
+ u = Unit("watt")
+ self.assertEqual(u.format(unit.UT_DEFINITION), "m2.kg.s-3")
def test_format_multiple_options(self):
- u = Unit('watt')
+ u = Unit("watt")
self.assertEqual(
u.format([unit.UT_NAMES, unit.UT_DEFINITION]),
- 'meter^2-kilogram-second^-3')
+ "meter^2-kilogram-second^-3",
+ )
def test_format_multiple_options_utf8(self):
- u = Unit('watt')
+ u = Unit("watt")
self.assertEqual(
u.format([unit.UT_NAMES, unit.UT_DEFINITION, unit.UT_UTF8]),
- u'meter²·kilogram·second⁻³')
+ "meter²·kilogram·second⁻³",
+ )
def test_format_unknown(self):
- u = Unit('?')
- self.assertEqual(u.format(), 'unknown')
+ u = Unit("?")
+ self.assertEqual(u.format(), "unknown")
def test_format_no_unit(self):
- u = Unit('nounit')
- self.assertEqual(u.format(), 'no_unit')
+ u = Unit("nounit")
+ self.assertEqual(u.format(), "no_unit")
def test_format_names_utf8(self):
- u = Unit('m2')
- self.assertEqual(u.format([unit.UT_UTF8, unit.UT_NAMES]), u'meter²')
+ u = Unit("m2")
+ self.assertEqual(u.format([unit.UT_UTF8, unit.UT_NAMES]), "meter²")
def test_format_latin1(self):
- u = Unit('m2')
- self.assertEqual(u.format(unit.UT_LATIN1), u'm²')
+ u = Unit("m2")
+ self.assertEqual(u.format(unit.UT_LATIN1), "m²")
class Test_name(unittest.TestCase):
-
def test_basic(self):
- u = Unit('newton')
- self.assertEqual(u.name, 'newton')
+ u = Unit("newton")
+ self.assertEqual(u.name, "newton")
def test_unknown(self):
- u = Unit('unknown')
- self.assertEqual(u.name, 'unknown')
+ u = Unit("unknown")
+ self.assertEqual(u.name, "unknown")
def test_no_unit(self):
- u = Unit('no unit')
- self.assertEqual(u.name, 'no_unit')
+ u = Unit("no unit")
+ self.assertEqual(u.name, "no_unit")
class Test_symbol(unittest.TestCase):
-
def test_basic(self):
- u = Unit('joule')
- self.assertEqual(u.symbol, 'J')
+ u = Unit("joule")
+ self.assertEqual(u.symbol, "J")
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(u.symbol, unit._UNKNOWN_UNIT_SYMBOL)
def test_no_unit(self):
- u = Unit('no unit')
+ u = Unit("no unit")
self.assertEqual(u.symbol, unit._NO_UNIT_SYMBOL)
class Test_definition(unittest.TestCase):
-
def test_basic(self):
- u = Unit('joule')
- self.assertEqual(u.definition, 'm2.kg.s-2')
+ u = Unit("joule")
+ self.assertEqual(u.definition, "m2.kg.s-2")
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(u.definition, unit._UNKNOWN_UNIT_SYMBOL)
def test_no_unit(self):
- u = Unit('no unit')
+ u = Unit("no unit")
self.assertEqual(u.definition, unit._NO_UNIT_SYMBOL)
class Test__apply_offset(unittest.TestCase):
-
def test_add_integer_offset(self):
- u = Unit('meter')
- self.assertEqual(u + 10, 'm @ 10')
+ u = Unit("meter")
+ self.assertEqual(u + 10, "m @ 10")
def test_add_float_offset(self):
- u = Unit('meter')
- self.assertEqual(u + 100.0, 'm @ 100')
+ u = Unit("meter")
+ self.assertEqual(u + 100.0, "m @ 100")
def test_not_numerical_offset(self):
- u = Unit('meter')
- with self.assertRaisesRegex(TypeError,
- 'unsupported operand type'):
- operator.add(u, 'not_a_number')
+ u = Unit("meter")
+ with self.assertRaisesRegex(TypeError, "unsupported operand type"):
+ operator.add(u, "not_a_number")
def test_unit_unknown(self):
- u = Unit('unknown')
- self.assertEqual(u + 10, 'unknown')
+ u = Unit("unknown")
+ self.assertEqual(u + 10, "unknown")
def test_no_unit(self):
- u = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot offset'):
+ u = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot offset"):
operator.add(u, 10)
class Test_offset_by_time(unittest.TestCase):
-
def test_offset(self):
- u = Unit('hour')
+ u = Unit("hour")
v = u.offset_by_time(unit.encode_time(2007, 1, 15, 12, 6, 0))
- self.assertEqual(v, '(3600 s) @ 20070115T120600.00000000 UTC')
+ self.assertEqual(v, "(3600 s) @ 20070115T120600.00000000 UTC")
def test_not_numerical_offset(self):
- u = Unit('hour')
- with self.assertRaisesRegex(TypeError, 'numeric type'):
- u.offset_by_time('not_a_number')
+ u = Unit("hour")
+ with self.assertRaisesRegex(TypeError, "numeric type"):
+ u.offset_by_time("not_a_number")
def test_not_time_unit(self):
- u = Unit('mile')
- with self.assertRaisesRegex(ValueError, 'Failed to offset'):
+ u = Unit("mile")
+ with self.assertRaisesRegex(ValueError, "Failed to offset"):
u.offset_by_time(10)
def test_unit_unknown(self):
- u = Unit('unknown')
- emsg = 'Failed to offset'
+ u = Unit("unknown")
+ emsg = "Failed to offset"
with self.assertRaisesRegex(ValueError, emsg), suppress_errors():
u.offset_by_time(unit.encode_time(1970, 1, 1, 0, 0, 0))
def test_no_unit(self):
- u = Unit('no unit')
- emsg = 'Failed to offset'
+ u = Unit("no unit")
+ emsg = "Failed to offset"
with self.assertRaisesRegex(ValueError, emsg), suppress_errors():
u.offset_by_time(unit.encode_time(1970, 1, 1, 0, 0, 0))
class Test_invert(unittest.TestCase):
-
def test_basic(self):
- u = Unit('newton')
- self.assertEqual(u.invert(), 'm-1.kg-1.s2')
+ u = Unit("newton")
+ self.assertEqual(u.invert(), "m-1.kg-1.s2")
def test_double_invert(self):
# Double-inverting a unit should take you back to where you started.
- u = Unit('newton')
+ u = Unit("newton")
self.assertEqual(u.invert().invert(), u)
def test_invert_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(u.invert(), u)
def test_invert_no_unit(self):
- u = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot invert'):
+ u = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot invert"):
u.invert()
class Test_root(unittest.TestCase):
-
def setUp(self):
unit.suppress_errors()
def test_square_root(self):
- u = Unit('volt^2')
- self.assertEqual(u.root(2), 'V')
+ u = Unit("volt^2")
+ self.assertEqual(u.root(2), "V")
def test_square_root_integer_float(self):
- u = Unit('volt^2')
- self.assertEqual(u.root(2.), 'V')
+ u = Unit("volt^2")
+ self.assertEqual(u.root(2.0), "V")
def test_not_numeric(self):
- u = Unit('volt')
- with self.assertRaisesRegex(TypeError, 'numeric type'):
- u.offset_by_time('not_a_number')
+ u = Unit("volt")
+ with self.assertRaisesRegex(TypeError, "numeric type"):
+ u.offset_by_time("not_a_number")
def test_not_integer(self):
- u = Unit('volt')
- with self.assertRaisesRegex(TypeError, 'integer .* required'):
+ u = Unit("volt")
+ with self.assertRaisesRegex(TypeError, "integer .* required"):
u.root(1.2)
def test_meaningless_operation(self):
- u = Unit('volt')
- emsg = 'UT_MEANINGLESS'
+ u = Unit("volt")
+ emsg = "UT_MEANINGLESS"
with self.assertRaisesRegex(ValueError, emsg), suppress_errors():
u.root(2)
def test_unit_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(u.root(2), u)
def test_no_unit(self):
- u = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot take the root'):
+ u = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot take the root"):
u.root(2)
class Test_log(unittest.TestCase):
-
def test_base_2(self):
- u = Unit('hPa')
- self.assertEqual(u.log(2), 'lb(re 100 Pa)')
+ u = Unit("hPa")
+ self.assertEqual(u.log(2), "lb(re 100 Pa)")
def test_base_10(self):
- u = Unit('hPa')
- self.assertEqual(u.log(10), 'lg(re 100 Pa)')
+ u = Unit("hPa")
+ self.assertEqual(u.log(10), "lg(re 100 Pa)")
def test_negative(self):
- u = Unit('hPa')
+ u = Unit("hPa")
msg = re.escape("Failed to calculate logorithmic base of Unit('hPa')")
with self.assertRaisesRegex(ValueError, msg):
u.log(-1)
def test_not_numeric(self):
- u = Unit('hPa')
- with self.assertRaisesRegex(TypeError, 'numeric type'):
- u.log('not_a_number')
+ u = Unit("hPa")
+ with self.assertRaisesRegex(TypeError, "numeric type"):
+ u.log("not_a_number")
def test_unit_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(u.log(10), u)
def test_no_unit(self):
- u = Unit('no unit')
- emsg = 'Cannot take the logarithm'
+ u = Unit("no unit")
+ emsg = "Cannot take the logarithm"
with self.assertRaisesRegex(ValueError, emsg):
u.log(10)
class Test_multiply(unittest.TestCase):
-
def test_multiply_by_int(self):
- u = Unit('amp')
- self.assertEqual((u * 10).format(), '10 A')
+ u = Unit("amp")
+ self.assertEqual((u * 10).format(), "10 A")
def test_multiply_by_float(self):
- u = Unit('amp')
- self.assertEqual((u * 100.0).format(), '100 A')
+ u = Unit("amp")
+ self.assertEqual((u * 100.0).format(), "100 A")
def test_multiply_electrical_units(self):
- u = Unit('amp')
- v = Unit('volt')
- self.assertEqual((u * v).format(), 'W')
+ u = Unit("amp")
+ v = Unit("volt")
+ self.assertEqual((u * v).format(), "W")
def test_multiply_not_numeric(self):
- u = Unit('amp')
- with self.assertRaisesRegex(ValueError, 'Failed to parse unit'):
- operator.mul(u, 'not_a_number')
+ u = Unit("amp")
+ with self.assertRaisesRegex(ValueError, "Failed to parse unit"):
+ operator.mul(u, "not_a_number")
def test_multiply_with_unknown_unit(self):
- u = Unit('unknown')
- v = Unit('meters')
+ u = Unit("unknown")
+ v = Unit("meters")
self.assertTrue((u * v).is_unknown())
self.assertTrue((v * u).is_unknown())
def test_multiply_with_no_unit(self):
- u = Unit('meters')
- v = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot multiply'):
+ u = Unit("meters")
+ v = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot multiply"):
operator.mul(u, v)
operator.mul(v, u)
def test_multiply_unknown_and_no_unit(self):
- u = Unit('unknown')
- v = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot multiply'):
+ u = Unit("unknown")
+ v = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot multiply"):
operator.mul(u, v)
operator.mul(v, u)
class Test_divide(unittest.TestCase):
-
def test_divide_by_int(self):
- u = Unit('watts')
- self.assertEqual((u / 10).format(), '0.1 W')
+ u = Unit("watts")
+ self.assertEqual((u / 10).format(), "0.1 W")
def test_divide_by_float(self):
- u = Unit('watts')
- self.assertEqual((u / 100.0).format(), '0.01 W')
+ u = Unit("watts")
+ self.assertEqual((u / 100.0).format(), "0.01 W")
def test_divide_electrical_units(self):
- u = Unit('watts')
- v = Unit('volts')
- self.assertEqual((u / v).format(), 'A')
+ u = Unit("watts")
+ v = Unit("volts")
+ self.assertEqual((u / v).format(), "A")
def test_divide_not_numeric(self):
- u = Unit('watts')
- with self.assertRaisesRegex(ValueError, 'Failed to parse unit'):
- truediv(u, 'not_a_number')
+ u = Unit("watts")
+ with self.assertRaisesRegex(ValueError, "Failed to parse unit"):
+ truediv(u, "not_a_number")
def test_divide_with_unknown_unit(self):
- u = Unit('unknown')
- v = Unit('meters')
+ u = Unit("unknown")
+ v = Unit("meters")
self.assertTrue((u / v).is_unknown())
self.assertTrue((v / u).is_unknown())
def test_divide_with_no_unit(self):
- u = Unit('meters')
- v = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot divide'):
+ u = Unit("meters")
+ v = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot divide"):
truediv(u, v)
truediv(v, u)
def test_divide_unknown_and_no_unit(self):
- u = Unit('unknown')
- v = Unit('no unit')
- with self.assertRaisesRegex(ValueError, 'Cannot divide'):
+ u = Unit("unknown")
+ v = Unit("no unit")
+ with self.assertRaisesRegex(ValueError, "Cannot divide"):
truediv(u, v)
truediv(v, u)
class Test_power(unittest.TestCase):
-
def test_basic(self):
- u = Unit('m^2')
- self.assertEqual(u ** 0.5, Unit('m'))
+ u = Unit("m^2")
+ self.assertEqual(u ** 0.5, Unit("m"))
def test_integer_power(self):
- u = Unit('amp')
- self.assertEqual(u ** 2, Unit('A^2'))
+ u = Unit("amp")
+ self.assertEqual(u ** 2, Unit("A^2"))
def test_float_power(self):
- u = Unit('amp')
- self.assertEqual(u ** 3.0, Unit('A^3'))
+ u = Unit("amp")
+ self.assertEqual(u ** 3.0, Unit("A^3"))
def test_dimensionless(self):
- u = Unit('1')
+ u = Unit("1")
self.assertEqual(u ** 2, u)
def test_power(self):
- u = Unit('amp')
- self.assertRaises(TypeError, operator.pow, u, Unit('m'))
- self.assertRaises(TypeError, operator.pow, u, Unit('unknown'))
- self.assertRaises(TypeError, operator.pow, u, Unit('no unit'))
+ u = Unit("amp")
+ self.assertRaises(TypeError, operator.pow, u, Unit("m"))
+ self.assertRaises(TypeError, operator.pow, u, Unit("unknown"))
+ self.assertRaises(TypeError, operator.pow, u, Unit("no unit"))
def test_not_numeric(self):
- u = Unit('m^2')
- emsg = 'numeric value is required'
+ u = Unit("m^2")
+ emsg = "numeric value is required"
with self.assertRaisesRegex(TypeError, emsg):
- operator.pow(u, 'not_a_number')
+ operator.pow(u, "not_a_number")
def test_bad_power(self):
- u = Unit('m^2')
- emsg = 'Cannot raise .* by a decimal'
+ u = Unit("m^2")
+ emsg = "Cannot raise .* by a decimal"
with self.assertRaisesRegex(ValueError, emsg):
operator.pow(u, 0.4)
def test_unit_power(self):
- u = Unit('amp')
- v = Unit('m')
- emsg = 'argument must be a string or a number'
+ u = Unit("amp")
+ v = Unit("m")
+ emsg = "argument must be a string or a number"
with self.assertRaisesRegex(TypeError, emsg):
operator.pow(u, v)
class Test_power__unknown(unittest.TestCase):
-
def setUp(self):
- self.u = Unit('unknown')
+ self.u = Unit("unknown")
def test_integer_power(self):
- self.assertEqual(self.u ** 2, Unit('unknown'))
+ self.assertEqual(self.u ** 2, Unit("unknown"))
def test_float_power(self):
- self.assertEqual(self.u ** 3.0, Unit('unknown'))
+ self.assertEqual(self.u ** 3.0, Unit("unknown"))
def test_not_numeric(self):
- emsg = 'numeric value is required'
+ emsg = "numeric value is required"
with self.assertRaisesRegex(TypeError, emsg):
- operator.pow(self.u, 'not_a_number')
+ operator.pow(self.u, "not_a_number")
def test_bad_power(self):
self.assertEqual(self.u ** 0.4, self.u)
def test_unit_power(self):
- v = Unit('m')
- emsg = 'argument must be a string or a number'
+ v = Unit("m")
+ emsg = "argument must be a string or a number"
with self.assertRaisesRegex(TypeError, emsg):
operator.pow(self.u, v)
class Test_power__no_unit(unittest.TestCase):
-
def setUp(self):
- self.u = Unit('no unit')
+ self.u = Unit("no unit")
def test_integer_power(self):
emsg = "Cannot raise .* a 'no-unit'"
@@ -554,9 +533,9 @@ def test_float_power(self):
operator.pow(self.u, 3.0)
def test_not_numeric(self):
- emsg = 'numeric value is required'
+ emsg = "numeric value is required"
with self.assertRaisesRegex(TypeError, emsg):
- operator.pow(self.u, 'not_a_number')
+ operator.pow(self.u, "not_a_number")
def test_bad_power(self):
emsg = "Cannot raise .* a 'no-unit'"
@@ -564,163 +543,159 @@ def test_bad_power(self):
operator.pow(self.u, 0.4)
def test_unit_power(self):
- v = Unit('m')
- emsg = 'argument must be a string or a number'
+ v = Unit("m")
+ emsg = "argument must be a string or a number"
with self.assertRaisesRegex(TypeError, emsg):
operator.pow(self.u, v)
class Test_copy(unittest.TestCase):
-
def test_basic(self):
- u = Unit('joule')
+ u = Unit("joule")
self.assertEqual(copy.copy(u), u)
def test_unit_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertEqual(copy.copy(u), u)
self.assertTrue(copy.copy(u).is_unknown())
def test_no_unit(self):
- u = Unit('no unit')
+ u = Unit("no unit")
self.assertEqual(copy.copy(u), u)
self.assertTrue(copy.copy(u).is_no_unit())
class Test_stringify(unittest.TestCase):
-
def test___str__(self):
- u = Unit('meter')
- self.assertEqual(str(u), 'meter')
+ u = Unit("meter")
+ self.assertEqual(str(u), "meter")
def test___repr___basic(self):
- u = Unit('meter')
+ u = Unit("meter")
self.assertEqual(repr(u), "Unit('meter')")
def test___repr___time_unit(self):
- u = Unit("hours since 2007-01-15 12:06:00",
- calendar=unit.CALENDAR_STANDARD)
+ u = Unit(
+ "hours since 2007-01-15 12:06:00", calendar=unit.CALENDAR_STANDARD
+ )
exp = "Unit('hours since 2007-01-15 12:06:00', calendar='gregorian')"
self.assertEqual(repr(u), exp)
class Test_equality(unittest.TestCase):
-
def test_basic(self):
- u = Unit('meter')
- self.assertEqual(u, 'meter')
+ u = Unit("meter")
+ self.assertEqual(u, "meter")
def test_equivalent_units(self):
- u = Unit('meter')
- v = Unit('m.s-1')
- w = Unit('hertz')
- self.assertEqual(u, v/w)
+ u = Unit("meter")
+ v = Unit("m.s-1")
+ w = Unit("hertz")
+ self.assertEqual(u, v / w)
def test_non_equivalent_units(self):
- u = Unit('meter')
- v = Unit('amp')
+ u = Unit("meter")
+ v = Unit("amp")
self.assertNotEqual(u, v)
def test_eq_cross_category(self):
- m = Unit('meter')
- u = Unit('unknown')
- n = Unit('no_unit')
+ m = Unit("meter")
+ u = Unit("unknown")
+ n = Unit("no_unit")
self.assertNotEqual(m, u)
self.assertNotEqual(m, n)
def test_unknown(self):
- u = Unit('unknown')
- self.assertEqual(u, 'unknown')
+ u = Unit("unknown")
+ self.assertEqual(u, "unknown")
def test_no_unit(self):
- u = Unit('no_unit')
- self.assertEqual(u, 'no_unit')
+ u = Unit("no_unit")
+ self.assertEqual(u, "no_unit")
def test_unknown_no_unit(self):
- u = Unit('unknown')
- v = Unit('no_unit')
+ u = Unit("unknown")
+ v = Unit("no_unit")
self.assertNotEqual(u, v)
def test_not_implemented(self):
- u = Unit('meter')
+ u = Unit("meter")
self.assertFalse(u == {})
class Test_non_equality(unittest.TestCase):
-
def test_basic(self):
- u = Unit('meter')
- self.assertFalse(u != 'meter')
+ u = Unit("meter")
+ self.assertFalse(u != "meter")
def test_non_equivalent_units(self):
- u = Unit('meter')
- v = Unit('amp')
+ u = Unit("meter")
+ v = Unit("amp")
self.assertNotEqual(u, v)
def test_ne_cross_category(self):
- m = Unit('meter')
- u = Unit('unknown')
- n = Unit('no_unit')
+ m = Unit("meter")
+ u = Unit("unknown")
+ n = Unit("no_unit")
self.assertNotEqual(m, u)
self.assertNotEqual(m, n)
self.assertNotEqual(u, n)
def test_unknown(self):
- u = Unit('unknown')
- self.assertFalse(u != 'unknown')
+ u = Unit("unknown")
+ self.assertFalse(u != "unknown")
def test_no_unit(self):
- u = Unit('no_unit')
- self.assertFalse(u != 'no_unit')
+ u = Unit("no_unit")
+ self.assertFalse(u != "no_unit")
def test_not_implemented(self):
- u = Unit('meter')
+ u = Unit("meter")
self.assertNotEqual(u, {})
class Test_convert(unittest.TestCase):
-
def test_convert_float(self):
- u = Unit('meter')
- v = Unit('mile')
+ u = Unit("meter")
+ v = Unit("mile")
result = u.convert(1609.344, v)
expected = 1.0
self.assertEqual(result, expected)
def test_convert_int(self):
- u = Unit('mile')
- v = Unit('meter')
+ u = Unit("mile")
+ v = Unit("meter")
result = u.convert(1, v)
expected = 1609.344
self.assertEqual(result, expected)
def test_convert_array(self):
- u = Unit('meter')
- v = Unit('mile')
+ u = Unit("meter")
+ v = Unit("mile")
expected = np.arange(2, dtype=np.float32) + 1
result = u.convert(expected * 1609.344, v)
np.testing.assert_array_equal(result, expected)
def test_convert_array_multidim(self):
- u = Unit('meter')
- v = Unit('mile')
+ u = Unit("meter")
+ v = Unit("mile")
expected = (np.arange(2, dtype=np.float32) + 1).reshape([1, 1, 2, 1])
result = u.convert(expected * 1609.344, v)
np.testing.assert_array_equal(result, expected)
def test_incompatible_units(self):
- u = Unit('m')
- v = Unit('V')
- emsg = 'Unable to convert'
+ u = Unit("m")
+ v = Unit("V")
+ emsg = "Unable to convert"
with self.assertRaisesRegex(ValueError, emsg):
u.convert(1.0, v)
def test_unknown_units(self):
- u = Unit('unknown')
- v = Unit('no unit')
- m = Unit('m')
+ u = Unit("unknown")
+ v = Unit("no unit")
+ m = Unit("m")
val = 1.0
- emsg = 'Unable to convert'
+ emsg = "Unable to convert"
with self.assertRaisesRegex(ValueError, emsg):
u.convert(val, m)
with self.assertRaisesRegex(ValueError, emsg):
@@ -729,11 +704,11 @@ def test_unknown_units(self):
u.convert(val, v)
def test_no_units(self):
- u = Unit('no unit')
- v = Unit('unknown')
- m = Unit('m')
+ u = Unit("no unit")
+ v = Unit("unknown")
+ m = Unit("m")
val = 1.0
- emsg = 'Unable to convert'
+ emsg = "Unable to convert"
with self.assertRaisesRegex(ValueError, emsg):
u.convert(val, m)
with self.assertRaisesRegex(ValueError, emsg):
@@ -742,49 +717,47 @@ def test_no_units(self):
u.convert(val, v)
def test_convert_time_units(self):
- u = Unit('seconds since 1978-09-01 00:00:00', calendar='360_day')
- v = Unit('seconds since 1979-04-01 00:00:00', calendar='360_day')
- u1point = np.array([54432000.], dtype=np.float32)
- u2point = np.array([36288000.], dtype=np.float32)
+ u = Unit("seconds since 1978-09-01 00:00:00", calendar="360_day")
+ v = Unit("seconds since 1979-04-01 00:00:00", calendar="360_day")
+ u1point = np.array([54432000.0], dtype=np.float32)
+ u2point = np.array([36288000.0], dtype=np.float32)
res = u.convert(u1point, v)
np.testing.assert_array_almost_equal(res, u2point, decimal=6)
def test_incompatible_time_units(self):
- u = Unit('seconds since 1978-09-01 00:00:00', calendar='360_day')
- v = Unit('seconds since 1979-04-01 00:00:00', calendar='gregorian')
- u1point = np.array([54432000.], dtype=np.float32)
- emsg = 'Unable to convert'
+ u = Unit("seconds since 1978-09-01 00:00:00", calendar="360_day")
+ v = Unit("seconds since 1979-04-01 00:00:00", calendar="gregorian")
+ u1point = np.array([54432000.0], dtype=np.float32)
+ emsg = "Unable to convert"
with self.assertRaisesRegex(ValueError, emsg):
u.convert(u1point, v)
class Test_order(unittest.TestCase):
-
def test(self):
- m = Unit('meter')
- u = Unit('unknown')
- n = Unit('no_unit')
+ m = Unit("meter")
+ u = Unit("unknown")
+ n = Unit("no_unit")
start = [m, u, n]
self.assertEqual(sorted(start), [m, n, u])
class Test_is_unknown(unittest.TestCase):
-
def _check(self, unknown_str):
u = Unit(unknown_str)
self.assertTrue(u.is_unknown())
def test_unknown_representations(self):
- representations = ['unknown', '?', '???']
+ representations = ["unknown", "?", "???"]
for representation in representations:
self._check(representation)
def test_no_unit(self):
- u = Unit('no unit')
+ u = Unit("no unit")
self.assertFalse(u.is_unknown())
def test_known_unit(self):
- u = Unit('meters')
+ u = Unit("meters")
self.assertFalse(u.is_unknown())
def test_no_ut_pointer(self):
@@ -795,68 +768,64 @@ def test_no_ut_pointer(self):
class Test_is_no_unit(unittest.TestCase):
-
def _check(self, no_unit_str):
u = Unit(no_unit_str)
self.assertTrue(u.is_no_unit())
def test_no_unit_representations(self):
- representations = ['no_unit', 'no unit', 'no-unit', 'nounit']
+ representations = ["no_unit", "no unit", "no-unit", "nounit"]
for representation in representations:
self._check(representation)
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_no_unit())
def test_known_unit(self):
- u = Unit('meters')
+ u = Unit("meters")
self.assertFalse(u.is_no_unit())
class Test_is_udunits(unittest.TestCase):
-
def test_basic(self):
- u = Unit('meters')
+ u = Unit("meters")
self.assertTrue(u.is_udunits())
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_udunits())
def test_no_unit(self):
- u = Unit('no_unit')
+ u = Unit("no_unit")
self.assertFalse(u.is_udunits())
class Test_is_time_reference(unittest.TestCase):
-
def test_basic(self):
- u = Unit('hours since epoch')
+ u = Unit("hours since epoch")
self.assertTrue(u.is_time_reference())
def test_not_time_reference(self):
- u = Unit('hours')
+ u = Unit("hours")
self.assertFalse(u.is_time_reference())
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_time_reference())
def test_no_unit(self):
- u = Unit('no_unit')
+ u = Unit("no_unit")
self.assertFalse(u.is_time_reference())
class Test_title(unittest.TestCase):
-
def test_basic(self):
- u = Unit('meter')
- self.assertEqual(u.title(10), '10 meter')
+ u = Unit("meter")
+ self.assertEqual(u.title(10), "10 meter")
def test_time_unit(self):
- u = Unit('hours since epoch', calendar=unit.CALENDAR_STANDARD)
- self.assertEqual(u.title(10), '1970-01-01 10:00:00')
+ u = Unit("hours since epoch", calendar=unit.CALENDAR_STANDARD)
+ self.assertEqual(u.title(10), "1970-01-01 10:00:00")
class Test__immutable(unittest.TestCase):
@@ -865,16 +834,16 @@ def _set_attr(self, unit, name):
raise ValueError("'Unit' attribute {!r} is mutable!".format(name))
def test_immutable(self):
- u = Unit('m')
+ u = Unit("m")
for name in dir(u):
- emsg = 'Instances .* are immutable'
+ emsg = "Instances .* are immutable"
with self.assertRaisesRegex(AttributeError, emsg):
self._set_attr(u, name)
def test_common_hash(self):
# Test different instances of length units (m) have a common hash.
- u1 = Unit('m')
- u2 = Unit('meter')
+ u1 = Unit("m")
+ u2 = Unit("meter")
u3 = copy.deepcopy(u1)
h = set()
for u in (u1, u2, u3):
@@ -882,8 +851,8 @@ def test_common_hash(self):
self.assertEqual(len(h), 1)
# Test different instances of electrical units (V) have a common hash.
- v1 = Unit('V')
- v2 = Unit('volt')
+ v1 = Unit("V")
+ v2 = Unit("volt")
for u in (v1, v2):
h.add(hash(u))
self.assertEqual(len(h), 2)
@@ -894,15 +863,15 @@ class Test__inplace(unittest.TestCase):
def test(self):
# Check conversions do not change original object.
- c = Unit('deg_c')
- f = Unit('deg_f')
+ c = Unit("deg_c")
+ f = Unit("deg_f")
orig = np.arange(3, dtype=np.float32)
# Test arrays are not equal without inplace conversions.
converted = c.convert(orig, f)
- emsg = 'Arrays are not equal'
+ emsg = "Arrays are not equal"
with self.assertRaisesRegex(AssertionError, emsg):
np.testing.assert_array_equal(orig, converted)
self.assertFalse(np.may_share_memory(orig, converted))
@@ -913,12 +882,17 @@ def test(self):
self.assertTrue(np.may_share_memory(orig, converted))
def test_multidim_masked(self):
- c = Unit('deg_c')
- f = Unit('deg_f')
+ c = Unit("deg_c")
+ f = Unit("deg_f")
# Manufacture a Fortran-ordered nd array to be converted.
- orig = np.ma.masked_array(np.arange(4, dtype=np.float32),
- mask=[1, 0, 0, 1]).reshape([2, 2]).T
+ orig = (
+ np.ma.masked_array(
+ np.arange(4, dtype=np.float32), mask=[1, 0, 0, 1]
+ )
+ .reshape([2, 2])
+ .T
+ )
# Test arrays are not equal without inplace conversions.
converted = c.convert(orig, f)
@@ -934,14 +908,16 @@ def test_multidim_masked(self):
self.assertTrue(np.may_share_memory(orig, converted))
def test_foreign_endian(self):
- c = Unit('deg_c')
- f = Unit('deg_f')
+ c = Unit("deg_c")
+ f = Unit("deg_f")
# Manufacture a non-native byte-order array to be converted.
orig = np.arange(4, dtype=np.float32).newbyteorder().byteswap()
- emsg = ('Unable to convert non-native byte ordered array in-place. '
- 'Consider byte-swapping first.')
+ emsg = (
+ "Unable to convert non-native byte ordered array in-place. "
+ "Consider byte-swapping first."
+ )
with self.assertRaisesRegex(ValueError, emsg):
c.convert(orig, f, inplace=True)
@@ -950,7 +926,6 @@ def test_foreign_endian(self):
class TestTimeEncoding(unittest.TestCase):
-
def test_encode_time(self):
result = unit.encode_time(2006, 1, 15, 12, 6, 0)
self.assertEqual(result, 159019560.0)
@@ -964,109 +939,109 @@ def test_encode_clock(self):
self.assertEqual(result, 43560.0)
def test_decode_time(self):
- result = unit.decode_time(158976000.0+43560.0)
+ result = unit.decode_time(158976000.0 + 43560.0)
year, month, day, hour, min, sec, res = result
- self.assertEqual((year, month, day, hour, min, sec),
- (2006, 1, 15, 12, 6, 0))
+ self.assertEqual(
+ (year, month, day, hour, min, sec), (2006, 1, 15, 12, 6, 0)
+ )
class TestNumsAndDates(unittest.TestCase):
-
def test_num2date(self):
- u = Unit('hours since 2010-11-02 12:00:00',
- calendar=unit.CALENDAR_STANDARD)
+ u = Unit(
+ "hours since 2010-11-02 12:00:00", calendar=unit.CALENDAR_STANDARD
+ )
res = u.num2date(1)
- self.assertEqual(str(res), '2010-11-02 13:00:00')
- self.assertEqual(res.calendar, 'gregorian')
+ self.assertEqual(str(res), "2010-11-02 13:00:00")
+ self.assertEqual(res.calendar, "gregorian")
self.assertIsInstance(res, cftime.datetime)
def test_num2date_py_datetime_type(self):
- u = Unit('hours since 2010-11-02 12:00:00',
- calendar=unit.CALENDAR_STANDARD)
+ u = Unit(
+ "hours since 2010-11-02 12:00:00", calendar=unit.CALENDAR_STANDARD
+ )
res = u.num2date(1, only_use_cftime_datetimes=False)
- self.assertEqual(str(res), '2010-11-02 13:00:00')
+ self.assertEqual(str(res), "2010-11-02 13:00:00")
self.assertIsInstance(res, datetime.datetime)
def test_date2num(self):
- u = Unit('hours since 2010-11-02 12:00:00',
- calendar=unit.CALENDAR_STANDARD)
+ u = Unit(
+ "hours since 2010-11-02 12:00:00", calendar=unit.CALENDAR_STANDARD
+ )
d = datetime.datetime(2010, 11, 2, 13, 0, 0)
- self.assertEqual(str(u.num2date(u.date2num(d))), '2010-11-02 13:00:00')
+ self.assertEqual(str(u.num2date(u.date2num(d))), "2010-11-02 13:00:00")
class Test_as_unit(unittest.TestCase):
-
def test_already_unit(self):
- u = Unit('m')
+ u = Unit("m")
result = unit.as_unit(u)
self.assertIs(result, u)
def test_known_unit_str(self):
- u_str = 'm'
- expected = Unit('m')
+ u_str = "m"
+ expected = Unit("m")
result = unit.as_unit(u_str)
self.assertEqual(expected, result)
def test_not_unit_str(self):
- u_str = 'wibble'
- emsg = 'Failed to parse unit'
+ u_str = "wibble"
+ emsg = "Failed to parse unit"
with self.assertRaisesRegex(ValueError, emsg):
unit.as_unit(u_str)
def test_unknown_str(self):
- u_str = 'unknown'
- expected = Unit('unknown')
+ u_str = "unknown"
+ expected = Unit("unknown")
result = unit.as_unit(u_str)
self.assertEqual(expected, result)
def test_no_unit_str(self):
- u_str = 'no_unit'
- expected = Unit('no_unit')
+ u_str = "no_unit"
+ expected = Unit("no_unit")
result = unit.as_unit(u_str)
self.assertEqual(expected, result)
class Test_is_time(unittest.TestCase):
-
def test_basic(self):
- u = Unit('hours')
+ u = Unit("hours")
self.assertTrue(u.is_time())
def test_not_time_unit(self):
- u = Unit('meters')
+ u = Unit("meters")
self.assertFalse(u.is_time())
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_time())
def test_no_unit(self):
- u = Unit('no_unit')
+ u = Unit("no_unit")
self.assertFalse(u.is_time())
class Test_is_vertical(unittest.TestCase):
-
def test_pressure_unit(self):
- u = Unit('millibar')
+ u = Unit("millibar")
self.assertTrue(u.is_vertical())
def test_length_unit(self):
- u = Unit('meters')
+ u = Unit("meters")
self.assertTrue(u.is_vertical())
def test_not_vertical_unit(self):
- u = Unit('hours')
+ u = Unit("hours")
self.assertFalse(u.is_vertical())
def test_unknown(self):
- u = Unit('unknown')
+ u = Unit("unknown")
self.assertFalse(u.is_vertical())
def test_no_unit(self):
- u = Unit('no_unit')
+ u = Unit("no_unit")
self.assertFalse(u.is_vertical())
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/cf_units/tests/unit/test__udunits2.py b/cf_units/tests/unit/test__udunits2.py
index c737cec1..069e8708 100644
--- a/cf_units/tests/unit/test__udunits2.py
+++ b/cf_units/tests/unit/test__udunits2.py
@@ -16,7 +16,6 @@
from cf_units import _udunits2 as _ud
-
_ud.set_error_message_handler(_ud.ignore)
@@ -25,6 +24,7 @@ class Test_get_system(unittest.TestCase):
Test case for operations which create a system object.
"""
+
def test_read_xml(self):
system = _ud.read_xml()
@@ -32,10 +32,10 @@ def test_read_xml(self):
def test_read_xml_invalid_path(self):
with self.assertRaises(_ud.UdunitsError) as cm:
- _ud.read_xml(b'/not/a/path.xml')
+ _ud.read_xml(b"/not/a/path.xml")
ex = cm.exception
- self.assertEqual(ex.status_msg(), 'UT_OPEN_ARG')
+ self.assertEqual(ex.status_msg(), "UT_OPEN_ARG")
self.assertEqual(ex.errnum, errno.ENOENT)
@@ -44,42 +44,44 @@ class Test_system(unittest.TestCase):
Test case for system operations.
"""
+
def setUp(self):
self.system = _ud.read_xml()
def test_get_unit_by_name(self):
- unit = _ud.get_unit_by_name(self.system, b'metre')
+ unit = _ud.get_unit_by_name(self.system, b"metre")
self.assertIsNotNone(unit)
def test_get_unit_by_name_invalid_unit(self):
with self.assertRaises(_ud.UdunitsError):
- _ud.get_unit_by_name(self.system, b'jigawatt')
+ _ud.get_unit_by_name(self.system, b"jigawatt")
def test_parse(self):
- unit = _ud.parse(self.system, b'gigawatt', _ud.UT_ASCII)
+ unit = _ud.parse(self.system, b"gigawatt", _ud.UT_ASCII)
self.assertIsNotNone(unit)
def test_parse_latin1(self):
- angstrom = _ud.parse(self.system, b'\xe5ngstr\xF6m', _ud.UT_LATIN1)
+ angstrom = _ud.parse(self.system, b"\xe5ngstr\xF6m", _ud.UT_LATIN1)
self.assertIsNotNone(angstrom)
def test_parse_ISO_8859_1(self):
- angstrom = _ud.parse(self.system, b'\xe5ngstr\xF6m', _ud.UT_ISO_8859_1)
+ angstrom = _ud.parse(self.system, b"\xe5ngstr\xF6m", _ud.UT_ISO_8859_1)
self.assertIsNotNone(angstrom)
def test_parse_UTF8(self):
- angstrom = _ud.parse(self.system, b'\xc3\xa5ngstr\xc3\xb6m',
- _ud.UT_UTF8)
+ angstrom = _ud.parse(
+ self.system, b"\xc3\xa5ngstr\xc3\xb6m", _ud.UT_UTF8
+ )
self.assertIsNotNone(angstrom)
def test_parse_invalid_unit(self):
with self.assertRaises(_ud.UdunitsError):
- _ud.parse(self.system, b'jigawatt', _ud.UT_ASCII)
+ _ud.parse(self.system, b"jigawatt", _ud.UT_ASCII)
class Test_unit(unittest.TestCase):
@@ -87,11 +89,12 @@ class Test_unit(unittest.TestCase):
Test case for unit operations.
"""
+
def setUp(self):
self.system = _ud.read_xml()
- self.metre = _ud.get_unit_by_name(self.system, b'metre')
- self.yard = _ud.get_unit_by_name(self.system, b'yard')
- self.second = _ud.get_unit_by_name(self.system, b'second')
+ self.metre = _ud.get_unit_by_name(self.system, b"metre")
+ self.yard = _ud.get_unit_by_name(self.system, b"yard")
+ self.second = _ud.get_unit_by_name(self.system, b"second")
def test_clone(self):
metre_clone = _ud.clone(self.metre)
@@ -99,7 +102,7 @@ def test_clone(self):
self.assertIsNot(metre_clone, self.metre)
def test_is_dimensionless_true(self):
- radian = _ud.get_unit_by_name(self.system, b'radian')
+ radian = _ud.get_unit_by_name(self.system, b"radian")
self.assertTrue(_ud.is_dimensionless(radian))
def test_is_dimensionless_false(self):
@@ -131,7 +134,7 @@ def test_get_converter_invalid(self):
_ud.get_converter(self.metre, self.second)
ex = cm.exception
- self.assertEqual(ex.status_msg(), 'UT_MEANINGLESS')
+ self.assertEqual(ex.status_msg(), "UT_MEANINGLESS")
def test_scale(self):
mm = _ud.scale(0.001, self.metre)
@@ -139,7 +142,7 @@ def test_scale(self):
self.assertIsNotNone(mm)
def test_offset(self):
- kelvin = _ud.get_unit_by_name(self.system, b'kelvin')
+ kelvin = _ud.get_unit_by_name(self.system, b"kelvin")
celsius = _ud.offset(kelvin, 273.15)
self.assertIsNotNone(celsius)
@@ -179,7 +182,7 @@ def test_raise_(self):
self.assertIsNotNone(sq_metre)
def test_root(self):
- hectare = _ud.get_unit_by_name(self.system, b'hectare')
+ hectare = _ud.get_unit_by_name(self.system, b"hectare")
hundred_metre = _ud.root(hectare, 2)
self.assertIsNotNone(hundred_metre)
@@ -190,20 +193,19 @@ def test_log(self):
self.assertIsNotNone(log_metre)
def test_format(self):
- pascal = _ud.get_unit_by_name(self.system, b'pascal')
+ pascal = _ud.get_unit_by_name(self.system, b"pascal")
symb = _ud.format(pascal)
name = _ud.format(pascal, _ud.UT_NAMES)
defn = _ud.format(pascal, _ud.UT_DEFINITION)
name_defn = _ud.format(pascal, _ud.UT_DEFINITION | _ud.UT_NAMES)
- self.assertEqual(symb, b'Pa')
- self.assertEqual(name, b'pascal')
- self.assertEqual(defn, b'm-1.kg.s-2')
- self.assertEqual(name_defn, b'meter^-1-kilogram-second^-2')
+ self.assertEqual(symb, b"Pa")
+ self.assertEqual(name, b"pascal")
+ self.assertEqual(defn, b"m-1.kg.s-2")
+ self.assertEqual(name_defn, b"meter^-1-kilogram-second^-2")
class Test_time_encoding(unittest.TestCase):
-
def setUp(self):
self.year, self.month, self.day = 2000, 1, 1
self.date_encoding = -31622400.0
@@ -216,29 +218,46 @@ def test_encode_date(self):
self.assertEqual(self.date_encoding, res_date_encoding)
def test_encode_clock(self):
- res_clock_encoding = _ud.encode_clock(self.hours, self.minutes,
- self.seconds)
+ res_clock_encoding = _ud.encode_clock(
+ self.hours, self.minutes, self.seconds
+ )
self.assertEqual(self.clock_encoding, res_clock_encoding)
def test_encode_time(self):
- res_time_encoding = _ud.encode_time(self.year, self.month, self.day,
- self.hours, self.minutes,
- self.seconds)
+ res_time_encoding = _ud.encode_time(
+ self.year,
+ self.month,
+ self.day,
+ self.hours,
+ self.minutes,
+ self.seconds,
+ )
- self.assertEqual(self.clock_encoding + self.date_encoding,
- res_time_encoding)
+ self.assertEqual(
+ self.clock_encoding + self.date_encoding, res_time_encoding
+ )
def test_decode_time(self):
- res_year, res_month, res_day, res_hours, res_minutes, res_seconds,\
- res_resolution =\
- _ud.decode_time(self.date_encoding + self.clock_encoding)
+ (
+ res_year,
+ res_month,
+ res_day,
+ res_hours,
+ res_minutes,
+ res_seconds,
+ res_resolution,
+ ) = _ud.decode_time(self.date_encoding + self.clock_encoding)
self.assertEqual(
(res_year, res_month, res_day, res_hours, res_minutes),
- (self.year, self.month, self.day, self.hours, self.minutes))
- self.assertTrue(res_seconds - res_resolution < self.seconds <
- res_seconds + res_resolution)
+ (self.year, self.month, self.day, self.hours, self.minutes),
+ )
+ self.assertTrue(
+ res_seconds - res_resolution
+ < self.seconds
+ < res_seconds + res_resolution
+ )
class Test_convert(unittest.TestCase):
@@ -246,10 +265,11 @@ class Test_convert(unittest.TestCase):
Test case for convert operations.
"""
+
def setUp(self):
system = _ud.read_xml()
- metre = _ud.get_unit_by_name(system, b'metre')
- yard = _ud.get_unit_by_name(system, b'yard')
+ metre = _ud.get_unit_by_name(system, b"metre")
+ yard = _ud.get_unit_by_name(system, b"yard")
self.converter = _ud.get_converter(metre, yard)
self.factor = 1.0936132669448853
@@ -258,7 +278,7 @@ def test_convert_float(self):
np.testing.assert_approx_equal(2.5 * self.factor, res)
def test_convert_floats(self):
- arr = np.array([2.5, 5., 10.], dtype=np.float32)
+ arr = np.array([2.5, 5.0, 10.0], dtype=np.float32)
res = np.empty_like(arr)
_ud.convert_floats(self.converter, arr, res)
np.testing.assert_array_almost_equal(arr * self.factor, res)
@@ -268,11 +288,11 @@ def test_convert_double(self):
np.testing.assert_approx_equal(2.5 * self.factor, res)
def test_convert_doubles(self):
- arr = np.array([2.5, 5., 10.], dtype=np.float64)
+ arr = np.array([2.5, 5.0, 10.0], dtype=np.float64)
res = np.empty_like(arr)
_ud.convert_doubles(self.converter, arr, res)
np.testing.assert_array_almost_equal(arr * self.factor, res)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/cf_units/tests/unit/unit/test_Unit.py b/cf_units/tests/unit/unit/test_Unit.py
index 49f7f77f..1ff356b9 100644
--- a/cf_units/tests/unit/unit/test_Unit.py
+++ b/cf_units/tests/unit/unit/test_Unit.py
@@ -14,26 +14,24 @@
class Test___init__(unittest.TestCase):
-
def test_capitalised_calendar(self):
- calendar = 'GrEgoRian'
+ calendar = "GrEgoRian"
expected = cf_units.CALENDAR_GREGORIAN
- u = Unit('hours since 1970-01-01 00:00:00', calendar=calendar)
+ u = Unit("hours since 1970-01-01 00:00:00", calendar=calendar)
self.assertEqual(u.calendar, expected)
def test_not_basestring_calendar(self):
with self.assertRaises(TypeError):
- Unit('hours since 1970-01-01 00:00:00', calendar=5)
+ Unit("hours since 1970-01-01 00:00:00", calendar=5)
def test_hash_replacement(self):
- hash_unit = 'm # s-1'
- expected = 'm 1 s-1'
+ hash_unit = "m # s-1"
+ expected = "m 1 s-1"
u = Unit(hash_unit)
self.assertEqual(u, expected)
class Test_convert__calendar(unittest.TestCase):
-
class MyStr(str):
pass
@@ -44,10 +42,10 @@ def test_gregorian_calendar_conversion_setup(self):
cal_str = cf_units.CALENDAR_GREGORIAN
calendar = self.MyStr(cal_str)
self.assertIsNot(calendar, cal_str)
- u1 = Unit('hours since 1970-01-01 00:00:00', calendar=calendar)
- u2 = Unit('hours since 1969-11-30 00:00:00', calendar=calendar)
- u1point = np.array([8.], dtype=np.float32)
- expected = np.array([776.], dtype=np.float32)
+ u1 = Unit("hours since 1970-01-01 00:00:00", calendar=calendar)
+ u2 = Unit("hours since 1969-11-30 00:00:00", calendar=calendar)
+ u1point = np.array([8.0], dtype=np.float32)
+ expected = np.array([776.0], dtype=np.float32)
result = u1.convert(u1point, u2)
return expected, result
@@ -72,8 +70,8 @@ def test_non_gregorian_calendar_conversion_dtype(self):
(np.int, False),
):
data = np.arange(4, dtype=start_dtype)
- u1 = Unit('hours since 2000-01-01 00:00:00', calendar='360_day')
- u2 = Unit('hours since 2000-01-02 00:00:00', calendar='360_day')
+ u1 = Unit("hours since 2000-01-01 00:00:00", calendar="360_day")
+ u2 = Unit("hours since 2000-01-02 00:00:00", calendar="360_day")
result = u1.convert(data, u2)
if exp_convert:
@@ -89,27 +87,34 @@ class Test_convert__endianness_time(unittest.TestCase):
def setUp(self):
self.time1_array = np.array([31.5, 32.5, 33.5])
self.time2_array = np.array([0.5, 1.5, 2.5])
- self.time1_unit = cf_units.Unit('days since 1970-01-01 00:00:00',
- calendar=cf_units.CALENDAR_STANDARD)
- self.time2_unit = cf_units.Unit('days since 1970-02-01 00:00:00',
- calendar=cf_units.CALENDAR_STANDARD)
+ self.time1_unit = cf_units.Unit(
+ "days since 1970-01-01 00:00:00",
+ calendar=cf_units.CALENDAR_STANDARD,
+ )
+ self.time2_unit = cf_units.Unit(
+ "days since 1970-02-01 00:00:00",
+ calendar=cf_units.CALENDAR_STANDARD,
+ )
def test_no_endian(self):
- dtype = 'f8'
- result = self.time1_unit.convert(self.time1_array.astype(dtype),
- self.time2_unit)
+ dtype = "f8"
+ result = self.time1_unit.convert(
+ self.time1_array.astype(dtype), self.time2_unit
+ )
np.testing.assert_array_almost_equal(result, self.time2_array)
def test_little_endian(self):
- dtype = '