Repository: twisted/towncrier

Issue:
Issue number incorrectly inferred when hash is all digits
I've been using towncrier with [mostly default configuration](https://github.com/jaraco/skeleton/blob/6ff02e0eefcd90e271cefd326b460ecfa0e3eb9e/towncrier.toml) to generate news entries. I'll sometimes give a filename with an issue number (`{issue}.{scope}.rst`) and other times use `+.{scope}.rst`, and that works well. When I supply an issue number, it's included in the changelog and when I don't, it's omitted.

However, today when using towncrier 23.11, my expectation was violated when the generated hash was all digits:

```
 backports.tarfile main @ towncrier create -c "Initial release based on tarfile from Python 3.12.2." +.removal.rst
Created news fragment at /Users/jaraco/code/jaraco/backports.tarfile/newsfragments/+96333363.removal.rst
 backports.tarfile main @ git add newsfragments; git commit -a -m "Add news fragment."
[main ec51166] Add news fragment.
 1 file changed, 1 insertion(+)
 create mode 100644 newsfragments/+96333363.removal.rst
 backports.tarfile main @ tox -e finalize
finalize: install_deps> python -I -m pip install 'jaraco.develop>=7.23' towncrier
finalize: commands[0]> python -m jaraco.develop.finalize
Loading template...
/Users/jaraco/code/jaraco/backports.tarfile/.tox/finalize/lib/python3.12/site-packages/towncrier/build.py:169: EncodingWarning: 'encoding' argument not specified
  resources.files(config.template[0]).joinpath(config.template[1]).read_text()
Finding news fragments...
Rendering news fragments...
Writing to newsfile...
Staging newsfile...
Removing the following files:
/Users/jaraco/code/jaraco/backports.tarfile/newsfragments/+96333363.removal.rst
Removing news fragments...
Done!
[main 786b22e] Finalize
 2 files changed, 7 insertions(+), 1 deletion(-)
 delete mode 100644 newsfragments/+96333363.removal.rst
  finalize: OK (5.36=setup[4.58]+cmd[0.78] seconds)
  congratulations :) (5.42 seconds)
 backports.tarfile main @ head NEWS.rst
v1.0.0
======

Deprecations and Removals
-------------------------

- Initial release based on tarfile from Python 3.12.2. (#96333363)

```

It seems that if the hex hash happens not to have any digits greater than 9, the hash is inferred to be an issue number. That feels like a bug, since the same behavior would not produce an issue number if there were hex digits greater than 9. Perhaps towncrier should provide a way to distinguish between a hash and a typical issue number.

Discussion:
SmileyChris: Looking closer the true source of the error is because `strip_if_integer_string` reads the orphan of `+12345` as an actual integer, stripping off the '+', causing this issue to lose its status as an orphan!
SmileyChris: There, that's tidier!
adiroiban: It looks good. I have enabled auto-merge.
I am happy to review and approve a PR that add more tests... but that should not be a blocker for this PR.

Patch:
diff --git a/src/towncrier/_builder.py b/src/towncrier/_builder.py
index 3d86354..a72a398 100644
--- a/src/towncrier/_builder.py
+++ b/src/towncrier/_builder.py
@@ -13,15 +13,6 @@ from typing import Any, DefaultDict, Iterable, Iterator, Mapping, Sequence
 from jinja2 import Template
 
 
-def strip_if_integer_string(s: str) -> str:
-    try:
-        i = int(s)
-    except ValueError:
-        return s
-
-    return str(i)
-
-
 # Returns ticket, category and counter or (None, None, None) if the basename
 # could not be parsed or doesn't contain a valid category.
 def parse_newfragment_basename(
@@ -45,7 +36,11 @@ def parse_newfragment_basename(
             # NOTE: This allows news fragment names like fix-1.2.3.feature or
             # something-cool.feature.ext for projects that don't use ticket
             # numbers in news fragment names.
-            ticket = strip_if_integer_string(".".join(parts[0:i]))
+            ticket = ".".join(parts[0:i]).strip()
+            # If the ticket is an integer, remove any leading zeros (to resolve
+            # issue #126).
+            if ticket.isdigit():
+                ticket = str(int(ticket))
             counter = 0
             # Use the following part as the counter if it exists and is a valid
             # digit.
diff --git a/src/towncrier/newsfragments/588.bugfix b/src/towncrier/newsfragments/588.bugfix
new file mode 100644
index 0000000..f1c3e8e
--- /dev/null
+++ b/src/towncrier/newsfragments/588.bugfix
@@ -0,0 +1,1 @@
+Orphan news fragments, fragments not associated with an issue, consisting of only digits (e.g. '+12345678.feature') now retain their leading marker character.


Test patch:
diff --git a/src/towncrier/test/test_builder.py b/src/towncrier/test/test_builder.py
index b62033a..9608e0a 100644
--- a/src/towncrier/test/test_builder.py
+++ b/src/towncrier/test/test_builder.py
@@ -125,3 +125,10 @@ class TestParseNewsfragmentBasename(TestCase):
             parse_newfragment_basename("+orphan_12.3.feature", ["feature"]),
             ("+orphan_12.3", "feature", 0),
         )
+
+    def test_orphan_all_digits(self):
+        """Orphaned snippets can consist of only digits."""
+        self.assertEqual(
+            parse_newfragment_basename("+123.feature", ["feature"]),
+            ("+123", "feature", 0),
+        )