From ee18874ac6d69c556b6d62b9d854716b302f394e Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Wed, 18 Aug 2021 09:41:25 +0100 Subject: [PATCH 1/2] ci: run mypy over code To allow mypy to run successfully it needs to have various imports from the `typing` module available. However, we don't want to add those as permanent dependencies of this library, so hack the code for the mypy CI step to add the typing imports. --- .github/workflows/ci.yml | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 232582d77..194d7fbc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,14 +3,14 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-18.04 strategy: matrix: - python-version: [2.7, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, pypy2, pypy3] + python-version: [2.7, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, pypy2, pypy3] steps: - - uses: actions/checkout@v2 + - name: Check out code + uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: @@ -33,3 +33,29 @@ jobs: git status exit 1 fi + + typecheck: + runs-on: ubuntu-18.04 + strategy: + matrix: + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + + steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install protobuf mypy + - name: Add typing imports + run: | + for file in $(find . -name \*.py); do + printf '%s\n%s\n' "from typing import Callable, Dict, List, Optional, Sequence, Set" "$(cat $file)" >$file + done + - name: Run mypy + run: | + (cd python && mypy phonenumbers --exclude phonenumbers/pb2 --show-error-codes) From 7fd9c7f3c77760f4e516a82c434e452eb09f5f4d Mon Sep 17 00:00:00 2001 From: David Drysdale Date: Mon, 9 Aug 2021 06:44:28 +0100 Subject: [PATCH 2/2] Type annotations for variable/fields Also update metadata generation tool to emit type annotation. --- python/phonenumbers/carrierdata/__init__.py | 2 +- python/phonenumbers/geodata/__init__.py | 2 +- python/phonenumbers/phonemetadata.py | 17 +++++++++-------- python/phonenumbers/phonenumberutil.py | 6 +++--- python/phonenumbers/timezone.py | 2 +- python/phonenumbers/tzdata/__init__.py | 2 +- python/phonenumbers/unicode_util.py | 7 ++++--- python/tests/testcarrierdata/__init__.py | 2 +- python/tests/testgeodata/__init__.py | 2 +- python/tests/testtzdata/__init__.py | 2 +- tools/python/buildprefixdata.py | 2 +- 11 files changed, 24 insertions(+), 22 deletions(-) diff --git a/python/phonenumbers/carrierdata/__init__.py b/python/phonenumbers/carrierdata/__init__.py index fb246ae95..da1aad1ba 100644 --- a/python/phonenumbers/carrierdata/__init__.py +++ b/python/phonenumbers/carrierdata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -CARRIER_DATA = {} +CARRIER_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data CARRIER_DATA.update(data) from .data1 import data diff --git a/python/phonenumbers/geodata/__init__.py b/python/phonenumbers/geodata/__init__.py index 096cdbad1..69cd0bdce 100644 --- a/python/phonenumbers/geodata/__init__.py +++ b/python/phonenumbers/geodata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -GEOCODE_DATA = {} +GEOCODE_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data GEOCODE_DATA.update(data) from .data1 import data diff --git a/python/phonenumbers/phonemetadata.py b/python/phonenumbers/phonemetadata.py index be6be8b8b..088adaaa6 100644 --- a/python/phonenumbers/phonemetadata.py +++ b/python/phonenumbers/phonemetadata.py @@ -254,23 +254,24 @@ class PhoneMetadata(UnicodeMixin, ImmutableMixin): # The modificiation involves loading data from a file, so we cannot just # rely on the GIL. _metadata_lock = threading.Lock() - # If a region code is a key in this dict, metadata for that region is available. + # If a region code (ISO 3166-1 alpha 2) is a key in this dict, metadata for that region is available. # The corresponding value of the map is either: # - a function which loads the region's metadata # - None, to indicate that the metadata is already loaded - _region_available = {} # ISO 3166-1 alpha 2 => function or None + _region_available = {} # type: Dict[str, Optional[Callable[[str], None]]] # Likewise for short number metadata. - _short_region_available = {} # ISO 3166-1 alpha 2 => function or None - # Likewise for non-geo country calling codes. - _country_code_available = {} # country calling code (as int) => function or None + _short_region_available = {} # type: Dict[str, Optional[Callable[[str], None]]] + # Likewise for non-geo country calling codes (indexed by country calling code as int) + _country_code_available = {} # type: Dict[str, Optional[Callable[[int], None]]] - _region_metadata = {} # ISO 3166-1 alpha 2 => PhoneMetadata - _short_region_metadata = {} # ISO 3166-1 alpha 2 => PhoneMetadata + # PhoneMetadata indexed by ISO 3166-1 alpha 2. + _region_metadata = {} # type: Dict[str, PhoneMetadata] + _short_region_metadata = {} # type: Dict[str, PhoneMetadata] # A mapping from a country calling code for a non-geographical entity to # the PhoneMetadata for that country calling code. Examples of the country # calling codes include 800 (International Toll Free Service) and 808 # (International Shared Cost Service). - _country_code_metadata = {} # country calling code (as int) => PhoneMetadata + _country_code_metadata = {} # type: Dict[int, PhoneMetadata] @classmethod def metadata_for_region(kls, region_code, default=None): diff --git a/python/phonenumbers/phonenumberutil.py b/python/phonenumbers/phonenumberutil.py index 6a73a7c47..6c4fba0f8 100644 --- a/python/phonenumbers/phonenumberutil.py +++ b/python/phonenumbers/phonenumberutil.py @@ -516,9 +516,9 @@ class ValidationResult(object): # Derived data structures -SUPPORTED_REGIONS = set() -COUNTRY_CODES_FOR_NON_GEO_REGIONS = set() -_NANPA_REGIONS = set() +SUPPORTED_REGIONS = set() # type: Set[str] +COUNTRY_CODES_FOR_NON_GEO_REGIONS = set() # type: Set[int] +_NANPA_REGIONS = set() # type: Set[str] def _regenerate_derived_data(): diff --git a/python/phonenumbers/timezone.py b/python/phonenumbers/timezone.py index 143f5adae..11cb0373e 100644 --- a/python/phonenumbers/timezone.py +++ b/python/phonenumbers/timezone.py @@ -49,7 +49,7 @@ if (os.path.basename(sys.argv[0]) == "buildmetadatafromxml.py" or os.path.basename(sys.argv[0]) == "buildprefixdata.py"): prnt("Failed to import generated data (but OK as during autogeneration)", file=sys.stderr) - TIMEZONE_DATA = {'4411': u('Europe/London')} + TIMEZONE_DATA = {'4411': (u('Europe/London'),)} TIMEZONE_LONGEST_PREFIX = 4 else: raise diff --git a/python/phonenumbers/tzdata/__init__.py b/python/phonenumbers/tzdata/__init__.py index 69f76d79d..cf093a7dd 100644 --- a/python/phonenumbers/tzdata/__init__.py +++ b/python/phonenumbers/tzdata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -TIMEZONE_DATA = {} +TIMEZONE_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data TIMEZONE_DATA.update(data) del data diff --git a/python/phonenumbers/unicode_util.py b/python/phonenumbers/unicode_util.py index c8e041a6c..8cc5d786b 100644 --- a/python/phonenumbers/unicode_util.py +++ b/python/phonenumbers/unicode_util.py @@ -72,7 +72,6 @@ """ import bisect import unicodedata - from .util import UnicodeMixin, unicod, u @@ -159,8 +158,10 @@ def __unicode__(self): class Block(object): """Description of the possible Unicode blocks""" - _RANGES = {} # lower end of range => _BlockRange object - _RANGE_KEYS = None # sorted list of _RANGES.keys() + # Map lower end of range to _BlockRange object + _RANGES = {} # type: Dict[int, _BlockRange] + # Sorted list of _RANGES.keys() + _RANGE_KEYS = None # type: Optional[List[int]] # Taken from http://www.unicode.org/Public/UNIDATA/Blocks.txt BASIC_LATIN = _BlockRange(0x0000, 0x007F, _RANGES) diff --git a/python/tests/testcarrierdata/__init__.py b/python/tests/testcarrierdata/__init__.py index 11c54c6e0..4518e293e 100644 --- a/python/tests/testcarrierdata/__init__.py +++ b/python/tests/testcarrierdata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -CARRIER_DATA = {} +CARRIER_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data CARRIER_DATA.update(data) del data diff --git a/python/tests/testgeodata/__init__.py b/python/tests/testgeodata/__init__.py index a379993e3..50558fd7f 100644 --- a/python/tests/testgeodata/__init__.py +++ b/python/tests/testgeodata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -GEOCODE_DATA = {} +GEOCODE_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data GEOCODE_DATA.update(data) del data diff --git a/python/tests/testtzdata/__init__.py b/python/tests/testtzdata/__init__.py index a2e0ddcdc..88c071f26 100644 --- a/python/tests/testtzdata/__init__.py +++ b/python/tests/testtzdata/__init__.py @@ -18,7 +18,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -TIMEZONE_DATA = {} +TIMEZONE_DATA = {} # # type: Dict[str, Sequence[str]] from .data0 import data TIMEZONE_DATA.update(data) del data diff --git a/tools/python/buildprefixdata.py b/tools/python/buildprefixdata.py index 86526a022..26071bdd6 100755 --- a/tools/python/buildprefixdata.py +++ b/tools/python/buildprefixdata.py @@ -192,7 +192,7 @@ def output_prefixdata_code(prefixdata, outfilename, module_prefix, varprefix, pe else: prnt(PREFIXDATA_FILE_PROLOG % {'module': module_prefix}, file=outfile) prnt(COPYRIGHT_NOTICE, file=outfile) - prnt("%s_DATA = {}" % varprefix, file=outfile) + prnt("%s_DATA = {} # # type: Dict[str, Sequence[str]]" % varprefix, file=outfile) for chunk_num in range(total_chunks): prnt("from .data%d import data" % chunk_num, file=outfile) prnt("%s_DATA.update(data)" % varprefix, file=outfile)