Repository: yt-project/unyt (python)

Issue:
"µm" is not recognized as a unit symbol
* unyt version: v1.0.4
* Python version: Python 3.6.4
* Operating System: macOS High Sierra 10.13.6 (17G65)

### Description

I was trying to convert meters to microns using the unicode symbol "µ", but the library failed to convert it (see the error below), though it works with `um`.
**What you expected to happen:** conversion is done successfully!

### What I Did

```py
$ ipython
Python 3.6.4 |Anaconda, Inc.| (default, Mar 12 2018, 20:05:31)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import unyt as u

In [2]: d = 1 * u.m

In [3]: d
Out[3]: unyt_quantity(1., 'm')

In [4]: d.to('um')
Out[4]: unyt_quantity(1000000., 'um')

In [5]: d.to('µm')
---------------------------------------------------------------------------
UnitParseError                            Traceback (most recent call last)
<ipython-input-5-7faeac57c1a5> in <module>()
----> 1 d.to('µm')

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/array.py in to(self, units, equivalence, **kwargs)
    828         898755178736817.6 J
    829         """
--> 830         return self.in_units(units, equivalence=equivalence, **kwargs)
    831
    832     def to_value(self, units=None, equivalence=None, **kwargs):

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/array.py in in_units(self, units, equivalence, **kwargs)
    760         898755178736817.6 J
    761         """
--> 762         units = _sanitize_units_convert(units, self.units.registry)
    763         if equivalence is None:
    764             try:

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/array.py in _sanitize_units_convert(possible_units, registry)
    254
    255     # let Unit() try to parse this if it's not already a Unit
--> 256     unit = Unit(possible_units, registry=registry)
    257
    258     return unit

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/unit_object.py in __new__(cls, unit_expr, base_value, base_offset, dimensions, registry, latex_repr)
    301         else:
    302             # lookup the unit symbols
--> 303             unit_data = _get_unit_data_from_expr(unit_expr, registry.lut)
    304             base_value = unit_data[0]
    305             dimensions = unit_data[1]

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/unit_object.py in _get_unit_data_from_expr(unit_expr, unit_symbol_lut)
    862
    863     if isinstance(unit_expr, Symbol):
--> 864         return _lookup_unit_symbol(str(unit_expr), unit_symbol_lut)
    865
    866     if isinstance(unit_expr, Pow):

~/anaconda3/envs/sirepo_bluesky/lib/python3.6/site-packages/unyt/unit_object.py in _lookup_unit_symbol(symbol_str, unit_symbol_lut)
    962     else:
    963         raise UnitParseError("Could not find unit symbol '%s' in the provided "
--> 964                              "symbols." % symbol_str)
    965
    966

UnitParseError: Could not find unit symbol 'µm' in the provided symbols.

In [6]:
```


Pull request:
add support for using unicode mu as the micro prefix.
closes #34 

I tried to get this working on python2.7 and it was not worth the trouble. Works more less out of the box on python3 though so I've elected to simply make the feature python3-only. However to make the tests continue to pass on Python2.7 I had to implement this in a sort of boroque way (thus the use of `globals` and `getattr` to make sure I'm always referring to the new variables with unicode identifiers I've defined using strings).

Patch:
diff --git a/unyt/_unit_lookup_table.py b/unyt/_unit_lookup_table.py
index e280d89..cc6e1c0 100644
--- a/unyt/_unit_lookup_table.py
+++ b/unyt/_unit_lookup_table.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 The default unit symbol lookup table.
 
@@ -12,6 +13,8 @@ The default unit symbol lookup table.
 # The full license is in the LICENSE file, distributed with this software.
 # -----------------------------------------------------------------------------
 
+import sys
+
 from unyt import dimensions
 from unyt._physical_ratios import (
     m_per_pc,
@@ -276,6 +279,13 @@ unit_prefixes = {
     "y": 1e-24,  # yocto
 }
 
+if sys.version_info > (3, 0, 0):
+    #  'MICRO SIGN' (U+00B5)
+    unit_prefixes["µ"] = 1e-6  # micro
+    #  'GREEK SMALL LETTER MU' (U+03BC)
+    unit_prefixes["μ"] = 1e-6  # micro
+
+
 latex_prefixes = {"u": r"\mu"}
 
 default_base_units = {
diff --git a/unyt/unit_symbols.py b/unyt/unit_symbols.py
index e704d0b..649229b 100644
--- a/unyt/unit_symbols.py
+++ b/unyt/unit_symbols.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Predefined useful aliases to physical units
 
@@ -19,6 +20,8 @@ For example::
 # The full license is in the LICENSE file, distributed with this software.
 # -----------------------------------------------------------------------------
 
+import sys
+
 from unyt.unit_object import Unit
 
 #
@@ -33,6 +36,11 @@ pm = picometer = Unit("pm")
 nm = nanometer = Unit("nm")
 #: micrometer
 um = micrometer = Unit("um")
+if sys.version_info > (3, 0, 0):
+    #: micrometer
+    globals()["µm"] = Unit("µm")
+    #: micrometer
+    globals()["μm"] = Unit("μm")
 #: millimeter
 mm = millimeter = Unit("mm")
 #: centimeter
@@ -67,6 +75,11 @@ pg = picogram = Unit("pg")
 ng = nanogram = Unit("ng")
 #: micogram
 ug = microgram = Unit("ug")
+if sys.version_info > (3, 0, 0):
+    #: microgram
+    globals()["µg"] = Unit("µg")
+    #: microgram
+    globals()["μg"] = Unit("μg")
 #: milligram
 mg = milligram = Unit("mg")
 #: gram
@@ -88,6 +101,11 @@ ps = picosecond = Unit("ps")
 ns = nanosecond = Unit("ns")
 #: microsecond
 us = microsecond = Unit("us")
+if sys.version_info > (3, 0, 0):
+    #: microgram
+    globals()["µs"] = Unit("µs")
+    #: microgram
+    globals()["μs"] = Unit("μs")
 #: millisecond
 ms = millisecond = Unit("ms")
 #: second


Test patch:
diff --git a/unyt/tests/test_units.py b/unyt/tests/test_units.py
index d796e64..1342012 100644
--- a/unyt/tests/test_units.py
+++ b/unyt/tests/test_units.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Test symbolic unit handling.
 
@@ -24,6 +25,7 @@ from numpy.testing import (
 )
 import operator
 import pytest
+import sys
 from six.moves import cPickle as pickle
 from sympy import Symbol
 from unyt._testing import assert_allclose_units
@@ -675,3 +677,16 @@ def test_simplify():
 
     for unit, answer in answers.items():
         assert str(unit.simplify()) == answer
+
+
+@pytest.mark.skipif(sys.version_info < (3, 0, 0), reason="Feature requires python3")
+def test_micro_prefix():
+    import unyt as u
+
+    # both versions of unicode mu work correctly
+    assert u.um == getattr(u, "µm")
+    assert u.um == getattr(u, "μm")
+
+    # parsing both versions works as well
+    assert u.ug == u.Unit("µg")
+    assert u.ug == u.Unit("μg")