Repository navigation
Replace pkg_resources with importlib.metadata for version detection - #44
Conversation
Generate man pages from scdoc sources during setup.py execution instead of requiring them to be pre-built. The data_files entry is moved from setup.cfg to setup.py where it's computed dynamically: - If scdoc is available, man pages are generated from .scd sources - If scdoc is unavailable, man pages are simply omitted - If man pages already exist (pre-built), they are used as-is This fixes the 'can't copy docs/beancount2ledger.1: doesn't exist' error when installing from git. Fixes: beancount#42
pkg_resources (from setuptools) is not always available in modern Python environments, causing 'ModuleNotFoundError: No module named pkg_resources'. importlib.metadata is in the standard library since Python 3.8.
There was a problem hiding this comment.
Pull request overview
This PR updates version detection to avoid pkg_resources and adjusts packaging of man pages so they’re only included when present (optionally generating them from scdoc sources).
Changes:
- Switch
__version__resolution frompkg_resourcestoimportlib.metadata. - Move man-page
data_filesconfiguration out ofsetup.cfgintosetup.py. - Add optional man-page generation via
scdocduring setup.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
setup.py |
Adds optional scdoc-based man-page generation and dynamically supplies data_files to setuptools.setup(). |
setup.cfg |
Removes static [options.data_files] man-page entries (now handled in setup.py). |
beancount2ledger/__init__.py |
Replaces pkg_resources-based version detection with importlib.metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def generate_man_pages(): | ||
| """Generate man pages from scdoc sources if scdoc is available.""" | ||
| man_pages = { | ||
| "docs/beancount2ledger.1": "docs/beancount2ledger.1.scd", | ||
| "docs/beancount2ledger.5": "docs/beancount2ledger.5.scd", | ||
| } | ||
| for output, source in man_pages.items(): | ||
| if os.path.exists(output): | ||
| continue | ||
| if not os.path.exists(source): | ||
| continue | ||
| try: | ||
| with open(source, "r") as src, open(output, "w") as dst: | ||
| subprocess.run(["scdoc"], stdin=src, stdout=dst, check=True) | ||
| except (FileNotFoundError, subprocess.CalledProcessError): | ||
| # scdoc not available or failed; skip man page generation | ||
| if os.path.exists(output): | ||
| os.remove(output) | ||
|
|
||
| def get_data_files(): | ||
| """Return data_files list with only man pages that exist.""" | ||
| generate_man_pages() | ||
| data_files = [] | ||
| if os.path.exists("docs/beancount2ledger.1"): | ||
| data_files.append(("share/man/man1", ["docs/beancount2ledger.1"])) | ||
| if os.path.exists("docs/beancount2ledger.5"): | ||
| data_files.append(("share/man/man5", ["docs/beancount2ledger.5"])) | ||
| return data_files |
There was a problem hiding this comment.
This PR introduces man-page generation/packaging logic in setup.py (running scdoc and dynamically setting data_files), which doesn’t align with the PR title about version detection. If the intent is only to swap pkg_resources for importlib.metadata, consider moving the man-page changes into a separate PR or updating the PR title/description to reflect the packaging behavior change.
| def get_data_files(): | ||
| """Return data_files list with only man pages that exist.""" | ||
| generate_man_pages() | ||
| data_files = [] | ||
| if os.path.exists("docs/beancount2ledger.1"): | ||
| data_files.append(("share/man/man1", ["docs/beancount2ledger.1"])) | ||
| if os.path.exists("docs/beancount2ledger.5"): | ||
| data_files.append(("share/man/man5", ["docs/beancount2ledger.5"])) |
There was a problem hiding this comment.
With docs/beancount2ledger.1 / .5 not present in the repo (only the .scd sources exist), builds performed without scdoc on PATH will now silently ship without installed man pages (since data_files becomes empty). If man pages are expected to be installed for end users, consider generating and committing the .1/.5 outputs as part of the release process (or failing the build when sources exist but generation is unavailable).
| from importlib.metadata import version, PackageNotFoundError | ||
|
|
||
| from .common import map_data | ||
| from .ledger import LedgerPrinter | ||
| from .hledger import HLedgerPrinter | ||
|
|
||
| try: | ||
| __version__ = get_distribution(__name__).version | ||
| except DistributionNotFound: | ||
| __version__ = version(__name__) | ||
| except PackageNotFoundError: |
There was a problem hiding this comment.
importlib.metadata is only available in the stdlib on Python 3.8+. This package declares python_requires = >=3.6 (setup.cfg:39), so importing version, PackageNotFoundError from importlib.metadata will raise ModuleNotFoundError on 3.6/3.7 at import time. Use a conditional import fallback to importlib_metadata for older Pythons (and add an environment-marked dependency for <3.8), or bump python_requires to >=3.8 if dropping older versions is intended.
No description provided.