Skip to content

Commit

Permalink
deprecation: add fine-grained deprecation on models
Browse files Browse the repository at this point in the history
Add fine grained deprecation, alloowing the developer to specify
individual fields of a model that are deprecated. For example:

    class A():
      deprecated = fields.Boolean()
      a = fields.Boolean()

    @deprecated('b.deprecated')
    class B():
      b = fields.Model(A)
      c = fields.Int()

Accessing (set or get) b_instance.b.deprecated triggers deprecation
warnining.
  • Loading branch information
Michal Hecko committed Jul 15, 2024
1 parent bf4fd61 commit 86e35e0
Showing 1 changed file with 38 additions and 0 deletions.
38 changes: 38 additions & 0 deletions leapp/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@ def __init__(self, init_method='from_initialization', **kwargs):
for field in defined_fields: # noqa; pylint: disable=consider-using-dict-items
getattr(defined_fields[field], init_method)(kwargs, field, self)

# The instance is prepared (fields are recursively populated). Modify the instance so that if we are accessing
# a deprecated field we will be triggering deprecation warnings
if hasattr(self, '_deprecated_nodes'):
# depr_path is of the form ['field1', 'field2', 'field3', ..]. Traverse the path to obtain
# x = self.field1.field2...field{N-1}. Add field{N} to x's _deprecation_attributes which
# will be checked when accessing its attributes using __getattribute__
for depr_path in self._deprecated_nodes:
root = self
depr_path = list(depr_path)

while len(depr_path) > 1:
current_field = depr_path.pop(0)
root = getattr(root, current_field)

# Path has length 1
if getattr(root, '_deprecated_attributes', None) is None:
root._deprecated_attributes = []

root._deprecated_attributes.append(depr_path[0])

topic = None
"""
`topic` has to be set to a subclass of :py:class:`leapp.topics.Topic`
Expand All @@ -113,6 +133,24 @@ def create(cls, data):
"""
return cls(init_method='to_model', **data)

def __getattribute__(self, name):
try:
depr_attribs = super().__getattribute__('_deprecated_attributes')
if name in depr_attribs:
raise NotImplementedError('Register a Deprecation warning here.')
return super().__getattribute__(name)
except AttributeError: # There are no _deprecated_attributes, access the fields normally
return super().__getattribute__(name)

def __setattr__(self, name, value):
try:
depr_attribs = super().__getattribute__('_deprecated_attributes')
if name in depr_attribs:
raise NotImplementedError('Register a Deprecation warning here.')
return super().__setattr__(name, value)
except AttributeError:
return super().__setattr__(name, value)

def dump(self):
"""
Dumps the data in the dictionary form that is safe to serialize to JSON.
Expand Down

0 comments on commit 86e35e0

Please sign in to comment.