Repository: twisted/towncrier

Issue:
Unfriendly error when the version can't be determined
Given this setup:

```console
$ ls
changes  towncrier.toml
$ cat towncrier.toml
[tool.towncrier]
directory = "changes"
```

running `towncrier build` yields this error message:

```
Loading template...
Finding news fragments...
Rendering news fragments...
Traceback (most recent call last):
  File "/home/dpb/.local/bin/towncrier", line 8, in <module>
    sys.exit(cli())
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/towncrier/build.py", line 89, in _main
    return __main(
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/towncrier/build.py", line 147, in __main
    project_version = get_version(
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/towncrier/_project.py", line 40, in get_version
    module = _get_package(package_dir, package)
  File "/home/dpb/.local/pipx/venvs/towncrier/lib/python3.10/site-packages/towncrier/_project.py", line 19, in _get_package
    module = import_module(package)
  File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1047, in _gcd_import
  File "<frozen importlib._bootstrap>", line 981, in _sanity_check
ValueError: Empty module name
```

This in no way describes the actual problem, which is that towncrier couldn't determine the project version. It would be nice if it could display a friendlier message, e.g. "Unable to determine the project version. Use the --version option."

Discussion:

Patch:
diff --git a/src/towncrier/build.py b/src/towncrier/build.py
index bb01644..ef7c5f3 100644
--- a/src/towncrier/build.py
+++ b/src/towncrier/build.py
@@ -15,7 +15,7 @@ from datetime import date
 
 import click
 
-from click import Context, Option
+from click import Context, Option, UsageError
 
 from towncrier import _git
 
@@ -151,6 +151,18 @@ def __main(
     base_directory, config = load_config_from_options(directory, config_file)
     to_err = draft
 
+    if project_version is None:
+        project_version = config.version
+    if project_version is None:
+        if not config.package:
+            raise UsageError(
+                "'--version' is required since the config file does "
+                "not contain 'version' or 'package'."
+            )
+        project_version = get_version(
+            os.path.join(base_directory, config.package_dir), config.package
+        ).strip()
+
     click.echo("Loading template...", err=to_err)
     if isinstance(config.template, tuple):
         template = resources.read_text(*config.template)
@@ -182,13 +194,6 @@ def __main(
         fragment_contents, config.types, all_bullets=config.all_bullets
     )
 
-    if project_version is None:
-        project_version = config.version
-        if project_version is None:
-            project_version = get_version(
-                os.path.join(base_directory, config.package_dir), config.package
-            ).strip()
-
     if project_name is None:
         project_name = config.name
         if not project_name:
diff --git a/src/towncrier/newsfragments/507.misc b/src/towncrier/newsfragments/507.misc
new file mode 100644
index 0000000..b8f0478
--- /dev/null
+++ b/src/towncrier/newsfragments/507.misc
@@ -0,0 +1,1 @@
+A friendly message is now provided, when it's necessary to pass the ``--version`` option explicitly.


Test patch:
diff --git a/src/towncrier/test/test_build.py b/src/towncrier/test/test_build.py
index 4dbc07c..d456126 100644
--- a/src/towncrier/test/test_build.py
+++ b/src/towncrier/test/test_build.py
@@ -511,6 +511,19 @@ class TestCli(TestCase):
         self.assertEqual(1, result.exit_code, result.output)
         self.assertTrue(result.output.startswith("No configuration file found."))
 
+    @with_isolated_runner
+    def test_needs_version(self, runner: CliRunner):
+        """
+        If the configuration file doesn't specify a version or a package, the version
+        option is required.
+        """
+        write("towncrier.toml", "[tool.towncrier]")
+
+        result = runner.invoke(_main, ["--draft"], catch_exceptions=False)
+
+        self.assertEqual(2, result.exit_code)
+        self.assertIn("Error: '--version' is required", result.output)
+
     def test_projectless_changelog(self):
         """In which a directory containing news files is built into a changelog