Repository: beeware/toga

Issue:
False positive "Multiple candidiate toga backends"
**Describe the bug**
When using a development version of *toga*, a `RuntimeError` of multiple candidate toga backends is raised with the same backend. Stacktrace example:

```
Traceback (most recent call last):
  File "C:\Users\sagi\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Users\sagi\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Users\sagi\PycharmProjects\toga\examples\canvas\canvas\__main__.py", line 4, in <module>
    main().main_loop()
  File "C:\Users\sagi\PycharmProjects\toga\examples\canvas\canvas\app.py", line 565, in main
    return ExampleCanvasApp('Canvas', 'org.beeware.widgets.canvas')
  File "C:\Users\sagi\PycharmProjects\toga\src\core\src\toga\app.py", line 308, in __init__
    self.factory = get_platform_factory()
  File "C:\Users\sagi\PycharmProjects\toga\src\core\src\toga\platform.py", line 106, in get_platform_factory
    raise RuntimeError(
RuntimeError: Multiple candidiate toga backends found: ('toga_winforms' (windows), 'toga_winforms' (windows)). Uninstall the backends you don't require, or use TOGA_BACKEND to specify a backend.
```

**To Reproduce**
Steps to reproduce the behavior:
1. Open a new virtual environment
2. Go to src\core
3. Run `pip install -e .`
4. Go to src\winforms
5. Run `pip install -e .`
6. Go to example\canvas (or any other example application)
7. Run `python -m canvas`

**Expected behavior**
`RuntimeError` should not be raised in that case.

**Environment:**
 - Operating System: Windows
 - Python version: 3.10.6
 - Software versions:
   - Briefcase: latest
   - Toga: latest



Discussion:
Darn... I'm seeing this on a fresh install too. If you manually delete the .egg-info file in the backend folder, or delete the pyproject.toml file, the problem goes away. Not sure what the permanent fix is.

Resolving patch:
diff --git a/src/core/src/toga/platform.py b/src/core/src/toga/platform.py
--- a/src/core/src/toga/platform.py
+++ b/src/core/src/toga/platform.py
@@ -67,7 +67,7 @@ def get_platform_factory(factory=None):
 
     toga_backends = entry_points(group='toga.backends')
     if not toga_backends:
-        raise RuntimeError("No toga backend could be loaded.")
+        raise RuntimeError("No Toga backend could be loaded.")
 
     backend_value = os.environ.get('TOGA_BACKEND')
     if backend_value:
@@ -80,8 +80,14 @@ def get_platform_factory(factory=None):
                 f"could not be loaded. It should be one of: {toga_backends_values}."
             )
     else:
+        # As of Setuptools 65.5, entry points are returned duplicated if the
+        # package is installed editable. Use a set to ensure that each entry point
+        # is only returned once.
+        # See https://github.com/pypa/setuptools/issues/3649
+        toga_backends = sorted(set(toga_backends))
+
         if len(toga_backends) == 1:
-            backend = list(toga_backends)[0]
+            backend = toga_backends[0]
         else:
             # multiple backends are installed: choose the one that matches the host platform
             matching_backends = [


Test patch: