Skip to content
4 changes: 2 additions & 2 deletions autorelease/gh_actions_stages/autorelease-gh-rel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ jobs:
fi
name: "Install autorelease"
- run: |
VERSION=`python setup.py --version`
PROJECT=`python setup.py --name`
VERSION=`autorelease metadata version`
PROJECT=`autorelease metadata name`
echo $$PROJECT $$VERSION
autorelease-release --project $$PROJECT --version $$VERSION --token $$AUTORELEASE_TOKEN
env:
Expand Down
2 changes: 1 addition & 1 deletion autorelease/gh_actions_stages/autorelease-prep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
name: "Install release tools"
- run: |
bump-dev-version
python setup.py --version
autorelease metadata version
name: "Bump testpypi dev version"
- run: |
python setup.py sdist bdist_wheel
Expand Down
24 changes: 13 additions & 11 deletions autorelease/scripts/bump_dev_version.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import argparse
import time

try:
from configparser import ConfigParser, NoSectionError, NoOptionError
except ImportError:
# py2
from ConfigParser import ConfigParser, NoSectionError, NoOptionError


from json import JSONDecodeError

from packaging.version import Version
import requests
from autorelease.utils import split_setup_cfg_path
from autorelease.version import get_setup_cfg, get_setup_name, get_setup_version

def get_latest_pypi(package, index="https://test.pypi.org/pypi"):
url = "/".join([index, package, 'json'])
Expand Down Expand Up @@ -60,10 +55,17 @@ def shared_parser():
return parser

def get_version_info(conf_name, index):
conf = ConfigParser()
conf.read(conf_name)
v_setup = conf.get('metadata', 'version')
package = conf.get('metadata', 'name')
directory, filename = split_setup_cfg_path(conf_name)
conf = get_setup_cfg(directory=directory, filename=filename)
if conf is None:
raise RuntimeError(f"Unable to find setup config: {conf_name}")

v_setup = get_setup_version(None, directory=directory, filename=filename)
package = get_setup_name(None, directory=directory, filename=filename)
Comment thread
dwhswenson marked this conversation as resolved.
Outdated
if v_setup is None:
raise RuntimeError(f"Missing [metadata] version in {conf_name}")
if package is None:
raise RuntimeError(f"Missing [metadata] name in {conf_name}")
v_pypi = get_latest_pypi(package, index)
return conf, package, v_setup, v_pypi

Expand Down
36 changes: 36 additions & 0 deletions autorelease/scripts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from autorelease.scripts.vendor import vendor_actions
from autorelease.scripts.check import run_checks
from autorelease.utils import split_setup_cfg_path
from autorelease.version import get_setup_name, get_setup_version
# from autorelease import ReleaseNoteWriter
from autorelease.gh_api4.notes4 import NotesWriter, prs_since_latest_release

Expand Down Expand Up @@ -77,6 +79,40 @@ def auth(auth):
auth = load_auth(auth)
pprint(auth)

@cli.group()
def metadata():
pass


@metadata.command(name="version")
@click.option("-c", "--conf", type=str, default="setup.cfg",
help="setup.cfg file to use")
def metadata_version(conf):
directory, filename = split_setup_cfg_path(conf)
value = get_setup_version(None, directory=directory, filename=filename)
field = "version"

if value is None:
raise click.ClickException(
f"Missing [metadata] {field} in {conf}"
)
Comment thread
dwhswenson marked this conversation as resolved.
Comment thread
dwhswenson marked this conversation as resolved.
Comment thread
dwhswenson marked this conversation as resolved.
click.echo(value)


@metadata.command(name="name")
@click.option("-c", "--conf", type=str, default="setup.cfg",
help="setup.cfg file to use")
def metadata_name(conf):
directory, filename = split_setup_cfg_path(conf)
value = get_setup_name(None, directory=directory, filename=filename)
field = "name"

if value is None:
raise click.ClickException(
f"Missing [metadata] {field} in {conf}"
)
Comment thread
dwhswenson marked this conversation as resolved.
Comment thread
dwhswenson marked this conversation as resolved.
Comment thread
dwhswenson marked this conversation as resolved.
click.echo(value)
Comment on lines +89 to +136

Copilot AI Feb 23, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two metadata commands (metadata_version and metadata_name) have significant code duplication. They differ only in the getter function called and the field name used. Consider refactoring to reduce duplication by creating a shared helper function or parameterizing the common logic. This would make the code more maintainable and reduce the chance of inconsistencies between the two commands.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deciding not to address this. The duplicated code is minimal.

Comment thread
dwhswenson marked this conversation as resolved.


@cli.command()
@click.option('--conf', type=click.File('r'))
Expand Down
51 changes: 50 additions & 1 deletion autorelease/tests/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import os


from autorelease.version import _find_rel_path_for_file
from autorelease.version import (
_find_rel_path_for_file, get_setup_name, get_setup_version
)

