|
| 1 | +phonenumbers Python Library |
| 2 | +=========================== |
| 3 | + |
| 4 | +This is a Python port of [libphonenumber](https://github.com/googlei18n/libphonenumber). |
| 5 | + |
| 6 | +Original Java code is Copyright (C) 2009-2016 The Libphonenumber Authors |
| 7 | + |
| 8 | + |
| 9 | +Manual phonenumbers Installation |
| 10 | +-------------------------------- |
| 11 | + |
| 12 | +Install using `setup.py`: |
| 13 | + |
| 14 | +```console |
| 15 | +$ tar xfz phonenumbers-<version>.tar.gz |
| 16 | +$ cd phonenumbers-<version> |
| 17 | +$ python setup.py build |
| 18 | +$ sudo python setup.py install # or su first |
| 19 | +``` |
| 20 | + |
| 21 | + |
| 22 | +Running Tests |
| 23 | +------------- |
| 24 | + |
| 25 | +With phonenumbers on the Python path, run: |
| 26 | + |
| 27 | +```console |
| 28 | +$ python -m testwrapper |
| 29 | +``` |
| 30 | + |
| 31 | + |
| 32 | +Auto-Generating Python Code |
| 33 | +--------------------------- |
| 34 | + |
| 35 | +Several subdirectories under `python/phonenumbers` are automatically generated |
| 36 | +from the master metadata under `resources/`: |
| 37 | + |
| 38 | + - `python/phonenumbers/data` is generated from `resources/PhoneNumberMetadata.xml`. |
| 39 | + - `python/phonenumbers/shortdata` is generated from `resources/ShortNumberMetadata.xml`. |
| 40 | + - `python/phonenumbers/geodata` is generated from files under `resources/geocoding/`. |
| 41 | + - `python/phonenumbers/carrierdata` is generated from files under `resources/carrier/`. |
| 42 | + - `python/phonenumbers/tzdata` is generated from files under `resources/timezones/`. |
| 43 | + |
| 44 | +The script `tools/buildmetadatafromxml.py` performs the first pair of autogenerations. |
| 45 | +The script `tools/buildprefixdata.py` performs the latter 3 sets of autogenerations. |
| 46 | + |
| 47 | +This approach results in a large set of Python files, but makes it easy |
| 48 | +to apply local fixes to the formatting metadata. |
| 49 | + |
| 50 | +The `phonenumberslite` version of the package does not include the prefix-based |
| 51 | +metadata (in order to reduce package size), so `phonenumbers.geodata`, |
| 52 | +`phonenumbers.carrierdata` and `phonenumbers.tzdata` are all unavailable with this |
| 53 | +version. |
| 54 | + |
| 55 | + |
| 56 | +Library Developer Internals |
| 57 | +--------------------------- |
| 58 | + |
| 59 | +The Python code is derived from the original Java code, and |
| 60 | +mostly sticks to the structure of that code to make it easier |
| 61 | +to include future changes to the upstream code. |
| 62 | + |
| 63 | +However, there are a number of differences: |
| 64 | + - Naming conventions are converted to Python standards; in |
| 65 | + particular, method names are `connected_with_underscores` |
| 66 | + rather than `beingInCamelCase`. |
| 67 | + - The `PhoneNumber` and `PhoneMetadata` classes are written by hand |
| 68 | + based on the Java code and the protocol buffer definitions, |
| 69 | + rather than by using the the Python protocol buffer library. |
| 70 | + This makes the mapping to the Java code easier to follow, and |
| 71 | + allows for the custom modifications that have been made to |
| 72 | + the base protocol buffer. Attribute values of `None` are used |
| 73 | + to indicate that a particular (optional) attribute is not |
| 74 | + present (instead of `hasAttribute()` methods). |
| 75 | + - The Java `PhoneNumberUtil` class was a singleton, and so its |
| 76 | + contents are included at the top level in `phonenumberutil.py`. |
| 77 | + Static methods from the `PhoneNumberUtil` class thus become |
| 78 | + functions in `phonenumberutil.py`; private and package methods |
| 79 | + get a leading underscore in their name. |
| 80 | + - Accessor functions (`setAttribute()` and `getAttribute()` are |
| 81 | + avoided, and direct access to attributes is used instead. |
| 82 | + - Methods named `get_something_from(object)` are typically renamed |
| 83 | + to `something_from(object)`. |
| 84 | + - The `format()` methods in `PhoneNumberUtil` were renamed to |
| 85 | + `format_number()` to avoid clashing with the Python built-in |
| 86 | + `format()`. |
| 87 | + - The internals of `phonenumberutil.py` do not have logging. |
| 88 | + - The Python version is less concerned with speed and size |
| 89 | + optimization than the Java version (as Python code is more likely |
| 90 | + to run on a server platform, and less likely to run on an |
| 91 | + embedded/smartphone platform). |
| 92 | + |
| 93 | +Much of the functionality of this library depends on regular |
| 94 | +expressions, so it's worth highlighting the translation between |
| 95 | +Java and Python regexps: |
| 96 | + - Java replacement group references are `"$1 $2"` etc, Python's are |
| 97 | + `"\1 \2"` etc. |
| 98 | + - Java `Matcher(x).lookingAt()` translates to Python `re_obj.match(x)`. |
| 99 | + - Java `Matcher(x).find()` translates to Python `m = re.search(x)`. |
| 100 | + - Java `Matcher(x).matches()` translates to Python `m = re_obj.match(x)` |
| 101 | + together with a check that `m.end() == len(x)`. |
| 102 | +The last of these is encapsulated in the `fullmatch()` function in |
| 103 | +`re_util.py`. |
| 104 | + |
| 105 | +Some other mappings between the Java and Python versions: |
| 106 | + |
| 107 | +|Java | Python | |
| 108 | +|------------------------------------------|--------------------------------------------| |
| 109 | +|`countryCallingCodeToRegionCodeMap` |`COUNTRY_CODE_TO_REGION_CODE` | |
| 110 | +|`getSupportedRegions()` |N/A | |
| 111 | +|`supportedRegions` |`SUPPORTED_REGIONS` | |
| 112 | +|`getSupportedGlobalNetworkCallingCodes()` |N/A | |
| 113 | +|`countryCodesForNonGeographicalRegions` |`COUNTRY_CODES_FOR_NON_GEO_REGIONS` | |
| 114 | +|`getMetadataForNonGeographicalRegion()` |`PhoneMetadata.metadata_for_nongeo_region()`| |
| 115 | + |
| 116 | + |
| 117 | +Release Procedure |
| 118 | +----------------- |
| 119 | + |
| 120 | + - Ensure that `python/HISTORY` file is up-to-date, and includes |
| 121 | + descriptions of changes in this version (adapted from the |
| 122 | + upstream [release notes](https://github.com/googlei18n/libphonenumber/blob/master/java/release_notes.txt), |
| 123 | + skipping the metadata changes chunks). |
| 124 | + - Set the `__version__` field in `python/phonenumbers/__init__.py` |
| 125 | + - Check that the list of symbols in `python/phonenumbers/__init__.py` `__all__` is |
| 126 | + up to date. The `tools/python/allcheck.py` script helps with this. |
| 127 | + - Optionally, force metadata regeneration: |
| 128 | + `cd tools/python && make metaclean alldata` |
| 129 | + - Check that the unit tests all run successfully: |
| 130 | + `cd tools/python && make test` |
| 131 | + - Optionally, check that metadata regeneration works in Py3k: |
| 132 | + `cd tools/python && make PYTHON=python3 metaclean alldata` |
| 133 | + - Check that the unit tests all run successfully in Py3k: |
| 134 | + `cd tools/python && make PYTHON=python3 test` |
| 135 | + - Check that Python 2.5 is still supported: |
| 136 | + `cd tools/python && make PYTHON=python2.5 test` |
| 137 | + - Create a release-<version> tag: |
| 138 | + `git tag release-<version>` |
| 139 | + - Push the tag to Github with: |
| 140 | + `git push <github-remote> release-<version>` |
| 141 | + - Push the package to PyPI with: |
| 142 | + `cd python && setup.py sdist bdist_wheel upload` |
| 143 | + - Push the lite package to PyPI with: |
| 144 | + `cd python && setup.py lite sdist bdist_wheel upload` |
0 commit comments