Repository: hgrecco/pint

Issue:
Numpy pad keyerror
Hey all,

First of all, really cool project. I ran into the following issue: when calling `numpy.pad` on a numpy array based quantity I ran into the following error:

```
np.pad(np.array([1,1]) * ureg.meter, pad_width=1)
  File "<stdin>", line 1, in <module>
  File "<__array_function__ internals>", line 6, in pad
  File "/usr/local/lib/python3.7/site-packages/pint/quantity.py", line 1513, in __array_function__
    return numpy_wrap("function", func, args, kwargs, types)
  File "/usr/local/lib/python3.7/site-packages/pint/numpy_func.py", line 889, in numpy_wrap
    return handled[name](*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/pint/numpy_func.py", line 668, in _pad
    kwargs["constant_values"] = _recursive_convert(kwargs["constant_values"], units)
KeyError: 'constant_values'
``` 

The problem lies in the following lines, it is not checked if `kwargs` does contain "constant_values" or "end_values". Adding the check `and "constant_values" in kwargs` should probably work.
```
if mode == "constant":
    kwargs["constant_values"] = _recursive_convert(kwargs["constant_values"], units)
elif mode == "linear_ramp":
    kwargs["end_values"] = _recursive_convert(kwargs["end_values"], units)
```

Discussion:

Patch:
diff --git a/CHANGES b/CHANGES
index 8e9c715..65fd3bc 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,29 +1,13 @@
 Pint Changelog
 ==============
 
-0.12 (unreleased)
+0.11 (unreleased)
 -----------------
 
