We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Names of models are not saved as settings
1. Put logistic regression on the canvas.
2. Edit its name
3. Save the schema
4. Close canvas
5. Open Orange and load the schema

The logistic regression name is reverted back to Logistic regression.

--------

The base class for learners sets `self.learner_name = self.name` in `__init__`, where `self.name` is the widgets class attribute `name`, which is set later in the derived classes. So `learner_name = Setting(name)` or `learner_name = Setting(OWWidget.name)` won't work since the name is only defined later. Having `learner_name = Setting("")` would require some magic.

`OWBaseLearner` already has a meta class. This may be the point where the setting can be properly initialized since the `name` is already set then.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Orange/widgets/utils/owlearnerwidget.py`
Content:
```
1 from copy import deepcopy
2 
3 import itertools
4 import numpy as np
5 
6 from AnyQt.QtCore import QTimer, Qt
7 
8 from Orange.data import Table
9 from Orange.modelling import Fitter, Learner, Model
10 from Orange.preprocess.preprocess import Preprocess
11 from Orange.widgets import gui
12 from Orange.widgets.settings import Setting
13 from Orange.widgets.utils import getmembers
14 from Orange.widgets.utils.signals import Output, Input
15 from Orange.widgets.utils.sql import check_sql_input
16 from Orange.widgets.widget import OWWidget, WidgetMetaClass, Msg
17 
18 
19 class OWBaseLearnerMeta(WidgetMetaClass):
20     """ Meta class for learner widgets
21 
22     OWBaseLearner declares two outputs, learner and model with
23     generic type (Learner and Model).
24 
25     This metaclass ensures that each of the subclasses gets
26     its own Outputs class with output that match the corresponding
27     learner.
28     """
29     def __new__(cls, name, bases, attributes):
30         def abstract_widget():
31             return not attributes.get("name")
32 
33         def copy_outputs(template):
34             result = type("Outputs", (), {})
35             for name, signal in getmembers(template, Output):
36                 setattr(result, name, deepcopy(signal))
37             return result
38 
39         obj = super().__new__(cls, name, bases, attributes)
40         if abstract_widget():
41             return obj
42 
43         learner = attributes.get("LEARNER")
44         if not learner:
45             raise AttributeError(
46                 "'{}' must declare attribute LEARNER".format(name))
47 
48         outputs = obj.Outputs = copy_outputs(obj.Outputs)
49         outputs.learner.type = learner
50         outputs.model.type = learner.__returns__
51 
52         return obj
53 
54 
55 class OWBaseLearner(OWWidget, metaclass=OWBaseLearnerMeta):
56     """Abstract widget for classification/regression learners.
57 
58     Notes
59     -----
60     All learner widgets should define learner class LEARNER.
61     LEARNER should have __returns__ attribute.
62 
63     Overwrite `create_learner`, `add_main_layout` and `get_learner_parameters`
64     in case LEARNER has extra parameters.
65 
66     """
67     LEARNER = None
68     supports_sparse = True
69 
70     want_main_area = False
71     resizing_enabled = False
72     auto_apply = Setting(True)
73 
74     class Error(OWWidget.Error):
75         data_error = Msg("{}")
76         fitting_failed = Msg("Fitting failed.\n{}")
77         sparse_not_supported = Msg("Sparse data is not supported.")
78         out_of_memory = Msg("Out of memory.")
79 
80     class Warning(OWWidget.Warning):
81         outdated_learner = Msg("Press Apply to submit changes.")
82 
83     class Inputs:
84         data = Input("Data", Table)
85         preprocessor = Input("Preprocessor", Preprocess)
86 
87     class Outputs:
88         learner = Output("Learner", Learner, dynamic=False)
89         model = Output("Model", Model, dynamic=False,
90                        replaces=["Classifier", "Predictor"])
91 
92     OUTPUT_MODEL_NAME = Outputs.model.name  # Attr for backcompat w/ self.send() code
93 
94     def __init__(self):
95         super().__init__()
96         self.data = None
97         self.valid_data = False
98         self.learner = None
99         self.learner_name = self.name
100         self.model = None
101         self.preprocessors = None
102         self.outdated_settings = False
103 
104         self.setup_layout()
105         QTimer.singleShot(0, getattr(self, "unconditional_apply", self.apply))
106 
107     def create_learner(self):
108         """Creates a learner with current configuration.
109 
110         Returns:
111             Learner: an instance of Orange.base.learner subclass.
112         """
113         return self.LEARNER(preprocessors=self.preprocessors)
114 
115     def get_learner_parameters(self):
116         """Creates an `OrderedDict` or a sequence of pairs with current model
117         configuration.
118 
119         Returns:
120             OrderedDict or List: (option, value) pairs or dict
121         """
122         return []
123 
124     @Inputs.preprocessor
125     def set_preprocessor(self, preprocessor):
126         self.preprocessors = preprocessor
127         self.apply()
128 
129     @Inputs.data
130     @check_sql_input
131     def set_data(self, data):
132         """Set the input train data set."""
133         self.Error.data_error.clear()
134         self.data = data
135         if data is not None and data.domain.class_var is None:
136             self.Error.data_error("Data has no target variable.")
137             self.data = None
138 
139         self.update_model()
140 
141     def apply(self):
142         """Applies learner and sends new model."""
143         self.update_learner()
144         self.update_model()
145 
146     def update_learner(self):
147         self.learner = self.create_learner()
148         if self.learner and issubclass(self.LEARNER, Fitter):
149             self.learner.use_default_preprocessors = True
150         if self.learner is not None:
151             self.learner.name = self.learner_name
152         self.Outputs.learner.send(self.learner)
153         self.outdated_settings = False
154         self.Warning.outdated_learner.clear()
155 
156     def show_fitting_failed(self, exc):
157         """Show error when fitting fails.
158             Derived widgets can override this to show more specific messages."""
159         self.Error.fitting_failed(str(exc), shown=exc is not None)
160 
161     def update_model(self):
162         self.show_fitting_failed(None)
163         self.model = None
164         if self.check_data():
165             try:
166                 self.model = self.learner(self.data)
167             except BaseException as exc:
168                 self.show_fitting_failed(exc)
169             else:
170                 self.model.name = self.learner_name
171                 self.model.instances = self.data
172         self.Outputs.model.send(self.model)
173 
174     def check_data(self):
175         self.valid_data = False
176         self.Error.sparse_not_supported.clear()
177         if self.data is not None and self.learner is not None:
178             self.Error.data_error.clear()
179             if not self.learner.check_learner_adequacy(self.data.domain):
180                 self.Error.data_error(self.learner.learner_adequacy_err_msg)
181             elif not len(self.data):
182                 self.Error.data_error("Data set is empty.")
183             elif len(np.unique(self.data.Y)) < 2:
184                 self.Error.data_error("Data contains a single target value.")
185             elif self.data.X.size == 0:
186                 self.Error.data_error("Data has no features to learn from.")
187             elif self.data.is_sparse() and not self.supports_sparse:
188                 self.Error.sparse_not_supported()
189             else:
190                 self.valid_data = True
191         return self.valid_data
192 
193     def settings_changed(self, *args, **kwargs):
194         self.outdated_settings = True
195         self.Warning.outdated_learner(shown=not self.auto_apply)
196         self.apply()
197 
198     def _change_name(self, instance, output):
199         if instance:
200             instance.name = self.learner_name
201             if self.auto_apply:
202                 output.send(instance)
203 
204     def learner_name_changed(self):
205         self._change_name(self.learner, self.Outputs.learner)
206         self._change_name(self.model, self.Outputs.model)
207 
208     def send_report(self):
209         self.report_items((("Name", self.learner_name),))
210 
211         model_parameters = self.get_learner_parameters()
212         if model_parameters:
213             self.report_items("Model parameters", model_parameters)
214 
215         if self.data:
216             self.report_data("Data", self.data)
217 
218     # GUI
219     def setup_layout(self):
220         self.add_learner_name_widget()
221         self.add_main_layout()
222         # Options specific to target variable type, if supported
223         if issubclass(self.LEARNER, Fitter):
224             # Only add a classification section if the method is overridden
225             if type(self).add_classification_layout is not \
226                     OWBaseLearner.add_classification_layout:
227                 classification_box = gui.widgetBox(
228                     self.controlArea, 'Classification')
229                 self.add_classification_layout(classification_box)
230             # Only add a regression section if the method is overridden
231             if type(self).add_regression_layout is not \
232                     OWBaseLearner.add_regression_layout:
233                 regression_box = gui.widgetBox(self.controlArea, 'Regression')
234                 self.add_regression_layout(regression_box)
235         self.add_bottom_buttons()
236 
237     def add_main_layout(self):
238         """Creates layout with the learner configuration widgets.
239 
240         Override this method for laying out any learner-specific parameter controls.
241         See setup_layout() method for execution order.
242         """
243         pass
244 
245     def add_classification_layout(self, box):
246         """Creates layout for classification specific options.
247 
248         If a widget outputs a learner dispatcher, sometimes the classification
249         and regression learners require different options.
250         See `setup_layout()` method for execution order.
251         """
252         pass
253 
254     def add_regression_layout(self, box):
255         """Creates layout for regression specific options.
256 
257         If a widget outputs a learner dispatcher, sometimes the classification
258         and regression learners require different options.
259         See `setup_layout()` method for execution order.
260         """
261         pass
262 
263     def add_learner_name_widget(self):
264         self.name_line_edit = gui.lineEdit(
265             self.controlArea, self, 'learner_name', box='Name',
266             tooltip='The name will identify this model in other widgets',
267             orientation=Qt.Horizontal, callback=self.learner_name_changed)
268 
269     def add_bottom_buttons(self):
270         box = gui.hBox(self.controlArea, True)
271         box.layout().addWidget(self.report_button)
272         gui.separator(box, 15)
273         self.apply_button = gui.auto_commit(box, self, 'auto_apply', '&Apply',
274                                             box=False, commit=self.apply)
275 
276     def send(self, signalName, value, id=None):
277         # A subclass might still use the old syntax to send outputs
278         # defined on this class
279         for _, output in getmembers(self.Outputs, Output):
280             if output.name == signalName or signalName in output.replaces:
281                 output.send(value, id=id)
282                 return
283 
284         super().send(signalName, value, id)
285 
286     @classmethod
287     def get_widget_description(cls):
288         # When a subclass defines defines old-style signals, those override
289         # the new-style ones, so we add them manually
290         desc = super().get_widget_description()
291 
292         if cls.outputs:
293             desc["outputs"].extend(cls.get_signals("outputs", True))
294         if cls.inputs:
295             desc["inputs"].extend(cls.get_signals("inputs", True))
296         return desc
297
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Orange/widgets/utils/owlearnerwidget.py b/Orange/widgets/utils/owlearnerwidget.py
--- a/Orange/widgets/utils/owlearnerwidget.py
+++ b/Orange/widgets/utils/owlearnerwidget.py
@@ -67,6 +67,7 @@
     LEARNER = None
     supports_sparse = True
 
