Source code for phonevalidator.phonevalidator

# -*- coding: utf-8 -*-

from os import environ
from phonenumbers import (
    format_number,
    is_valid_number,
    parse,
    NumberParseException,
    PhoneNumberFormat,
    SUPPORTED_REGIONS,
)
from cerberus import Validator as SuperValidator


[docs]class ValidatorMixin(object): """ A custom cerberus.Validator subclass adding the `phonenumber` constraint to Cerberus validation's. :Example: .. code-block:: python >>> from phonevalidator import ValidatorMixin >>> from eve.io.mongo import Validator >>> from eve import Eve >>> class MyValidator(Validator, ValidatorMixin): ... ''' Custom validator that adds phone number ... validations. ... ''' ... pass >>> settings = {'DOMAIN': {}} >>> app = Eve(validator=MyValidator, settings=settings) """ def _default_region(self): """ Return's a default region string. We will try to get a region from the environment variable 'DEFAULT_PHONE_REGION' if not found or not valid, then we default to 'US' """ region = environ.get('DEFAULT_PHONE_REGION') if region and region.upper() in SUPPORTED_REGIONS: return region.upper() return 'US' def _is_valid_region(self, region): """ Return's True if region is valid, False otherwise """ if isinstance(region, str) and region.upper() in SUPPORTED_REGIONS: return True return False def _set_region(self, region=None): """ Set's a region to use to validate phone numbers for this instance. """ if self._is_valid_region(region): self.region = region.upper() else: self.region = self._default_region() def _set_default_formatter(self): """ Try's to get a formatter string from the environment or defaults to `phonenumbers.PhoneNumberFormat.NATIONAL` """ env_format = environ.get('DEFAULT_PHONE_FORMAT') if env_format is not None: self._set_formatter( formatter=env_format ) else: self.formatter = PhoneNumberFormat.NATIONAL def _set_formatter(self, formatter=None): """ Set's formatter for this instance, or defaults to `phonenumbers.PhoneNumberFormat.NATIONAL` """ if formatter is None or not isinstance(formatter, str): self._set_default_formatter() else: self.formatter = getattr( PhoneNumberFormat, formatter.upper(), PhoneNumberFormat.NATIONAL ) def _validate_formatPhoneNumber(self, formatPhoneNumber, field, value): """ Fake validate function to let cerberus accept "formatPhoneNumber" as a keyword in the schema. """ pass def _validate_phoneNumberFormat(self, phoneNumberFormat, field, value): """ Validates a phoneNumberFormat for a phone number. :param phoneNumberFormat: a string for accepted format. :accepted formats: ['NATIONAL', 'INTERNATIONAL', 'RFC3966', 'E164' ] """ keys = PhoneNumberFormat.__dict__.keys() valids = [key for key in keys if not key.startswith('_')] if phoneNumberFormat.upper() not in valids: self._error(field, 'Not a valid phone number format: {}'.format(value)) def _validate_region(self, region, field, value): """ Validates a region for a phone number. :param region: a region. """ if self._is_valid_region(region) is False: self._error(field, 'Region not valid: {}'.format(region)) def _validate_type_phonenumber(self, field, value): """ Validates a phone number is valid. Optionally formatting the number. :param field: field name. :param value: field value. """ # get the region from schema for this field or use default self._set_region(self.schema[field].get('region')) try: phone_number = parse(value, self.region) # check that it's valid number if not is_valid_number(phone_number): self._error(field, 'Phone Number not valid: {}'.format(value)) elif self.schema[field].get('formatPhoneNumber'): # if the schema's 'formatPhoneNumber' is set to True, # format the phone number using a formatter derived from # the schema's 'phoneNumberFormat' value, next checks the # environmen variable 'PHONE_NUMBER_FORMAT', # or defaults to 'NATIONAL'. formatter = self.schema[field].get('phoneNumberFormat') self._set_formatter( formatter=formatter ) self.document[field] = format_number(phone_number, self.formatter) except NumberParseException: self._error(field, 'Phone Number not valid: {}'.format(value))
[docs]class Validator(SuperValidator, ValidatorMixin): """ Extends `cerberus.Validator` and adds the `phonenumber` constraint to Cerburus validation's. :Example: .. code-block:: python >>> from phonevalidator import Validator >>> schema = { ... 'phone': { ... 'type': 'phonenumber', ... 'formatPhoneNumber': True, ... 'phoneNumberFormat': 'NATIONAL', ... 'region': 'US' ... } ... } >>> doc = {'phone': '5135555555'} >>> v = Validator(schema) >>> v.validate(doc) True >>> v.document {'phone': '(513) 555-5555'} >>> doc = {'phone': 'gibberish'} >>> v.validate(doc) False """ pass