# deps management

## eval formatting
%pyproject_builddep_format 'BuildRequires: python3-module-$nname'

%pyproject_runtimedep_format 'Requires: python3-module-$nname'

## updated PEP518 specification removed 'wheel' from default build system deps
%_pyproject_deps_pep518_filter 'wheel$'

## setuptools 70.1.0 no longer depends on wheel
%_pyproject_deps_pep517_filter 'wheel$'

## dependencies filters

### exclude filter used for check
%pyproject_deps_check_filter %{?pypi_name:'%{pep503_name %pypi_name}$'} 'flake8$' 'flake8-.*' '.*-flake8$' 'isort$' 'isort-.*' '.*-isort$' 'black$' 'black-.*' '.*-black$' 'coverage$' 'coverage-.*' '.*-coverage$' pytest-cov covdefaults 'mypy$' 'mypy-.*' '.*-mypy$' pytest-checkdocs 'pre-commit$' 'pre-commit-.*' '.*-pre-commit$' 'pylint$' 'pylint-.*' '.*-pylint$' 'nose$' 'codecov$' 'codecov-.*' '.*-codecov$' 'coveralls$' 'coveralls-.*' '.*-coveralls$' 'twine$' 'twine-.*' '.*-twine$' 'pytest-runner$' 'ruff$' 'ruff-.*' '.*-ruff$' 'types-.*' '.*-stubs$' 'pyright$' 'pyright-.*' '.*-pyright$' 'sphinx$' 'sphinx-.*' '.*-sphinx$'

%add_pyproject_deps_check_filter() %global pyproject_deps_check_filter %{?pyproject_deps_check_filter} %*
%set_pyproject_deps_check_filter() %global pyproject_deps_check_filter %*

### exclude filter used for build
%pyproject_deps_build_filter 'coherent-licensed$'

%add_pyproject_deps_build_filter() %global pyproject_deps_build_filter %{?pyproject_deps_build_filter} %*
%set_pyproject_deps_build_filter() %global pyproject_deps_build_filter %*

### exclude filter used for runtime
%add_pyproject_deps_runtime_filter() %global pyproject_deps_runtime_filter %{?pyproject_deps_runtime_filter} %*
%set_pyproject_deps_runtime_filter() %global pyproject_deps_runtime_filter %*

## common deps actions
%pyproject_deps_config_name pyproject_deps.json

%pyproject_deps_config %_sourcedir/%pyproject_deps_config_name

%pyproject_deps %{pyproject:} deps --depsconfig %pyproject_deps_config

%pyproject_deps_eval %pyproject_deps eval

%pyproject_deps_show %pyproject_deps show

%pyproject_deps_sync %pyproject_deps sync

%pyproject_deps_add %pyproject_deps add

### verify source and resync if needed
%pyproject_deps_resync() \
%pyproject_deps_show %1 1>/dev/null 2>&1 || %pyproject_deps_add %* \
%pyproject_deps_sync %1 %{!?pyproject_deps_verify_skip:--verify %{expand:%%{?_pyproject_deps_%{1}_filter:--verify-exclude %%_pyproject_deps_%{1}_filter}} || { rc="$?" ; if [ "$rc" -eq 4 ] ; then printf 'error: dependencies of source "%%s" changed since last check\\nconfiguration was synced and saved into "%%s"\\nplease update it, for example, this can be done with:\\nhsh-run -- cat "%pyproject_deps_config" > .gear/pyproject_deps.json\\n' "%1" "%pyproject_deps_config" ; fi ; exit "$rc" ; }} \
%nil

### eval source in current Python environment and format as build requirement
%pyproject_builddeps() \
%(%pyproject_deps_eval --depformat=%pyproject_builddep_format %* 2>/dev/null ||:) \
%nil

### eval source in current Python environment and format as runtime requirement
%pyproject_runtimedeps() \
%(%pyproject_deps_eval --depformat=%pyproject_runtimedep_format %* 2>/dev/null ||:) \
%nil

## common types of deps

### PEP518
#### eval 'pep518' source and format as BuildRequires
%pyproject_builddeps_pep518() %{expand:%%pyproject_builddeps -- pep518 %*}

#### verify or sync 'pep518' source
%pyproject_deps_resync_pep518 %pyproject_deps_resync pep518 pep518

### PEP517
#### eval 'pep517' source and format as BuildRequires
%pyproject_builddeps_pep517() %{expand:%%pyproject_builddeps -- pep517 %*}

#### sync, filter and verify 'pep517' source, pep517 is dynamic source
%pyproject_deps_resync_pep517 %pyproject_deps_resync pep517 pep517

