Skip to content

Commit

Permalink
Python 3.12 support (#178)
Browse files Browse the repository at this point in the history
* py3.12 compat

* mention in docs

* enable CI for 3.12

* 3.12 in tox actions
  • Loading branch information
obriencj authored Jun 13, 2024
1 parent f749d63 commit 0be3f70
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 60 deletions.
1 change: 1 addition & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
- '3.9'
- '3.10'
- '3.11'
- '3.12'

needs:
- pre-tests
Expand Down
1 change: 1 addition & 0 deletions docs/release_notes/v2.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Bugfix
------

* added missing caret support to `kojismokydingo.rpm.evr_compare`
* entry points compatability for Python 3.12


Other
Expand Down
23 changes: 19 additions & 4 deletions koji_cli_plugins/kojismokydingometa.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,19 @@ def __plugin__(glbls):

from operator import attrgetter
from os import getenv
from pkg_resources import iter_entry_points
from sys import stderr
from sys import stderr, version_info

if version_info < (3, 11):
from pkg_resources import iter_entry_points
ep_key = attrgetter('module_name', 'name')

else:
from importlib.metadata import entry_points as _entry_points

def iter_entry_points(group):
return _entry_points(group=group)

ep_key = attrgetter('module', 'name')

# these env var checks were introduced in v2.2.0
verbose = getenv("KSD_VERBOSE", None) == "1"
Expand All @@ -49,7 +60,7 @@ def __plugin__(glbls):
# we sort the entry points by module name so that duplicate
# commands have a predictable resolution order
points = sorted(iter_entry_points('koji_smoky_dingo'),
key=attrgetter('module_name', 'name'))
key=ep_key)

for entry_point in points:
try:
Expand All @@ -59,7 +70,11 @@ def __plugin__(glbls):
# or a callable appropriate for use as a koji command
# handler. See `kojismokydingo.types.CLIHandler`

entry_fn = entry_point.resolve()
if version_info < (3, 11):
entry_fn = entry_point.resolve()
else:
entry_fn = entry_point.load()

handler = entry_fn(entry_point.name) if entry_fn else None

except Exception as ex:
Expand Down
15 changes: 13 additions & 2 deletions kojismokydingo/cli/sift.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
from functools import partial
from operator import attrgetter, itemgetter
from os.path import basename
from pkg_resources import EntryPoint, iter_entry_points
from typing import Callable, Dict, Iterable, List, Optional, Type
from sys import version_info
from typing import (
TYPE_CHECKING, Callable, Dict, Iterable, List, Optional, Type, )

from . import open_output, printerr, resplit
from ..common import escapable_replace
Expand All @@ -38,6 +39,16 @@
from ..types import KeySpec


if version_info < (3, 11):
from pkg_resources import EntryPoint, iter_entry_points

elif not TYPE_CHECKING:
from importlib.metadata import EntryPoint, entry_points as _entry_points

def iter_entry_points(group):
return _entry_points(group=group)


__all__ = (
"BuildSifting",
"Sifting",
Expand Down
16 changes: 10 additions & 6 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ test = nosetests

[tox:tox]

envlist = flake8,mypy,bandit,twine,py{37,38,39,310,311},coverage
envlist = flake8,mypy,bandit,twine,py{37,38,39,310,311,312},coverage
skip_missing_interpreters = True


Expand All @@ -140,13 +140,14 @@ setenv =
COVERAGE_FILE = .coverage.{envname}

commands =
python -B -m coverage run -m nose
python -B -m coverage run -m nose tests

sitepackages = False

deps =
coverage
nose-py3 == 1.6.3
git+https://github.com/obriencj/nose-py3@py3_compat
# nose-py3

download = True

Expand All @@ -172,7 +173,8 @@ basepython = python3.9

deps =
git+https://pagure.io/koji.git@master
nose-py3 == 1.6.3
git+https://github.com/obriencj/nose-py3@py3_compat
# nose-py3 == 1.6.3

recreate = True

Expand Down Expand Up @@ -254,10 +256,11 @@ deps =
basepython = python3.9

commands =
python -B -I -m nose
python -B -I -m nose tests

deps =
nose-py3 == 1.6.3
git+https://github.com/obriencj/nose-py3@py3_compat
# nose-py3 == 1.6.3

download = True

Expand Down Expand Up @@ -384,6 +387,7 @@ python =
3.9: py39
3.10: py310
3.11: py311
3.12: py312


#
Expand Down
40 changes: 34 additions & 6 deletions tests/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@


from io import StringIO
from pkg_resources import EntryPoint
from sys import version_info
from unittest import TestCase
from unittest.mock import patch

Expand All @@ -22,6 +22,12 @@
print_history_results, resplit, space_normalize, tabulate)


if version_info < (3, 11):
from pkg_resources import EntryPoint
else:
from importlib.metadata import EntryPoint


ENTRY_POINTS = {
"affected-targets": "kojismokydingo.cli.tags:AffectedTargets",
"block-env-var": "kojismokydingo.cli.tags:BlockEnvVar",
Expand Down Expand Up @@ -57,6 +63,31 @@
}


def get_entry_point(name, group="koji_smoky_dingo"):
ref = ENTRY_POINTS[name]

if version_info < (3, 11):
return EntryPoint.parse("=".join((name, ref)))
else:
return EntryPoint(group=group, name=name, value=ref)


def iter_entry_points(group="koji_smoky_dingo"):
if version_info < (3, 11):
for name, ref in ENTRY_POINTS.items():
yield EntryPoint.parse("=".join((name, ref)))
else:
for name, ref in ENTRY_POINTS.items():
yield EntryPoint(group=group, name=name, value=ref)


