Skip to content
This repository was archived by the owner on Apr 30, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM fedora
RUN dnf -y install --setopt=install_weak_deps=false --setopt=tsflags=nodocs \
--setopt=deltarpm=false python2-rpm libtaskotron-core libtaskotron-fedora \
python3-rpm tox python2 python3 python2-dnf python3-dnf \
python2-libarchive-c && dnf clean all
python2-libarchive-c python-bugzilla && dnf clean all

ENV LANG=C.UTF-8 LC_ALL=C.UTF-8

Expand Down
7 changes: 5 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ Currently the following checks are available:

- Whether the package uses versioned Python prefix in requirements' names;

- Whether only Python 2 version of the package contains the executables.
- Whether only Python 2 version of the package contains the executables;

- Whether the package uses versioned shebangs in its executables;

- Whether the package supports Python 3 upstream but not in the package.

- Whether the package uses versioned shebangs in its executables.

Running
-------
Expand Down
3 changes: 3 additions & 0 deletions python_versions_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
task_requires_naming_scheme,
task_executables,
task_unversioned_shebangs,
task_py3_support,
)
from taskotron_python_versions.common import log, Package, PackageException

Expand Down Expand Up @@ -59,6 +60,8 @@ def run(koji_build, workdir='.', artifactsdir='artifacts'):
srpm_packages + packages, koji_build, artifact))
details.append(task_executables(packages, koji_build, artifact))
details.append(task_unversioned_shebangs(packages, koji_build, artifact))
details.append(task_py3_support(
srpm_packages + packages, koji_build, artifact))

# finally, the main detail with overall results
outcome = 'PASSED'
Expand Down
1 change: 1 addition & 0 deletions runtask.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ environment:
- rpm-python
- python2-dnf
- python2-libarchive-c
- python-bugzilla

actions:
- name: download rpms from koji
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
url='https://github.com/fedora-python/taskotron-python-versions',
license='Public Domain',
packages=find_packages(),
install_requires=['libarchive-c'],
install_requires=['libarchive-c', 'bugzilla'],
setup_requires=['setuptools', 'pytest-runner'],
tests_require=['pytest', 'pyyaml'],
classifiers=[
Expand Down
2 changes: 2 additions & 0 deletions taskotron_python_versions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .requires import task_requires_naming_scheme
from .two_three import task_two_three
from .unversioned_shebangs import task_unversioned_shebangs
from .py3_support import task_py3_support


__all__ = (
Expand All @@ -11,4 +12,5 @@
'task_requires_naming_scheme',
'task_executables',
'task_unversioned_shebangs',
'task_py3_support',
)
14 changes: 14 additions & 0 deletions taskotron_python_versions/common.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import collections
import logging
import os

Expand Down Expand Up @@ -32,6 +33,19 @@ def write_to_artifact(artifact, message, info_url):
bug_url=BUG_URL))


def packages_by_version(packages):
"""Given the list of packages, group them by the Python
version they are built for.

Return: (dict) Python version: list of packages
"""
pkg_by_version = collections.defaultdict(list)
for package in packages:
for version in package.py_versions:
pkg_by_version[version].append(package)
return pkg_by_version


class PackageException(Exception):

"""Base Exception class for Package API."""
Expand Down
8 changes: 2 additions & 6 deletions taskotron_python_versions/executables.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import collections

from .common import log, write_to_artifact
from .common import log, write_to_artifact, packages_by_version