#### today's packaging standard requires PEP518 and PEP517 dependencies for build
%pyproject_builddeps_build() %{expand:%%pyproject_builddeps_pep518 -- %{?pyproject_deps_build_filter:--exclude %pyproject_deps_build_filter} %*}%{expand:%%pyproject_builddeps_pep517 -- %{?pyproject_deps_build_filter:--exclude %pyproject_deps_build_filter} %*}

%pyproject_deps_resync_build %pyproject_deps_resync_pep518 %pyproject_deps_resync_pep517

### core metadata
#### eval 'metadata' source and format as BuildRequires
%pyproject_builddeps_metadata() %{expand:%%pyproject_builddeps -- metadata %{?pyproject_deps_check_filter:--exclude %pyproject_deps_check_filter} %*}

#### eval 'metadata' source with given extra and format as BuildRequires
%pyproject_builddeps_metadata_extra() %{expand:%%pyproject_builddeps_metadata -- --extra %*}

#### eval 'metadata' source and format as Requires
%pyproject_runtimedeps_metadata() %{expand:%%pyproject_runtimedeps -- metadata %{?pyproject_deps_runtime_filter:--exclude %pyproject_deps_runtime_filter} %*}

#### eval 'metadata' source with given extra and format as Requires
%pyproject_runtimedeps_metadata_extra() %{expand:%%pyproject_runtimedeps_metadata -- --extra %*}

#### verify or sync 'metadata' source
%pyproject_deps_resync_metadata %pyproject_deps_resync metadata metadata

## check

### eval 'check' source
%pyproject_builddeps_check() %{expand:%%pyproject_builddeps -- check %{?pyproject_deps_check_filter:--exclude %pyproject_deps_check_filter} %*}

### verify or sync 'check' source of 'pip_reqfile' type
%pyproject_deps_resync_check_pipreqfile() %{expand:%%pyproject_deps_resync check pip_reqfile %*}

### verify or sync 'check' source of 'poetry' type
%pyproject_deps_resync_check_poetry() %{expand:%%pyproject_deps_resync check poetry %*}
#
### verify or sync 'check' source of 'tox' type
%pyproject_deps_resync_check_tox() %{expand:%%pyproject_deps_resync check tox %*}

### verify or sync 'check' source of 'hatch' type
%pyproject_deps_resync_check_hatch() %{expand:%%pyproject_deps_resync check hatch %*}

### verify or sync 'check' source of 'pdm' type
%pyproject_deps_resync_check_pdm() %{expand:%%pyproject_deps_resync check pdm %*}

### verify or sync 'check' source of 'pipenv' type
%pyproject_deps_resync_check_pipenv() %{expand:%%pyproject_deps_resync check pipenv %*}

### verify or sync 'check' source of 'pep735' type
%pyproject_deps_resync_check_depgroup() %{expand:%%pyproject_deps_resync check pep735 %*}

# misc

## init new git repo, commit everything and tag with given name or %%version
%pyproject_scm_init() \
%global _buildrequires_build %_buildrequires_build /usr/bin/git \
if [ ! -d .git ]; then \
    git init \
    git config user.email author@example.com \
    git config user.name author \
    git add . \
    git commit -m release \
    git tag %{?1}%{!?1:'%version'} \
fi \
%nil

## Current Python version in 'major.minor' format.
##
## Examples (current version is 3.13):
## $ rpm --eval '%python3_version'
## 3.13
##
## $ (eval "$(rpm --eval '%{expand:%(cat macros/pyproject.env 2>/dev/null)}')" && echo $RPM_PYTHON3_VERSION)
## 3.13

