Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Dynamically determine latest init runtime #7593

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
51 changes: 49 additions & 2 deletions samcli/commands/init/interactive_init_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
import pathlib
import re
import tempfile
from typing import Optional, Tuple

Expand All @@ -28,6 +29,7 @@
)
from samcli.lib.config.samconfig import DEFAULT_CONFIG_FILE_NAME
from samcli.lib.schemas.schemas_code_manager import do_download_source_code_binding, do_extract_and_merge_schemas_code
from samcli.lib.utils.architecture import SUPPORTED_RUNTIMES
from samcli.lib.utils.osutils import remove
from samcli.lib.utils.packagetype import IMAGE, ZIP
from samcli.local.common.runtime_template import (
Expand Down Expand Up @@ -323,6 +325,49 @@ def _generate_from_use_case(
)


def _get_latest_python_runtime() -> str:
"""
Returns the latest support version of Python
SAM CLI supports

Returns
-------
str:
The name of the latest Python runtime (ex. "python3.12")
"""

# set python3.9 as fallback
latest_major = 3
latest_minor = 9
lucashuy marked this conversation as resolved.
Show resolved Hide resolved

compiled_regex = re.compile(r"python(.*?)\.(.*)")

for runtime in SUPPORTED_RUNTIMES:
if not runtime.startswith("python"):
continue

# python3.12 => 3.12 => (3, 12)
version_match = re.match(compiled_regex, runtime)

if not version_match:
LOG.debug(f"Failed to match version while checking {runtime}")
continue

matched_groups = version_match.groups()

try:
version_major = int(matched_groups[0])
version_minor = int(matched_groups[1])
except (ValueError, IndexError):
LOG.debug(f"Failed to parse version while checking {runtime}")
continue

latest_major = version_major if version_major > latest_major else latest_major
latest_minor = version_minor if version_minor > latest_minor else latest_minor
lucashuy marked this conversation as resolved.
Show resolved Hide resolved

return f"python{latest_major}.{latest_minor}"
lucashuy marked this conversation as resolved.
Show resolved Hide resolved


def _generate_default_hello_world_application(
use_case: str,
package_type: Optional[str],
Expand Down Expand Up @@ -356,8 +401,10 @@ def _generate_default_hello_world_application(
"""
is_package_type_image = bool(package_type == IMAGE)
if use_case == "Hello World Example" and not (runtime or base_image or is_package_type_image or dependency_manager):
if click.confirm("\nUse the most popular runtime and package type? (Python and zip)"):
runtime, package_type, dependency_manager, pt_explicit = "python3.9", ZIP, "pip", True
latest_python = _get_latest_python_runtime()

if click.confirm(f"\nUse the most popular runtime and package type? ({latest_python} and zip)"):
runtime, package_type, dependency_manager, pt_explicit = _get_latest_python_runtime(), ZIP, "pip", True
return (runtime, package_type, dependency_manager, pt_explicit)


Expand Down
44 changes: 37 additions & 7 deletions tests/unit/commands/init/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import shutil
import subprocess
import tempfile
from unittest import mock
from parameterized import parameterized
import requests
from pathlib import Path
from typing import Dict, Any
Expand All @@ -25,7 +27,7 @@
get_template_value,
template_does_not_meet_filter_criteria,
)
from samcli.commands.init.interactive_init_flow import get_sorted_runtimes
from samcli.commands.init.interactive_init_flow import _get_latest_python_runtime, get_sorted_runtimes
from samcli.lib.init import GenerateProjectFailedError
from samcli.lib.utils import osutils
from samcli.lib.utils.git_repo import GitRepo
Expand Down Expand Up @@ -2006,9 +2008,9 @@ def test_init_cli_generate_default_hello_world_app(
request_mock.side_effect = requests.Timeout()
init_options_from_manifest_mock.return_value = [
{
"directory": "python3.9/cookiecutter-aws-sam-hello-python",
"directory": "python3.12/cookiecutter-aws-sam-hello-python",
"displayName": "Hello World Example",
"dependencyManager": "npm",
"dependencyManager": "pip",
"appTemplate": "hello-world",
"packageType": "Zip",
"useCaseName": "Hello World Example",
Expand All @@ -2026,10 +2028,10 @@ def test_init_cli_generate_default_hello_world_app(

get_preprocessed_manifest_mock.return_value = {
"Hello World Example": {
"python3.9": {
"python3.12": {
"Zip": [
{
"directory": "python3.9/cookiecutter-aws-sam-hello-python3.9",
"directory": "python3.12/cookiecutter-aws-sam-hello-python3.12",
"displayName": "Hello World Example",
"dependencyManager": "pip",
"appTemplate": "hello-world",
Expand Down Expand Up @@ -2070,16 +2072,17 @@ def test_init_cli_generate_default_hello_world_app(

runner = CliRunner()
result = runner.invoke(init_cmd, input=user_input)
print(result.stdout)
self.assertFalse(result.exception)
generate_project_patch.assert_called_once_with(
ANY,
ZIP,
"python3.9",
"python3.12",
"pip",
".",
"test-project",
True,
{"project_name": "test-project", "runtime": "python3.9", "architectures": {"value": ["x86_64"]}},
{"project_name": "test-project", "runtime": "python3.12", "architectures": {"value": ["x86_64"]}},
False,
False,
False,
Expand Down Expand Up @@ -3193,3 +3196,30 @@ def test_init_cli_generate_app_template_provide_via_application_insights_options
True,
False,
)

def test_latest_python_fetcher_returns_latest(self):
latest_python = "python3.100000"
lucashuy marked this conversation as resolved.
Show resolved Hide resolved

with mock.patch(
"samcli.commands.init.interactive_init_flow.SUPPORTED_RUNTIMES",
{"python3.2": Any, latest_python: Any, "python3.14": Any},
):
result = _get_latest_python_runtime()

self.assertEqual(result, latest_python)

@parameterized.expand(
[
("dotnet3.1",),
("foo bar",),
("",),
]
)
def test_latest_python_fetcher_fallback_invalid_runtime(self, invalid_runtime):
with mock.patch(
"samcli.commands.init.interactive_init_flow.SUPPORTED_RUNTIMES",
{invalid_runtime: Any},
):
result = _get_latest_python_runtime()

self.assertEqual(result, "python3.9")
Loading