@@ -246,33 +246,22 @@ def rename(cls, name):
246246
247247 def _create_inverse_property (cls , members ):
248248 inverse = members .get ('inverse' )
249- if inverse is None :
249+ if inverse is None or cls .__bases__ [0 ] is object :
250+ # The latter clause is the prevent the below code from running on
251+ # the Model base class, which implements the default getter and
252+ # setter for .inverse
250253 return
251254
252255 if isinstance (inverse , property ):
253- fget = inverse .fget
254- else :
255256 # We allow the @property decorator to be omitted entirely from
256257 # the class definition, though its use should be encouraged for
257258 # clarity
258- fget = inverse
259-
260- def wrapped_fget (self ):
261- if self ._custom_inverse is not None :
262- return self ._custom_inverse
263-
264- return fget (self )
265-
266- def fset (self , value ):
267- if not isinstance (value , (Model , type (None ))):
268- raise ValueError (
269- "The ``inverse`` attribute may be assigned a `Model` "
270- "instance or `None` (where `None` restores the default "
271- "inverse for this model if one is defined." )
272-
273- self ._custom_inverse = value
259+ inverse = inverse .fget
274260
275- cls .inverse = property (wrapped_fget , fset , doc = inverse .__doc__ )
261+ # Store the inverse getter internally, then delete the given .inverse
262+ # attribute so that cls.inverse resolves to Model.inverse instead
263+ cls ._inverse = inverse
264+ del cls .inverse
276265
277266 def _handle_backwards_compat (cls , name , members ):
278267 # Backwards compatibility check for 'eval' -> 'evaluate'
@@ -579,7 +568,8 @@ class Model(object):
579568 # optionally; in that case it is of course up to the user to determine
580569 # whether their inverse is *actually* an inverse to the model they assign
581570 # it to.
582- _custom_inverse = None
571+ _inverse = None
572+ _user_inverse = None
583573
584574 # If a bounding_box_default function is defined in the model,
585575 # then the _bounding_box attribute should be set to 'auto' in the model.
@@ -777,8 +767,6 @@ def ineqcons(self):
777767
778768 return self ._constraints ['ineqcons' ]
779769
780- # *** Public methods ***
781-
782770 @property
783771 def inverse (self ):
784772 """
@@ -791,16 +779,73 @@ def inverse(self):
791779 `~astropy.modeling.polynomial.PolynomialModel`, but not by
792780 requirement).
793781
782+ A custom inverse can be deleted with ``del model.inverse``. In this
783+ case the model's inverse is reset to its default, if a default exists
784+ (otherwise the default is to raise `NotImplementedError`).
785+
794786 Note to authors of `~astropy.modeling.Model` subclasses: To define an
795787 inverse for a model simply override this property to return the
796788 appropriate model representing the inverse. The machinery that will
797789 make the inverse manually-overridable is added automatically by the
798790 base class.
799791 """
800792
793+ if self ._user_inverse is not None :
794+ return self ._user_inverse
795+ elif self ._inverse is not None :
796+ return self ._inverse ()
797+
801798 raise NotImplementedError ("An analytical inverse transform has not "
802799 "been implemented for this model." )
803800
801+ @inverse .setter
802+ def inverse (self , value ):
803+ if not isinstance (value , (Model , type (None ))):
804+ raise ValueError (
805+ "The ``inverse`` attribute may be assigned a `Model` "
806+ "instance or `None` (where `None` restores the default "
807+ "inverse for this model if one is defined." )
808+
809+ if value is None :
810+ warnings .warn (
811+ "Currently setting `model.inverse = None` resets the inverse "
812+ "to the default inverse (if one exists). However, starting "
813+ "in Astropy 1.2, setting `model.inverse = None` explicitly "
814+ "forces a model to have no inverse (such that accessing "
815+ "`model.inverse` raises a NotImplementedError) even if that "
816+ "model's class has a default inverse.\n \n "
817+ "Instead, call `del model.inverse` to reset the inverse to "
818+ "its default (if a default exists for that model's class--"
819+ "otherwise the model is reset to having no inverse." ,
820+ AstropyDeprecationWarning )
821+
822+ self ._user_inverse = value
823+
824+ @inverse .deleter
825+ def inverse (self ):
826+ """
827+ Resets the model's inverse to its default (if one exists, otherwise
828+ the model will have no inverse).
829+ """
830+
831+ del self ._user_inverse
832+
833+ @property
834+ def has_user_inverse (self ):
835+ """
836+ A flag indicating whether or not a custom inverse model has been
837+ assigned to this model by a user, via assignment to ``model.inverse``.
838+ """
839+
840+ return self ._user_inverse is not None
841+
842+ @property
843+ def _custom_inverse (self ):
844+ # Deprecated alias for _user_inverse--included solely for temporary
845+ # backwards compatibility for pyasdf--remove after Astropy v1.1
846+ # release.
847+ return self ._user_inverse
848+
804849 @property
805850 def bounding_box (self ):
806851 """
@@ -889,6 +934,8 @@ def bounding_box(self, limits):
889934
890935 self ._bounding_box = limits
891936
937+ # *** Public methods ***
938+
892939 @abc .abstractmethod
893940 def evaluate (self , * args , ** kwargs ):
894941 """Evaluate the model on some input variables."""
@@ -1832,7 +1879,7 @@ class members that should be added to the generated
18321879 # intermediate compound models are stored in the tree as well then
18331880 # we can immediately check for custom inverses on sub-models when
18341881 # computing the inverse
1835- instance ._custom_inverse = mcls ._make_custom_inverse (
1882+ instance ._user_inverse = mcls ._make_user_inverse (
18361883 operator , left , right )
18371884
18381885 return instance
@@ -1851,7 +1898,7 @@ def _handle_backwards_compat(mcls, name, members):
18511898 return
18521899
18531900 @classmethod
1854- def _make_custom_inverse (mcls , operator , left , right ):
1901+ def _make_user_inverse (mcls , operator , left , right ):
18551902 """
18561903 Generates an inverse `Model` for this `_CompoundModel` when either
18571904 model in the operation has a *custom inverse* that was manually
@@ -1873,7 +1920,7 @@ def _make_custom_inverse(mcls, operator, left, right):
18731920 """
18741921
18751922 if not (operator in ('&' , '|' ) and
1876- (left ._custom_inverse or right ._custom_inverse )):
1923+ (left ._user_inverse or right ._user_inverse )):
18771924 # These are the only operators that support an inverse right now
18781925 return None
18791926
0 commit comments