-- Nothing changed yet.
-
-
-0.11 (2020-02-19)
------------------
-
-- Added pint-convert script.
-- Remove `default_en_0.6.txt`.
-- Make `__str__` and `__format__` locale configurable.
-  (Issue #984)
-- Quantities wrapping NumPy arrays will no longer warning for the changed
-  array function behavior introduced in 0.10.
-  (Issue #1029, Thanks Jon Thielen)
-- **BREAKING CHANGE**:
-  The array protocol fallback deprecated in version 0.10 has been removed.
-  (Issue #1029, Thanks Jon Thielen)
 - Now we use `pyproject.toml` for providing `setuptools_scm` settings
 - Remove `default_en_0.6.txt`
 - Reorganize long_description.
-- Moved Pi to definitions files.
+- Moved Pi to defintions files.
 - Use ints (not floats) a defaults at many points in the codebase as in Python 3
   the true division is the default one.
 - **BREAKING CHANGE**:
@@ -35,6 +19,8 @@ Pint Changelog
 - Allow constants in units by using a leading underscore (Issue #989, Thanks
   Juan Nunez-Iglesias)
 - Fixed bug where to_compact handled prefix units incorrectly (Issue #960)
+- Fixed bug where numpy.pad didn't work without specifying constant_values or
+  end_values (Issue #1026)
 
 0.10.1 (2020-01-07)
 -------------------
@@ -251,7 +237,7 @@ Pint Changelog
 - Added several new units and Systems
   (Issues #749, #737, )
 - Started experimental pandas support
-  (Issue #746 and others. Thanks andrewgsavage, znicholls and others)  
+  (Issue #746 and others. Thanks andrewgsavage, znicholls and others)
 - wraps and checks now supports kwargs and defaults.
   (Issue #660, thanks jondoesntgit)
 
diff --git a/MANIFEST.in b/MANIFEST.in
index cd897ad..0cf6865 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt readthedocs.yml .coveragerc
+include AUTHORS CHANGES LICENSE README.rst BADGES.rst version.txt readthedocs.yml
 recursive-include pint *
 recursive-include docs *
 recursive-include bench *
diff --git a/README.rst b/README.rst
index 97cda30..f90ea45 100644
--- a/README.rst
+++ b/README.rst
@@ -87,20 +87,12 @@ Documentation
 
 Full documentation is available at http://pint.readthedocs.org/
 
-
 GUI Website 
 -----------
 
 This Website_ wraps Pint's "dimensional analysis" methods to provide a GUI.
 
 
-Command-line converter
-----------------------
-
-A command-line script `pint-convert` provides a quick way to convert between
-units or get conversion factors.
-
-
 Design principles
 -----------------
 
@@ -166,4 +158,4 @@ see CHANGES_
 .. _`Pandas Extension Types`: https://pandas.pydata.org/pandas-docs/stable/extending.html#extension-types
 .. _`pint-pandas Jupyter notebook`: https://github.com/hgrecco/pint-pandas/blob/master/notebooks/pandas_support.ipynb
 .. _`AUTHORS`: https://github.com/hgrecco/pint/blob/master/AUTHORS
-.. _`CHANGES`: https://github.com/hgrecco/pint/blob/master/CHANGES
+.. _`CHANGES`: https://github.com/hgrecco/pint/blob/master/CHANGES
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 5d22dda..e72ed87 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -128,7 +128,6 @@ User Guide
     systems
     currencies
     pint-pandas.ipynb
-    pint-convert
 
 More information
 ----------------
diff --git a/docs/numpy.ipynb b/docs/numpy.ipynb
index 41b6491..4ed462b 100644
--- a/docs/numpy.ipynb
+++ b/docs/numpy.ipynb
@@ -24,6 +24,10 @@
     "# Import NumPy\n",
     "import numpy as np\n",
     "\n",
+    "# Disable Pint's old fallback behavior (must come before importing Pint)\n",
+    "import os\n",
+    "os.environ['PINT_ARRAY_PROTOCOL_FALLBACK'] = \"0\"\n",
+    "\n",
     "# Import Pint\n",
     "import pint\n",
     "ureg = pint.UnitRegistry()\n",
@@ -776,7 +780,8 @@
     "\n",
     "This behaviour introduces some performance penalties and increased memory usage. Quantities that must be converted to other units require additional memory and CPU cycles. Therefore, for numerically intensive code, you might want to convert the objects first and then use directly the magnitude, such as by using Pint's `wraps` utility (see [wrapping](wrapping.rst)).\n",
     "\n",
-    "Attempting to access array interface protocol attributes (such as `__array_struct__` and `__array_interface__`) on Pint Quantities will raise an AttributeError, since a Quantity is meant to behave as a \"duck array,\" and not a pure ndarray."
+    "Array interface protocol attributes (such as `__array_struct__` and\n",
+    "`__array_interface__`) are available on Pint Quantities by deferring to the corresponding `__array_*` attribute on the magnitude as casted to an ndarray. This has been found to be potentially incorrect and to cause unexpected behavior, and has therefore been deprecated. As of the next minor version of Pint (or when the `PINT_ARRAY_PROTOCOL_FALLBACK` environment variable is set to 0 prior to importing Pint as done at the beginning of this page), attempting to access these attributes will instead raise an AttributeError."
    ]
   }
  ],
@@ -796,7 +801,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.6"
+   "version": "3.6.7"
   }
  },
  "nbformat": 4,
diff --git a/docs/pint-convert.rst b/docs/pint-convert.rst
deleted file mode 100644
index 8d548f8..0000000
--- a/docs/pint-convert.rst
+++ /dev/null
@@ -1,91 +0,0 @@
-.. _convert:
-
-Command-line script
-===================
-
-The script `pint-convert` allows a quick conversion to a target system or
-between arbitrary compatible units.
-
-By default, `pint-convert` converts to SI units::
-
-    $ pint-convert 225lb
-    225 pound = 102.05828325 kg
-
-use the `--sys` argument to change it::
-
-    $ pint-convert --sys US 102kg
-    102 kilogram = 224.871507429 lb
-
-or specify directly the target units::
-
-    $ pint-convert 102kg lb
-    102 kilogram = 224.871507429 lb
-
-The input quantity can contain expressions::
-
-    $ pint-convert 7ft+2in
-    7.166666666666667 foot = 2.1844 m
-
-in some cases parentheses and quotes may be needed::
-
-    $ pint-convert "225lb/(7ft+2in)"
-    31.3953488372093 pound / foot = 46.7214261353 kg/m
-
-If a number is omitted, 1 is assumed::
-
-    $ pint-convert km mi
-    1 kilometer = 0.621371192237 mi
-
-The default precision is 12 significant figures, it can be changed with `-p`,
-but note that the accuracy may be affected by floating-point errors::
-
-    $ pint-convert -p 3 mi
-    1 mile = 1.61e+03 m
-
-    $ pint-convert -p 30 ly km
-    1 light_year = 9460730472580.80078125 km
-
-Some contexts are automatically enabled, allowing conversion between not fully
-compatible units::
-
-    $ pint-convert 540nm
-    540 nanometer = 5.4e-07 m
-
-    $ pint-convert kcal/mol
-    $ 1.0 kilocalorie / mole = 4184 kg·m²/mol/s²
-
-    $ pint-convert 540nm kcal/mol
-    540 nanometer = 52.9471025594 kcal/mol
-
-With the `uncertainties` package, the experimental uncertainty in the physical
-constants is considered, and the result is given in compact notation, with the
-uncertainty in the last figures in parentheses::
-
-    $ pint-convert Eh eV
-    1 hartree = 27.21138624599(5) eV
-
-The precision is limited by both the maximum number of significant digits (`-p`)
-and the maximum number of uncertainty digits (`-u`, 2 by default)::
-
-    $ pint-convert -p 20 Eh eV
-    1 hartree = 27.211386245988(52) eV
-
-    $ pint-convert -p 20 -u 4 Eh eV
-    1 hartree = 27.21138624598847(5207) eV
-
-The uncertainty can be disabled with `-U`)::
-
-    $ pint-convert -p 20 -U Eh eV
-    1 hartree = 27.211386245988471444 eV
-
-Correlations between experimental constants are also known, and taken into
-account. Use `-C` to disable it::
-
-    $ pint-convert --sys atomic m_p
-    1 proton_mass = 1836.15267344(11) m_e
-
-    $ pint-convert --sys atomic -C m_p
-    1 proton_mass = 1836.15267344(79) m_e
-
-Again, note that results may differ slightly, usually in the last figure, from
-more authoritative sources, mainly due to floating-point errors.
diff --git a/docs/tutorial.rst b/docs/tutorial.rst
index ac9c2b3..9693444 100644
--- a/docs/tutorial.rst
+++ b/docs/tutorial.rst
@@ -419,27 +419,10 @@ Finally, if Babel_ is installed you can translate unit names to any language
    >>> accel.format_babel(locale='fr_FR')
    '1.3 mètre par seconde²'
 
-You can also specify the format locale at the registry level either at creation:
+You can also specify the format locale at u
 
     >>> ureg = UnitRegistry(fmt_locale='fr_FR')
 
-or later:
-
-.. doctest::
-
-    >>> ureg.set_fmt_locale('fr_FR')
-
-and by doing that, string formatting is now localized:
-
-.. doctest::
-
-    >>> str(accel)
-    '1.3 mètre par seconde²'
-    >>> "%s" % accel
-    '1.3 mètre par seconde²'
-    >>> "{}".format(accel)
-    '1.3 mètre par seconde²'
-
 
 Using Pint in your projects
 ---------------------------
diff --git a/pint/compat.py b/pint/compat.py
index e8e1a1b..6fb27ee 100644
--- a/pint/compat.py
+++ b/pint/compat.py
@@ -7,6 +7,7 @@
     :copyright: 2013 by Pint Authors, see AUTHORS for more details.
     :license: BSD, see LICENSE for more details.
 """
+import os
 import tokenize
 from decimal import Decimal
 from io import BytesIO
@@ -36,6 +37,27 @@ class BehaviorChangeWarning(UserWarning):
     pass
 
 
+array_function_change_msg = """The way Pint handles NumPy operations has changed with the
+implementation of NEP 18. Unimplemented NumPy operations will now fail instead of making
+assumptions about units. Some functions, eg concat, will now return Quanties with units, where
+they returned ndarrays previously. See https://github.com/hgrecco/pint/pull/905.
+
+To hide this warning, wrap your first creation of an array Quantity with
+warnings.catch_warnings(), like the following:
+
+import numpy as np
+import warnings
+from pint import Quantity
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore")
+    Quantity([])
+
+To disable the new behavior, see
+https://www.numpy.org/neps/nep-0018-array-function-protocol.html#implementation
+"""
+
+
 try:
     import numpy as np
     from numpy import ndarray
@@ -71,9 +93,12 @@ try:
             return False
 
     HAS_NUMPY_ARRAY_FUNCTION = _test_array_function_protocol()
+    SKIP_ARRAY_FUNCTION_CHANGE_WARNING = not HAS_NUMPY_ARRAY_FUNCTION
 
     NP_NO_VALUE = np._NoValue
 
+    ARRAY_FALLBACK = bool(int(os.environ.get("PINT_ARRAY_PROTOCOL_FALLBACK", 1)))
+
 except ImportError:
 
     np = None
@@ -85,7 +110,9 @@ except ImportError:
     NUMPY_VER = "0"
     NUMERIC_TYPES = (Number, Decimal)
     HAS_NUMPY_ARRAY_FUNCTION = False
+    SKIP_ARRAY_FUNCTION_CHANGE_WARNING = True
     NP_NO_VALUE = None
+    ARRAY_FALLBACK = False
 
     def _to_magnitude(value, force_ndarray=False, force_ndarray_like=False):
         if force_ndarray or force_ndarray_like:
diff --git a/pint/constants_en.txt b/pint/constants_en.txt
index c3ecbf1..fa485fa 100644
--- a/pint/constants_en.txt
+++ b/pint/constants_en.txt
@@ -8,7 +8,7 @@
 #### MATHEMATICAL CONSTANTS ####
 # As computed by Maxima with fpprec:50
 
-pi     = 3.1415926535897932384626433832795028841971693993751 = π  # pi
+pi    = 3.1415926535897932384626433832795028841971693993751 = π  # pi
 tansec = 4.8481368111333441675396429478852851658848753880815e-6   # tangent of 1 arc-second ~ arc_second/radian
 ln10   = 2.3025850929940456840179914546843642076011014886288      # natural logarithm of 10
 wien_x = 4.9651142317442763036987591313228939440555849867973      # solution to (x-5)*exp(x)+5 = 0 => x = W(5/exp(5))+5
diff --git a/pint/definitions.py b/pint/definitions.py
index 5dfc0ea..8ffa153 100644
--- a/pint/definitions.py
+++ b/pint/definitions.py
@@ -15,8 +15,8 @@ from .errors import DefinitionSyntaxError
 from .util import ParserHelper, UnitsContainer, _is_dim
 
 
-class PreprocessedDefinition(
-    namedtuple("PreprocessedDefinition", "name symbol aliases value rhs_parts")
+class ParsedDefinition(
+    namedtuple("ParsedDefinition", "name symbol aliases value rhs_parts")
 ):
     """Splits a definition into the constitutive parts.
 
@@ -125,7 +125,7 @@ class Definition:
 
         Parameters
         ----------
-        definition : str or PreprocessedDefinition
+        definition : str or ParsedDefinition
 
         Returns
         -------
@@ -133,7 +133,7 @@ class Definition:
         """
 
         if isinstance(definition, str):
-            definition = PreprocessedDefinition.from_string(definition)
+            definition = ParsedDefinition.from_string(definition)
 
         if definition.name.startswith("@alias "):
             return AliasDefinition.from_string(definition)
@@ -184,7 +184,7 @@ class PrefixDefinition(Definition):
     @classmethod
     def from_string(cls, definition):
         if isinstance(definition, str):
-            definition = PreprocessedDefinition.from_string(definition)
+            definition = ParsedDefinition.from_string(definition)
 
         aliases = tuple(alias.strip("-") for alias in definition.aliases)
         if definition.symbol:
@@ -228,7 +228,7 @@ class UnitDefinition(Definition):
     @classmethod
     def from_string(cls, definition):
         if isinstance(definition, str):
-            definition = PreprocessedDefinition.from_string(definition)
+            definition = ParsedDefinition.from_string(definition)
 
         if ";" in definition.value:
             [converter, modifiers] = definition.value.split(";", 2)
@@ -294,7 +294,7 @@ class DimensionDefinition(Definition):
     @classmethod
     def from_string(cls, definition):
         if isinstance(definition, str):
-            definition = PreprocessedDefinition.from_string(definition)
+            definition = ParsedDefinition.from_string(definition)
 
         converter = ParserHelper.from_string(definition.value)
 
@@ -336,7 +336,7 @@ class AliasDefinition(Definition):
     def from_string(cls, definition):
 
         if isinstance(definition, str):
-            definition = PreprocessedDefinition.from_string(definition)
+            definition = ParsedDefinition.from_string(definition)
 
         name = definition.name[len("@alias ") :].lstrip()
         return AliasDefinition(name, tuple(definition.rhs_parts))
diff --git a/pint/numpy_func.py b/pint/numpy_func.py
index 10f4114..c03887b 100644
--- a/pint/numpy_func.py
+++ b/pint/numpy_func.py
@@ -667,10 +667,9 @@ def _pad(array, pad_width, mode="constant", **kwargs):
 
     # Handle flexible constant_values and end_values, converting to units if Quantity
     # and ignoring if not
-    if mode == "constant":
-        kwargs["constant_values"] = _recursive_convert(kwargs["constant_values"], units)
-    elif mode == "linear_ramp":
-        kwargs["end_values"] = _recursive_convert(kwargs["end_values"], units)
+    for key in ("constant_values", "end_values"):
+        if key in kwargs:
+            kwargs[key] = _recursive_convert(kwargs[key], units)
 
     return units._REGISTRY.Quantity(
         np.pad(array._magnitude, pad_width, mode=mode, **kwargs), units
diff --git a/pint/pint-convert b/pint/pint-convert
deleted file mode 100755
index da7df23..0000000
--- a/pint/pint-convert
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python3
-
-"""
-    pint-convert
-    ~~~~~~~~~~~~
-
-    :copyright: 2020 by Pint Authors, see AUTHORS for more details.
-    :license: BSD, see LICENSE for more details.
-"""
-
-import argparse
-import re
-import sys
-
-from pint import UnitRegistry
-
-parser = argparse.ArgumentParser(description='Unit converter.', usage=argparse.SUPPRESS)
-parser.add_argument('-s', '--system', metavar='sys', default='SI', help='unit system to convert to (default: SI)')
-parser.add_argument('-p', '--prec', metavar='n', type=int, default=12, help='number of maximum significant figures (default: 12)')
-parser.add_argument('-u', '--prec-unc', metavar='n', type=int, default=2, help='number of maximum uncertainty digits (default: 2)')
-parser.add_argument('-U', '--no-unc', dest='unc', action='store_false', help='ignore uncertainties in constants')
-parser.add_argument('-C', '--no-corr', dest='corr', action='store_false', help='ignore correlations between constants')
-parser.add_argument('fr', metavar='from', type=str, help='unit or quantity to convert from')
-parser.add_argument('to', type=str, nargs='?', help='unit to convert to')
-try:
-    args = parser.parse_args()
-except SystemExit:
-    parser.print_help()
-    raise
-
-ureg = UnitRegistry()
-ureg.auto_reduce_dimensions = True
-ureg.autoconvert_offset_to_baseunit = True
-ureg.enable_contexts('Gau', 'ESU', 'sp', 'energy', 'boltzmann')
-ureg.default_system = args.system
-
-if args.unc:
-    import uncertainties
-    # Measured constans subject to correlation
-    #  R_i: Rydberg constant
-    #  g_e: Electron g factor
-    #  m_u: Atomic mass constant
-    #  m_e: Electron mass
-    #  m_p: Proton mass
-    #  m_n: Neutron mass
-    R_i = (ureg._units['R_inf'].converter.scale, 0.0000000000021e7)
-    g_e = (ureg._units['g_e'].converter.scale,   0.00000000000035)
-    m_u = (ureg._units['m_u'].converter.scale,   0.00000000050e-27)
-    m_e = (ureg._units['m_e'].converter.scale,   0.00000000028e-30)
-    m_p = (ureg._units['m_p'].converter.scale,   0.00000000051e-27)
-    m_n = (ureg._units['m_n'].converter.scale,   0.00000000095e-27)
-    if args.corr:
-        # Correlation matrix between measured constants (to be completed below)
-        #          R_i       g_e       m_u       m_e       m_p       m_n
-        corr = [[  1.0    , -0.00206,  0.00369,  0.00436,  0.00194,  0.00233],  # R_i
-                [ -0.00206,  1.0    ,  0.99029,  0.99490,  0.97560,  0.52445],  # g_e
-                [  0.00369,  0.99029,  1.0    ,  0.99536,  0.98516,  0.52959],  # m_u
-                [  0.00436,  0.99490,  0.99536,  1.0    ,  0.98058,  0.52714],  # m_e
-                [  0.00194,  0.97560,  0.98516,  0.98058,  1.0    ,  0.51521],  # m_p
-                [  0.00233,  0.52445,  0.52959,  0.52714,  0.51521,  1.0    ]]  # m_n
-        (R_i, g_e, m_u, m_e, m_p, m_n) = uncertainties.correlated_values_norm([R_i, g_e, m_u, m_e, m_p, m_n], corr)
-    else:
-        R_i = uncertainties.ufloat(*R_i)
-        g_e = uncertainties.ufloat(*g_e)
-        m_u = uncertainties.ufloat(*m_u)
-        m_e = uncertainties.ufloat(*m_e)
-        m_p = uncertainties.ufloat(*m_p)
-        m_n = uncertainties.ufloat(*m_n)
-    ureg._units['R_inf'].converter.scale = R_i
-    ureg._units['g_e'].converter.scale   = g_e
-    ureg._units['m_u'].converter.scale   = m_u
-    ureg._units['m_e'].converter.scale   = m_e
-    ureg._units['m_p'].converter.scale   = m_p
-    ureg._units['m_n'].converter.scale   = m_n
-
-    # Measured constants with zero correlation
-    ureg._units['gravitational_constant'].converter.scale = uncertainties.ufloat(ureg._units['gravitational_constant'].converter.scale, 0.00015e-11)
-    ureg._units['d_220'].converter.scale                  = uncertainties.ufloat(ureg._units['d_220'].converter.scale,                  0.000000032e-10)
-    ureg._units['K_alpha_Cu_d_220'].converter.scale       = uncertainties.ufloat(ureg._units['K_alpha_Cu_d_220'].converter.scale,       0.00000022)
-    ureg._units['K_alpha_Mo_d_220'].converter.scale       = uncertainties.ufloat(ureg._units['K_alpha_Mo_d_220'].converter.scale,       0.00000019)
-    ureg._units['K_alpha_W_d_220'].converter.scale        = uncertainties.ufloat(ureg._units['K_alpha_W_d_220'].converter.scale,        0.000000098)
-
-    ureg._root_units_cache = dict()
-    ureg._build_cache()
-
-def convert(u_from, u_to=None, unc=None, factor=None):
-    q = ureg.Quantity(u_from)
-    fmt = '.{}g'.format(args.prec)
-    if unc:
-        q = q.plus_minus(unc)
-    if u_to:
-        nq = q.to(u_to)
-    else:
-        nq = q.to_base_units()
-    if (factor):
-        q *= ureg.Quantity(factor)
-        nq *= ureg.Quantity(factor).to_base_units()
-    prec_unc = use_unc(nq.magnitude, fmt, args.prec_unc)
-    if (prec_unc > 0):
-        fmt = '.{}uS'.format(prec_unc)
-    else:
-        try:
-            nq = nq.magnitude.n * nq.units
-        except:
-            pass
-    fmt = '{:' + fmt + '} {:~P}'
-    print(('{:} = ' + fmt).format(q, nq.magnitude, nq.units))
-
-def use_unc(num, fmt, prec_unc):
-    unc = 0
-    try:
-        if (isinstance(num, uncertainties.UFloat)):
-            full = ('{:'+fmt+'}').format(num)
-            unc = re.search(r'\+\/-[0.]*([\d.]*)', full).group(1)
-            unc = len(unc.replace('.', ''))
-    except:
-        pass
-    return max(0, min(prec_unc, unc))
-
-convert(args.fr, args.to)
diff --git a/pint/quantity.py b/pint/quantity.py
index c8fcde3..051fc84 100644
--- a/pint/quantity.py
+++ b/pint/quantity.py
@@ -20,9 +20,13 @@ import warnings
 
 from pkg_resources.extern.packaging import version
 
+from .compat import SKIP_ARRAY_FUNCTION_CHANGE_WARNING  # noqa: F401
 from .compat import (
+    ARRAY_FALLBACK,
     NUMPY_VER,
+    BehaviorChangeWarning,
     _to_magnitude,
+    array_function_change_msg,
     babel_parse,
     eq,
     is_duck_array_type,
@@ -157,6 +161,8 @@ class Quantity(PrettyIPython, SharedRegistryObject):
         return _unpickle, (Quantity, self.magnitude, self._units)
 
     def __new__(cls, value, units=None):
+        global SKIP_ARRAY_FUNCTION_CHANGE_WARNING
+
         if is_upcast_type(type(value)):
             raise TypeError(f"Quantity cannot wrap upcast type {type(value)}")
         elif units is None:
@@ -209,6 +215,12 @@ class Quantity(PrettyIPython, SharedRegistryObject):
         inst.__used = False
         inst.__handling = None
 
+        if not SKIP_ARRAY_FUNCTION_CHANGE_WARNING and isinstance(
+            inst._magnitude, ndarray
+        ):
+            warnings.warn(array_function_change_msg, BehaviorChangeWarning)
+            SKIP_ARRAY_FUNCTION_CHANGE_WARNING = True
+
         return inst
 
     @property
@@ -240,9 +252,6 @@ class Quantity(PrettyIPython, SharedRegistryObject):
         return ret
 
     def __str__(self):
-        if self._REGISTRY.fmt_locale is not None:
-            return self.format_babel()
-
         return format(self)
 
     def __bytes__(self):
@@ -261,9 +270,6 @@ class Quantity(PrettyIPython, SharedRegistryObject):
     _exp_pattern = re.compile(r"([0-9]\.?[0-9]*)e(-?)\+?0*([0-9]+)")
 
     def __format__(self, spec):
-        if self._REGISTRY.fmt_locale is not None:
-            return self.format_babel(spec)
-
         spec = spec or self.default_format
 
         if "L" in spec:
@@ -498,40 +504,6 @@ class Quantity(PrettyIPython, SharedRegistryObject):
 
         return self._REGISTRY.get_compatible_units(self._units)
 
-    def is_compatible_with(self, other, *contexts, **ctx_kwargs):
-        """ check if the other object is compatible
-
-        Parameters
-        ----------
-        other
-            The object to check. Treated as dimensionless if not a
-            Quantity, Unit or str.
-        *contexts : str or pint.Context
-            Contexts to use in the transformation.
-        **ctx_kwargs :
-            Values for the Context/s
-
-        Returns
-        -------
-        bool
-        """
-        if contexts:
-            try:
-                self.to(other, *contexts, **ctx_kwargs)
-                return True
-            except DimensionalityError:
-                return False
-
-        if isinstance(other, (self._REGISTRY.Quantity, self._REGISTRY.Unit)):
-            return self.dimensionality == other.dimensionality
-
-        if isinstance(other, str):
-            return (
-                self.dimensionality == self._REGISTRY.parse_units(other).dimensionality
-            )
-
-        return self.dimensionless
-
     def _convert_magnitude_not_inplace(self, other, *contexts, **ctx_kwargs):
         if contexts:
             with self._REGISTRY.context(*contexts, **ctx_kwargs):
@@ -1686,7 +1658,34 @@ class Quantity(PrettyIPython, SharedRegistryObject):
     def __getattr__(self, item):
         if item.startswith("__array_"):
             # Handle array protocol attributes other than `__array__`
-            raise AttributeError(f"Array protocol attribute {item} not available.")
+            if ARRAY_FALLBACK:
+                # Deprecated fallback behavior
+                warnings.warn(
+                    (
+                        f"Array protocol attribute {item} accessed, with unit of the "
+                        "Quantity being stripped. This attribute will become unavailable "
+                        "in the next minor version of Pint. To make this potentially "
+                        "incorrect attribute unavailable now, set the "
+                        "PINT_ARRAY_PROTOCOL_FALLBACK environment variable to 0 before "
+                        "importing Pint."
+                    ),
+                    DeprecationWarning,
+                    stacklevel=2,
+                )
+
+                if is_duck_array_type(type(self._magnitude)):
+                    # Defer to magnitude, and don't catch any AttributeErrors
+                    return getattr(self._magnitude, item)
+                else:
+                    # If an `__array_` attribute is requested but the magnitude is not
+                    # a duck array, we convert the magnitude to a numpy ndarray.
+                    magnitude_as_array = _to_magnitude(
+                        self._magnitude, force_ndarray=True
+                    )
+                    return getattr(magnitude_as_array, item)
+            else:
+                # TODO (next minor version): ARRAY_FALLBACK is removed and this becomes the standard behavior
+                raise AttributeError(f"Array protocol attribute {item} not available.")
         elif item in HANDLED_UFUNCS or item in self._wrapped_numpy_methods:
             magnitude_as_duck_array = _to_magnitude(
                 self._magnitude, force_ndarray_like=True
diff --git a/pint/registry.py b/pint/registry.py
index c222346..c42729f 100644
--- a/pint/registry.py
+++ b/pint/registry.py
@@ -172,9 +172,6 @@ class BaseRegistry(metaclass=RegistryMeta):
     #: type: Dict[str, (SourceIterator -> None)]
     _parsers = None
 
-    #: Babel.Locale instance or None
-    fmt_locale = None
-
     #: List to be used in addition of units when dir(registry) is called.
     #: Also used for autocompletion in IPython.
     _dir = [
@@ -219,7 +216,7 @@ class BaseRegistry(metaclass=RegistryMeta):
         self.auto_reduce_dimensions = auto_reduce_dimensions
 
         #: Default locale identifier string, used when calling format_babel without explicit locale.
-        self.set_fmt_locale(fmt_locale)
+        self.fmt_locale = self.set_fmt_locale(fmt_locale)
 
         #: Map between name (string) and value (string) of defaults stored in the
         #: definitions file.
@@ -868,33 +865,6 @@ class BaseRegistry(metaclass=RegistryMeta):
         src_dim = self._get_dimensionality(input_units)
         return self._cache.dimensional_equivalents[src_dim]
 
-    def is_compatible_with(self, obj1, obj2, *contexts, **ctx_kwargs):
-        """ check if the other object is compatible
-
-        Parameters
-        ----------
-        obj1, obj2
-            The objects to check against each other. Treated as
-            dimensionless if not a Quantity, Unit or str.
-        *contexts : str or pint.Context
-            Contexts to use in the transformation.
-        **ctx_kwargs :
-            Values for the Context/s
-
-        Returns
-        -------
-        bool
-        """
-        if isinstance(obj1, (self.Quantity, self.Unit)):
-            return obj1.is_compatible_with(obj2, *contexts, **ctx_kwargs)
-
-        if isinstance(obj1, str):
-            return self.parse_expression(obj1).is_compatible_with(
-                obj2, *contexts, **ctx_kwargs
-            )
-
-        return not isinstance(obj2, (self.Quantity, self.Unit))
-
     def convert(self, value, src, dst, inplace=False):
         """Convert value from some source to destination units.
 
diff --git a/pint/unit.py b/pint/unit.py
index 25084b4..5db27cd 100644
--- a/pint/unit.py
+++ b/pint/unit.py
@@ -15,7 +15,6 @@ from numbers import Number
 
 from .compat import NUMERIC_TYPES, is_upcast_type
 from .definitions import UnitDefinition
-from .errors import DimensionalityError
 from .formatting import siunitx_format_unit
 from .util import PrettyIPython, SharedRegistryObject, UnitsContainer
 
@@ -144,40 +143,6 @@ class Unit(PrettyIPython, SharedRegistryObject):
 
         return self._REGISTRY.get_compatible_units(self)
 
-    def is_compatible_with(self, other, *contexts, **ctx_kwargs):
-        """ check if the other object is compatible
-
-        Parameters
-        ----------
-        other
-            The object to check. Treated as dimensionless if not a
-            Quantity, Unit or str.
-        *contexts : str or pint.Context
-            Contexts to use in the transformation.
-        **ctx_kwargs :
-            Values for the Context/s
-
-        Returns
-        -------
-        bool
-        """
-        if contexts:
-            try:
-                (1 * self).to(other, *contexts, **ctx_kwargs)
-                return True
-            except DimensionalityError:
-                return False
-
-        if isinstance(other, (self._REGISTRY.Quantity, self._REGISTRY.Unit)):
-            return self.dimensionality == other.dimensionality
-
-        if isinstance(other, str):
-            return (
-                self.dimensionality == self._REGISTRY.parse_units(other).dimensionality
-            )
-
-        return self.dimensionless
-
     def __mul__(self, other):
         if self._check(other):
             if isinstance(other, self.__class__):
diff --git a/setup.cfg b/setup.cfg
index 2777599..83320c1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -30,7 +30,6 @@ python_requires = >=3.6
 install_requires = setuptools
 setup_requires = setuptools; setuptools_scm
 test_suite = pint.testsuite.testsuite
-scripts = pint/pint-convert
 
 [options.extras_require]
 numpy = numpy >= 1.14
@@ -75,4 +74,4 @@ use_parentheses=True
 line_length=88
 
 [zest.releaser]
-python-file-with-version = version.py
+python-file-with-version = version.py
\ No newline at end of file
diff --git a/version.py b/version.py
index 16c455f..17a86c7 100644
--- a/version.py
+++ b/version.py
@@ -2,5 +2,5 @@
 # flake8: noqa
 
 # fmt: off
-__version__ = '0.12.dev0'
+__version__ = '0.11.dev0'
 # fmt: on


Test patch:
diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py
index 2bf743c..5a91391 100644
--- a/pint/testsuite/helpers.py
+++ b/pint/testsuite/helpers.py
@@ -67,10 +67,6 @@ def requires_babel():
     return unittest.skipUnless(HAS_BABEL, "Requires Babel with units support")
 
 
-def requires_not_babel():
-    return unittest.skipIf(HAS_BABEL, "Requires Babel is not installed")
-
-
 def requires_uncertainties():
     return unittest.skipUnless(HAS_UNCERTAINTIES, "Requires Uncertainties")
 
diff --git a/pint/testsuite/test_babel.py b/pint/testsuite/test_babel.py
index def7c83..2aac0cd 100644
--- a/pint/testsuite/test_babel.py
+++ b/pint/testsuite/test_babel.py
@@ -5,14 +5,6 @@ from pint.testsuite import BaseTestCase, helpers
 
 
 class TestBabel(BaseTestCase):
-    @helpers.requires_not_babel()
-    def test_no_babel(self):
-        ureg = UnitRegistry()
-        distance = 24.0 * ureg.meter
-        self.assertRaises(
-            Exception, distance.format_babel, locale="fr_FR", length="long"
-        )
-
     @helpers.requires_babel()
     def test_format(self):
         ureg = UnitRegistry()
@@ -54,32 +46,9 @@ class TestBabel(BaseTestCase):
         mks = ureg.get_system("mks")
         self.assertEqual(mks.format_babel(locale="fr_FR"), "métrique")
 
-    @helpers.requires_babel()
-    def test_no_registry_locale(self):
+    def test_nobabel(self):
         ureg = UnitRegistry()
         distance = 24.0 * ureg.meter
         self.assertRaises(
-            Exception, distance.format_babel,
+            Exception, distance.format_babel, locale="fr_FR", length="long"
         )
-
-    @helpers.requires_babel()
-    def test_str(self):
-        ureg = UnitRegistry()
-        d = 24.0 * ureg.meter
-
-        s = "24.0 meter"
-        self.assertEqual(str(d), s)
-        self.assertEqual("%s" % d, s)
-        self.assertEqual("{}".format(d), s)
-
-        ureg.set_fmt_locale("fr_FR")
-        s = "24.0 mètres"
-        self.assertEqual(str(d), s)
-        self.assertEqual("%s" % d, s)
-        self.assertEqual("{}".format(d), s)
-
-        ureg.set_fmt_locale(None)
-        s = "24.0 meter"
-        self.assertEqual(str(d), s)
-        self.assertEqual("%s" % d, s)
-        self.assertEqual("{}".format(d), s)
diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py
index 3eeab6c..6775e75 100644
--- a/pint/testsuite/test_numpy.py
+++ b/pint/testsuite/test_numpy.py
@@ -1,6 +1,7 @@
 import copy
 import operator as op
 import unittest
+from unittest.mock import patch
 
 from pint import DimensionalityError, OffsetUnitCalculusError, UnitStrippedWarning
 from pint.compat import np
@@ -1030,15 +1031,28 @@ class TestNumpyUnclassified(TestNumpyMethods):
             np.array([[1, 0, 2], [3, 0, 4]]) * self.ureg.m,
         )
 
+    @patch("pint.quantity.ARRAY_FALLBACK", False)
     def test_ndarray_downcast(self):
         with self.assertWarns(UnitStrippedWarning):
             np.asarray(self.q)
 
+    @patch("pint.quantity.ARRAY_FALLBACK", False)
     def test_ndarray_downcast_with_dtype(self):
         with self.assertWarns(UnitStrippedWarning):
             qarr = np.asarray(self.q, dtype=np.float64)
             self.assertEqual(qarr.dtype, np.float64)
 
+    def test_array_protocol_fallback(self):
+        with self.assertWarns(DeprecationWarning) as cm:
+            for attr in ("__array_struct__", "__array_interface__"):
+                getattr(self.q, attr)
+                warning_text = str(cm.warnings[0].message)
+                self.assertTrue(
+                    f"unit of the Quantity being stripped" in warning_text
+                    and "will become unavailable" in warning_text
+                )
+
+    @patch("pint.quantity.ARRAY_FALLBACK", False)
     def test_array_protocol_unavailable(self):
         for attr in ("__array_struct__", "__array_interface__"):
             self.assertRaises(AttributeError, getattr, self.q, attr)
@@ -1055,6 +1069,9 @@ class TestNumpyUnclassified(TestNumpyMethods):
         a = [1, 2, 3, 4, 5] * self.ureg.m
         b = self.Q_([4.0, 6.0, 8.0, 9.0, -3.0], "degC")
 
+        self.assertQuantityEqual(
+            np.pad(a, (2, 3), "constant"), [0, 0, 1, 2, 3, 4, 5, 0, 0, 0] * self.ureg.m,
+        )
         self.assertQuantityEqual(
             np.pad(a, (2, 3), "constant", constant_values=(0, 600 * self.ureg.cm)),
             [0, 0, 1, 2, 3, 4, 5, 6, 6, 6] * self.ureg.m,
@@ -1071,6 +1088,10 @@ class TestNumpyUnclassified(TestNumpyMethods):
         self.assertQuantityEqual(
             np.pad(a, (2, 3), "edge"), [1, 1, 1, 2, 3, 4, 5, 5, 5, 5] * self.ureg.m
         )
+        self.assertQuantityEqual(
+            np.pad(a, (2, 3), "linear_ramp"),
+            [0, 0, 1, 2, 3, 4, 5, 3, 1, 0] * self.ureg.m,
+        )
         self.assertQuantityEqual(
             np.pad(a, (2, 3), "linear_ramp", end_values=(5, -4) * self.ureg.m),
             [5, 3, 1, 2, 3, 4, 5, 2, -1, -4] * self.ureg.m,
diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py
index 51faf1a..fbfd773 100644
--- a/pint/testsuite/test_quantity.py
+++ b/pint/testsuite/test_quantity.py
@@ -6,7 +6,7 @@ import warnings
 from unittest.mock import patch
 
 from pint import DimensionalityError, OffsetUnitCalculusError, UnitRegistry
-from pint.compat import np
+from pint.compat import BehaviorChangeWarning, np
 from pint.testsuite import QuantityTestCase, helpers
 from pint.testsuite.parameterized import ParameterizedTestCase
 from pint.unit import UnitsContainer
@@ -531,8 +531,11 @@ class TestQuantity(QuantityTestCase):
             iter(x)
 
     @helpers.requires_array_function_protocol()
-    def test_no_longer_array_function_warning_on_creation(self):
-        # Test that warning is no longer raised on first creation
+    @patch("pint.quantity.SKIP_ARRAY_FUNCTION_CHANGE_WARNING", False)
+    def test_array_function_warning_on_creation(self):
+        # Test that warning is raised on first creation, but not second
+        with self.assertWarns(BehaviorChangeWarning):
+            self.Q_([])
         with warnings.catch_warnings():
             warnings.filterwarnings("error")
             self.Q_([])