Skip to content

Commit

Permalink
Improve: Use distribution package names in version strings (#462)
Browse files Browse the repository at this point in the history
  • Loading branch information
hukkin authored Oct 24, 2024
1 parent 9ed01b7 commit 8ddd9b0
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 20 deletions.
54 changes: 35 additions & 19 deletions src/mdformat/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import argparse
from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
import contextlib
import inspect
import itertools
import logging
import os.path
Expand Down Expand Up @@ -303,35 +304,50 @@ def log_handler_applied(
logger.removeHandler(handler)


def get_package_name(obj: object) -> str:
# Packages and modules should have `__package__`
if hasattr(obj, "__package__"):
package_name = obj.__package__
else: # class or function
module_name = obj.__module__
package_name = module_name.split(".", maxsplit=1)[0]
return package_name
def get_package_name(obj: object) -> str | None:
"""Return top level module name, or None if not found."""
module = inspect.getmodule(obj)
return module.__name__.split(".", maxsplit=1)[0] if module else None


def get_plugin_versions(
parser_extensions: Mapping[str, mdformat.plugins.ParserExtensionInterface],
codeformatters: Mapping[str, Callable[[str, str], str]],
) -> dict[str, str]:
versions = {}
) -> list[tuple[str, str]]:
"""Return a list of (plugin_distro, plugin_version) tuples.
If many plugins come from the same distribution package, only return
the version of that distribution once. If we have no reliable way to
one-to-one map a plugin to a distribution package, use the top level
module name and set version to "unknown". If we don't even know the
top level module name, return the tuple ("unknown", "unknown").
"""
problematic_versions = []
# Use a dict for successful version lookups so that if more than one plugin
# originates from the same distribution, it only shows up once in a version
# string.
success_versions = {}
import_package_to_distro = importlib_metadata.packages_distributions()
for iface in itertools.chain(parser_extensions.values(), codeformatters.values()):
package_name = get_package_name(iface)
try:
package_version = importlib_metadata.version(package_name)
except importlib_metadata.PackageNotFoundError:
# In test scenarios the package may not exist
package_version = "unknown"
versions[package_name] = package_version
return versions
import_package = get_package_name(iface)
if import_package is None:
problematic_versions.append(("unknown", "unknown"))
continue
distro_list = import_package_to_distro.get(import_package)
if (
not distro_list # No distribution package found
or len(distro_list) > 1 # Don't make any guesses with namespace packages
):
problematic_versions.append((import_package, "unknown"))
continue
distro_name = distro_list[0]
success_versions[distro_name] = importlib_metadata.version(distro_name)
return [(k, v) for k, v in success_versions.items()] + problematic_versions


def get_plugin_versions_str(
parser_extensions: Mapping[str, mdformat.plugins.ParserExtensionInterface],
codeformatters: Mapping[str, Callable[[str, str], str]],
) -> str:
plugin_versions = get_plugin_versions(parser_extensions, codeformatters)
return ", ".join(f"{name}: {version}" for name, version in plugin_versions.items())
return ", ".join(f"{name}: {version}" for name, version in plugin_versions)
14 changes: 13 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest

import mdformat
from mdformat._cli import get_package_name, run, wrap_paragraphs
from mdformat._cli import get_package_name, get_plugin_versions, run, wrap_paragraphs
from mdformat.plugins import CODEFORMATTERS

UNFORMATTED_MARKDOWN = "\n\n# A header\n\n"
Expand Down Expand Up @@ -350,6 +350,18 @@ def test_get_package_name():
assert get_package_name(mdformat) == "mdformat"


def test_get_plugin_versions():
# Pretend that "pytest" and "unittest.mock.patch" are plugins
versions = get_plugin_versions({"p1": pytest}, {"f1": patch}) # type: ignore[dict-item] # noqa: E501
assert versions[0][0] == "pytest"
assert versions[0][1] != "unknown"
assert versions[1] == ("unittest", "unknown")

with patch("mdformat._cli.inspect.getmodule", return_value=None):
versions = get_plugin_versions({"p1": pytest}, {}) # type: ignore[dict-item]
assert versions[0] == ("unknown", "unknown")


def test_no_timestamp_modify(tmp_path):
file_path = tmp_path / "test.md"

Expand Down

0 comments on commit 8ddd9b0

Please sign in to comment.