Skip to content

Commit

Permalink
Implement a sieve that drops Pandas>=1.2 on Python 3.6
Browse files Browse the repository at this point in the history
  • Loading branch information
fridex committed Sep 23, 2020
1 parent f606268 commit 9aa5171
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 7 deletions.
18 changes: 18 additions & 0 deletions tests/sieves/pandas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
# thoth-adviser
# Copyright(C) 2020 Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Tests for sieve units for Pandas."""
102 changes: 102 additions & 0 deletions tests/sieves/pandas/test_py36_drop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
# thoth-adviser
# Copyright(C) 2020 Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Test sieve to filter out Pandas>=1.2 on Python 3.6."""

import flexmock
import pytest

from thoth.adviser.enums import DecisionType
from thoth.adviser.enums import RecommendationType
from thoth.adviser.pipeline_builder import PipelineBuilderContext
from thoth.adviser.sieves import PandasPy36Drop
from thoth.python import PackageVersion
from thoth.python import Source

from ...base import AdviserTestCase


class TestPandasPy36DropSieve(AdviserTestCase):
"""Test sieve to filter out Pandas>=1.2 on Python 3.6."""

@pytest.mark.parametrize(
"recommendation_type",
[RecommendationType.STABLE, RecommendationType.PERFORMANCE, RecommendationType.SECURITY,],
)
def test_include(self, builder_context: PipelineBuilderContext, recommendation_type: RecommendationType,) -> None:
"""Test including this pipeline unit."""
builder_context.recommendation_type = recommendation_type
builder_context.project.runtime_environment.python_version = "3.6"

assert builder_context.is_adviser_pipeline()
assert PandasPy36Drop.should_include(builder_context) == {}

@pytest.mark.parametrize(
"recommendation_type,decision_type,python_version",
[
(RecommendationType.TESTING, None, "3.6"),
(RecommendationType.LATEST, None, "3.6"),
(None, DecisionType.RANDOM, "3.6"),
(RecommendationType.STABLE, None, "3.9"),
(RecommendationType.PERFORMANCE, None, "3.8"),
],
)
def test_no_include(
self,
builder_context: PipelineBuilderContext,
recommendation_type: RecommendationType,
decision_type: DecisionType,
python_version: str,
) -> None:
"""Test not including this pipeline unit step."""
builder_context.decision_type = decision_type
builder_context.recommendation_type = recommendation_type
builder_context.project.runtime_environment.python_version = python_version
assert PandasPy36Drop.should_include(builder_context) is None

@pytest.mark.parametrize("pandas_version", ["1.2.0", "2.0.0"])
def test_run(self, pandas_version: str) -> None:
"""Test filtering out Pandas that dropped Python 3.6 support."""
package_version = PackageVersion(
name="pandas", version=f"=={pandas_version}", develop=False, index=Source("https://pypi.org/simple"),
)

context = flexmock()
unit = PandasPy36Drop()
unit.pre_run()
with PandasPy36Drop.assigned_context(context):
assert unit._message_logged is False
assert list(unit.run(p for p in [package_version])) == []
assert unit._message_logged is True

def test_no_filter(self) -> None:
"""Test not filtering packages that can be included."""
pkg1 = PackageVersion(name="pandas", version="==1.1.2", develop=False, index=Source("https://pypi.org/simple"),)
pkg2 = PackageVersion(name="pandas", version="==1.0.0", develop=False, index=Source("https://pypi.org/simple"),)
pkg3 = PackageVersion(
name="pandas", version="==0.24.2", develop=False, index=Source("https://pypi.org/simple"),
)

pkgs = [pkg1, pkg2, pkg3]

context = flexmock()
unit = PandasPy36Drop()
unit.pre_run()
with PandasPy36Drop.assigned_context(context):
assert unit._message_logged is False
assert list(unit.run(p for p in pkgs)) == pkgs
assert unit._message_logged is False
16 changes: 9 additions & 7 deletions thoth/adviser/sieves/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@
"""Implementation of sieves used in adviser pipeline."""

