Skip to content

Commit bc6e367

Browse files
committed
Add mutating_method decorator
1 parent 561ebe4 commit bc6e367

File tree

2 files changed

+19
-10
lines changed

2 files changed

+19
-10
lines changed

python/phonenumbers/phonemetadata.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@
1616
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1717
# See the License for the specific language governing permissions and
1818
# limitations under the License.
19-
from .util import UnicodeMixin, ImmutableMixin
19+
from .util import UnicodeMixin, ImmutableMixin, mutating_method
2020

2121
REGION_CODE_FOR_NON_GEO_ENTITY = u"001"
2222

2323

2424
class NumberFormat(UnicodeMixin, ImmutableMixin):
2525
"""Representation of way that a phone number can be formatted for output"""
26+
@mutating_method
2627
def __init__(self,
2728
pattern=None,
2829
format=None,
2930
leading_digits_pattern=None,
3031
national_prefix_formatting_rule=None,
3132
national_prefix_optional_when_formatting=False,
3233
domestic_carrier_code_formatting_rule=None):
33-
self._mutable = True
3434
# pattern is a regex that is used to match the national (significant)
3535
# number. For example, the pattern "(20)(\d{4})(\d{4})" will match
3636
# number "2070313000", which is the national (significant) number for
@@ -99,7 +99,6 @@ def __init__(self,
9999
# formatted when format_with_carrier_code is called, if carrier codes
100100
# are used for a certain country.
101101
self.domestic_carrier_code_formatting_rule = domestic_carrier_code_formatting_rule # None or Unicode string
102-
self._mutable = False
103102

104103
def merge_from(self, other):
105104
"""Merge information from another NumberFormat object into this one."""
@@ -144,11 +143,11 @@ def __unicode__(self):
144143

145144
class PhoneNumberDesc(UnicodeMixin, ImmutableMixin):
146145
"""Class representing the description of a set of phone numbers."""
146+
@mutating_method
147147
def __init__(self,
148148
national_number_pattern=None,
149149
possible_number_pattern=None,
150150
example_number=None):
151-
self._mutable = True
152151
# The national_number_pattern is the pattern that a valid national
153152
# significant number would match. This specifies information such as
154153
# its total length and leading digits.
@@ -166,7 +165,6 @@ def __init__(self,
166165
# An example national significant number for the specific type. It
167166
# should not contain any formatting information.
168167
self.example_number = example_number # None or Unicode string
169-
self._mutable = False
170168

171169
def merge_from(self, other):
172170
"""Merge information from another PhoneNumberDesc object into this one."""
@@ -225,6 +223,7 @@ def metadata_for_region_or_calling_code(kls, country_calling_code, region_code):
225223
else:
226224
return kls.region_metadata.get(region_code, None)
227225

226+
@mutating_method
228227
def __init__(self,
229228
id,
230229
general_desc=None,
@@ -253,8 +252,6 @@ def __init__(self,
253252
leading_digits=None,
254253
leading_zero_possible=False,
255254
register=True):
256-
self._mutable = True
257-
258255
# The general_desc contains information which is a superset of
259256
# descriptions for all types of phone numbers. If any element is
260257
# missing in the description of a specific type of number, the element
@@ -424,7 +421,6 @@ def __init__(self,
424421
raise Exception("Duplicate PhoneMetadata for %s (from %s:%s)" % (id, self.id, self.country_code))
425422
else:
426423
kls_map[id] = self
427-
self._mutable = False
428424

429425
def __eq__(self, other):
430426
if not isinstance(other, PhoneMetadata):

python/phonenumbers/util.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,27 @@ class ImmutableMixin(object):
1313
"""Mixin class to make objects of subclasses immutable"""
1414
_mutable = False
1515

16-
def __setattr__(self, name, value): # pragma no cover
16+
def __setattr__(self, name, value):
1717
if self._mutable or name == "_mutable":
1818
object.__setattr__(self, name, value)
1919
else:
2020
raise TypeError("Can't modify immutable instance")
2121

22-
def __delattr__(self, name): # pragma no cover
22+
def __delattr__(self, name):
2323
if self._mutable:
2424
object.__delattr__(self, name)
2525
else:
2626
raise TypeError("Can't modify immutable instance")
27+
28+
29+
def mutating_method(func):
30+
"""Decorator for methods that are allowed to modify immutable objects"""
31+
def wrapper(self, *__args, **__kwargs):
32+
old_mutable = self._mutable
33+
self._mutable = True
34+
try:
35+
# Call the wrapped function
36+
return func(self, *__args, **__kwargs)
37+
finally:
38+
self._mutable = old_mutable
39+
return wrapper

0 commit comments

Comments
 (0)