33from collections .abc import Iterable
44import re
55import warnings
6- from typing import Callable , Dict
6+ from typing import Callable , Dict , Optional
77
88import numpy as np
99import scipy
1010
11- from Orange .data import Table , Storage , Instance , Value
11+ from Orange .data import Table , Storage , Instance , Value , Domain
1212from Orange .data .filter import HasClass
1313from Orange .data .table import DomainTransformationError
1414from Orange .data .util import one_hot
@@ -86,6 +86,9 @@ class Learner(ReprableWithPreprocessors):
8686 #: A sequence of data preprocessors to apply on data prior to
8787 #: fitting the model
8888 preprocessors = ()
89+
90+ # Note: Do not use this class attribute.
91+ # It remains here for compatibility reasons.
8992 learner_adequacy_err_msg = ''
9093
9194 def __init__ (self , preprocessors = None ):
@@ -95,6 +98,7 @@ def __init__(self, preprocessors=None):
9598 elif preprocessors :
9699 self .preprocessors = (preprocessors ,)
97100
101+ # pylint: disable=R0201
98102 def fit (self , X , Y , W = None ):
99103 raise RuntimeError (
100104 "Descendants of Learner must overload method fit or fit_storage" )
@@ -106,8 +110,23 @@ def fit_storage(self, data):
106110 return self .fit (X , Y , W )
107111
108112 def __call__ (self , data , progress_callback = None ):
109- if not self .check_learner_adequacy (data .domain ):
110- raise ValueError (self .learner_adequacy_err_msg )
113+
114+ for cls in type (self ).mro ():
115+ if 'incompatibility_reason' in cls .__dict__ :
116+ incompatibility_reason = \
117+ self .incompatibility_reason (data .domain ) # pylint: disable=assignment-from-none
118+ if incompatibility_reason is not None :
119+ raise ValueError (incompatibility_reason )
120+ break
121+ if 'check_learner_adequacy' in cls .__dict__ :
122+ warnings .warn (
123+ "check_learner_adequacy is deprecated and will be removed "
124+ "in upcoming releases. Learners should instead implement "
125+ "the incompatibility_reason method." ,
126+ OrangeDeprecationWarning )
127+ if not self .check_learner_adequacy (data .domain ):
128+ raise ValueError (self .learner_adequacy_err_msg )
129+ break
111130
112131 origdomain = data .domain
113132
@@ -176,6 +195,11 @@ def active_preprocessors(self):
176195 def check_learner_adequacy (self , _ ):
177196 return True
178197
198+ # pylint: disable=no-self-use
199+ def incompatibility_reason (self , _ : Domain ) -> Optional [str ]:
200+ """Return None if a learner can fit domain or string explaining why it can not."""
201+ return None
202+
179203 @property
180204 def name (self ):
181205 """Return a short name derived from Learner type name"""
0 commit comments