def entry_point_load(ep):
if version_info < (3, 11):
return ep.resolve()
else:
return ep.load()


def default_koji_config(profile="koji"):
return {
'profile': profile,
Expand Down Expand Up @@ -99,11 +130,8 @@ class TestExpectedEntryPoints(TestCase):
def test_entry_points(self):
# verify the expected entry points resolve and can be
# initialized
for nameref in ENTRY_POINTS.items():
cmd = "=".join(nameref)
ep = EntryPoint.parse(cmd)

cmd_cls = ep.resolve()
for ep in iter_entry_points():
cmd_cls = entry_point_load(ep)

self.assertTrue(issubclass(cmd_cls, SmokyDingo))

Expand Down
14 changes: 3 additions & 11 deletions tests/cli/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@
from functools import partial
from io import StringIO
from nose.tools import assert_equal, assert_raises, assert_true
from pkg_resources import EntryPoint
from unittest.mock import patch

from kojismokydingo.cli import SmokyDingo, space_normalize

from . import ENTRY_POINTS, GOptions
from . import ENTRY_POINTS, GOptions, get_entry_point, entry_point_load


_varargs = re.compile(r'\[(\w*) \[\1 \.\.\.\]\]')
Expand Down Expand Up @@ -99,18 +98,11 @@ def find_usage(filename):


def check_command_help(cmdname):
ref = ENTRY_POINTS[cmdname]

ep = EntryPoint.parse("=".join([cmdname, ref]))
ep = get_entry_point(cmdname)
name = ep.name

if hasattr(ep, "resolve"):
#new environments
cmd_cls = ep.resolve()
else:
# old environments
cmd_cls = ep.load(require=False)

cmd_cls = entry_point_load(ep)
command = cmd_cls(name)

# this ugly little dance needs to happen because the default
Expand Down
11 changes: 4 additions & 7 deletions tests/cli/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@
# along with this library; if not, see <http://www.gnu.org/licenses/>.


from pkg_resources import EntryPoint
from unittest import TestCase

from kojismokydingo.cli import AnonSmokyDingo

from . import ENTRY_POINTS
from . import ENTRY_POINTS, get_entry_point, entry_point_load


class TestMetaPlugin(TestCase):
Expand All @@ -28,11 +27,9 @@ def test_handlers_exist(self):

# verify the expected entry points resolve and can be
# initialized
for name, ref in ENTRY_POINTS.items():
cmd = f"{name}={ref}"
ep = EntryPoint.parse(cmd)

cmd_cls = ep.resolve()
for name in ENTRY_POINTS:
ep = get_entry_point(name)
cmd_cls = entry_point_load(ep)
cmd_inst = cmd_cls(name)

if not (cmd_inst and getattr(cmd_inst, "enabled", True)):
Expand Down
15 changes: 12 additions & 3 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from contextlib import contextmanager
from datetime import datetime
from operator import itemgetter
from pkg_resources import resource_filename
from os.path import dirname, join
from sys import version_info
from unittest import TestCase
from unittest.mock import MagicMock, patch

Expand All @@ -26,6 +27,9 @@
globfilter, load_full_config, load_plugin_config, merge_extend,
parse_datetime, unique, update_extend)

if version_info < (3, 11):
from pkg_resources import resource_filename


class TestRelace(TestCase):

Expand Down Expand Up @@ -304,8 +308,13 @@ def check_datetime(src, **magic):
class TestConfig(TestCase):

def data_dirs(self):
return (resource_filename(__name__, "data/system"),
resource_filename(__name__, "data/user"))
if version_info < (3, 11):
return (resource_filename(__name__, "data/system"),
resource_filename(__name__, "data/user"))
else:
path = dirname(__file__)
return (join(path, "data/system"),
join(path, "data/user"))


def faux_appdir(self):
Expand Down
37 changes: 26 additions & 11 deletions tests/standalone/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,46 @@
# along with this library; if not, see <http://www.gnu.org/licenses/>.


from pkg_resources import EntryPoint
from sys import version_info
from unittest import TestCase


if version_info < (3, 11):
from pkg_resources import EntryPoint
else:
from importlib.metadata import EntryPoint


ENTRY_POINTS = {
"ksd-filter-builds": "kojismokydingo.standalone.builds:ksd_filter_builds",
"ksd-filter-tags": "kojismokydingo.standalone.tags:ksd_filter_tags",
}


def get_entry_point(name, group="koji_smoky_dingo"):
ref = ENTRY_POINTS[name]

if version_info < (3, 11):
return EntryPoint.parse("=".join((name, ref)))
else:
return EntryPoint(group=group, name=name, value=ref)


def entry_point_load(ep):
if version_info < (3, 11):
return ep.resolve()
else:
return ep.load()


class TestExpectedStandalone(TestCase):

def test_entry_points(self):
# verify the expected entry points resolve and can be
# initialized
for nameref in ENTRY_POINTS.items():
cmd = "=".join(nameref)
ep = EntryPoint.parse(cmd)

if hasattr(ep, "resolve"):
#new environments
cmd_inst = ep.resolve()
else:
# old environments
cmd_inst = ep.load(require=False)
for name in ENTRY_POINTS:
ep = get_entry_point(name)
cmd_inst = entry_point_load(ep)

self.assertTrue(callable(cmd_inst))

Expand Down
Loading

0 comments on commit 0be3f70

Please sign in to comment.