From 0eceb2834609f7950c1caa494a2dadc26558e6fe Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Fri, 23 Aug 2024 11:17:24 +0200 Subject: [PATCH] refactor: Deprecate old, now-unused functions --- capellambse/loader/core.py | 9 ++++++ capellambse/model/__init__.py | 14 +++++++- capellambse/model/_descriptors.py | 53 ++++++++++++++++++++++++++----- capellambse/model/_obj.py | 12 +++++++ 4 files changed, 79 insertions(+), 9 deletions(-) diff --git a/capellambse/loader/core.py b/capellambse/loader/core.py index f5150be15..dc3e4df0e 100644 --- a/capellambse/loader/core.py +++ b/capellambse/loader/core.py @@ -36,6 +36,11 @@ from capellambse.loader import exs from capellambse.loader.modelinfo import ModelInfo +if sys.version_info >= (3, 13): + from warnings import deprecated +else: + from typing_extensions import deprecated + E = builder.ElementMaker() LOGGER = logging.getLogger(__name__) PROJECT_NATURE = "org.polarsys.capella.project.nature" @@ -419,6 +424,10 @@ def __replace_nsmap(self, new_nsmap: dict[str | None, str]) -> None: self.root = new_root + @deprecated( + "iterall_xt() is deprecated," + " use iterall() or iter_qtypes() + iter_qtype() instead" + ) def iterall_xt( self, xtypes: cabc.Container[str] ) -> cabc.Iterator[etree._Element]: diff --git a/capellambse/model/__init__.py b/capellambse/model/__init__.py index ff2c0c645..18a8d8c24 100644 --- a/capellambse/model/__init__.py +++ b/capellambse/model/__init__.py @@ -7,9 +7,15 @@ import collections.abc as cabc import enum import functools +import sys import typing as t import warnings +if sys.version_info >= (3, 13): + from warnings import deprecated +else: + from typing_extensions import deprecated + E = t.TypeVar("E", bound=enum.Enum) """TypeVar for ":py:class:`~enum.Enum`".""" S = t.TypeVar("S", bound=str | None) @@ -24,6 +30,7 @@ """Covariant TypeVar (unbound).""" +@deprecated("set_accessor is deprecated and no longer needed") def set_accessor( cls: type[ModelObject], attr: str, accessor: Accessor ) -> None: @@ -31,11 +38,16 @@ def set_accessor( accessor.__set_name__(cls, attr) +@deprecated("set_self_references is deprecated, use a 'Containment' instead") def set_self_references(*args: tuple[type[ModelObject], str]) -> None: for cls, attr in args: - setattr(cls, attr, DirectProxyAccessor(cls, aslist=ElementList)) + setattr(cls, attr, DirectProxyAccessor(cls, aslist=ElementList)) # type: ignore[deprecated] +@deprecated( + '`@attr_equal("...")` is deprecated,' + ' use `class X(ModelElement, eq="...")` instead' +) def attr_equal(attr: str) -> cabc.Callable[[type[T]], type[T]]: def add_wrapped_eq(cls: type[T]) -> type[T]: orig_eq = cls.__eq__ diff --git a/capellambse/model/_descriptors.py b/capellambse/model/_descriptors.py index 35e1b45a3..1da4cb334 100644 --- a/capellambse/model/_descriptors.py +++ b/capellambse/model/_descriptors.py @@ -44,6 +44,7 @@ import itertools import logging import operator +import sys import types import typing as t import warnings @@ -58,6 +59,11 @@ from . import T, T_co, U_co +if sys.version_info >= (3, 13): + from warnings import deprecated +else: + from typing_extensions import deprecated + _NotSpecifiedType = t.NewType("_NotSpecifiedType", object) _NOT_SPECIFIED = _NotSpecifiedType(object()) "Used to detect unspecified optional arguments" @@ -65,6 +71,7 @@ LOGGER = logging.getLogger(__name__) +@deprecated("@xtype_handler is deprecated and no longer used") def xtype_handler( arch: str | None = None, /, *xtypes: str ) -> cabc.Callable[[type[T]], type[T]]: @@ -77,6 +84,7 @@ def xtype_handler( return lambda i: i +@deprecated("build_xtype is deprecated") def build_xtype(class_: type[_obj.ModelObject]) -> str: ns: _obj.Namespace | None = getattr(class_, "__capella_namespace__", None) if ns is None: @@ -588,6 +596,7 @@ def _resolve_super_attributes( self.single_attr = super_acc.single_attr +@deprecated("WritableAccessor is deprecated, use Relation instead") class WritableAccessor(Accessor["_obj.ElementList[T_co]"], t.Generic[T_co]): """An Accessor that also provides write support on lists it returns.""" @@ -821,6 +830,7 @@ def purge_references(self, obj, target): ) +@deprecated("PhysicalAccessor is deprecated, use Relation instead") class PhysicalAccessor(Accessor["_obj.ElementList[T_co]"], t.Generic[T_co]): """Helper super class for accessors that work with real elements.""" @@ -854,18 +864,19 @@ def __init__( super().__init__() if xtypes is None: self.xtypes = ( - {build_xtype(class_)} + {build_xtype(class_)} # type: ignore[deprecated] if class_ is not _obj.ModelElement else set() ) elif isinstance(xtypes, type): assert issubclass(xtypes, _obj.ModelElement) - self.xtypes = {build_xtype(xtypes)} + self.xtypes = {build_xtype(xtypes)} # type: ignore[deprecated] elif isinstance(xtypes, str): self.xtypes = {xtypes} else: self.xtypes = { - i if isinstance(i, str) else build_xtype(i) for i in xtypes + i if isinstance(i, str) else build_xtype(i) # type: ignore[deprecated] + for i in xtypes } self.aslist = aslist @@ -901,6 +912,7 @@ def _make_list(self, parent_obj, elements): ) +@deprecated("DirectProxyAccessor is deprecated, use Containment instead") class DirectProxyAccessor(WritableAccessor[T_co], PhysicalAccessor[T_co]): """Creates proxy objects on the fly.""" @@ -993,10 +1005,11 @@ def __init__( elif isinstance(rootelem, type) and issubclass( rootelem, _obj.ModelElement ): - self.rootelem = [build_xtype(rootelem)] + self.rootelem = [build_xtype(rootelem)] # type: ignore[deprecated] else: self.rootelem = [ - i if isinstance(i, str) else build_xtype(i) for i in rootelem + i if isinstance(i, str) else build_xtype(i) # type: ignore[deprecated] + for i in rootelem ] def __get__(self, obj, objtype=None): @@ -1165,6 +1178,9 @@ def purge_references( yield +@deprecated( + "DeepProxyAccessor is deprecated, use @property and model.search() instead" +) class DeepProxyAccessor(PhysicalAccessor[T_co]): """A DirectProxyAccessor that searches recursively through the tree.""" @@ -1214,9 +1230,9 @@ def __init__( elif isinstance(rootelem, type) and issubclass( rootelem, _obj.ModelElement ): - self.rootelem = (build_xtype(rootelem),) + self.rootelem = (build_xtype(rootelem),) # type: ignore[deprecated] elif not isinstance(rootelem, str): # type: ignore[unreachable] - self.rootelem = tuple(build_xtype(i) for i in rootelem) + self.rootelem = tuple(build_xtype(i) for i in rootelem) # type: ignore[deprecated] else: raise TypeError( "Invalid 'rootelem', expected a type or list of types: " @@ -1269,6 +1285,10 @@ class Allocation(Relationship[T_co]): backattr: str | None @t.overload + @deprecated( + "Raw classes, xsi:type strings and 'aslist' are deprecated," + " migrate to (Namespace, 'ClassName') tuples and drop aslist=..." + ) def __init__( self, tag: str | None, @@ -1927,6 +1947,10 @@ def _resolve_super_attributes( self.attr = super_acc.attr +@deprecated( + "PhysicalLinkEndsAccessor is deprecated," + " use Association with fixed_length=2 instead" +) class PhysicalLinkEndsAccessor(Association[T_co]): def __init__( self, @@ -2041,6 +2065,9 @@ def __get__(self, obj, objtype=None): return _obj.wrap_xml(obj._model, parent) +@deprecated( + "AttributeMatcherAccessor is deprecated, use FilterAccessor instead" +) class AttributeMatcherAccessor(DirectProxyAccessor[T_co]): __slots__ = ( "_AttributeMatcherAccessor__aslist", @@ -2457,6 +2484,11 @@ def purge_references( yield +@deprecated( + "TypecastAccessor is deprecated," + " use Filter to perform filtering" + " or Alias to create an unfiltered Alias" +) class TypecastAccessor(WritableAccessor[T_co], PhysicalAccessor[T_co]): """Changes the static type of the value of another accessor. @@ -2540,7 +2572,7 @@ def create( if typehint: raise TypeError(f"{self._qualname} does not support type hints") acc: WritableAccessor = getattr(self.class_, self.attr) - obj = acc.create(elmlist, build_xtype(self.class_), **kw) + obj = acc.create(elmlist, build_xtype(self.class_), **kw) # type: ignore[deprecated] assert isinstance(obj, self.class_) return obj @@ -2579,9 +2611,14 @@ def purge_references( class Containment(Relationship[T_co]): __slots__ = ("classes", "role_tag") + aslist: type[_obj.ElementListCouplingMixin] alternate: type[_obj.ModelObject] | None @t.overload + @deprecated( + "Raw classes, xsi:type strings and 'aslist' are deprecated," + " migrate to (Namespace, 'ClassName') tuples and drop aslist=..." + ) def __init__( self, role_tag: str, diff --git a/capellambse/model/_obj.py b/capellambse/model/_obj.py index 9945d08d5..7b56a0d85 100644 --- a/capellambse/model/_obj.py +++ b/capellambse/model/_obj.py @@ -41,6 +41,7 @@ import logging import operator import re +import sys import textwrap import typing as t import warnings @@ -55,6 +56,11 @@ from . import T, U, _descriptors, _pods, _styleclass +if sys.version_info >= (3, 13): + from warnings import deprecated +else: + from typing_extensions import deprecated + if t.TYPE_CHECKING: import capellambse.metamodel as mm @@ -582,6 +588,7 @@ def progress_status(self) -> str: return wrap_xml(self._model, self._model._loader[uuid]).name @classmethod + @deprecated("ModelElement.from_model is deprecated, use wrap_xml instead") def from_model( cls, model: capellambse.MelodyModel, element: etree._Element ) -> ModelObject: @@ -1558,6 +1565,7 @@ def _newlist(self, elements: list[etree._Element]) -> ElementList[T]: return newlist +@deprecated("MixedElementList is deprecated, use base ElementList instead") class MixedElementList(ElementList[ModelElement]): """ElementList that handles proxies using ``XTYPE_HANDLERS``.""" @@ -1969,6 +1977,10 @@ def wrap_xml( return obj +@deprecated( + "find_wrapper is deprecated," + " use resolve_class_name or MelodyModel.resolve_class instead" +) @functools.cache def find_wrapper(typehint: str) -> tuple[type[ModelObject], ...]: """Find the possible wrapper classes for the hinted type.