Repository: pypa/setuptools

Issue:
setup.cfg: entry_points keys are made lowercase
This breaks when the entry point is actually case-sensitive.

1. `git clone https://github.com/pydoit/doit-plugin-sample`
2. Delete setup.py, use the following setup.cfg:
```text
[metadata]
name = doit-plugin-sample
description = a simple doit command plugin

[options]
py_modules = 
    doit_sample_cmd
install_requires = 
    doit

[options.entry_points]
doit.COMMAND =
    plug_sample = doit_sample_cmd:SampleCmd
```
3. Use this pyproject.toml:
```text
[build-system]
requires = ["setuptools>=30.3.0", "wheel"]
```
4. `pip install .` inside a venv.

Inspecting `venv/*/site-packages/doit_plugin_sample-0.0.0.dist-info/entry_points.txt`:
```text
[doit.command]
plug_sample = doit_sample_cmd:SampleCmd
```

This will not work, as the entry point is `doit.COMMAND`.
setup.cfg: entry_points keys are made lowercase
This breaks when the entry point is actually case-sensitive.

1. `git clone https://github.com/pydoit/doit-plugin-sample`
2. Delete setup.py, use the following setup.cfg:
```text
[metadata]
name = doit-plugin-sample
description = a simple doit command plugin

[options]
py_modules = 
    doit_sample_cmd
install_requires = 
    doit

[options.entry_points]
doit.COMMAND =
    plug_sample = doit_sample_cmd:SampleCmd
```
3. Use this pyproject.toml:
```text
[build-system]
requires = ["setuptools>=30.3.0", "wheel"]
```
4. `pip install .` inside a venv.

Inspecting `venv/*/site-packages/doit_plugin_sample-0.0.0.dist-info/entry_points.txt`:
```text
[doit.command]
plug_sample = doit_sample_cmd:SampleCmd
```

This will not work, as the entry point is `doit.COMMAND`.


Discussion:
This does look like a bug. `setup.cfg` should avoid changing the case of entry point names.