from .abi_compat import AbiCompatibilitySieve
from .filter_index import FilterIndexSieve
from .index_enabled import PackageIndexSieve
from .locked import CutLockedSieve
from .prereleases import CutPreReleasesSieve
from .solved import SolvedSieve
from .version_constraint import VersionConstraintSieve
from .backports import Enum34BackportSieve
from .backports import Functools32BackportSieve
from .backports import ImportlibMetadataBackportSieve
from .backports import ImportlibResourcesBackportSieve
from .backports import MockBackportSieve
from .tensorflow import TensorFlowCUDASieve
from .filter_index import FilterIndexSieve
from .index_enabled import PackageIndexSieve
from .locked import CutLockedSieve
from .pandas import PandasPy36Drop
from .prereleases import CutPreReleasesSieve
from .setuptools import Py36SetuptoolsSieve
from .solved import SolvedSieve
from .tensorflow import TensorFlowCUDASieve
from .version_constraint import VersionConstraintSieve


# Relative ordering of units is relevant, as the order specifies order
Expand All @@ -51,4 +52,5 @@
"MockBackportSieve",
"Py36SetuptoolsSieve",
"TensorFlowCUDASieve",
"PandasPy36Drop",
]
25 changes: 25 additions & 0 deletions thoth/adviser/sieves/pandas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
# thoth-adviser
# Copyright(C) 2020 Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Implementation of sieves used, specific for Pandas package."""

from .py36_drop import PandasPy36Drop


__all__ = [
"PandasPy36Drop",
]
79 changes: 79 additions & 0 deletions thoth/adviser/sieves/pandas/py36_drop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
# thoth-adviser
# Copyright(C) 2020 Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Sieve Pandas>=1.2 on Python 3.6 as Pandas>=1.2 dropped Python 3.6 support."""

import attr
from typing import Any
from typing import Optional
from typing import Generator
from typing import Dict
from typing import TYPE_CHECKING
import logging

from thoth.common import get_justification_link as jl
from thoth.python import PackageVersion

from ...enums import RecommendationType
from ...sieve import Sieve


if TYPE_CHECKING:
from ..pipeline_builder import PipelineBuilderContext


_LOGGER = logging.getLogger(__name__)


@attr.s(slots=True)
class PandasPy36Drop(Sieve):
"""A sieve that makes sure Pandas>=1.2 is not used on Python 3.6 or older.
See https://github.com/pandas-dev/pandas/pull/35214
"""

_MESSAGE = f"Pandas in versions >=1.2 dropped Python 3.6 support - see {jl('pandas_py36_drop')}"

_message_logged = attr.ib(type=bool, default=False, init=False)

def pre_run(self) -> None:
"""Initialize this pipeline unit before each run."""
self._message_logged = False

@classmethod
def should_include(cls, builder_context: "PipelineBuilderContext") -> Optional[Dict[str, Any]]:
"""Register this pipeline unit for adviser when Python 3.6 is used, not latest/testing recommendations."""
if not builder_context.is_adviser_pipeline() or builder_context.is_included(cls):
return None

if builder_context.recommendation_type in (RecommendationType.LATEST, RecommendationType.TESTING):
return None

if builder_context.project.runtime_environment.get_python_version_tuple() > (3, 6):
# Not a Python 3.6 or older environment.
return None

return {}

def run(self, package_versions: Generator[PackageVersion, None, None]) -> Generator[PackageVersion, None, None]:
"""Do not use Pandas>=1.2 on Python 3.6."""
for package_version in package_versions:
if package_version.name != "pandas" or package_version.semantic_version.release[:2] < (1, 2):
yield package_version
elif not self._message_logged:
self._message_logged = True
_LOGGER.warning(self._MESSAGE)

0 comments on commit 9aa5171

Please sign in to comment.