diff --git a/capellambse/loader/core.py b/capellambse/loader/core.py index 85a68fd8..4fe28621 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" @@ -401,6 +406,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 257bb01e..5d9cf186 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 + VIRTUAL_NAMESPACE_PREFIX = ( "https://dsd-dbs.github.io/py-capellambse/virtual-namespace/" ) @@ -34,6 +40,7 @@ """Covariant TypeVar (unbound).""" +@deprecated("set_accessor is deprecated and no longer needed") def set_accessor( cls: type[ModelObject], attr: str, accessor: Accessor ) -> None: @@ -41,11 +48,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 a671664c..db6de21d 100644 --- a/capellambse/model/_descriptors.py +++ b/capellambse/model/_descriptors.py @@ -40,6 +40,7 @@ import itertools import logging import operator +import sys import types import typing as t import warnings @@ -53,6 +54,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" @@ -60,6 +66,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]]: @@ -72,6 +79,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: @@ -581,6 +589,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.""" @@ -814,6 +823,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.""" @@ -847,18 +857,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 @@ -894,6 +905,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.""" @@ -986,10 +998,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): @@ -1158,6 +1171,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.""" @@ -1207,9 +1223,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: " @@ -1262,6 +1278,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, @@ -1921,6 +1941,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, @@ -2035,6 +2059,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", @@ -2462,6 +2489,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. @@ -2545,7 +2577,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 @@ -2584,9 +2616,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 de965a12..902807a0 100644 --- a/capellambse/model/_obj.py +++ b/capellambse/model/_obj.py @@ -38,6 +38,7 @@ import logging import operator import re +import sys import textwrap import typing as t import warnings @@ -52,6 +53,11 @@ from . import VIRTUAL_NAMESPACE_PREFIX, 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 @@ -598,6 +604,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 ) -> te.Self: @@ -1603,6 +1610,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``.""" @@ -2038,6 +2046,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.