Repository: asottile/setup-cfg-fmt

Issue:
fix: add option for defining long_description
Hello!
Thanks for this awesome project and you determination to open source.

I use Asciidoctor for documentation.

Because PyPI does not support rendering this format, I made a CI workflow to convert my README.adoc to a README.md before building the python wheels and uploading to PyPI etc (unrelevant to this ticket, just a info).

The current alphabetical-sort determination made by this hook ranks `.adoc` (with content_type of `text/plain`) as first, 
which results in PyPI showing `The author of this package has not provided a project description` as it does not support Asciidoctor.

I tried to do a workaround by renaming the markdown file to `README-.md` for it to rank first but then python/pypi does not like it:

```
...(README-.md file is not being copied)...

/tmp/build-env-cbglbinp/lib/python3.8/site-packages/setuptools/config/expand.py:142: UserWarning: File '/tmp/build-via-sdist-wi26p40b/test_application-0.3.6/README-.md' cannot be found
  warnings.warn(f"File {path!r} cannot be found")

warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
```
I then tried to do a workaround by renaming the markdown file to `README`, but then this hook determines it as `text/plain`.
I reverted to commenting this pre-commit hook out for now.


I'd like to add an option for defining the filename for `long_description` like this:

```yaml
  - repo: https://github.com/asottile/setup-cfg-fmt
    rev: v1.20.2
    hooks:
      - id: setup-cfg-fmt
        long_description: README.md
```

I'd love to make my first PR for this!

Discussion:
JonasPammer: Sorry, looking at it myself now i too see how fucking badly this excessive cc cluttered this small thing, thanks for pointing it out. Also thanks for reviewing at my code with helpful comments nontheless. Will take a good look at it tommorow!
JonasPammer: i really messed up my first pr with many commits instead of quality code, but it should be good now after having breathed in at the surrounding code and the given tips/opinions ;)

it should be good now except for the thing in my conversation i started

Patch:
diff --git a/README.md b/README.md
index f6c2f8c..cd5ecc3 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ setuptools allows dashed names but does not document them.
 +long_description = file: README.md
 ```
 
-### adds `long_description` if `README.md` is present
+### adds `long_description` if `README` is present
 
 This will show up on the pypi project page
 
diff --git a/setup_cfg_fmt.py b/setup_cfg_fmt.py
index afd3a45..299d670 100644
--- a/setup_cfg_fmt.py
+++ b/setup_cfg_fmt.py
@@ -103,7 +103,13 @@ def _case_insensitive_glob(s: str) -> str:
 def _first_file(setup_cfg: str, prefix: str) -> str | None:
     prefix = _case_insensitive_glob(prefix)
     path = _adjacent_filename(setup_cfg, prefix)
-    for filename in sorted(glob.iglob(f'{path}*')):
+
+    # prefer non-asciidoc because pypi does not render it
+    # https://github.com/asottile/setup-cfg-fmt/issues/149
+    def sort_key(filename: str) -> tuple[bool, str]:
+        return (filename.endswith(('.adoc', '.asciidoc')), filename)
+
+    for filename in sorted(glob.iglob(f'{path}*'), key=sort_key):
         if os.path.isfile(filename):
             return filename
     else:
@@ -369,7 +375,7 @@ def format_file(
     # normalize names to underscores so sdist / wheel have the same prefix
     cfg['metadata']['name'] = cfg['metadata']['name'].replace('-', '_')
 
-    # if README.md exists, set `long_description` + content type
+    # if README exists, set `long_description` + content type
     readme = _first_file(filename, 'readme')
     if readme is not None:
         long_description = f'file: {os.path.basename(readme)}'


Test patch:
diff --git a/tests/setup_cfg_fmt_test.py b/tests/setup_cfg_fmt_test.py
index b3372a7..4527f1b 100644
--- a/tests/setup_cfg_fmt_test.py
+++ b/tests/setup_cfg_fmt_test.py
@@ -296,6 +296,68 @@ def test_adds_long_description_with_readme(filename, content_type, tmpdir):
     )
 
 
+@pytest.mark.parametrize(
+    ('filename', 'content_type'),
+    (
+        ('README.rst', 'text/x-rst'),
+        ('README.markdown', 'text/markdown'),
+        ('README.md', 'text/markdown'),
+        ('README', 'text/plain'),
+        ('README.txt', 'text/plain'),
+        ('readme.txt', 'text/plain'),
+    ),
+)
+def test_readme_discover_does_not_prefer_adoc(filename, content_type, tmpdir):
+    tmpdir.join(filename).write('my project!')
+    tmpdir.join('README.adoc').write('my project!')
+    tmpdir.join('README.asciidoc').write('my project!')
+
+    setup_cfg = tmpdir.join('setup.cfg')
+    setup_cfg.write(
+        '[metadata]\n'
+        'name = pkg\n'
+        'version = 1.0\n',
+    )
+
+    assert main((str(setup_cfg),))
+
+    assert setup_cfg.read() == (
+        f'[metadata]\n'
+        f'name = pkg\n'
+        f'version = 1.0\n'
+        f'long_description = file: {filename}\n'
+        f'long_description_content_type = {content_type}\n'
+    )
+
+
+@pytest.mark.parametrize(
+    'filename',
+    (
+        'README.adoc',
+        'README.asciidoc',
+    ),
+)
+def test_readme_discover_uses_asciidoc_if_none_other_found(filename, tmpdir):
+    tmpdir.join(filename).write('my project!')
+
+    setup_cfg = tmpdir.join('setup.cfg')
+    setup_cfg.write(
+        '[metadata]\n'
+        'name = pkg\n'
+        'version = 1.0\n',
+    )
+
+    assert main((str(setup_cfg),))
+
+    assert setup_cfg.read() == (
+        f'[metadata]\n'
+        f'name = pkg\n'
+        f'version = 1.0\n'
+        f'long_description = file: {filename}\n'
+        f'long_description_content_type = text/plain\n'
+    )
+
+
 def test_readme_discover_prefers_file_over_directory(tmpdir):
     tmpdir.join('README').mkdir()
     tmpdir.join('README.md').write('my project!')