INFO_URL = ('https://fedoraproject.org/wiki/Packaging:Python#'
Expand Down Expand Up @@ -66,11 +66,7 @@ def task_executables(packages, koji_build, artifact):
outcome = 'PASSED'
message = ''

pkg_by_version = collections.defaultdict(list)
for package in packages:
for version in package.py_versions:
pkg_by_version[version].append(package)

pkg_by_version = packages_by_version(packages)
py2_packages = pkg_by_version[2]
py3_packages = pkg_by_version[3]

Expand Down
124 changes: 124 additions & 0 deletions taskotron_python_versions/py3_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import bugzilla

from .common import log, write_to_artifact, packages_by_version


INFO_URL = 'https://fedoraproject.org/wiki/Packaging:Python'

MESSAGE = """
This software supports Python 3 upstream, but is not
packaged for Python 3 in Fedora.

Software MUST be packaged for Python 3 if upstream supports it.
See the following Bugzilla:
{}
"""

BUGZILLA_URL = "bugzilla.redhat.com"
PY3_TRACKER_BUG = 1285816
# Bugzilla trackers, for which taskotron checks already exist.
IGNORE_TRACKER_BUGS = [
1432186, # Missing PY3-EXECUTABLES
1340802, # Depends on both Py2 and Py3
]
IGNORE_STATUSES = [
'CLOSED',
'VERIFIED',
'RELEASE_PENDING',
'ON_QA',
]


def ignored(bug):
"""Check if the Bugzilla should be ignored.

Reasons to ignore a bug:
- tracked by any of IGNORE_TRACKER_BUGS, so there is a
separate check for it;
- status is one of IGNORE_STATUSES, so the package is most
probably ported in rawhide.

Return: (bool) True if bug should be ignored, False otherwise
"""
for tracker in bug.blocks:
if tracker in IGNORE_TRACKER_BUGS:
return True
return bug.status in IGNORE_STATUSES


def filter_urls(bugs):
"""Given the list of bugs, return the list of URLs
for those which should not be ignored.

Return: (list of str) List of links
"""
return [bug.weburl for bug in bugs if not ignored(bug)]


def get_py3_bugzillas_for(srpm_name):
"""Fetch all Bugzillas for the package given it's SRPM name,
which are tracked by PY3_TRACKER_BUG.

Return: (list) List of Bugzilla URLs
"""
bzapi = bugzilla.Bugzilla(BUGZILLA_URL)
query = bzapi.build_query(
product="Fedora",
component=srpm_name)
query['blocks'] = PY3_TRACKER_BUG
bugs = bzapi.query(query)
return filter_urls(bugs)


def ported_to_py3(packages):
"""Check if the package is ported to Python 3,
by comparing the number of it's binary RPMs for each
Python version.

Return: (bool) True if ported, False otherwise
"""
pkg_by_version = packages_by_version(packages)
return len(pkg_by_version[2]) <= len(pkg_by_version[3])


def task_py3_support(packages, koji_build, artifact):
"""Check that the package is packaged for Python 3,
if upstream is Python 3 ready.

Source of data: https://bugzilla.redhat.com/show_bug.cgi?id=1285816
"""
# libtaskotron is not available on Python 3, so we do it inside
# to make the above functions testable anyway
from libtaskotron import check

outcome = 'PASSED'
message = ''

srpm, packages = packages[0], packages[1:]
if not ported_to_py3(packages):
bugzilla_urls = get_py3_bugzillas_for(srpm.name)
if bugzilla_urls:
outcome = 'FAILED'
log.error(
'This software supports Python 3 upstream,'
' but is not packaged for Python 3 in Fedora')
message = ', '.join(bugzilla_urls)
else:
log.info(
'This software does not support Python 3'
' upstream, skipping Py3 support check')

detail = check.CheckDetail(
checkname='python-versions.py3_support',
item=koji_build,
report_type=check.ReportType.KOJI_BUILD,
outcome=outcome)

if message:
detail.artifact = artifact
write_to_artifact(artifact, MESSAGE.format(message), INFO_URL)

log.info('python-versions.py3_support {} for {}'.format(
outcome, koji_build))

return detail
66 changes: 66 additions & 0 deletions test/functional/test_py3_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from collections import namedtuple

import pytest

from taskotron_python_versions.py3_support import (
ignored,
filter_urls,
ported_to_py3,
PY3_TRACKER_BUG,
IGNORE_TRACKER_BUGS,
)
from taskotron_python_versions.two_three import check_two_three
from .common import gpkg


BugStub = namedtuple('BugStub', 'blocks, weburl, status')
BugStub.__new__.__defaults__ = (None, 'http://test', 'NEW')


@pytest.mark.parametrize('bug', (
BugStub(blocks=IGNORE_TRACKER_BUGS),
BugStub(blocks=[PY3_TRACKER_BUG] + IGNORE_TRACKER_BUGS),
BugStub(blocks=IGNORE_TRACKER_BUGS + ['X']),
BugStub(blocks=IGNORE_TRACKER_BUGS, status='NEW'),
BugStub(blocks=[PY3_TRACKER_BUG], status='CLOSED'),
BugStub(blocks=[PY3_TRACKER_BUG], status='VERIFIED'),
BugStub(blocks=[PY3_TRACKER_BUG], status='RELEASE_PENDING'),
BugStub(blocks=[PY3_TRACKER_BUG], status='ON_QA'),
))
def test_ignored(bug):
assert ignored(bug)


@pytest.mark.parametrize('bug', (
BugStub(blocks=[PY3_TRACKER_BUG]),
BugStub(blocks=[PY3_TRACKER_BUG, 'X']),
BugStub(blocks=[PY3_TRACKER_BUG], status='NEW'),
BugStub(blocks=[PY3_TRACKER_BUG], status='ASSIGNED'),
))
def test_not_ignored(bug):
assert not ignored(bug)


@pytest.mark.parametrize(('bugs', 'expected'), (
([BugStub(blocks=[PY3_TRACKER_BUG])], ['http://test']),
([BugStub(blocks=[PY3_TRACKER_BUG], status='NEW')], ['http://test']),
([BugStub(blocks=[PY3_TRACKER_BUG], status='CLOSED')], []),
([BugStub(blocks=IGNORE_TRACKER_BUGS)], []),
([BugStub(blocks=IGNORE_TRACKER_BUGS, status='NEW')], []),
([BugStub(blocks=[PY3_TRACKER_BUG], weburl='test1'),
BugStub(blocks=IGNORE_TRACKER_BUGS, weburl='test2')], ['test1']),
))
def test_filter_urls(bugs, expected):
assert filter_urls(bugs) == expected


@pytest.mark.parametrize(('pkgglobs', 'expected'), (
(('pyserial*', 'python3-pyserial*'), True),
(('pyserial*',), False),
(('python3-pyserial*',), True),
))
def test_ported_to_py3(pkgglobs, expected):
packages = [gpkg(pkg) for pkg in pkgglobs]
for package in packages:
check_two_three(package)
assert ported_to_py3(packages) == expected
Loading