%__python3_version_do %(%__python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")' 2>/dev/null || echo unknown)
## expand once to avoid redundant process execution
%python3_version %{!?__python3_version_cache:%global __python3_version_cache %__python3_version_do}%__python3_version_cache

## Current Python version in nodot format ({major}{minor}).
##
## Examples (current version is 3.13):
## $ rpm --eval '%python3_version_nodot'
## 313
##
## $ (eval "$(rpm --eval '%{expand:%(cat macros/pyproject.env 2>/dev/null)}')" && echo $RPM_PYTHON3_VERSION_NODOT)
## 313

%__python3_version_nodot_do %(%__python3 -c 'import sys; print(f"{sys.version_info.major}{sys.version_info.minor}")' 2>/dev/null || echo unknown)
## expand once to avoid redundant process execution
%python3_version_nodot %{!?__python3_version_nodot_cache:%global __python3_version_nodot_cache %__python3_version_nodot_do}%__python3_version_nodot_cache

## Convert Python version into nodot format.
## Accepts a string in 'major.minor' format.
##
## Examples:
## $ rpm --eval '%{python3_version_to_nodot 3.13}'
## 313

%python3_version_to_nodot() %(%__python3 -c 'print("".join(map(str, map(int,"%1".split(".", maxsplit=1)))))' 2>/dev/null || echo unknown)

## Next Python version with increased minor by 1, in 'major.minor' format.
##
## Examples (current version is 3.13):
## $ rpm --eval '%python3_version_next'
## 3.14
##
## $ (eval "$(rpm --eval '%{expand:%(cat macros/pyproject.env 2>/dev/null)}')" && echo $RPM_PYTHON3_VERSION_NEXT)
## 3.14

%__python3_version_next_do %(%__python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor+1}")' 2>/dev/null || echo unknown)
## expand once to avoid redundant process execution
%python3_version_next %{!?__python3_version_next_cache:%global __python3_version_next_cache %__python3_version_next_do}%__python3_version_next_cache

## Convert Python version into hex format used by Py_LIMITED_API.
## Accepts a string in 'major.minor' format.
##
## https://docs.python.org/3/c-api/apiabiversion.html#c.PY_VERSION_HEX
## https://docs.python.org/3/c-api/stable.html#limited-c-api
##
## Examples:
## $ rpm --eval '%{python3_version_to_hex 3.12}'
## 0x030C0000

%python3_version_to_hex() %(%__python3 -c 'v=tuple(map(int,"%{1}".split(".", maxsplit=1)));print(f"0x{v[0]:02X}{v[1]:02X}0000")' 2>/dev/null || echo unknown)

## Current Python version in hex format used by Py_LIMITED_API.
##
## https://docs.python.org/3/c-api/apiabiversion.html#c.PY_VERSION_HEX
## https://docs.python.org/3/c-api/stable.html#limited-c-api
##
## Examples (current version is 3.13):
## $ rpm --eval '%python3_version_hex'
## 0x030D0000

%python3_version_hex %{!?__python3_version_hex_cache:%global __python3_version_hex_cache %{python3_version_to_hex %python3_version}}%__python3_version_hex_cache

## abi3audit scans Python extensions for abi3 violations and inconsistencies
%abi3audit_buildrequires python3-module-abi3audit

## Set version of Python Limited C API and enables validation of extensions
## marked as abi3 compatible. The macro doesn’t directly enforce actual build
## with Limited API and additional configuration of build backend maybe
## required. To help with this some macro names and env variables are exposed:
##
## - %python3_limited_api_version
## - %python3_limited_api_version_hex
## - %python3_limited_api_version_nodot
## - RPM_PYTHON3_LIMITED_API_VERSION
## - RPM_PYTHON3_LIMITED_API_VERSION_HEX
## - RPM_PYTHON3_LIMITED_API_VERSION_NODOT
##
## Accepts a string in 'major.minor' format, defaults to current version.
##
## Examples (current version is 3.13):
## - current version
## $ rpm --eval '%python3_set_limited_api' --eval '%{echo:%python3_limited_api_version:%python3_limited_api_version_hex:%python3_limited_api_version_nodot}'
## 3.13:0x030D0000:313
##
## $ (eval "$(rpm --eval '%python3_set_limited_api' --eval '%{expand:%(cat macros/pyproject.env 2>/dev/null)}')" && echo $RPM_PYTHON3_LIMITED_API_VERSION:$RPM_PYTHON3_LIMITED_API_VERSION_HEX:$RPM_PYTHON3_LIMITED_API_VERSION_NODOT)
## 3.13:0x030D0000:313
##
## - specific version
## $ rpm --eval '%python3_set_limited_api 3.10' --eval '%{echo:%python3_limited_api_version:%python3_limited_api_version_hex:%python3_limited_api_version_nodot}'
## 3.10:0x030A0000:310
##
## $ (eval "$(rpm --eval '%python3_set_limited_api 3.10' --eval '%{expand:%(cat macros/pyproject.env 2>/dev/null)}')" && echo $RPM_PYTHON3_LIMITED_API_VERSION:$RPM_PYTHON3_LIMITED_API_VERSION_HEX:$RPM_PYTHON3_LIMITED_API_VERSION_NODOT)
## 3.10:0x030A0000:310
##
## https://docs.python.org/3/c-api/stable.html#limited-c-api

%python3_set_limited_api() \
%global _buildrequires_build %_buildrequires_build %abi3audit_buildrequires \
%global python3_limited_api_version %{?1}%{!?1:%python3_version} \
%global python3_limited_api_version_hex %{?1:%{expand:%%python3_version_to_hex -- %1}}%{!?1:%python3_version_hex} \
%global python3_limited_api_version_nodot %{?1:%{expand:%%python3_version_to_nodot -- %1}}%{!?1:%python3_version_nodot} \
%nil
