We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Setting `Table(missing_value="")` does not silence the warning about a missing value in data
**Describe the bug**
Explicitly setting `Table(missing_value="")` (i.e. the empty string and not `None`) does not silence the warning about the missing value:
```
WARNING: Using empty string for missing value in data. Define a 'missing_value' on the table to silence this message
```

**To Reproduce**
Steps to reproduce the behavior:
1. Go to https://toga.readthedocs.io/en/latest/tutorial/tutorial-2.html
2. Run the code
3. Notice the terminal says, `WARNING: Using empty string for missing value in data. Define a 'missing_value' on the table to silence this message`
4. add `missing_value=""` to the `toga.Table()` call
5. Re-launch the app
6. Notice the warning persists

**Expected behavior**
To have no warning when the empty string is specified.

**Environment:**
 - Operating System: Windows 10
 - Python version: CPython 3.8.3
 - Software versions:
   - Briefcase:
   - Toga: 0.3.0dev22
   - ...

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/core/toga/widgets/table.py`
Content:
```
1 from toga.handlers import wrapped_handler
2 from toga.sources import ListSource
3 from toga.sources.accessors import build_accessors, to_accessor
4 
5 from .base import Widget
6 
7 
8 class Table(Widget):
9     """ A Table Widget allows the display of data in the form of columns and rows.
10 
11     Args:
12         headings (``list`` of ``str``): The list of headings for the table.
13         id (str): An identifier for this widget.
14         data (``list`` of ``tuple``): The data to be displayed on the table.
15         accessors: A list of methods, same length as ``headings``, that describes
16             how to extract the data value for each column from the row. (Optional)
17         style (:obj:`Style`): An optional style object.
18             If no style is provided` then a new one will be created for the widget.
19         on_select (``callable``): A function to be invoked on selecting a row of the table.
20         missing_value (``str`` or ``None``): value for replacing a missing value
21             in the data source. (Default: None). When 'None', a warning message
22             will be shown.
23         factory (:obj:`module`): A python module that is capable to return a
24             implementation of this class with the same name. (optional & normally not needed)
25 
26     Examples:
27         >>> headings = ['Head 1', 'Head 2', 'Head 3']
28         >>> data = []
29         >>> table = Table(headings, data=data)
30 
31         # Data can be in several forms.
32         # A list of dictionaries, where the keys match the heading names:
33         >>> data = [{'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}),
34         >>>         {'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}]
35 
36         # A list of lists. These will be mapped to the headings in order:
37         >>> data = [('value 1', 'value 2', 'value3'),
38         >>>         ('value 1', 'value 2', 'value3')]
39 
40         # A list of values. This is only accepted if there is a single heading.
41         >>> data = ['item 1', 'item 2', 'item 3']
42     """
43     MIN_WIDTH = 100
44     MIN_HEIGHT = 100
45 
46     def __init__(self, headings, id=None, style=None, data=None, accessors=None,
47                  multiple_select=False, on_select=None, missing_value=None,
48                  factory=None):
49         super().__init__(id=id, style=style, factory=factory)
50         self.headings = headings[:]
51         self._accessors = build_accessors(self.headings, accessors)
52         self._multiple_select = multiple_select
53         self._on_select = None
54         self._selection = None
55         self._data = None
56         if not missing_value:
57             print("WARNING: Using empty string for missing value in data. "
58                   "Define a 'missing_value' on the table to silence this message")
59         self._missing_value = missing_value or ''
60 
61         self._impl = self.factory.Table(interface=self)
62         self.data = data
63 
64         self.on_select = on_select
65 
66     @property
67     def data(self):
68         """ The data source of the widget. It accepts table data
69         in the form of ``list``, ``tuple``, or :obj:`ListSource`
70 
71         Returns:
72             Returns a (:obj:`ListSource`).
73         """
74         return self._data
75 
76     @data.setter
77     def data(self, data):
78         if data is None:
79             self._data = ListSource(accessors=self._accessors, data=[])
80         elif isinstance(data, (list, tuple)):
81             self._data = ListSource(accessors=self._accessors, data=data)
82         else:
83             self._data = data
84 
85         self._data.add_listener(self._impl)
86         self._impl.change_source(source=self._data)
87 
88     @property
89     def multiple_select(self):
90         """Does the table allow multiple rows to be selected?"""
91         return self._multiple_select
92 
93     @property
94     def selection(self):
95         """The current selection of the table.
96 
97         A value of None indicates no selection.
98         If the table allows multiple selection, returns a list of
99         selected data nodes. Otherwise, returns a single data node.
100         """
101         return self._selection
102 
103     def scroll_to_top(self):
104         """Scroll the view so that the top of the list (first row) is visible
105         """
106         self.scroll_to_row(0)
107 
108     def scroll_to_row(self, row):
109         """Scroll the view so that the specified row index is visible.
110 
111         Args:
112             row: The index of the row to make visible. Negative values refer
113                  to the nth last row (-1 is the last row, -2 second last,
114                  and so on)
115         """
116         if row >= 0:
117             self._impl.scroll_to_row(row)
118         else:
119             self._impl.scroll_to_row(len(self.data) + row)
120 
121     def scroll_to_bottom(self):
122         """Scroll the view so that the bottom of the list (last row) is visible
123         """
124         self.scroll_to_row(-1)
125 
126     @property
127     def on_select(self):
128         """ The callback function that is invoked when a row of the table is selected.
129         The provided callback function has to accept two arguments table (``:obj:Table`)
130         and row (``int`` or ``None``).
131 
132         Returns:
133             (``callable``) The callback function.
134         """
135         return self._on_select
136 
137     @on_select.setter
138     def on_select(self, handler):
139         """
140         Set the function to be executed on node selection
141 
142         :param handler: callback function
143         :type handler: ``callable``
144         """
145         self._on_select = wrapped_handler(self, handler)
146         self._impl.set_on_select(self._on_select)
147 
148     def add_column(self, heading, accessor=None):
149         """
150         Add a new column to the table
151 
152         :param heading: title of the column
153         :type heading: ``string``
154         :param accessor: accessor of this new column
155         :type heading: ``string``
156         """
157 
158         if not accessor:
159             accessor = to_accessor(heading)
160 
161         if accessor in self._accessors:
162             raise ValueError('Accessor "{}" is already in use'.format(accessor))
163 
164         self.headings.append(heading)
165         self._accessors.append(accessor)
166 
167         self._impl.add_column(heading, accessor)
168 
169     def remove_column(self, column):
170         """
171         Remove a table column.
172 
173         :param column: accessor or position (>0)
174         :type column: ``string``
175         :type column: ``int``
176         """
177 
178         if isinstance(column, str):
179             # Column is a string; use as-is
180             accessor = column
181         else:
182             try:
183                 accessor = self._accessors[column]
184             except IndexError:
185                 # Column specified as an integer, but the integer is out of range.
186                 raise ValueError("Column {} does not exist".format(column))
187             except TypeError:
188                 # Column specified as something other than int or str
189                 raise ValueError("Column must be an integer or string")
190 
191         try:
192             # Remove column
193             self._impl.remove_column(accessor)
194             del self.headings[self._accessors.index(accessor)]
195             self._accessors.remove(accessor)
196         except KeyError:
197             raise ValueError('Invalid column: "{}"'.format(column))
198 
199     @property
200     def missing_value(self):
201         return self._missing_value
202
```
--- 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/src/core/toga/widgets/table.py b/src/core/toga/widgets/table.py
--- a/src/core/toga/widgets/table.py
+++ b/src/core/toga/widgets/table.py
@@ -53,7 +53,7 @@
         self._on_select = None
         self._selection = None
         self._data = None
-        if not missing_value:
+        if missing_value is None:
             print("WARNING: Using empty string for missing value in data. "
                   "Define a 'missing_value' on the table to silence this message")
         self._missing_value = missing_value or ''

{"golden_diff": "diff --git a/src/core/toga/widgets/table.py b/src/core/toga/widgets/table.py\n--- a/src/core/toga/widgets/table.py\n+++ b/src/core/toga/widgets/table.py\n@@ -53,7 +53,7 @@\n         self._on_select = None\n         self._selection = None\n         self._data = None\n-        if not missing_value:\n+        if missing_value is None:\n             print(\"WARNING: Using empty string for missing value in data. \"\n                   \"Define a 'missing_value' on the table to silence this message\")\n         self._missing_value = missing_value or ''\n", "issue": "Setting `Table(missing_value=\"\")` does not silence the warning about a missing value in data\n**Describe the bug**\r\nExplicitly setting `Table(missing_value=\"\")` (i.e. the empty string and not `None`) does not silence the warning about the missing value:\r\n```\r\nWARNING: Using empty string for missing value in data. Define a 'missing_value' on the table to silence this message\r\n```\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Go to https://toga.readthedocs.io/en/latest/tutorial/tutorial-2.html\r\n2. Run the code\r\n3. Notice the terminal says, `WARNING: Using empty string for missing value in data. Define a 'missing_value' on the table to silence this message`\r\n4. add `missing_value=\"\"` to the `toga.Table()` call\r\n5. Re-launch the app\r\n6. Notice the warning persists\r\n\r\n**Expected behavior**\r\nTo have no warning when the empty string is specified.\r\n\r\n**Environment:**\r\n - Operating System: Windows 10\r\n - Python version: CPython 3.8.3\r\n - Software versions:\r\n   - Briefcase:\r\n   - Toga: 0.3.0dev22\r\n   - ...\n", "before_files": [{"content": "from toga.handlers import wrapped_handler\nfrom toga.sources import ListSource\nfrom toga.sources.accessors import build_accessors, to_accessor\n\nfrom .base import Widget\n\n\nclass Table(Widget):\n    \"\"\" A Table Widget allows the display of data in the form of columns and rows.\n\n    Args:\n        headings (``list`` of ``str``): The list of headings for the table.\n        id (str): An identifier for this widget.\n        data (``list`` of ``tuple``): The data to be displayed on the table.\n        accessors: A list of methods, same length as ``headings``, that describes\n            how to extract the data value for each column from the row. (Optional)\n        style (:obj:`Style`): An optional style object.\n            If no style is provided` then a new one will be created for the widget.\n        on_select (``callable``): A function to be invoked on selecting a row of the table.\n        missing_value (``str`` or ``None``): value for replacing a missing value\n            in the data source. (Default: None). When 'None', a warning message\n            will be shown.\n        factory (:obj:`module`): A python module that is capable to return a\n            implementation of this class with the same name. (optional & normally not needed)\n\n    Examples:\n        >>> headings = ['Head 1', 'Head 2', 'Head 3']\n        >>> data = []\n        >>> table = Table(headings, data=data)\n\n        # Data can be in several forms.\n        # A list of dictionaries, where the keys match the heading names:\n        >>> data = [{'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}),\n        >>>         {'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}]\n\n        # A list of lists. These will be mapped to the headings in order:\n        >>> data = [('value 1', 'value 2', 'value3'),\n        >>>         ('value 1', 'value 2', 'value3')]\n\n        # A list of values. This is only accepted if there is a single heading.\n        >>> data = ['item 1', 'item 2', 'item 3']\n    \"\"\"\n    MIN_WIDTH = 100\n    MIN_HEIGHT = 100\n\n    def __init__(self, headings, id=None, style=None, data=None, accessors=None,\n                 multiple_select=False, on_select=None, missing_value=None,\n                 factory=None):\n        super().__init__(id=id, style=style, factory=factory)\n        self.headings = headings[:]\n        self._accessors = build_accessors(self.headings, accessors)\n        self._multiple_select = multiple_select\n        self._on_select = None\n        self._selection = None\n        self._data = None\n        if not missing_value:\n            print(\"WARNING: Using empty string for missing value in data. \"\n                  \"Define a 'missing_value' on the table to silence this message\")\n        self._missing_value = missing_value or ''\n\n        self._impl = self.factory.Table(interface=self)\n        self.data = data\n\n        self.on_select = on_select\n\n    @property\n    def data(self):\n        \"\"\" The data source of the widget. It accepts table data\n        in the form of ``list``, ``tuple``, or :obj:`ListSource`\n\n        Returns:\n            Returns a (:obj:`ListSource`).\n        \"\"\"\n        return self._data\n\n    @data.setter\n    def data(self, data):\n        if data is None:\n            self._data = ListSource(accessors=self._accessors, data=[])\n        elif isinstance(data, (list, tuple)):\n            self._data = ListSource(accessors=self._accessors, data=data)\n        else:\n            self._data = data\n\n        self._data.add_listener(self._impl)\n        self._impl.change_source(source=self._data)\n\n    @property\n    def multiple_select(self):\n        \"\"\"Does the table allow multiple rows to be selected?\"\"\"\n        return self._multiple_select\n\n    @property\n    def selection(self):\n        \"\"\"The current selection of the table.\n\n        A value of None indicates no selection.\n        If the table allows multiple selection, returns a list of\n        selected data nodes. Otherwise, returns a single data node.\n        \"\"\"\n        return self._selection\n\n    def scroll_to_top(self):\n        \"\"\"Scroll the view so that the top of the list (first row) is visible\n        \"\"\"\n        self.scroll_to_row(0)\n\n    def scroll_to_row(self, row):\n        \"\"\"Scroll the view so that the specified row index is visible.\n\n        Args:\n            row: The index of the row to make visible. Negative values refer\n                 to the nth last row (-1 is the last row, -2 second last,\n                 and so on)\n        \"\"\"\n        if row >= 0:\n            self._impl.scroll_to_row(row)\n        else:\n            self._impl.scroll_to_row(len(self.data) + row)\n\n    def scroll_to_bottom(self):\n        \"\"\"Scroll the view so that the bottom of the list (last row) is visible\n        \"\"\"\n        self.scroll_to_row(-1)\n\n    @property\n    def on_select(self):\n        \"\"\" The callback function that is invoked when a row of the table is selected.\n        The provided callback function has to accept two arguments table (``:obj:Table`)\n        and row (``int`` or ``None``).\n\n        Returns:\n            (``callable``) The callback function.\n        \"\"\"\n        return self._on_select\n\n    @on_select.setter\n    def on_select(self, handler):\n        \"\"\"\n        Set the function to be executed on node selection\n\n        :param handler: callback function\n        :type handler: ``callable``\n        \"\"\"\n        self._on_select = wrapped_handler(self, handler)\n        self._impl.set_on_select(self._on_select)\n\n    def add_column(self, heading, accessor=None):\n        \"\"\"\n        Add a new column to the table\n\n        :param heading: title of the column\n        :type heading: ``string``\n        :param accessor: accessor of this new column\n        :type heading: ``string``\n        \"\"\"\n\n        if not accessor:\n            accessor = to_accessor(heading)\n\n        if accessor in self._accessors:\n            raise ValueError('Accessor \"{}\" is already in use'.format(accessor))\n\n        self.headings.append(heading)\n        self._accessors.append(accessor)\n\n        self._impl.add_column(heading, accessor)\n\n    def remove_column(self, column):\n        \"\"\"\n        Remove a table column.\n\n        :param column: accessor or position (>0)\n        :type column: ``string``\n        :type column: ``int``\n        \"\"\"\n\n        if isinstance(column, str):\n            # Column is a string; use as-is\n            accessor = column\n        else:\n            try:\n                accessor = self._accessors[column]\n            except IndexError:\n                # Column specified as an integer, but the integer is out of range.\n                raise ValueError(\"Column {} does not exist\".format(column))\n            except TypeError:\n                # Column specified as something other than int or str\n                raise ValueError(\"Column must be an integer or string\")\n\n        try:\n            # Remove column\n            self._impl.remove_column(accessor)\n            del self.headings[self._accessors.index(accessor)]\n            self._accessors.remove(accessor)\n        except KeyError:\n            raise ValueError('Invalid column: \"{}\"'.format(column))\n\n    @property\n    def missing_value(self):\n        return self._missing_value\n", "path": "src/core/toga/widgets/table.py"}], "after_files": [{"content": "from toga.handlers import wrapped_handler\nfrom toga.sources import ListSource\nfrom toga.sources.accessors import build_accessors, to_accessor\n\nfrom .base import Widget\n\n\nclass Table(Widget):\n    \"\"\" A Table Widget allows the display of data in the form of columns and rows.\n\n    Args:\n        headings (``list`` of ``str``): The list of headings for the table.\n        id (str): An identifier for this widget.\n        data (``list`` of ``tuple``): The data to be displayed on the table.\n        accessors: A list of methods, same length as ``headings``, that describes\n            how to extract the data value for each column from the row. (Optional)\n        style (:obj:`Style`): An optional style object.\n            If no style is provided` then a new one will be created for the widget.\n        on_select (``callable``): A function to be invoked on selecting a row of the table.\n        missing_value (``str`` or ``None``): value for replacing a missing value\n            in the data source. (Default: None). When 'None', a warning message\n            will be shown.\n        factory (:obj:`module`): A python module that is capable to return a\n            implementation of this class with the same name. (optional & normally not needed)\n\n    Examples:\n        >>> headings = ['Head 1', 'Head 2', 'Head 3']\n        >>> data = []\n        >>> table = Table(headings, data=data)\n\n        # Data can be in several forms.\n        # A list of dictionaries, where the keys match the heading names:\n        >>> data = [{'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}),\n        >>>         {'head_1': 'value 1', 'head_2': 'value 2', 'head_3': 'value3'}]\n\n        # A list of lists. These will be mapped to the headings in order:\n        >>> data = [('value 1', 'value 2', 'value3'),\n        >>>         ('value 1', 'value 2', 'value3')]\n\n        # A list of values. This is only accepted if there is a single heading.\n        >>> data = ['item 1', 'item 2', 'item 3']\n    \"\"\"\n    MIN_WIDTH = 100\n    MIN_HEIGHT = 100\n\n    def __init__(self, headings, id=None, style=None, data=None, accessors=None,\n                 multiple_select=False, on_select=None, missing_value=None,\n                 factory=None):\n        super().__init__(id=id, style=style, factory=factory)\n        self.headings = headings[:]\n        self._accessors = build_accessors(self.headings, accessors)\n        self._multiple_select = multiple_select\n        self._on_select = None\n        self._selection = None\n        self._data = None\n        if missing_value is None:\n            print(\"WARNING: Using empty string for missing value in data. \"\n                  \"Define a 'missing_value' on the table to silence this message\")\n        self._missing_value = missing_value or ''\n\n        self._impl = self.factory.Table(interface=self)\n        self.data = data\n\n        self.on_select = on_select\n\n    @property\n    def data(self):\n        \"\"\" The data source of the widget. It accepts table data\n        in the form of ``list``, ``tuple``, or :obj:`ListSource`\n\n        Returns:\n            Returns a (:obj:`ListSource`).\n        \"\"\"\n        return self._data\n\n    @data.setter\n    def data(self, data):\n        if data is None:\n            self._data = ListSource(accessors=self._accessors, data=[])\n        elif isinstance(data, (list, tuple)):\n            self._data = ListSource(accessors=self._accessors, data=data)\n        else:\n            self._data = data\n\n        self._data.add_listener(self._impl)\n        self._impl.change_source(source=self._data)\n\n    @property\n    def multiple_select(self):\n        \"\"\"Does the table allow multiple rows to be selected?\"\"\"\n        return self._multiple_select\n\n    @property\n    def selection(self):\n        \"\"\"The current selection of the table.\n\n        A value of None indicates no selection.\n        If the table allows multiple selection, returns a list of\n        selected data nodes. Otherwise, returns a single data node.\n        \"\"\"\n        return self._selection\n\n    def scroll_to_top(self):\n        \"\"\"Scroll the view so that the top of the list (first row) is visible\n        \"\"\"\n        self.scroll_to_row(0)\n\n    def scroll_to_row(self, row):\n        \"\"\"Scroll the view so that the specified row index is visible.\n\n        Args:\n            row: The index of the row to make visible. Negative values refer\n                 to the nth last row (-1 is the last row, -2 second last,\n                 and so on)\n        \"\"\"\n        if row >= 0:\n            self._impl.scroll_to_row(row)\n        else:\n            self._impl.scroll_to_row(len(self.data) + row)\n\n    def scroll_to_bottom(self):\n        \"\"\"Scroll the view so that the bottom of the list (last row) is visible\n        \"\"\"\n        self.scroll_to_row(-1)\n\n    @property\n    def on_select(self):\n        \"\"\" The callback function that is invoked when a row of the table is selected.\n        The provided callback function has to accept two arguments table (``:obj:Table`)\n        and row (``int`` or ``None``).\n\n        Returns:\n            (``callable``) The callback function.\n        \"\"\"\n        return self._on_select\n\n    @on_select.setter\n    def on_select(self, handler):\n        \"\"\"\n        Set the function to be executed on node selection\n\n        :param handler: callback function\n        :type handler: ``callable``\n        \"\"\"\n        self._on_select = wrapped_handler(self, handler)\n        self._impl.set_on_select(self._on_select)\n\n    def add_column(self, heading, accessor=None):\n        \"\"\"\n        Add a new column to the table\n\n        :param heading: title of the column\n        :type heading: ``string``\n        :param accessor: accessor of this new column\n        :type heading: ``string``\n        \"\"\"\n\n        if not accessor:\n            accessor = to_accessor(heading)\n\n        if accessor in self._accessors:\n            raise ValueError('Accessor \"{}\" is already in use'.format(accessor))\n\n        self.headings.append(heading)\n        self._accessors.append(accessor)\n\n        self._impl.add_column(heading, accessor)\n\n    def remove_column(self, column):\n        \"\"\"\n        Remove a table column.\n\n        :param column: accessor or position (>0)\n        :type column: ``string``\n        :type column: ``int``\n        \"\"\"\n\n        if isinstance(column, str):\n            # Column is a string; use as-is\n            accessor = column\n        else:\n            try:\n                accessor = self._accessors[column]\n            except IndexError:\n                # Column specified as an integer, but the integer is out of range.\n                raise ValueError(\"Column {} does not exist\".format(column))\n            except TypeError:\n                # Column specified as something other than int or str\n                raise ValueError(\"Column must be an integer or string\")\n\n        try:\n            # Remove column\n            self._impl.remove_column(accessor)\n            del self.headings[self._accessors.index(accessor)]\n            self._accessors.remove(accessor)\n        except KeyError:\n            raise ValueError('Invalid column: \"{}\"'.format(column))\n\n    @property\n    def missing_value(self):\n        return self._missing_value\n", "path": "src/core/toga/widgets/table.py"}]}