+    learner_name = Setting(None, schema_only=True)
     want_main_area = False
     resizing_enabled = False
     auto_apply = Setting(True)
@@ -96,7 +97,8 @@
         self.data = None
         self.valid_data = False
         self.learner = None
-        self.learner_name = self.name
+        if self.learner_name is None:
+            self.learner_name = self.name
         self.model = None
         self.preprocessors = None
         self.outdated_settings = False

{"golden_diff": "diff --git a/Orange/widgets/utils/owlearnerwidget.py b/Orange/widgets/utils/owlearnerwidget.py\n--- a/Orange/widgets/utils/owlearnerwidget.py\n+++ b/Orange/widgets/utils/owlearnerwidget.py\n@@ -67,6 +67,7 @@\n     LEARNER = None\n     supports_sparse = True\n \n+    learner_name = Setting(None, schema_only=True)\n     want_main_area = False\n     resizing_enabled = False\n     auto_apply = Setting(True)\n@@ -96,7 +97,8 @@\n         self.data = None\n         self.valid_data = False\n         self.learner = None\n-        self.learner_name = self.name\n+        if self.learner_name is None:\n+            self.learner_name = self.name\n         self.model = None\n         self.preprocessors = None\n         self.outdated_settings = False\n", "issue": "Names of models are not saved as settings\n1. Put logistic regression on the canvas.\r\n2. Edit its name\r\n3. Save the schema\r\n4. Close canvas\r\n5. Open Orange and load the schema\r\n\r\nThe logistic regression name is reverted back to Logistic regression.\r\n\r\n--------\r\n\r\nThe base class for learners sets `self.learner_name = self.name` in `__init__`, where `self.name` is the widgets class attribute `name`, which is set later in the derived classes. So `learner_name = Setting(name)` or `learner_name = Setting(OWWidget.name)` won't work since the name is only defined later. Having `learner_name = Setting(\"\")` would require some magic.\r\n\r\n`OWBaseLearner` already has a meta class. This may be the point where the setting can be properly initialized since the `name` is already set then.\n", "before_files": [{"content": "from copy import deepcopy\n\nimport itertools\nimport numpy as np\n\nfrom AnyQt.QtCore import QTimer, Qt\n\nfrom Orange.data import Table\nfrom Orange.modelling import Fitter, Learner, Model\nfrom Orange.preprocess.preprocess import Preprocess\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils import getmembers\nfrom Orange.widgets.utils.signals import Output, Input\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.widget import OWWidget, WidgetMetaClass, Msg\n\n\nclass OWBaseLearnerMeta(WidgetMetaClass):\n    \"\"\" Meta class for learner widgets\n\n    OWBaseLearner declares two outputs, learner and model with\n    generic type (Learner and Model).\n\n    This metaclass ensures that each of the subclasses gets\n    its own Outputs class with output that match the corresponding\n    learner.\n    \"\"\"\n    def __new__(cls, name, bases, attributes):\n        def abstract_widget():\n            return not attributes.get(\"name\")\n\n        def copy_outputs(template):\n            result = type(\"Outputs\", (), {})\n            for name, signal in getmembers(template, Output):\n                setattr(result, name, deepcopy(signal))\n            return result\n\n        obj = super().__new__(cls, name, bases, attributes)\n        if abstract_widget():\n            return obj\n\n        learner = attributes.get(\"LEARNER\")\n        if not learner:\n            raise AttributeError(\n                \"'{}' must declare attribute LEARNER\".format(name))\n\n        outputs = obj.Outputs = copy_outputs(obj.Outputs)\n        outputs.learner.type = learner\n        outputs.model.type = learner.__returns__\n\n        return obj\n\n\nclass OWBaseLearner(OWWidget, metaclass=OWBaseLearnerMeta):\n    \"\"\"Abstract widget for classification/regression learners.\n\n    Notes\n    -----\n    All learner widgets should define learner class LEARNER.\n    LEARNER should have __returns__ attribute.\n\n    Overwrite `create_learner`, `add_main_layout` and `get_learner_parameters`\n    in case LEARNER has extra parameters.\n\n    \"\"\"\n    LEARNER = None\n    supports_sparse = True\n\n    want_main_area = False\n    resizing_enabled = False\n    auto_apply = Setting(True)\n\n    class Error(OWWidget.Error):\n        data_error = Msg(\"{}\")\n        fitting_failed = Msg(\"Fitting failed.\\n{}\")\n        sparse_not_supported = Msg(\"Sparse data is not supported.\")\n        out_of_memory = Msg(\"Out of memory.\")\n\n    class Warning(OWWidget.Warning):\n        outdated_learner = Msg(\"Press Apply to submit changes.\")\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n        preprocessor = Input(\"Preprocessor\", Preprocess)\n\n    class Outputs:\n        learner = Output(\"Learner\", Learner, dynamic=False)\n        model = Output(\"Model\", Model, dynamic=False,\n                       replaces=[\"Classifier\", \"Predictor\"])\n\n    OUTPUT_MODEL_NAME = Outputs.model.name  # Attr for backcompat w/ self.send() code\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n        self.valid_data = False\n        self.learner = None\n        self.learner_name = self.name\n        self.model = None\n        self.preprocessors = None\n        self.outdated_settings = False\n\n        self.setup_layout()\n        QTimer.singleShot(0, getattr(self, \"unconditional_apply\", self.apply))\n\n    def create_learner(self):\n        \"\"\"Creates a learner with current configuration.\n\n        Returns:\n            Learner: an instance of Orange.base.learner subclass.\n        \"\"\"\n        return self.LEARNER(preprocessors=self.preprocessors)\n\n    def get_learner_parameters(self):\n        \"\"\"Creates an `OrderedDict` or a sequence of pairs with current model\n        configuration.\n\n        Returns:\n            OrderedDict or List: (option, value) pairs or dict\n        \"\"\"\n        return []\n\n    @Inputs.preprocessor\n    def set_preprocessor(self, preprocessor):\n        self.preprocessors = preprocessor\n        self.apply()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        \"\"\"Set the input train data set.\"\"\"\n        self.Error.data_error.clear()\n        self.data = data\n        if data is not None and data.domain.class_var is None:\n            self.Error.data_error(\"Data has no target variable.\")\n            self.data = None\n\n        self.update_model()\n\n    def apply(self):\n        \"\"\"Applies learner and sends new model.\"\"\"\n        self.update_learner()\n        self.update_model()\n\n    def update_learner(self):\n        self.learner = self.create_learner()\n        if self.learner and issubclass(self.LEARNER, Fitter):\n            self.learner.use_default_preprocessors = True\n        if self.learner is not None:\n            self.learner.name = self.learner_name\n        self.Outputs.learner.send(self.learner)\n        self.outdated_settings = False\n        self.Warning.outdated_learner.clear()\n\n    def show_fitting_failed(self, exc):\n        \"\"\"Show error when fitting fails.\n            Derived widgets can override this to show more specific messages.\"\"\"\n        self.Error.fitting_failed(str(exc), shown=exc is not None)\n\n    def update_model(self):\n        self.show_fitting_failed(None)\n        self.model = None\n        if self.check_data():\n            try:\n                self.model = self.learner(self.data)\n            except BaseException as exc:\n                self.show_fitting_failed(exc)\n            else:\n                self.model.name = self.learner_name\n                self.model.instances = self.data\n        self.Outputs.model.send(self.model)\n\n    def check_data(self):\n        self.valid_data = False\n        self.Error.sparse_not_supported.clear()\n        if self.data is not None and self.learner is not None:\n            self.Error.data_error.clear()\n            if not self.learner.check_learner_adequacy(self.data.domain):\n                self.Error.data_error(self.learner.learner_adequacy_err_msg)\n            elif not len(self.data):\n                self.Error.data_error(\"Data set is empty.\")\n            elif len(np.unique(self.data.Y)) < 2:\n                self.Error.data_error(\"Data contains a single target value.\")\n            elif self.data.X.size == 0:\n                self.Error.data_error(\"Data has no features to learn from.\")\n            elif self.data.is_sparse() and not self.supports_sparse:\n                self.Error.sparse_not_supported()\n            else:\n                self.valid_data = True\n        return self.valid_data\n\n    def settings_changed(self, *args, **kwargs):\n        self.outdated_settings = True\n        self.Warning.outdated_learner(shown=not self.auto_apply)\n        self.apply()\n\n    def _change_name(self, instance, output):\n        if instance:\n            instance.name = self.learner_name\n            if self.auto_apply:\n                output.send(instance)\n\n    def learner_name_changed(self):\n        self._change_name(self.learner, self.Outputs.learner)\n        self._change_name(self.model, self.Outputs.model)\n\n    def send_report(self):\n        self.report_items(((\"Name\", self.learner_name),))\n\n        model_parameters = self.get_learner_parameters()\n        if model_parameters:\n            self.report_items(\"Model parameters\", model_parameters)\n\n        if self.data:\n            self.report_data(\"Data\", self.data)\n\n    # GUI\n    def setup_layout(self):\n        self.add_learner_name_widget()\n        self.add_main_layout()\n        # Options specific to target variable type, if supported\n        if issubclass(self.LEARNER, Fitter):\n            # Only add a classification section if the method is overridden\n            if type(self).add_classification_layout is not \\\n                    OWBaseLearner.add_classification_layout:\n                classification_box = gui.widgetBox(\n                    self.controlArea, 'Classification')\n                self.add_classification_layout(classification_box)\n            # Only add a regression section if the method is overridden\n            if type(self).add_regression_layout is not \\\n                    OWBaseLearner.add_regression_layout:\n                regression_box = gui.widgetBox(self.controlArea, 'Regression')\n                self.add_regression_layout(regression_box)\n        self.add_bottom_buttons()\n\n    def add_main_layout(self):\n        \"\"\"Creates layout with the learner configuration widgets.\n\n        Override this method for laying out any learner-specific parameter controls.\n        See setup_layout() method for execution order.\n        \"\"\"\n        pass\n\n    def add_classification_layout(self, box):\n        \"\"\"Creates layout for classification specific options.\n\n        If a widget outputs a learner dispatcher, sometimes the classification\n        and regression learners require different options.\n        See `setup_layout()` method for execution order.\n        \"\"\"\n        pass\n\n    def add_regression_layout(self, box):\n        \"\"\"Creates layout for regression specific options.\n\n        If a widget outputs a learner dispatcher, sometimes the classification\n        and regression learners require different options.\n        See `setup_layout()` method for execution order.\n        \"\"\"\n        pass\n\n    def add_learner_name_widget(self):\n        self.name_line_edit = gui.lineEdit(\n            self.controlArea, self, 'learner_name', box='Name',\n            tooltip='The name will identify this model in other widgets',\n            orientation=Qt.Horizontal, callback=self.learner_name_changed)\n\n    def add_bottom_buttons(self):\n        box = gui.hBox(self.controlArea, True)\n        box.layout().addWidget(self.report_button)\n        gui.separator(box, 15)\n        self.apply_button = gui.auto_commit(box, self, 'auto_apply', '&Apply',\n                                            box=False, commit=self.apply)\n\n    def send(self, signalName, value, id=None):\n        # A subclass might still use the old syntax to send outputs\n        # defined on this class\n        for _, output in getmembers(self.Outputs, Output):\n            if output.name == signalName or signalName in output.replaces:\n                output.send(value, id=id)\n                return\n\n        super().send(signalName, value, id)\n\n    @classmethod\n    def get_widget_description(cls):\n        # When a subclass defines defines old-style signals, those override\n        # the new-style ones, so we add them manually\n        desc = super().get_widget_description()\n\n        if cls.outputs:\n            desc[\"outputs\"].extend(cls.get_signals(\"outputs\", True))\n        if cls.inputs:\n            desc[\"inputs\"].extend(cls.get_signals(\"inputs\", True))\n        return desc\n", "path": "Orange/widgets/utils/owlearnerwidget.py"}], "after_files": [{"content": "from copy import deepcopy\n\nimport itertools\nimport numpy as np\n\nfrom AnyQt.QtCore import QTimer, Qt\n\nfrom Orange.data import Table\nfrom Orange.modelling import Fitter, Learner, Model\nfrom Orange.preprocess.preprocess import Preprocess\nfrom Orange.widgets import gui\nfrom Orange.widgets.settings import Setting\nfrom Orange.widgets.utils import getmembers\nfrom Orange.widgets.utils.signals import Output, Input\nfrom Orange.widgets.utils.sql import check_sql_input\nfrom Orange.widgets.widget import OWWidget, WidgetMetaClass, Msg\n\n\nclass OWBaseLearnerMeta(WidgetMetaClass):\n    \"\"\" Meta class for learner widgets\n\n    OWBaseLearner declares two outputs, learner and model with\n    generic type (Learner and Model).\n\n    This metaclass ensures that each of the subclasses gets\n    its own Outputs class with output that match the corresponding\n    learner.\n    \"\"\"\n    def __new__(cls, name, bases, attributes):\n        def abstract_widget():\n            return not attributes.get(\"name\")\n\n        def copy_outputs(template):\n            result = type(\"Outputs\", (), {})\n            for name, signal in getmembers(template, Output):\n                setattr(result, name, deepcopy(signal))\n            return result\n\n        obj = super().__new__(cls, name, bases, attributes)\n        if abstract_widget():\n            return obj\n\n        learner = attributes.get(\"LEARNER\")\n        if not learner:\n            raise AttributeError(\n                \"'{}' must declare attribute LEARNER\".format(name))\n\n        outputs = obj.Outputs = copy_outputs(obj.Outputs)\n        outputs.learner.type = learner\n        outputs.model.type = learner.__returns__\n\n        return obj\n\n\nclass OWBaseLearner(OWWidget, metaclass=OWBaseLearnerMeta):\n    \"\"\"Abstract widget for classification/regression learners.\n\n    Notes\n    -----\n    All learner widgets should define learner class LEARNER.\n    LEARNER should have __returns__ attribute.\n\n    Overwrite `create_learner`, `add_main_layout` and `get_learner_parameters`\n    in case LEARNER has extra parameters.\n\n    \"\"\"\n    LEARNER = None\n    supports_sparse = True\n\n    learner_name = Setting(None, schema_only=True)\n    want_main_area = False\n    resizing_enabled = False\n    auto_apply = Setting(True)\n\n    class Error(OWWidget.Error):\n        data_error = Msg(\"{}\")\n        fitting_failed = Msg(\"Fitting failed.\\n{}\")\n        sparse_not_supported = Msg(\"Sparse data is not supported.\")\n        out_of_memory = Msg(\"Out of memory.\")\n\n    class Warning(OWWidget.Warning):\n        outdated_learner = Msg(\"Press Apply to submit changes.\")\n\n    class Inputs:\n        data = Input(\"Data\", Table)\n        preprocessor = Input(\"Preprocessor\", Preprocess)\n\n    class Outputs:\n        learner = Output(\"Learner\", Learner, dynamic=False)\n        model = Output(\"Model\", Model, dynamic=False,\n                       replaces=[\"Classifier\", \"Predictor\"])\n\n    OUTPUT_MODEL_NAME = Outputs.model.name  # Attr for backcompat w/ self.send() code\n\n    def __init__(self):\n        super().__init__()\n        self.data = None\n        self.valid_data = False\n        self.learner = None\n        if self.learner_name is None:\n            self.learner_name = self.name\n        self.model = None\n        self.preprocessors = None\n        self.outdated_settings = False\n\n        self.setup_layout()\n        QTimer.singleShot(0, getattr(self, \"unconditional_apply\", self.apply))\n\n    def create_learner(self):\n        \"\"\"Creates a learner with current configuration.\n\n        Returns:\n            Learner: an instance of Orange.base.learner subclass.\n        \"\"\"\n        return self.LEARNER(preprocessors=self.preprocessors)\n\n    def get_learner_parameters(self):\n        \"\"\"Creates an `OrderedDict` or a sequence of pairs with current model\n        configuration.\n\n        Returns:\n            OrderedDict or List: (option, value) pairs or dict\n        \"\"\"\n        return []\n\n    @Inputs.preprocessor\n    def set_preprocessor(self, preprocessor):\n        self.preprocessors = preprocessor\n        self.apply()\n\n    @Inputs.data\n    @check_sql_input\n    def set_data(self, data):\n        \"\"\"Set the input train data set.\"\"\"\n        self.Error.data_error.clear()\n        self.data = data\n        if data is not None and data.domain.class_var is None:\n            self.Error.data_error(\"Data has no target variable.\")\n            self.data = None\n\n        self.update_model()\n\n    def apply(self):\n        \"\"\"Applies learner and sends new model.\"\"\"\n        self.update_learner()\n        self.update_model()\n\n    def update_learner(self):\n        self.learner = self.create_learner()\n        if self.learner and issubclass(self.LEARNER, Fitter):\n            self.learner.use_default_preprocessors = True\n        if self.learner is not None:\n            self.learner.name = self.learner_name\n        self.Outputs.learner.send(self.learner)\n        self.outdated_settings = False\n        self.Warning.outdated_learner.clear()\n\n    def show_fitting_failed(self, exc):\n        \"\"\"Show error when fitting fails.\n            Derived widgets can override this to show more specific messages.\"\"\"\n        self.Error.fitting_failed(str(exc), shown=exc is not None)\n\n    def update_model(self):\n        self.show_fitting_failed(None)\n        self.model = None\n        if self.check_data():\n            try:\n                self.model = self.learner(self.data)\n            except BaseException as exc:\n                self.show_fitting_failed(exc)\n            else:\n                self.model.name = self.learner_name\n                self.model.instances = self.data\n        self.Outputs.model.send(self.model)\n\n    def check_data(self):\n        self.valid_data = False\n        self.Error.sparse_not_supported.clear()\n        if self.data is not None and self.learner is not None:\n            self.Error.data_error.clear()\n            if not self.learner.check_learner_adequacy(self.data.domain):\n                self.Error.data_error(self.learner.learner_adequacy_err_msg)\n            elif not len(self.data):\n                self.Error.data_error(\"Data set is empty.\")\n            elif len(np.unique(self.data.Y)) < 2:\n                self.Error.data_error(\"Data contains a single target value.\")\n            elif self.data.X.size == 0:\n                self.Error.data_error(\"Data has no features to learn from.\")\n            elif self.data.is_sparse() and not self.supports_sparse:\n                self.Error.sparse_not_supported()\n            else:\n                self.valid_data = True\n        return self.valid_data\n\n    def settings_changed(self, *args, **kwargs):\n        self.outdated_settings = True\n        self.Warning.outdated_learner(shown=not self.auto_apply)\n        self.apply()\n\n    def _change_name(self, instance, output):\n        if instance:\n            instance.name = self.learner_name\n            if self.auto_apply:\n                output.send(instance)\n\n    def learner_name_changed(self):\n        self._change_name(self.learner, self.Outputs.learner)\n        self._change_name(self.model, self.Outputs.model)\n\n    def send_report(self):\n        self.report_items(((\"Name\", self.learner_name),))\n\n        model_parameters = self.get_learner_parameters()\n        if model_parameters:\n            self.report_items(\"Model parameters\", model_parameters)\n\n        if self.data:\n            self.report_data(\"Data\", self.data)\n\n    # GUI\n    def setup_layout(self):\n        self.add_learner_name_widget()\n        self.add_main_layout()\n        # Options specific to target variable type, if supported\n        if issubclass(self.LEARNER, Fitter):\n            # Only add a classification section if the method is overridden\n            if type(self).add_classification_layout is not \\\n                    OWBaseLearner.add_classification_layout:\n                classification_box = gui.widgetBox(\n                    self.controlArea, 'Classification')\n                self.add_classification_layout(classification_box)\n            # Only add a regression section if the method is overridden\n            if type(self).add_regression_layout is not \\\n                    OWBaseLearner.add_regression_layout:\n                regression_box = gui.widgetBox(self.controlArea, 'Regression')\n                self.add_regression_layout(regression_box)\n        self.add_bottom_buttons()\n\n    def add_main_layout(self):\n        \"\"\"Creates layout with the learner configuration widgets.\n\n        Override this method for laying out any learner-specific parameter controls.\n        See setup_layout() method for execution order.\n        \"\"\"\n        pass\n\n    def add_classification_layout(self, box):\n        \"\"\"Creates layout for classification specific options.\n\n        If a widget outputs a learner dispatcher, sometimes the classification\n        and regression learners require different options.\n        See `setup_layout()` method for execution order.\n        \"\"\"\n        pass\n\n    def add_regression_layout(self, box):\n        \"\"\"Creates layout for regression specific options.\n\n        If a widget outputs a learner dispatcher, sometimes the classification\n        and regression learners require different options.\n        See `setup_layout()` method for execution order.\n        \"\"\"\n        pass\n\n    def add_learner_name_widget(self):\n        self.name_line_edit = gui.lineEdit(\n            self.controlArea, self, 'learner_name', box='Name',\n            tooltip='The name will identify this model in other widgets',\n            orientation=Qt.Horizontal, callback=self.learner_name_changed)\n\n    def add_bottom_buttons(self):\n        box = gui.hBox(self.controlArea, True)\n        box.layout().addWidget(self.report_button)\n        gui.separator(box, 15)\n        self.apply_button = gui.auto_commit(box, self, 'auto_apply', '&Apply',\n                                            box=False, commit=self.apply)\n\n    def send(self, signalName, value, id=None):\n        # A subclass might still use the old syntax to send outputs\n        # defined on this class\n        for _, output in getmembers(self.Outputs, Output):\n            if output.name == signalName or signalName in output.replaces:\n                output.send(value, id=id)\n                return\n\n        super().send(signalName, value, id)\n\n    @classmethod\n    def get_widget_description(cls):\n        # When a subclass defines defines old-style signals, those override\n        # the new-style ones, so we add them manually\n        desc = super().get_widget_description()\n\n        if cls.outputs:\n            desc[\"outputs\"].extend(cls.get_signals(\"outputs\", True))\n        if cls.inputs:\n            desc[\"inputs\"].extend(cls.get_signals(\"inputs\", True))\n        return desc\n", "path": "Orange/widgets/utils/owlearnerwidget.py"}]}