Inspired from the PHP implementation with some differences (see below).
Algerian phone numbers as a value object implementation in Python. This can be used in your domain models or be integrated with your favorite framework.
What is value object?
In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object.
Read more on wikipedia.
pip install dz-phone-numbers
from dz_phone_number import DZPhoneNumber
dz_phone_number = DZPhoneNumber("0599000000") # or DZPhoneNumber.from_string("0599000000")
dz_phone_number.indicative # <CountryCode.LOCAL: '0'>
dz_phone_number.operator_or_region # <MobileOperator.OOREDOO: 5>
dz_phone_number.suffix # '99000000'
dz_phone_number.is_mobile() # True
dz_phone_number.is_landline() # a.k.a Fixe. False
dz_phone_number.is_ooredoo() # True
dz_phone_number.is_djezzy() # false
dz_phone_number.is_annaba() # false
DZPhoneNumber("038123456").is_annaba() # True
# repr:
<DZPhoneNumber:CountryCode.LOCAL - MobileOperator: OOREDOO - 99000000>
# str:
str(dz_phone_number) # 0599000000
DZPhoneNumber("0599000000") == DZPhoneNumber("+213 599000000") # True
DZPhoneNumber("(0) 599000000") == DZPhoneNumber("0 599-00-00-00") # True
DZPhoneNumber("(0) 599000000") == DZPhoneNumber("(0) 699000000"") # False
try:
DZPhoneNumber("09 12 34 56 78")
except ValueError: # ValueError can catch it
pass
# Otherwise you can also expect `InvalidDZPhoneNumber` (an alias of ValueError).
The object can't be modified if you try to modify of its members, a TypeError
will be raised:
dz_phone_number.number = '038123456' # will raise TypeError.
03 main parts of the full number are categorized into three groups: indicative (Country Code), Operator or Region (e.g. Ooredoo or Annaba), and the rest of the dial number.
The regex uses Python's capturing group feature built in its regex engine. Where "Country Code", "Operator or Region" and the "Number" are put into numbered groups when matched.
The regex also uses a conditional statemet in the form of (?(1)yes|no)
where (1)
is the capturing group number. The following picture explain how it's working:
- This raises a
ValueError
(Python built-in) instead of the broadException
InvalidDZPhoneNumber
is an alias ofValueError
. - A different version of regex with support for landline (a.k.a fixe) numbers.
- Enums are used to both limit landline possible values, and make it extensible (e.g. very easy if, say, a new operator got into Algeria).
- This uses Python regex "capturing groups" feature. Where "Country Code", "Operator or Region" and the "Number" are put in groups when matched.
- pytest are used instead of any other spec or behavior testing.
- Immutability is achieved through
__slots__
and overriding__setattr__
and__delattr__
. This was a bit more flexibile that@dataclass(frozen=True)
.
This is a simple, self-contained problem. One of the reasons I wrote this is to serve as a python package example for the Algerian Python community where:
- The code is Black-formatted.
- Type annotated code using Python type hints. Checked with MyPy.
- The project structure follows what's common for Python projects. See the Hitchhiker guide.
- A simple
Makefile
within to show how to build this (with Python wheels support) and how to release a package to pypi. Just issuemake
to see the available commands. - Example
setup.py
- Testing with pytest.