I'll start by replicating your finding. I'll fork [the project](https://github.com/pydoit/doit-plugin-sample) and add the prescribed changes to the fork and then determine how it doesn't work (how it fails).
I created the repro as [jaraco/doit-plugin-sample@setuptools/1937](https://github.com/jaraco/doit-plugin-sample/tree/setuptools/1937).

<details>

```
draft $ git clone gh://pydoit/doit-plugin-sample
Cloning into 'doit-plugin-sample'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 1), reused 7 (delta 1), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.
draft $ cd doit-plugin-sample/
doit-plugin-sample master $ hub fork
Updating jaraco
From https://github.com/pydoit/doit-plugin-sample
 * [new branch]      master     -> jaraco/master
new remote: jaraco
doit-plugin-sample master $ git checkout -b setuptools/1937
Switched to a new branch 'setuptools/1937'
doit-plugin-sample setuptools/1937 $ git rm setup.py
rm 'setup.py'
doit-plugin-sample setuptools/1937 $ cat > setup.cfg
[metadata]
name = doit-plugin-sample
description = a simple doit command plugin

[options]
py_modules = 
    doit_sample_cmd
install_requires = 
    doit

[options.entry_points]
doit.COMMAND =
    plug_sample = doit_sample_cmd:SampleCmd
doit-plugin-sample setuptools/1937 $ cat > pyproject.toml
[build-system]
requires = ["setuptools>=30.3.0", "wheel"]
doit-plugin-sample setuptools/1937 $ git add .
doit-plugin-sample setuptools/1937 $ git commit -a -m "Alter project as described in pypa/setuptools#1937"
[setuptools/1937 6a47e86] Alter project as described in pypa/setuptools#1937
 2 files changed, 15 insertions(+), 17 deletions(-)
 create mode 100644 pyproject.toml
 rewrite setup.py (99%)
 mode change 100755 => 100644
doit-plugin-sample setuptools/1937 $ git push jaraco
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 605 bytes | 605.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
remote: 
remote: Create a pull request for 'setuptools/1937' on GitHub by visiting:
remote:      https://github.com/jaraco/doit-plugin-sample/pull/new/setuptools/1937
remote: 
To github.com:jaraco/doit-plugin-sample.git
 * [new branch]      setuptools/1937 -> setuptools/1937
```

</details>

With that, I'm able to demonstrate how the doit plugin works when running under the HEAD of the repo (7f56bc5):

```
draft $ touch dodo.py
draft $ pip-run -q --use-pep517 doit git+https://github.com/pydoit/doit-plugin-sample@7f56bc5 setuptools -- -m doit plug_sample
  WARNING: Did not find branch or tag '7f56bc5', assuming revision or ref.
This sample command does nothing!
```

And fails when running under the fork version:

```
draft $ pip-run -q --use-pep517 doit git+https://github.com/jaraco/doit-plugin-sample@setuptools/1937 setuptools -- -m doit plug_sample
ERROR: Invalid parameter: "plug_sample". Must be a command, task, or a target.
Type "-m help" to see available commands.
Type "-m list" to see available tasks.
```
Note, I'm using [pip-run](https://pypi.org/project/pip-run) to quickly facilitate swapping out different versions of `doit-plugin-sample`. I have to use `--use-pep517` doit or its dependencies have an implicit dependency on setuptools. And I have to include `setuptools` because doit has an undeclared dependency on setuptools and [fails silently](https://github.com/pydoit/doit/blob/d1a1b7b3abc7641d977d3b78b580d97aea4e27ea/doit/plugin.py#L72) if it's not present.
The problem can be more easily elicited with just the plugin by trying to load the plugin directly:

```
draft $ pip-run -q setuptools git+https://github.com/jaraco/doit-plugin-sample@setuptools/1937 -- -c "import pkg_resources; ep, = pkg_resources.iter_entry_points('doit.COMMAND'); print(ep)"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: not enough values to unpack (expected 1, got 0)
```
I believe it would be best if Setuptools could honor the case of the entry points as indicated in `setup.cfg`.
I'll take this on :)
This does look like a bug. `setup.cfg` should avoid changing the case of entry point names.

I'll start by replicating your finding. I'll fork [the project](https://github.com/pydoit/doit-plugin-sample) and add the prescribed changes to the fork and then determine how it doesn't work (how it fails).
I created the repro as [jaraco/doit-plugin-sample@setuptools/1937](https://github.com/jaraco/doit-plugin-sample/tree/setuptools/1937).

<details>

```
draft $ git clone gh://pydoit/doit-plugin-sample
Cloning into 'doit-plugin-sample'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 1), reused 7 (delta 1), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.
draft $ cd doit-plugin-sample/
doit-plugin-sample master $ hub fork
Updating jaraco
From https://github.com/pydoit/doit-plugin-sample
 * [new branch]      master     -> jaraco/master
new remote: jaraco
doit-plugin-sample master $ git checkout -b setuptools/1937
Switched to a new branch 'setuptools/1937'
doit-plugin-sample setuptools/1937 $ git rm setup.py
rm 'setup.py'
doit-plugin-sample setuptools/1937 $ cat > setup.cfg
[metadata]
name = doit-plugin-sample
description = a simple doit command plugin

[options]
py_modules = 
    doit_sample_cmd
install_requires = 
    doit

[options.entry_points]
doit.COMMAND =
    plug_sample = doit_sample_cmd:SampleCmd
doit-plugin-sample setuptools/1937 $ cat > pyproject.toml
[build-system]
requires = ["setuptools>=30.3.0", "wheel"]
doit-plugin-sample setuptools/1937 $ git add .
doit-plugin-sample setuptools/1937 $ git commit -a -m "Alter project as described in pypa/setuptools#1937"
[setuptools/1937 6a47e86] Alter project as described in pypa/setuptools#1937
 2 files changed, 15 insertions(+), 17 deletions(-)
 create mode 100644 pyproject.toml
 rewrite setup.py (99%)
 mode change 100755 => 100644
doit-plugin-sample setuptools/1937 $ git push jaraco
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 605 bytes | 605.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
remote: 
remote: Create a pull request for 'setuptools/1937' on GitHub by visiting:
remote:      https://github.com/jaraco/doit-plugin-sample/pull/new/setuptools/1937
remote: 
To github.com:jaraco/doit-plugin-sample.git
 * [new branch]      setuptools/1937 -> setuptools/1937
```

</details>

With that, I'm able to demonstrate how the doit plugin works when running under the HEAD of the repo (7f56bc5):

```
draft $ touch dodo.py
draft $ pip-run -q --use-pep517 doit git+https://github.com/pydoit/doit-plugin-sample@7f56bc5 setuptools -- -m doit plug_sample
  WARNING: Did not find branch or tag '7f56bc5', assuming revision or ref.
This sample command does nothing!
```

And fails when running under the fork version:

```
draft $ pip-run -q --use-pep517 doit git+https://github.com/jaraco/doit-plugin-sample@setuptools/1937 setuptools -- -m doit plug_sample
ERROR: Invalid parameter: "plug_sample". Must be a command, task, or a target.
Type "-m help" to see available commands.
Type "-m list" to see available tasks.
```
Note, I'm using [pip-run](https://pypi.org/project/pip-run) to quickly facilitate swapping out different versions of `doit-plugin-sample`. I have to use `--use-pep517` doit or its dependencies have an implicit dependency on setuptools. And I have to include `setuptools` because doit has an undeclared dependency on setuptools and [fails silently](https://github.com/pydoit/doit/blob/d1a1b7b3abc7641d977d3b78b580d97aea4e27ea/doit/plugin.py#L72) if it's not present.
The problem can be more easily elicited with just the plugin by trying to load the plugin directly:

```
draft $ pip-run -q setuptools git+https://github.com/jaraco/doit-plugin-sample@setuptools/1937 -- -c "import pkg_resources; ep, = pkg_resources.iter_entry_points('doit.COMMAND'); print(ep)"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ValueError: not enough values to unpack (expected 1, got 0)
```
I believe it would be best if Setuptools could honor the case of the entry points as indicated in `setup.cfg`.
I'll take this on :)

Resolving patch:
diff --git a/setuptools/dist.py b/setuptools/dist.py
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -583,6 +583,7 @@ def _parse_config_files(self, filenames=None):  # noqa: C901
             self.announce("Distribution.parse_config_files():")
 
         parser = ConfigParser()
+        parser.optionxform = str
         for filename in filenames:
             with io.open(filename, encoding='utf-8') as reader:
                 if DEBUG:


Test patch:
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -802,6 +802,24 @@ def test_entry_points(self, tmpdir):
         with get_dist(tmpdir) as dist:
             assert dist.entry_points == expected
 
+    def test_case_sensitive_entry_points(self, tmpdir):
+        _, config = fake_env(
+            tmpdir,
+            '[options.entry_points]\n'
+            'GROUP1 = point1 = pack.module:func, '
+            '.point2 = pack.module2:func_rest [rest]\n'
+            'group2 = point3 = pack.module:func2\n'
+        )
+
+        with get_dist(tmpdir) as dist:
+            assert dist.entry_points == {
+                'GROUP1': [
+                    'point1 = pack.module:func',
+                    '.point2 = pack.module2:func_rest [rest]',
+                ],
+                'group2': ['point3 = pack.module:func2']
+            }
+
     def test_data_files(self, tmpdir):
         fake_env(
             tmpdir,