Building newsfiles for your project.
Extract project metadata like version and name from packages for automatic changelog generation.
Get the version of a package from metadata or source code.
def get_version(package_dir: str, package: str) -> str:
"""
Get the version of a package.
Try to extract the version from the distribution version metadata that matches
`package`, then fall back to looking for the package in `package_dir`.
Args:
package_dir: Directory containing the package source
package: Package name to get version for
Returns:
str: Package version string
Raises:
ImportError: If package cannot be imported
AttributeError: If version cannot be determined
"""Get the project name from package metadata.
def get_project_name(package_dir: str, package: str) -> str:
"""
Get the project name from package metadata.
Args:
package_dir: Directory containing the package source
package: Package name to get project name for
Returns:
str: Project name from metadata
Raises:
ImportError: If package cannot be imported
AttributeError: If project name cannot be determined
"""Helper functions for package loading and metadata access.
def _get_package(package_dir: str, package: str) -> ModuleType:
"""
Import a package from a specific directory.
First tries to import normally, then adds package_dir to sys.path
and tries again if the initial import fails.
Args:
package_dir: Directory to search for package
package: Package name to import
Returns:
ModuleType: Imported package module
Raises:
ImportError: If package cannot be imported from any location
"""
def _get_metadata_version(package: str) -> str | None:
"""
Try to get the version from package distribution metadata.
Args:
package: Package name to look up
Returns:
str | None: Version string if found, None otherwise
"""from towncrier._project import get_version, get_project_name
# Get version from installed package metadata first, then source
version = get_version(
package_dir="src",
package="myproject"
)
print(f"Version: {version}")
# Get project name
project_name = get_project_name(
package_dir="src",
package="myproject"
)
print(f"Project: {project_name}")from towncrier._project import get_version
from towncrier._settings import load_config_from_options
# Load config and get version if not specified
base_directory, config = load_config_from_options(None, None)
if not config.version and config.package:
# Auto-detect version from package
detected_version = get_version(
package_dir=os.path.join(base_directory, config.package_dir),
package=config.package
)
print(f"Detected version: {detected_version}")from towncrier._project import _get_package
try:
# Import package from source directory
module = _get_package(
package_dir="/path/to/src",
package="mypackage"
)
# Access package attributes
if hasattr(module, '__version__'):
version = module.__version__
except ImportError as e:
print(f"Cannot import package: {e}")The get_version function uses this resolution order:
importlib.metadata.version()__version__ attribute_version attributeVERSION attributeversion attributeThe get_project_name function uses:
importlib.metadata.metadata()Project utilities handle these error scenarios:
These utilities are used by:
Install with Tessl CLI
npx tessl i tessl/pypi-towncrier