Source code for salespyforce.utils.version
# -*- coding: utf-8 -*-
"""
:Module: salespyforce.utils.version
:Synopsis: Utilities for working with the package version
:Created By: Jeff Shurtliff
:Last Modified: Jeff Shurtliff
:Modified Date: 19 Feb 2026
"""
from __future__ import annotations
from pathlib import Path
from typing import Optional
from importlib.metadata import version, PackageNotFoundError
try:
import tomllib # Python 3.11+
except ModuleNotFoundError: # pragma: no cover
import tomli as tomllib # type: ignore[import-not-found]
from . import log_utils
# Initialize logging
logger = log_utils.initialize_logging(__name__)
[docs]
def get_full_version() -> str:
"""This function returns the current full version of the ``salespyforce`` package.
.. versionchanged:: 1.5.0
The function will now attempt to retrieve the version from the ``pyproject.toml``
file if it cannot be retrieved via the package metadata.
.. versionchanged:: 1.4.0
The function now retrieves the version from the package metadata,
rather than from the ``__version__`` special variable.
The package version is retrieved from the installed package metadata, which is
populated from the ``version`` field in ``pyproject.toml``.
:returns: The current package version as a string
"""
try:
return version('salespyforce')
except PackageNotFoundError:
# This can happen if the package is not installed in the environment
# (e.g. running from a source checkout without an editable install)
logger.debug('Package is not installed and version will be retrieved from pyproject.toml file')
return get_version_from_pyproject()
[docs]
def get_major_minor_version(full_version: Optional[str] = None) -> str:
"""Return the current major.minor (i.e., X.Y) version of the package.
.. versionchanged:: 1.5.0
The function now accepts an optional full version if already defined.
.. versionchanged:: 1.4.0
The function utilizes the :py:func:`salespyforce.utils.version.get_full_version`
function to get the package version rather than using ``__version__``.
:param full_version: The full package version (e.g. X.Y.Z)
:type full_version: str, None
:returns: The current package version (X.Y) as a string
"""
if not full_version:
full_version = get_full_version()
parts = full_version.split('.')
if len(parts) >= 2:
return '.'.join(parts[:2])
return full_version
[docs]
def get_version_from_pyproject(pyproject_path: Optional[str] = None) -> str:
"""This function retrieves the current version from the pyproject.toml file.
.. versionadded:: 1.5.0
:param pyproject_path: The path to the pyproject.toml file (optional)
:type pyproject_path: str, None
:returns: The current package version as a string
"""
path = Path(pyproject_path) if pyproject_path else Path(__file__).resolve().parents[3] / 'pyproject.toml'
# tomllib.loads() expects a string, while Path.read_bytes() returns bytes
# Prefer tomllib.load() with a binary file handle to avoid encoding pitfalls
with path.open('rb') as fp:
data = tomllib.load(fp)
# PEP 621
project_version = data.get('project', {}).get('version')
if project_version:
return str(project_version)
# Poetry legacy layout
project_version = data.get('tool', {}).get('poetry', {}).get('version')
if project_version:
return str(project_version)
logger.warning("salespyforce version could not be retrieved; falling back to '0.0.0' as version")
return '0.0.0'
# Define __version__ for backward compatibility and to utilize as needed
__version__ = get_full_version()