diff --git a/Dockerfile b/Dockerfile index 37374bba5aa..1d1a1bea8b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,11 @@ COPY . /azure-cli # 1. Build packages and store in tmp dir # 2. Install the cli and the other command modules that weren't included + +# Python image has build-in env $PYTHON_VERSION=3.10.10. +# `ARG PYTHON_VERSION="3.10"` works on ARM64, but it can't override the default value on AMD64. RUN ./scripts/install_full.sh && python ./scripts/trim_sdk.py \ + && python ./scripts/use_pyc.py /usr/local/lib/python${PYTHON_VERSION:0:4}/site-packages/ \ && cat /azure-cli/az.completion > ~/.bashrc \ && runDeps="$( \ scanelf --needed --nobanner --recursive /usr/local \ diff --git a/build_scripts/windows/scripts/build.cmd b/build_scripts/windows/scripts/build.cmd index 3944e96b925..4ce7d6fc438 100644 --- a/build_scripts/windows/scripts/build.cmd +++ b/build_scripts/windows/scripts/build.cmd @@ -139,36 +139,8 @@ copy %REPO_ROOT%\build_scripts\windows\resources\CLI_LICENSE.rtf %BUILDING_DIR% copy %REPO_ROOT%\build_scripts\windows\resources\ThirdPartyNotices.txt %BUILDING_DIR% copy %REPO_ROOT%\NOTICE.txt %BUILDING_DIR% -REM Remove .py and only deploy .pyc files -pushd %BUILDING_DIR%\Lib\site-packages -for /f %%f in ('dir /b /s *.pyc') do ( - set PARENT_DIR=%%~df%%~pf.. - echo !PARENT_DIR! | findstr /C:\Lib\site-packages\pip\ 1>nul - if !errorlevel! neq 0 ( - REM Only take the file name without 'pyc' extension: e.g., (same below) __init__.cpython-310 - set FILENAME=%%~nf - REM Truncate the '.cpython-310' postfix which is 12 chars long: __init__ - REM https://stackoverflow.com/a/636391/2199657 - set BASE_FILENAME=!FILENAME:~0,-12! - REM __init__.pyc - set pyc=!BASE_FILENAME!.pyc - REM Delete ..\__init__.py - del !PARENT_DIR!\!BASE_FILENAME!.py - REM Copy to ..\__init__.pyc - copy %%~f !PARENT_DIR!\!pyc! >nul - REM Delete __init__.pyc - del %%~f - ) ELSE ( - echo --SKIP !PARENT_DIR! under pip - ) -) -popd - -REM Remove __pycache__ -echo remove pycache -for /d /r %BUILDING_DIR%\Lib\site-packages\pip %%d in (__pycache__) do ( - if exist %%d rmdir /s /q "%%d" -) +REM replace .py with .pyc and remove __pycache__ dir to save space +%BUILDING_DIR%\python.exe %REPO_ROOT%\scripts\use_pyc.py %BUILDING_DIR%\Lib\site-packages\ %PYTHON_VERSION% REM Remove dist-info echo remove dist-info diff --git a/scripts/release/debian/build.sh b/scripts/release/debian/build.sh index f6471251312..8ca2eeeefb3 100755 --- a/scripts/release/debian/build.sh +++ b/scripts/release/debian/build.sh @@ -54,6 +54,10 @@ find ${WORKDIR}/src/ -name setup.py -type f | xargs -I {} dirname {} | grep -v a pip3 install -r ${WORKDIR}/src/azure-cli/requirements.py3.$(uname).txt $WORKDIR/python_env/bin/python3 ${WORKDIR}/scripts/trim_sdk.py +# replace .py with .pyc and remove __pycache__ dir to save space +site_packages_dir=$(cd $WORKDIR/python_env/lib/python3.*/site-packages/ ; pwd) +$WORKDIR/python_env/bin/python3 ${WORKDIR}/scripts/use_pyc.py $site_packages_dir + # Create create directory for debian build mkdir -p $WORKDIR/debian $SCRIPT_DIR/prepare.sh $WORKDIR/debian $WORKDIR/az.completion $WORKDIR diff --git a/scripts/release/rpm/azure-cli.spec b/scripts/release/rpm/azure-cli.spec index e63f58f7be6..aff29700fad 100644 --- a/scripts/release/rpm/azure-cli.spec +++ b/scripts/release/rpm/azure-cli.spec @@ -54,6 +54,10 @@ source %{repo_path}/scripts/install_full.sh # cffi 1.15.0 doesn't work with RPM: https://foss.heptapod.net/pypy/cffi/-/issues/513 %{python_cmd} -m pip install cffi==1.14.6 +# replace .py with .pyc and remove __pycache__ dir to save space +site_packages_dir=$(cd %{buildroot}%{cli_lib_dir}/lib/python3.*/site-packages/ ; pwd) +%{python_cmd} %{repo_path}/scripts/use_pyc.py $site_packages_dir + deactivate # Fix up %{buildroot} appearing in some files... diff --git a/scripts/use_pyc.py b/scripts/use_pyc.py new file mode 100644 index 00000000000..72c0106c999 --- /dev/null +++ b/scripts/use_pyc.py @@ -0,0 +1,81 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +import logging +import glob +import os +import platform +import re +import sys +from pathlib import Path +import shutil + +_LOGGER = logging.getLogger(__name__) + + +def calculate_folder_size(start_path): + """Calculate total size of a folder and file count.""" + # https://stackoverflow.com/questions/1392413/calculating-a-directorys-size-using-python + total_size = 0 + total_count = 0 + for dirpath, dirnames, filenames in os.walk(start_path): + for f in filenames: + fp = os.path.join(dirpath, f) + # skip if it is symbolic link + if not os.path.islink(fp): + total_count += 1 + total_size += os.path.getsize(fp) + + return total_size, total_count + + +def _print_folder_size(folder): + size, count = calculate_folder_size(folder) + size_in_mb = size / 1048576 # 1 MB = 1024 * 1024 B = 1048576 B + _LOGGER.info(f"{size_in_mb:.2f} MB, {count} files") + + +def main(folder, version=None): + _LOGGER.info(f'Replace .py with .pyc, base folder: {folder}') + _print_folder_size(folder) + if version is None: + version = re.search(r'python(\d\.\d+)', folder).group(1) + else: + # 3.10.10 + version = '.'.join(version.split('.')[:2]) + # invoke==1.2.0 has a weird file: invoke/completion/__pycache__/__init__.cpython-36.pyc + # define pyc suffix to skip it + pyc_suffix = f'cpython-{version.replace(".", "")}.pyc' + _LOGGER.info(f'pyc suffix: {pyc_suffix}') + for file in glob.glob(f'{folder}/**/__pycache__/*{pyc_suffix}', recursive=True): + # If pip's py files are also removed, the error is raised when installing some packages. + # See https://github.com/Azure/azure-cli/pull/25801 for details. + if os.path.join('site-packages', 'pip') in file: + continue + + # file is /opt/az/lib/python3.10/site-packages/websocket/__pycache__/_app.cpython-310.pyc + # py_filename is _app.py + py_filename = Path(file).name[:-len(pyc_suffix)] + 'py' + # py_path is /opt/az/lib/python3.10/site-packages/websocket/_app.py + py_path = Path(file).parent.parent / py_filename + if py_path.exists(): + py_path.unlink() + shutil.move(file, py_path.with_suffix('.pyc')) + + for f in glob.glob(f'{folder}/**/__pycache__', recursive=True): + # Remove pip __pycache__ folder for Windows only to save more space + if 'site-packages/pip' in f and not platform.system() == 'Windows': + continue + shutil.rmtree(f) + + _LOGGER.info('Finish processing') + _print_folder_size(folder) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + if len(sys.argv) == 2: + main(sys.argv[1]) + else: + main(sys.argv[1], sys.argv[2]) diff --git a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py index e4d1b93b12e..8a40b26be74 100644 --- a/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py +++ b/src/azure-cli/azure/cli/command_modules/rdbms/tests/latest/test_rdbms_flexible_commands.py @@ -28,13 +28,13 @@ AbstractPreparer, SingleValueReplacer) from azure.core.exceptions import HttpResponseError -from ..._client_factory import cf_mysql_flexible_private_dns_zone_suffix_operations, cf_postgres_flexible_private_dns_zone_suffix_operations -from ...flexible_server_virtual_network import prepare_private_network, prepare_private_dns_zone, DEFAULT_VNET_ADDRESS_PREFIX, DEFAULT_SUBNET_ADDRESS_PREFIX -from ...flexible_server_custom_postgres import DbContext as PostgresDbContext -from ...flexible_server_custom_mysql import DbContext as MysqlDbContext -from ...flexible_server_custom_mysql import _determine_iops -from ..._flexible_server_util import get_mysql_list_skus_info -from ..._util import retryable_method +from azure.cli.command_modules.rdbms._client_factory import cf_mysql_flexible_private_dns_zone_suffix_operations, cf_postgres_flexible_private_dns_zone_suffix_operations +from azure.cli.command_modules.rdbms.flexible_server_virtual_network import prepare_private_network, prepare_private_dns_zone, DEFAULT_VNET_ADDRESS_PREFIX, DEFAULT_SUBNET_ADDRESS_PREFIX +from azure.cli.command_modules.rdbms.flexible_server_custom_postgres import DbContext as PostgresDbContext +from azure.cli.command_modules.rdbms.flexible_server_custom_mysql import DbContext as MysqlDbContext +from azure.cli.command_modules.rdbms.flexible_server_custom_mysql import _determine_iops +from azure.cli.command_modules.rdbms._flexible_server_util import get_mysql_list_skus_info +from azure.cli.command_modules.rdbms._util import retryable_method from .conftest import mysql_location, mysql_paired_location, mysql_general_purpose_sku, mysql_memory_optimized_sku # Constants SERVER_NAME_PREFIX = 'azuredbclitest-'