@pytest.mark.parametrize("depth, result", [
(0, '.'), (1, '..'), (2, '..' + os.sep + '..'),
Expand All @@ -25,3 +27,50 @@ def test_find_rel_path_for_file_finds_no_file():
with mock.patch('autorelease.version.os.path.isfile', lambda x: False):
assert _find_rel_path_for_file(-1, 'setup.cfg') is None


def test_get_setup_name_and_version(tmp_path):
setup_cfg = tmp_path / "setup.cfg"
setup_cfg.write_text(
"[metadata]\n"
"name = mypkg\n"
"version = 1.2.3.dev0\n"
)
assert get_setup_name(None, str(tmp_path), "setup.cfg") == "mypkg"
assert get_setup_version(None, str(tmp_path), "setup.cfg") == "1.2.3.dev0"
Comment thread
dwhswenson marked this conversation as resolved.
Outdated


def test_get_setup_name_and_version_missing_file(tmp_path):
default_name = "default-name"
default_version = "0.0.0"
assert get_setup_name(default_name, str(tmp_path), "setup.cfg") == default_name
assert get_setup_version(default_version, str(tmp_path), "setup.cfg") == default_version


def test_get_setup_name_and_version_missing_fields(tmp_path):
setup_cfg_no_name = tmp_path / "setup_no_name.cfg"
setup_cfg_no_name.write_text(
"[metadata]\n"
"version = 2.0.0\n"
)
assert get_setup_name("default-name", str(tmp_path), "setup_no_name.cfg") == "default-name"
assert get_setup_version(None, str(tmp_path), "setup_no_name.cfg") == "2.0.0"

setup_cfg_no_version = tmp_path / "setup_no_version.cfg"
setup_cfg_no_version.write_text(
"[metadata]\n"
"name = pkg-without-version\n"
)
assert get_setup_version("0.0.0", str(tmp_path), "setup_no_version.cfg") == "0.0.0"
assert get_setup_name(None, str(tmp_path), "setup_no_version.cfg") == "pkg-without-version"


def test_get_setup_name_and_version_malformed_cfg(tmp_path):
setup_cfg = tmp_path / "setup.cfg"
setup_cfg.write_text(
"[metadata\n"
"name = badpkg\n"
"version = 0.0.0\n"
)

assert get_setup_name("default-name", str(tmp_path), "setup.cfg") == "default-name"
assert get_setup_version("0.0.0", str(tmp_path), "setup.cfg") == "0.0.0"
11 changes: 10 additions & 1 deletion autorelease/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import yaml
import re
import os
import sys

def conda_recipe_version(recipe_file):
import yaml
with open(recipe_file) as f:
dct = yaml.load(f.read(), Loader=yaml.FullLoader)
return dct['package']['version']
Expand Down Expand Up @@ -35,3 +35,12 @@ def _import_setup_py3(directory):
setup = importlib.util.module_from_spec(spec)
spec.loader.exec_module(setup)
return setup


def split_setup_cfg_path(conf_name):
directory, filename = os.path.split(conf_name)
if directory == "":
directory = "."
if filename == "":
filename = "setup.cfg"
return directory, filename
51 changes: 43 additions & 8 deletions autorelease/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import subprocess

try:
from configparser import ConfigParser, NoSectionError, NoOptionError
from configparser import (
ConfigParser, Error as ConfigParserError, NoSectionError, NoOptionError
)
except ImportError:
# py2
from ConfigParser import ConfigParser, NoSectionError, NoOptionError
from ConfigParser import (
ConfigParser, Error as ConfigParserError, NoSectionError, NoOptionError
)

try:
from ._installed_version import _installed_version
Expand Down Expand Up @@ -109,21 +113,52 @@ def get_setup_cfg(directory, filename="setup.cfg"):
conf = None
if os.path.exists(setup_cfg):
conf = ConfigParser()
conf.read(setup_cfg)
try:
conf.read(setup_cfg)
except ConfigParserError:
conf = None
Comment thread
dwhswenson marked this conversation as resolved.
Outdated

return conf


def get_setup_version(default_version, directory, filename="setup.cfg"):
version = default_version
def get_setup_value(default_value, directory, option, section='metadata',
filename="setup.cfg"):
"""Get a setup.cfg value or return the provided default.

Parameters
----------
default_value
value to return when setup.cfg is missing or does not define field
directory : str or int
directory for setup.cfg (or search depth if int)
option : str
field name to retrieve from section
section : str
section name to query; default 'metadata'
filename : str
filename for setup.cfg; default 'setup.cfg'
"""
value = default_value
conf = get_setup_cfg(directory, filename)
try:
version = conf.get('metadata', 'version')
value = conf.get(section, option)
except (NoSectionError, NoOptionError):
pass # version (or metadata) not defined in setup.cfg
pass # option (or section) not defined in setup.cfg
except AttributeError:
pass # no setup.cfg found (conf is None)
return version
return value


def get_setup_version(default_version, directory, filename="setup.cfg"):
return get_setup_value(default_version, directory=directory,
option='version', section='metadata',
filename=filename)


def get_setup_name(default_name, directory, filename="setup.cfg"):
return get_setup_value(default_name, directory=directory,
option='name', section='metadata',
filename=filename)


short_version = get_setup_version(_installed_version,
Expand Down
2 changes: 1 addition & 1 deletion script_stages/deploy-pypi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ EXTRA_TWINE_ARGS="$@"
python -m pip install twine wheel

bump-dev-version
python setup.py --version
autorelease metadata version
python setup.py sdist bdist_wheel

twine check dist/* || exit 1
Expand Down
2 changes: 1 addition & 1 deletion script_stages/install-testpypi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if [ -z "$DRY" ]; then
wait-for-testpypi
fi

export PROJECT=`python setup.py --name`
export PROJECT=`autorelease metadata name`
export VERSION=`pypi-max-version $PROJECT`
echo "Installing ${PROJECT}==${VERSION} (allowing pre-releases)"
if [ -z "$DRY" ]; then
Expand Down