Repository: daviddrysdale/python-phonenumbers

Issue:
NumberParseException is not picklable
Hi,

I noticed that `NumberParseException` is not picklable. It deviates from the `__init__` signature of `Exception`, which itself implements `__reduce__` s.t. only the `msg` argument is pickled. This program:

```python
import pickle
from phonenumbers.phonenumberutil import NumberParseException

error = NumberParseException(NumberParseException.TOO_SHORT_AFTER_IDD, 'hello world')
s = pickle.dumps(error)
again = pickle.loads(s)
```

produces the output:

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'msg'
```

Adding a `__reduce__` method to `NumberParseException` seems  to fix it:

```python
    def __reduce__(self):
        return (type(self), (self.error_type, self._msg))
```

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/35742044/badge)](https://coveralls.io/builds/35742044)

Coverage remained the same at 100.0% when pulling **89a0f3cbdd78f2006c19f7fa0ba5e29877e6d7b3 on pickled-ex** into **5bcfeb70b6ae8d4ebe525999910f3180f6870807 on dev**.


Patch:
diff --git a/python/phonenumbers/phonenumberutil.py b/python/phonenumbers/phonenumberutil.py
index 8ff04afd..0948934d 100644
--- a/python/phonenumbers/phonenumberutil.py
+++ b/python/phonenumbers/phonenumberutil.py
@@ -3223,6 +3223,9 @@ class NumberParseException(UnicodeMixin, Exception):
         self.error_type = error_type
         self._msg = msg
 
+    def __reduce__(self):
+        return (type(self), (self.error_type, self._msg))
+
     def __unicode__(self):
         return unicod("(%s) %s") % (self.error_type, self._msg)
 


Test patch:
diff --git a/python/tests/phonenumberutiltest.py b/python/tests/phonenumberutiltest.py
index 592fec80..c9d2cedf 100755
--- a/python/tests/phonenumberutiltest.py
+++ b/python/tests/phonenumberutiltest.py
@@ -17,6 +17,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import sys
+import pickle
 
 import phonenumbers
 from phonenumbers import PhoneNumber, PhoneMetadata
@@ -26,6 +27,7 @@ from phonenumbers import ValidationResult, NumberFormat, CountryCodeSource
 from phonenumbers import region_code_for_country_code
 # Access internal functions of phonenumberutil.py
 from phonenumbers import phonenumberutil, shortnumberinfo
+from phonenumbers.phonenumberutil import NumberParseException
 from phonenumbers.util import u, to_long
 from .testmetadatatest import TestMetadataTestCase
 
@@ -3165,6 +3167,12 @@ class PhoneNumberUtilTest(TestMetadataTestCase):
                              'register': True})
         self.assertTrue(phonenumbers.example_number_for_type('XY', PhoneNumberType.PERSONAL_NUMBER) is None)
 
+    def testPickledException(self):
+        err = NumberParseException(NumberParseException.TOO_SHORT_AFTER_IDD, 'hello world')
+        pickled = pickle.dumps(err)
+        recovered = pickle.loads(pickled)
+        self.assertEqual("%r" % err, "%r" % recovered)
+
     def testCoverage(self):
         # Python version extra tests
         self.assertTrue(phonenumberutil._region_code_for_number_from_list(GB_NUMBER, ("XX",)) is None)