diff --git a/.circleci/config.yml b/.circleci/config.yml index d718a3b..532c900 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: build: docker: - - image: circleci/python:3.6.1 + - image: circleci/python:3.6.15 working_directory: ~/skelebot steps: - checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ffe0f..a2d5b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,17 @@ Documenting All Changes to the Skelebot Project --- +## v1.37.0 +#### Changed +- **Python versions** | Support for python versions 3.6, 3.7, and 3.8, including base docker images, is **deprecated** and will be removed in skelebot 2.0 +- **R support** | All support for `R` and `R+Python` projects, including base docker images, is **deprecated** and will be removed in skelebot 2.0 +- **Python 3.11** | Adding support for Python 3.11 + +--- + ## v1.36.1 +#### Merged: 2023-06-21 +#### Released: 2023-06-21 #### Changed - **Artifactory** | Using the default aws profile should pass `None` to boto3 Session. diff --git a/VERSION b/VERSION index 60b0043..e4dce85 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.36.1 \ No newline at end of file +1.37.0 \ No newline at end of file diff --git a/base-images/python-base/3.11/Dockerfile b/base-images/python-base/3.11/Dockerfile new file mode 100644 index 0000000..ddfdba8 --- /dev/null +++ b/base-images/python-base/3.11/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.11-slim +MAINTAINER Sean Shookman + +# Install basic compilers and libraries commonly needed for downstream packages +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive \ + apt-get install -y -q build-essential libgomp1 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN pip --no-cache-dir install -U pip +RUN pip --no-cache-dir install jupyter +RUN pip --no-cache-dir install jupyterlab diff --git a/jobs/build.sh b/jobs/build.sh index 5f74c16..22896f2 100755 --- a/jobs/build.sh +++ b/jobs/build.sh @@ -16,6 +16,7 @@ docker build -t skelebot/python-base:3.7 base-images/python-base/3.7/ docker build -t skelebot/python-base:3.8 base-images/python-base/3.8/ docker build -t skelebot/python-base:3.9 base-images/python-base/3.9/ docker build -t skelebot/python-base:3.10 base-images/python-base/3.10/ +docker build -t skelebot/python-base:3.11 base-images/python-base/3.11/ # Build python-krb docker build -t skelebot/python-krb base-images/python-krb/ diff --git a/jobs/publish.sh b/jobs/publish.sh index bb236f0..9d9e51e 100755 --- a/jobs/publish.sh +++ b/jobs/publish.sh @@ -23,6 +23,7 @@ docker build -t skelebot/python-base:3.7 base-images/python-base/3.7/ docker build -t skelebot/python-base:3.8 base-images/python-base/3.8/ docker build -t skelebot/python-base:3.9 base-images/python-base/3.9/ docker build -t skelebot/python-base:3.10 base-images/python-base/3.10/ +docker build -t skelebot/python-base:3.11 base-images/python-base/3.11/ # Newer Docker clients will push latest by default and will need to do `docker push skelebot/python-base --all-tags` docker push skelebot/python-base diff --git a/skelebot/common.py b/skelebot/common.py index 9892ef6..f0b17fb 100644 --- a/skelebot/common.py +++ b/skelebot/common.py @@ -40,7 +40,10 @@ "R": _all_deps["R"], "R+Python": _all_deps, } -PYTHON_VERSIONS = ['3.6', '3.7', '3.8', '3.9', '3.10'] +DEPRECATED_LANGUAGES = ["R", "R+Python"] + +PYTHON_VERSIONS = ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11'] +DEPRECATED_VERSIONS = ['3.6', '3.7', '3.8'] TEMPLATE_PATH = "templates/{name}" TEMPLATES = { diff --git a/skelebot/objects/config.py b/skelebot/objects/config.py index dd4ffd5..b6c5cc3 100644 --- a/skelebot/objects/config.py +++ b/skelebot/objects/config.py @@ -5,7 +5,7 @@ from .param import Param from .skeleYaml import SkeleYaml from .component import Activation -from ..common import LANGUAGE_IMAGE, PYTHON_VERSIONS +from ..common import LANGUAGE_IMAGE, PYTHON_VERSIONS, DEPRECATED_VERSIONS, DEPRECATION_WARNING from ..components.componentFactory import ComponentFactory class Config(SkeleYaml): @@ -93,6 +93,13 @@ def __init__(self, name=None, env=None, description=None, version=None, maintain self.pythonVersion = pythonVersion self.gpu = gpu + if self.pythonVersion in DEPRECATED_VERSIONS: + print(DEPRECATION_WARNING.format( + code=f"support for Python version {self.pythonVersion}", + version="1.37.0", + msg="Please use a higher version." + )) + def toDict(self): """ Extends the parent function in order to logic for handling the conversion of diff --git a/skelebot/systems/scaffolding/prompt.py b/skelebot/systems/scaffolding/prompt.py index cb16564..712eaf9 100644 --- a/skelebot/systems/scaffolding/prompt.py +++ b/skelebot/systems/scaffolding/prompt.py @@ -1,16 +1,21 @@ """Prompt Function""" -def promptUser(message, options=None, boolean=False): +def promptUser(message, options=None, boolean=False, deprecated_options=None): """Construct dynamic prompts for user input and return the user-entered value""" inp = None + if deprecated_options is None: + deprecated_options = [] if (options): options = list(map(str, options)) + p_options = [] + for o in options: + p_options.append(o + " (DEPRECATED)" if o in deprecated_options else o) if (len(options) == 1): inp = options[0] while inp not in options: - inp = input("{msg} [{options}]: ".format(msg=message, options=', '.join(options))) + inp = input("{msg} [{options}]: ".format(msg=message, options=', '.join(p_options))) elif (boolean): inp = input("{msg} [Y/N]: ".format(msg=message)) in ["Y", "y"] else: diff --git a/skelebot/systems/scaffolding/scaffolder.py b/skelebot/systems/scaffolding/scaffolder.py index 934970e..4d6ac27 100644 --- a/skelebot/systems/scaffolding/scaffolder.py +++ b/skelebot/systems/scaffolding/scaffolder.py @@ -9,7 +9,8 @@ from ...objects.config import Config from ...components.componentFactory import ComponentFactory from ...systems.generators import dockerfile, dockerignore, readme, yaml -from ...common import LANGUAGE_DEPENDENCIES, TEMPLATES, TEMPLATE_PATH, GITHUB_RAW +from ...common import (LANGUAGE_DEPENDENCIES, TEMPLATES, TEMPLATE_PATH, GITHUB_RAW, + DEPRECATED_LANGUAGES, DEPRECATION_WARNING) from .prompt import promptUser class Scaffolder: @@ -74,7 +75,16 @@ def scaffold(self): description = promptUser("Enter a PROJECT DESCRIPTION") maintainer = promptUser("Enter a MAINTAINER NAME") contact = promptUser("Enter a CONTACT EMAIL") - language = promptUser("Select a LANGUAGE", options=list(LANGUAGE_DEPENDENCIES.keys())) + language = promptUser("Select a LANGUAGE", + options=list(LANGUAGE_DEPENDENCIES.keys()), + deprecated_options=DEPRECATED_LANGUAGES) + + if language in DEPRECATED_LANGUAGES: + print(DEPRECATION_WARNING.format( + code=f"support for {language} language", + version="1.37.0", + msg="Please use a different language." + )) # Configure Template Variables self.variables["name"] = name diff --git a/test/test_objects_config_validate.py b/test/test_objects_config_validate.py index 9f64275..7795ad2 100644 --- a/test/test_objects_config_validate.py +++ b/test/test_objects_config_validate.py @@ -1,5 +1,8 @@ import copy import unittest +from unittest import mock + +from colorama import Fore, Style from schema import SchemaError @@ -45,6 +48,15 @@ def test_valid(self): except: self.fail("Validation Raised Exception Unexpectedly") + @mock.patch('skelebot.objects.config.print') + def test_valid_deprecated(self, mock_print): + _ = sb.objects.config.Config(pythonVersion='3.6') + mock_print.assert_called_once_with( + Fore.YELLOW + "WARN" + Style.RESET_ALL + + " | The support for Python version 3.6 has been deprecated as of v1.37.0." + + " Please use a higher version." + ) + def test_invalid_mising(self): config = copy.deepcopy(self.config) del config['name'] diff --git a/test/test_systems_scaffolding_scaffolder.py b/test/test_systems_scaffolding_scaffolder.py index 28ce7b4..9acc86d 100644 --- a/test/test_systems_scaffolding_scaffolder.py +++ b/test/test_systems_scaffolding_scaffolder.py @@ -3,6 +3,8 @@ import unittest from unittest import mock +from colorama import Fore, Style + import skelebot as sb from skelebot.components.bump import Bump @@ -62,10 +64,42 @@ def test_execute_scaffold_abort(self, mock_prompt, mock_cFactory, mock_getcwd, m mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") mock_prompt.assert_any_call("Enter a MAINTAINER NAME") mock_prompt.assert_any_call("Enter a CONTACT EMAIL") - mock_prompt.assert_any_call("Select a LANGUAGE", options=["Python", "R", "R+Python"]) + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Dash", "Git"]) mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True) + @mock.patch('os.path.expanduser') + @mock.patch('os.getcwd') + @mock.patch('skelebot.systems.scaffolding.scaffolder.ComponentFactory') + @mock.patch('skelebot.systems.scaffolding.scaffolder.promptUser') + @mock.patch('skelebot.systems.scaffolding.scaffolder.print') + def test_execute_scaffold_deprecated(self, mock_print, mock_prompt, mock_cFactory, + mock_getcwd, mock_expanduser): + mock_expanduser.return_value = "test/plugins" + mock_prompt.side_effect = ["test", "test proj", "sean", "email", "R", "Default", False] + try: + scaffolder = sb.systems.scaffolding.scaffolder.Scaffolder(existing=False) + scaffolder.scaffold() + self.fail("Exception Expected") + except Exception as exc: + self.assertEqual(str(exc), "Aborting Scaffolding Process") + + mock_prompt.assert_any_call("Enter a PROJECT NAME") + mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") + mock_prompt.assert_any_call("Enter a MAINTAINER NAME") + mock_prompt.assert_any_call("Enter a CONTACT EMAIL") + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) + mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Git"]) + mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True) + + mock_print.assert_any_call(Fore.YELLOW + "WARN" + Style.RESET_ALL + + " | The support for R language has been deprecated as of v1.37.0." + + " Please use a different language.") + @mock.patch('os.path.expanduser') @mock.patch('os.getcwd') @mock.patch('os.makedirs') @@ -85,7 +119,9 @@ def test_execute_scaffold_existing_init(self, mock_prompt, mock_yaml, #mock_cFac mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") mock_prompt.assert_any_call("Enter a MAINTAINER NAME") mock_prompt.assert_any_call("Enter a CONTACT EMAIL") - mock_prompt.assert_any_call("Select a LANGUAGE", options=["Python", "R", "R+Python"]) + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Dash", "Git"]) mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True) @@ -137,7 +173,9 @@ def test_execute_scaffold_git_pull(self, mock_prompt, mock_yaml, mock_readme, mo mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") mock_prompt.assert_any_call("Enter a MAINTAINER NAME") mock_prompt.assert_any_call("Enter a CONTACT EMAIL") - mock_prompt.assert_any_call("Select a LANGUAGE", options=["Python", "R", "R+Python"]) + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Dash", "Git"]) mock_prompt.assert_any_call("Enter AWS-PROD PROFILE") mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True) @@ -207,7 +245,9 @@ def test_execute_scaffold_git_clone(self, mock_prompt, mock_yaml, mock_readme, m mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") mock_prompt.assert_any_call("Enter a MAINTAINER NAME") mock_prompt.assert_any_call("Enter a CONTACT EMAIL") - mock_prompt.assert_any_call("Select a LANGUAGE", options=["Python", "R", "R+Python"]) + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Dash", "Git"]) mock_prompt.assert_any_call("Enter AWS-PROD PROFILE") mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True) @@ -267,7 +307,9 @@ def test_execute_scaffold_dash(self, mock_prompt, mock_yaml, mock_readme, mock_p mock_prompt.assert_any_call("Enter a PROJECT DESCRIPTION") mock_prompt.assert_any_call("Enter a MAINTAINER NAME") mock_prompt.assert_any_call("Enter a CONTACT EMAIL") - mock_prompt.assert_any_call("Select a LANGUAGE", options=["Python", "R", "R+Python"]) + mock_prompt.assert_any_call("Select a LANGUAGE", + options=["Python", "R", "R+Python"], + deprecated_options=["R", "R+Python"]) mock_prompt.assert_any_call("Select a TEMPLATE", options=["Default", "Dash", "Git"]) mock_prompt.assert_any_call("Enter AWS-PROD PROFILE") mock_prompt.assert_any_call("Confirm Skelebot Setup", boolean=True)