From 8eb74e6c68f8425b66f092f1c1e516260dcf2d85 Mon Sep 17 00:00:00 2001 From: Filip Lajszczak <filip@lajszczak.dev> Date: Tue, 26 Nov 2024 14:37:22 +0000 Subject: [PATCH] Updates for different python and django versions. Version bump and new core. Actions betterification. Co-authored-by: Piotr Kaznowski <piotr@kazno.dev> --- .github/workflows/tests.yaml | 9 +- cli/django.py | 7 + pythonanywhere/__init__.py | 2 +- setup.py | 6 +- tests/conftest.py | 19 +++ tests/test_cli_django.py | 20 +-- tests/test_django_project.py | 22 ++-- tests/test_pa_autoconfigure_django.py | 13 +- ..._pa_start_django_webapp_with_virtualenv.py | 124 +++++++++--------- tests/test_virtualenvs.py | 6 +- 10 files changed, 126 insertions(+), 102 deletions(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 98d3d2c..fa9ed53 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -1,24 +1,19 @@ name: Tests -on: [push] +on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: - python-version: [ "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12", "3.13" ] name: Python ${{ matrix.python-version }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - name: Setup timezone - uses: zcong1993/setup-timezone@master - with: - timezone: UTC - - name: Set up Python uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5 with: diff --git a/cli/django.py b/cli/django.py index 7c6cc0a..ac36b7b 100644 --- a/cli/django.py +++ b/cli/django.py @@ -12,6 +12,12 @@ @app.command() def autoconfigure( repo_url: str = typer.Argument(..., help="url of remote git repository of your django project"), + branch: str = typer.Option( + "None", + "-b", + "--branch", + help="Branch name in case of multiple branches", + ), domain_name: str = typer.Option( "your-username.pythonanywhere.com", "-d", @@ -43,6 +49,7 @@ def autoconfigure( project = DjangoProject(domain, python_version) project.sanity_checks(nuke=nuke) project.download_repo(repo_url, nuke=nuke), + project.ensure_branch(branch), project.create_virtualenv(nuke=nuke) project.create_webapp(nuke=nuke) project.add_static_file_mappings() diff --git a/pythonanywhere/__init__.py b/pythonanywhere/__init__.py index 8782a8b..32e2f39 100644 --- a/pythonanywhere/__init__.py +++ b/pythonanywhere/__init__.py @@ -1 +1 @@ -__version__ = "0.15.4" +__version__ = "0.15.5" diff --git a/setup.py b/setup.py index 09c26a6..ef57b92 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setup( name="pythonanywhere", - version="0.15.4", + version="0.15.5", description="PythonAnywhere helper tools for users", long_description=long_description, long_description_content_type="text/markdown", @@ -22,6 +22,8 @@ "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.9", @@ -36,7 +38,7 @@ "docopt", "packaging", "python-dateutil", - "pythonanywhere_core==0.2.3", + "pythonanywhere_core==0.2.4", "requests", "schema", "snakesay", diff --git a/tests/conftest.py b/tests/conftest.py index ecd66b4..37794ef 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ import tempfile from getpass import getuser from pathlib import Path +from platform import python_version from unittest.mock import Mock, patch import psutil @@ -120,3 +121,21 @@ def process_killer(): for child in psutil.Process(p.pid).children(): child.kill() p.kill() + +@pytest.fixture +def running_python_version(): + return ".".join(python_version().split(".")[:2]) + +@pytest.fixture +def new_django_version(running_python_version): + if running_python_version in ["3.10", "3.11", "3.12", "3.13"]: + return "5.1.3" + else: + return "4.2.16" + +@pytest.fixture +def old_django_version(running_python_version): + if running_python_version in ["3.10", "3.11", "3.12", "3.13"]: + return "5.1.2" + else: + return "4.2.15" diff --git a/tests/test_cli_django.py b/tests/test_cli_django.py index 726dbde..9d7a4a3 100644 --- a/tests/test_cli_django.py +++ b/tests/test_cli_django.py @@ -59,6 +59,7 @@ def test_autoconfigure_calls_all_stuff_in_right_order(mock_django_project): assert mock_django_project.return_value.method_calls == [ call.sanity_checks(nuke=True), call.download_repo("repo.url", nuke=True), + call.ensure_branch("None"), call.create_virtualenv(nuke=True), call.create_webapp(nuke=True), call.add_static_file_mappings(), @@ -86,6 +87,8 @@ def test_autoconfigure_actually_works_against_example_repo( process_killer, running_python_version, ): + git_ref = "non-nested-old" if running_python_version in ["3.8", "3.9"] else "master" + expected_django_version = "4.2.16" if running_python_version in ["3.8", "3.9"] else "5.1.3" mocker.patch("cli.django.DjangoProject.start_bash") repo = "https://github.com/pythonanywhere/example-django-project.git" domain = "mydomain.com" @@ -99,10 +102,11 @@ def test_autoconfigure_actually_works_against_example_repo( domain, "-p", running_python_version, + "--branch", + git_ref, ], ) - expected_django_version = "3.0.6" expected_virtualenv = virtualenvs_folder / domain expected_project_path = fake_home / domain django_project_name = "myproject" @@ -122,7 +126,7 @@ def test_autoconfigure_actually_works_against_example_repo( with expected_settings_path.open() as f: lines = f.read().split("\n") - assert "MEDIA_ROOT = os.path.join(BASE_DIR, 'media')" in lines + assert "MEDIA_ROOT = Path(BASE_DIR / 'media')" in lines assert "ALLOWED_HOSTS = ['mydomain.com'] # type: List[str]" in lines assert "base.css" in os.listdir(str(fake_home / domain / "static/admin/css")) @@ -180,6 +184,7 @@ def test_start_actually_creates_django_project_in_virtualenv_with_hacked_setting virtualenvs_folder, api_token, running_python_version, + new_django_version, ): runner.invoke( app, @@ -188,7 +193,7 @@ def test_start_actually_creates_django_project_in_virtualenv_with_hacked_setting "-d", "mydomain.com", "-j", - "2.2.12", + new_django_version, "-p", running_python_version, ], @@ -204,11 +209,11 @@ def test_start_actually_creates_django_project_in_virtualenv_with_hacked_setting .decode() .strip() ) - assert django_version == "2.2.12" + assert django_version == new_django_version with (fake_home / "mydomain.com/mysite/settings.py").open() as f: lines = f.read().split("\n") - assert "MEDIA_ROOT = os.path.join(BASE_DIR, 'media')" in lines + assert "MEDIA_ROOT = Path(BASE_DIR / 'media')" in lines assert "ALLOWED_HOSTS = ['mydomain.com']" in lines assert "base.css" in os.listdir(str(fake_home / "mydomain.com/static/admin/css")) @@ -222,10 +227,9 @@ def test_nuke_option_lets_you_run_twice( virtualenvs_folder, api_token, running_python_version, + old_django_version, + new_django_version, ): - old_django_version = "2.2.12" - new_django_version = "3.0.6" - runner.invoke( app, [ diff --git a/tests/test_django_project.py b/tests/test_django_project.py index 10507ff..d2c7469 100644 --- a/tests/test_django_project.py +++ b/tests/test_django_project.py @@ -247,19 +247,21 @@ def test_nuke_option_handles_directory_not_existing(self, mock_subprocess, fake_ @pytest.fixture -def non_nested_submodule(): +def non_nested_submodule(running_python_version): + git_ref = "non-nested-old" if running_python_version in ["3.8", "3.9"] else "master" subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"]) submodule_path = Path(__file__).parents[1] / "submodules" / "example-django-project" - subprocess.check_call(["git", "checkout", "master"], cwd=str(submodule_path)) + subprocess.check_call(["git", "checkout", git_ref], cwd=str(submodule_path)) yield submodule_path subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"]) @pytest.fixture -def more_nested_submodule(): +def more_nested_submodule(running_python_version): + git_ref = "more-nested-old" if running_python_version in ["3.8", "3.9"] else "morenested" subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"]) submodule_path = Path(__file__).parents[1] / "submodules" / "example-django-project" - subprocess.check_call(["git", "checkout", "morenested"], cwd=str(submodule_path)) + subprocess.check_call(["git", "checkout", git_ref], cwd=str(submodule_path)) yield submodule_path subprocess.check_call(["git", "submodule", "update", "--init", "--recursive"]) @@ -509,9 +511,8 @@ def test_updates_wsgi_file_from_template(self, virtualenvs_folder): @pytest.mark.slowtest def test_actually_produces_wsgi_file_that_can_import_project_non_nested( - self, fake_home, non_nested_submodule, virtualenvs_folder + self, fake_home, non_nested_submodule, virtualenvs_folder, running_python_version ): - running_python_version = ".".join(python_version().split(".")[:2]) project = DjangoProject("mydomain.com", running_python_version) shutil.copytree(str(non_nested_submodule), str(project.project_path)) if running_python_version in ["3.8", "3.9", "3.10", "3.11"]: @@ -529,15 +530,14 @@ def test_actually_produces_wsgi_file_that_can_import_project_non_nested( @pytest.mark.slowtest def test_actually_produces_wsgi_file_that_can_import_nested_project( - self, fake_home, more_nested_submodule, virtualenvs_folder + self, fake_home, more_nested_submodule, virtualenvs_folder, running_python_version ): - running_python_version = ".".join(python_version().split(".")[:2]) project = DjangoProject("mydomain.com", running_python_version) shutil.copytree(str(more_nested_submodule), str(project.project_path)) - if running_python_version in ["3.8", "3.9", "3.10", "3.11"]: - project.create_virtualenv(django_version="latest") - else: + if running_python_version in ["3.8", "3.9"]: project.create_virtualenv() + else: + project.create_virtualenv(django_version="latest") project.find_django_files() project.wsgi_file_path = Path(tempfile.NamedTemporaryFile().name) diff --git a/tests/test_pa_autoconfigure_django.py b/tests/test_pa_autoconfigure_django.py index fbf879f..1e4e90c 100644 --- a/tests/test_pa_autoconfigure_django.py +++ b/tests/test_pa_autoconfigure_django.py @@ -1,4 +1,3 @@ -from platform import python_version from unittest.mock import call, patch import os import pytest @@ -7,6 +6,7 @@ import time from scripts.pa_autoconfigure_django import main +from tests.conftest import new_django_version class TestMain: @@ -33,9 +33,9 @@ def test_calls_all_stuff_in_right_order(self): @pytest.mark.slowtest def test_actually_works_against_example_repo( - self, fake_home, virtualenvs_folder, api_token, process_killer + self, fake_home, virtualenvs_folder, api_token, process_killer, running_python_version, new_django_version ): - running_python_version = ".".join(python_version().split(".")[:2]) + git_ref = "non-nested-old" if running_python_version in ["3.8", "3.9"] else "master" repo = 'https://github.com/pythonanywhere/example-django-project.git' domain = 'mydomain.com' with patch('scripts.pa_autoconfigure_django.DjangoProject.update_wsgi_file'): @@ -43,13 +43,12 @@ def test_actually_works_against_example_repo( with patch('pythonanywhere_core.webapp.call_api'): main( repo_url=repo, - branch="master", + branch=git_ref, domain=domain, python_version=running_python_version, nuke=False ) - expected_django_version = '3.0.6' expected_virtualenv = virtualenvs_folder / domain expected_project_path = fake_home / domain django_project_name = 'myproject' @@ -60,11 +59,11 @@ def test_actually_works_against_example_repo( '-c' 'import django; print(django.get_version())' ]).decode().strip() - assert django_version == expected_django_version + assert django_version == new_django_version with expected_settings_path.open() as f: lines = f.read().split('\n') - assert "MEDIA_ROOT = os.path.join(BASE_DIR, 'media')" in lines + assert "MEDIA_ROOT = Path(BASE_DIR / 'media')" in lines assert "ALLOWED_HOSTS = ['mydomain.com'] # type: List[str]" in lines assert 'base.css' in os.listdir(str(fake_home / domain / 'static/admin/css')) diff --git a/tests/test_pa_start_django_webapp_with_virtualenv.py b/tests/test_pa_start_django_webapp_with_virtualenv.py index 194ca76..c56c5d1 100644 --- a/tests/test_pa_start_django_webapp_with_virtualenv.py +++ b/tests/test_pa_start_django_webapp_with_virtualenv.py @@ -1,78 +1,78 @@ import os import subprocess -from platform import python_version -from unittest.mock import call, patch +from unittest.mock import call, patch, sentinel import pytest from scripts.pa_start_django_webapp_with_virtualenv import main -class TestMain: - def test_calls_all_stuff_in_right_order(self): - with patch("scripts.pa_start_django_webapp_with_virtualenv.DjangoProject") as mock_DjangoProject: - main("www.domain.com", "django.version", "python.version", nuke="nuke option") - assert mock_DjangoProject.call_args == call("www.domain.com", "python.version") - assert mock_DjangoProject.return_value.method_calls == [ - call.sanity_checks(nuke="nuke option"), - call.create_virtualenv("django.version", nuke="nuke option"), - call.run_startproject(nuke="nuke option"), - call.find_django_files(), - call.update_settings_file(), - call.run_collectstatic(), - call.create_webapp(nuke="nuke option"), - call.add_static_file_mappings(), - call.update_wsgi_file(), - call.webapp.reload(), - ] +def test_calls_all_stuff_in_right_order(mocker): + mock_DjangoProject = mocker.patch( + "scripts.pa_start_django_webapp_with_virtualenv.DjangoProject" + ) - @pytest.mark.slowtest - def test_actually_creates_django_project_in_virtualenv_with_hacked_settings_and_static_files( - self, fake_home, virtualenvs_folder, api_token - ): - running_python_version = ".".join(python_version().split(".")[:2]) - with patch("scripts.pa_start_django_webapp_with_virtualenv.DjangoProject.update_wsgi_file"): - with patch("pythonanywhere_core.webapp.call_api"): - main("mydomain.com", "2.2.12", running_python_version, nuke=False) + main( + sentinel.domain, sentinel.django_version, sentinel.python_version, nuke=sentinel.nuke + ) + assert mock_DjangoProject.call_args == call(sentinel.domain, sentinel.python_version) + assert mock_DjangoProject.return_value.method_calls == [ + call.sanity_checks(nuke=sentinel.nuke), + call.create_virtualenv(sentinel.django_version, nuke=sentinel.nuke), + call.run_startproject(nuke=sentinel.nuke), + call.find_django_files(), + call.update_settings_file(), + call.run_collectstatic(), + call.create_webapp(nuke=sentinel.nuke), + call.add_static_file_mappings(), + call.update_wsgi_file(), + call.webapp.reload(), + ] - django_version = ( - subprocess.check_output( - [ - str(virtualenvs_folder / "mydomain.com/bin/python"), - "-c" "import django; print(django.get_version())", - ] - ) - .decode() - .strip() - ) - assert django_version == "2.2.12" +@pytest.mark.slowtest +def test_actually_creates_django_project_in_virtualenv_with_hacked_settings_and_static_files( + fake_home, virtualenvs_folder, api_token, running_python_version, new_django_version +): + with patch("scripts.pa_start_django_webapp_with_virtualenv.DjangoProject.update_wsgi_file"): + with patch("pythonanywhere_core.webapp.call_api"): + main("mydomain.com", new_django_version, running_python_version, nuke=False) - with (fake_home / "mydomain.com/mysite/settings.py").open() as f: - lines = f.read().split("\n") - assert "MEDIA_ROOT = os.path.join(BASE_DIR, 'media')" in lines - assert "ALLOWED_HOSTS = ['mydomain.com']" in lines + output_django_version = ( + subprocess.check_output( + [ + str(virtualenvs_folder / "mydomain.com/bin/python"), + "-c" "import django; print(django.get_version())", + ] + ) + .decode() + .strip() + ) + assert output_django_version == new_django_version - assert "base.css" in os.listdir(str(fake_home / "mydomain.com/static/admin/css")) + with (fake_home / "mydomain.com/mysite/settings.py").open() as f: + lines = f.read().split("\n") + assert "MEDIA_ROOT = Path(BASE_DIR / 'media')" in lines + assert "ALLOWED_HOSTS = ['mydomain.com']" in lines - @pytest.mark.slowtest - def test_nuke_option_lets_you_run_twice(self, fake_home, virtualenvs_folder, api_token): + assert "base.css" in os.listdir(str(fake_home / "mydomain.com/static/admin/css")) - with patch("scripts.pa_start_django_webapp_with_virtualenv.DjangoProject.update_wsgi_file"): - with patch("pythonanywhere_core.webapp.call_api"): - running_python_version = ".".join(python_version().split(".")[:2]) - old_django_version = "2.2.12" - new_django_version = "3.0.6" +@pytest.mark.slowtest +def test_nuke_option_lets_you_run_twice( + fake_home, virtualenvs_folder, api_token, running_python_version, new_django_version, old_django_version +): - main("mydomain.com", old_django_version, running_python_version, nuke=False) - main("mydomain.com", new_django_version, running_python_version, nuke=True) + with patch("scripts.pa_start_django_webapp_with_virtualenv.DjangoProject.update_wsgi_file"): + with patch("pythonanywhere_core.webapp.call_api"): + main("mydomain.com", old_django_version, running_python_version, nuke=False) + main("mydomain.com", new_django_version, running_python_version, nuke=True) - django_version = ( - subprocess.check_output( - [ - str(virtualenvs_folder / "mydomain.com/bin/python"), - "-c" "import django; print(django.get_version())", - ] - ) - .decode() - .strip() + django_version = ( + subprocess.check_output( + [ + str(virtualenvs_folder / "mydomain.com/bin/python"), + "-c" "import django; print(django.get_version())", + ] ) - assert django_version == new_django_version + .decode() + .strip() + ) + assert django_version == new_django_version diff --git a/tests/test_virtualenvs.py b/tests/test_virtualenvs.py index 783afad..5ce69a6 100644 --- a/tests/test_virtualenvs.py +++ b/tests/test_virtualenvs.py @@ -45,8 +45,7 @@ def test_install_pip_installs_each_package(self, mock_subprocess, virtualenvs_fo assert command_list == [pip_path, "install", "package1", "package2==1.1.2"] @pytest.mark.slowtest - def test_actually_installing_a_real_package(self, fake_home, virtualenvs_folder): - running_python_version = ".".join(python_version().split(".")[:2]) + def test_actually_installing_a_real_package(self, fake_home, virtualenvs_folder, running_python_version): v = Virtualenv("www.adomain.com", running_python_version) v.create(nuke=False) v.pip_install("aafigure") @@ -54,8 +53,7 @@ def test_actually_installing_a_real_package(self, fake_home, virtualenvs_folder) subprocess.check_call([str(v.path / "bin/python"), "-c" "import aafigure"]) @pytest.mark.slowtest - def test_gets_version(self, fake_home, virtualenvs_folder): - running_python_version = ".".join(python_version().split(".")[:2]) + def test_gets_version(self, fake_home, virtualenvs_folder, running_python_version): v = Virtualenv("www.adomain.com", running_python_version) v.create(nuke=False) v.pip_install("aafigure==0.6")