From 41be0b1e2c2c305cbe01b45468d91b596131f08f Mon Sep 17 00:00:00 2001 From: "Brutus (robot)" Date: Sun, 17 May 2026 21:07:55 +0000 Subject: [PATCH 1/2] Bump docformatter from v1.7.7 to v1.7.8 --- .pre-commit-config.yaml | 2 +- debugger/setup.py | 10 +- src/briefcase/bootstraps/console.py | 8 +- src/briefcase/bootstraps/empty.py | 8 +- src/briefcase/bootstraps/toga.py | 8 +- src/briefcase/channels/base.py | 4 +- src/briefcase/commands/base.py | 33 ++- src/briefcase/commands/build.py | 8 +- src/briefcase/commands/convert.py | 8 +- src/briefcase/commands/create.py | 8 +- src/briefcase/commands/dev.py | 9 +- src/briefcase/commands/new.py | 1 - src/briefcase/commands/package.py | 8 +- src/briefcase/commands/run.py | 18 +- src/briefcase/commands/update.py | 1 - src/briefcase/config.py | 96 ++++---- src/briefcase/console.py | 25 +- src/briefcase/integrations/android_sdk.py | 90 ++++--- src/briefcase/integrations/base.py | 4 +- src/briefcase/integrations/docker.py | 13 +- src/briefcase/integrations/file.py | 15 +- src/briefcase/integrations/flatpak.py | 81 ++++--- src/briefcase/integrations/java.py | 16 +- src/briefcase/integrations/subprocess.py | 37 +-- .../integrations/virtual_environment.py | 7 +- src/briefcase/integrations/visualstudio.py | 4 +- src/briefcase/integrations/xcode.py | 220 +++++++++--------- src/briefcase/platforms/android/gradle.py | 32 +-- src/briefcase/platforms/iOS/xcode.py | 29 +-- src/briefcase/platforms/linux/appimage.py | 90 +++---- src/briefcase/platforms/linux/flatpak.py | 36 +-- src/briefcase/platforms/linux/system.py | 135 ++++++----- src/briefcase/platforms/macOS/__init__.py | 85 +++---- src/briefcase/platforms/macOS/filters.py | 8 +- src/briefcase/platforms/web/static.py | 6 +- src/briefcase/platforms/windows/__init__.py | 86 +++---- tests/commands/base/test_end_of_life.py | 1 - tests/commands/base/test_finalize.py | 1 - .../base/test_verify_requires_python.py | 4 - .../convert/test__find_changelog_filename.py | 1 - .../test_cleanup_app_support_package.py | 1 - tests/commands/create/test_create_app.py | 1 - .../create/test_generate_app_template.py | 1 - .../commands/create/test_install_app_code.py | 1 - .../create/test_install_app_requirements.py | 4 +- .../test_install_app_support_package.py | 1 - .../create/test_install_stub_binary.py | 1 - tests/commands/new/test_build_app_context.py | 4 - tests/commands/new/test_build_gui_context.py | 13 +- tests/commands/new/test_call.py | 1 - tests/commands/new/test_select_bootstrap.py | 3 - tests/commands/package/test_call.py | 1 - tests/commands/update/test_update_app.py | 1 - tests/commands/upgrade/test_call.py | 1 - tests/config/test_parse_config.py | 24 +- .../test_validate_document_type_config.py | 4 - tests/conftest.py | 1 - .../Console/test_release_console_control.py | 1 - tests/console/Console/test_text_question.py | 1 - tests/console/Console/test_warning_banner.py | 149 ++++++------ tests/console/test_Log.py | 1 - .../android_sdk/AndroidSDK/test_avd_config.py | 15 +- .../AndroidSDK/test_list_installed_images.py | 1 - .../AndroidSDK/test_select_target_device.py | 7 - .../AndroidSDK/test_start_emulator.py | 1 - .../AndroidSDK/test_update_emulator_config.py | 44 +--- .../android_sdk/AndroidSDK/test_verify.py | 3 - .../docker/test_DockerAppContext__Popen.py | 1 - .../docker/test_DockerAppContext__run.py | 2 - .../docker/test_DockerAppContext__verify.py | 1 - .../docker/test_Docker__x11_passthrough.py | 5 - .../integrations/file/test_File__download.py | 5 +- tests/integrations/java/test_JDK__upgrade.py | 1 - tests/integrations/java/test_JDK__verify.py | 1 - .../test_LinuxDeployURLPlugin__verify.py | 2 - .../test_LinuxDeploy__verify_plugins.py | 5 - tests/integrations/linuxdeploy/utils.py | 1 - .../subprocess/test_Subprocess__Popen.py | 6 - .../test_Subprocess__check_output.py | 4 - .../test_Subprocess__parse_output.py | 5 - .../subprocess/test_Subprocess__prepare.py | 1 - ...est_Subprocess__run__controlled_console.py | 2 - ...t_Subprocess__run__stream_output__False.py | 6 - ...st_Subprocess__run__stream_output__True.py | 5 - .../test_Subprocess__stream_output.py | 2 - .../test_NoOpVenvContext__clean.py | 1 - .../test_VenvContext__check_output.py | 1 - .../test_VenvContext__clean.py | 1 - .../test_VenvContext__recreate.py | 2 - .../windows_sdk/test_WindowsSDK__verify.py | 4 +- .../test_verify_command_line_tools_install.py | 1 - .../xcode/test_verify_xcode_install.py | 1 - tests/platforms/android/gradle/conftest.py | 1 - tests/platforms/iOS/xcode/test_create.py | 1 - tests/platforms/linux/appimage/test_build.py | 2 - .../platforms/linux/appimage/test_package.py | 1 - tests/platforms/linux/system/test_build.py | 1 - .../system/test_mixin___deb_devirtualize.py | 2 - .../test_mixin__verify_system_packages.py | 2 + tests/platforms/linux/system/test_run.py | 3 - .../platforms/linux/test_DockerOpenCommand.py | 1 - .../linux/test_LocalRequirementsMixin.py | 1 - .../macOS/app/package/test_package.py | 5 - .../app/package/test_resume_notarization.py | 1 - tests/platforms/macOS/app/test_create.py | 13 +- tests/platforms/macOS/app/test_signing.py | 6 +- tests/platforms/macOS/conftest.py | 1 - ...ackagesMergeMixin__find_binary_packages.py | 2 - tests/platforms/macOS/xcode/conftest.py | 8 +- tests/platforms/web/static/conftest.py | 34 +-- .../web/static/test_build__process_wheel.py | 2 - .../test_build_extract_pyscript_config.py | 9 +- .../web/static/test_build_write_inserts.py | 33 +-- tests/platforms/web/static/test_package.py | 1 - .../windows/app/create/test_create.py | 1 - .../app/create/test_install_app_resources.py | 2 - .../windows/app/create/test_txt_to_rtf.py | 20 +- tests/platforms/windows/app/test_build.py | 2 - tests/platforms/windows/app/test_package.py | 5 - tests/platforms/windows/app/test_run.py | 1 - .../windows/visualstudio/test_build.py | 1 - .../windows/visualstudio/test_package.py | 1 - .../windows/visualstudio/test_run.py | 2 - tests/test_cmdline.py | 4 - tests/utils.py | 29 ++- 125 files changed, 788 insertions(+), 1018 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c2541a6ac3..0c8581437a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/PyCQA/docformatter - rev: v1.7.7 + rev: v1.7.8 hooks: - id: docformatter args: [--in-place, --black] diff --git a/debugger/setup.py b/debugger/setup.py index fce94a2faf..31ea8e10eb 100644 --- a/debugger/setup.py +++ b/debugger/setup.py @@ -9,11 +9,11 @@ class install_with_pth(install): """Custom install command to install a .pth file for distutils patching. - This hack is necessary because there's no standard way to install behavior - on startup (and it's debatable if there should be one). This hack (ab)uses - the `extra_path` behavior in Setuptools to install a `.pth` file with - implicit behavior on startup to give higher precedence to the local version - of `distutils` over the version from the standard library. + This hack is necessary because there's no standard way to install behavior on + startup (and it's debatable if there should be one). This hack (ab)uses the + `extra_path` behavior in Setuptools to install a `.pth` file with implicit behavior + on startup to give higher precedence to the local version of `distutils` over the + version from the standard library. Please do not replicate this behavior. """ diff --git a/src/briefcase/bootstraps/console.py b/src/briefcase/bootstraps/console.py index ff244c1b80..96d3ce0466 100644 --- a/src/briefcase/bootstraps/console.py +++ b/src/briefcase/bootstraps/console.py @@ -22,12 +22,10 @@ def main(): """ def app_start_source(self): - return """\ -from {{ cookiecutter.module_name }}.app import main + return r"""\ from {{ cookiecutter.module_name }}.app import main. -if __name__ == "__main__": - main() -""" + if __name__ == "__main__": main() + """ def pyproject_table_briefcase_app_extra_content(self): return """ diff --git a/src/briefcase/bootstraps/empty.py b/src/briefcase/bootstraps/empty.py index 9fc0ee36de..a01efae5b0 100644 --- a/src/briefcase/bootstraps/empty.py +++ b/src/briefcase/bootstraps/empty.py @@ -13,12 +13,10 @@ def main(): """ def app_start_source(self): - return """\ -from {{ cookiecutter.module_name }}.app import main + return r"""\ from {{ cookiecutter.module_name }}.app import main. -if __name__ == "__main__": - main() -""" + if __name__ == "__main__": main() + """ def pyproject_table_briefcase_app_extra_content(self): return """ diff --git a/src/briefcase/bootstraps/toga.py b/src/briefcase/bootstraps/toga.py index 671e7aad6a..6c12a20f3d 100644 --- a/src/briefcase/bootstraps/toga.py +++ b/src/briefcase/bootstraps/toga.py @@ -28,12 +28,10 @@ def main(): ''' def app_start_source(self): - return """\ -from {{ cookiecutter.module_name }}.app import main + return r"""\ from {{ cookiecutter.module_name }}.app import main. -if __name__ == "__main__": - main().main_loop() -""" + if __name__ == "__main__": main().main_loop() + """ def pyproject_table_briefcase_app_extra_content(self): return """ diff --git a/src/briefcase/channels/base.py b/src/briefcase/channels/base.py index 00a2089112..e08459a479 100644 --- a/src/briefcase/channels/base.py +++ b/src/briefcase/channels/base.py @@ -14,8 +14,8 @@ class PublishCommandAPI(Protocol): """Stable API surface exposed to publication channel plugins. - This defines the minimal set of attributes and methods that a plugin - can rely on from the ``command`` parameter passed to ``publish_app()``. + This defines the minimal set of attributes and methods that a plugin can rely on + from the ``command`` parameter passed to ``publish_app()``. """ console: Console diff --git a/src/briefcase/commands/base.py b/src/briefcase/commands/base.py index 7158bd206f..24e849f92e 100644 --- a/src/briefcase/commands/base.py +++ b/src/briefcase/commands/base.py @@ -79,8 +79,8 @@ def full_options(state, options): :param state: The current command state. Can be ``None``. :param options: The base options. - :returns: A dictionary containing all of ``options``, with any values - provided in ``state`` overriding the base ``options`` values. + :returns: A dictionary containing all of ``options``, with any values provided in + ``state`` overriding the base ``options`` values. """ if state is not None: full = options.copy() @@ -446,7 +446,6 @@ def unbuilt_executable_path(self, app) -> Path: :param app: The app config """ - if sys.platform == "win32": # pragma: no-cover-if-not-windows suffix = self.binary_executable_path(app).suffix else: # pragma: no-cover-if-is-windows @@ -691,8 +690,8 @@ def finalize_app_config(self, app: DraftAppConfig, **kwargs) -> FinalizedAppConf configuration, and performs any other app-specific platform configuration and verification that is required as a result of command-line arguments. - Platform overrides should call ``super().finalize_app_config(app, **kwargs)`` - to construct the ``FinalizedAppConfig``. + Platform overrides should call ``super().finalize_app_config(app, **kwargs)`` to + construct the ``FinalizedAppConfig``. :param app: The app configuration to finalize. :param kwargs: Runtime attributes forwarded to the FinalizedAppConfig @@ -812,7 +811,6 @@ def verify_app_template(self, app: FinalizedAppConfig): :param app: app configuration """ - # Skip this check if the template isn't rolled out # or if the command doesn't support templates try: @@ -841,7 +839,6 @@ def verify_app_template(self, app: FinalizedAppConfig): def verify_required_python(self, app: FinalizedAppConfig): """Verify that the running version of Python meets the project's specifications.""" - requires_python = getattr(self.global_config, "requires_python", None) if not requires_python: return @@ -1149,8 +1146,8 @@ def update_cookiecutter_cache(self, template: str, branch="master"): :param template: The template URL or path. :param branch: The template branch to use. Default: ``master`` - :return: The path to the cached template. This may be the originally - provided path if the template was a file path. + :return: The path to the cached template. This may be the originally provided + path if the template was a file path. """ if is_repo_url(template): # The app template is a repository URL. @@ -1235,17 +1232,17 @@ def update_cookiecutter_cache(self, template: str, branch="master"): # warn the user that the template may be stale. self.console.debug(str(e)) self.console.warning( - """ -************************************************************************* -** WARNING: Unable to update template ** -************************************************************************* + """************************************************************* + ************ ** WARNING: Unable to update template + ** ************************************************************* + ************ - Briefcase is unable the update the application template. This - may be because your computer is currently offline. Briefcase will - use existing template without updating. + Briefcase is unable the update the application template. This + may be because your computer is currently offline. Briefcase will + use existing template without updating. -************************************************************************* -""" + ************************************************************************* + """ ) try: diff --git a/src/briefcase/commands/build.py b/src/briefcase/commands/build.py index 5b160cc4c0..e200862b12 100644 --- a/src/briefcase/commands/build.py +++ b/src/briefcase/commands/build.py @@ -32,6 +32,7 @@ def build_app(self, app: FinalizedAppConfig, **options): :param app: The application to build """ + # Default implementation; nothing to build. def _build_app( @@ -45,9 +46,10 @@ def _build_app( no_update: bool, **options, ) -> dict | None: - """Internal method to invoke a build on a single app. Ensures the app exists, - and has been updated (if requested) before attempting to issue the actual build - command. + """Internal method to invoke a build on a single app. + + Ensures the app exists, and has been updated (if requested) before attempting to + issue the actual build command. :param app: The application to build :param update: Should the application be updated before building? diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index 2141f45801..b853a18f6c 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -722,12 +722,10 @@ def convert_app( prefix=context["app_name"], ) self.console.info( - """ -To run your application, type: - - $ briefcase dev + """To run your application, type: -""" + $ briefcase dev + """ ) def validate_pyproject_file(self) -> None: diff --git a/src/briefcase/commands/create.py b/src/briefcase/commands/create.py index 3a7aa146cc..f04e1ed87e 100644 --- a/src/briefcase/commands/create.py +++ b/src/briefcase/commands/create.py @@ -36,8 +36,7 @@ def cookiecutter_cache_path(template): This will return a valid path, regardless of whether `template` - :param template: The template to use. This can be a filesystem path or - a URL. + :param template: The template to use. This can be a filesystem path or a URL. :returns: The path that cookiecutter would use for the given template name. """ template = template.rstrip("/") @@ -191,8 +190,8 @@ def _x_permissions(self, app: FinalizedAppConfig): """Extract the known cross-platform permission definitions from the app's permissions definitions. - After calling this method, the ``permissions`` declaration for the app will - only contain keys that are *not* cross-platform keys. + After calling this method, the ``permissions`` declaration for the app will only + contain keys that are *not* cross-platform keys. :param app: The config object for the app :returns: A dictionary of known cross-platform permission definitions. @@ -548,7 +547,6 @@ def _write_requirements_file( additional requirement installer argumentss should be written if the template supports it. """ - with self.console.wait_bar("Writing requirements file..."): with requirements_path.open("w", encoding="utf-8") as f: # Add timestamp so build systems (such as Gradle) detect a change diff --git a/src/briefcase/commands/dev.py b/src/briefcase/commands/dev.py index f702dc3187..6c8f6ded79 100644 --- a/src/briefcase/commands/dev.py +++ b/src/briefcase/commands/dev.py @@ -102,7 +102,6 @@ def install_dev_requirements( :param venv: The context object used to run commands inside the virtual environment. """ - requires = app.requires or [] if app.test_requires: requires.extend(app.test_requires) @@ -214,7 +213,6 @@ def get_environment(self, app: FinalizedAppConfig): :param app: The config object for the app """ - env = { "PYTHONPATH": os.pathsep.join( os.fsdecode(Path.cwd() / path) for path in app.PYTHONPATH() @@ -237,10 +235,9 @@ def get_environment(self, app: FinalizedAppConfig): def venv_name(self) -> str: """Returns the name of the virtual environment directory. - The environment name is platform and Python version specific, so - that multiple OSes and Python versions can share a `.briefcase` - folder. The name is based on the extension module filename that the - platform uses (e.g., cpython-313-darwin). + The environment name is platform and Python version specific, so that multiple + OSes and Python versions can share a `.briefcase` folder. The name is based on + the extension module filename that the platform uses (e.g., cpython-313-darwin). :returns: Name for virtual environment directory """ diff --git a/src/briefcase/commands/new.py b/src/briefcase/commands/new.py index dcf1296296..44a4480673 100644 --- a/src/briefcase/commands/new.py +++ b/src/briefcase/commands/new.py @@ -475,7 +475,6 @@ def build_gui_context( project_overrides: dict[str, str], ) -> dict[str, str]: """Build context specific to the GUI toolkit.""" - gui_context = {} # Iterate over the Bootstrap interface to build the context. diff --git a/src/briefcase/commands/package.py b/src/briefcase/commands/package.py index 473addb788..549fabde23 100644 --- a/src/briefcase/commands/package.py +++ b/src/briefcase/commands/package.py @@ -62,6 +62,7 @@ def package_app(self, app: FinalizedAppConfig, **options): :param app: The application to package """ + # Default implementation; nothing to do. def _package_app( @@ -71,9 +72,10 @@ def _package_app( packaging_format: str, **options, ) -> dict | None: - """Internal method to invoke packaging on a single app. Ensures the app exists, - and has been updated (if requested) before attempting to issue the actual - package command. + """Internal method to invoke packaging on a single app. + + Ensures the app exists, and has been updated (if requested) before attempting to + issue the actual package command. :param app: The application to package :param update: Should the application be updated (and rebuilt) first? diff --git a/src/briefcase/commands/run.py b/src/briefcase/commands/run.py index c2eb57d2a4..09d74d6d6a 100644 --- a/src/briefcase/commands/run.py +++ b/src/briefcase/commands/run.py @@ -148,16 +148,16 @@ def _stream_app_logs( :param app: The app to be launched :param popen: The Popen object for the stream we are monitoring; this Popen process will be closed after log streaming completes. - :param clean_filter: The log cleaning filter to use; see ``LogFilter`` - for details. + :param clean_filter: The log cleaning filter to use; see ``LogFilter`` for + details. :param clean_output: Should the cleaned output be presented to the user? - :param stop_func: (Optional) A function that will be invoked to determine - if the log stream should be terminated. - :param log_stream: Is this a log stream, rather than a literal app stream? - On some platforms (especially mobile), we monitor a log stream, - rather that the output of the app itself. If this case, the cleanup - process is different, as the reported exit status of the popen object - is of the log, not the app itself. + :param stop_func: (Optional) A function that will be invoked to determine if the + log stream should be terminated. + :param log_stream: Is this a log stream, rather than a literal app stream? On + some platforms (especially mobile), we monitor a log stream, rather that the + output of the app itself. If this case, the cleanup process is different, as + the reported exit status of the popen object is of the log, not the app + itself. """ try: exit_filter = LogFilter.test_filter( diff --git a/src/briefcase/commands/update.py b/src/briefcase/commands/update.py index 521ffb90b8..ec29f26c42 100644 --- a/src/briefcase/commands/update.py +++ b/src/briefcase/commands/update.py @@ -45,7 +45,6 @@ def update_app( :param update_support: Should app support be updated? :param update_stub: Should stub binary be updated? """ - if app.external_package_path: raise BriefcaseCommandError( f"{app.app_name!r} is declared as an external app. External apps " diff --git a/src/briefcase/config.py b/src/briefcase/config.py index f32c5383bb..75b8d7eefa 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -237,9 +237,9 @@ def validate_install_options_config(config, opt_type, **others): :param config: The table form of options :param opt_type: The label of the option type being parsed ("install" or "uninstall") - :param others: A dictionary of other parsed option types. The keys are - the option types, and the values are the dictionary of parse options. Options - in `config` must be unique against these keys. + :param others: A dictionary of other parsed option types. The keys are the option + types, and the values are the dictionary of parse options. Options in `config` + must be unique against these keys. """ options = {} known_names = set() @@ -331,7 +331,6 @@ def is_valid_bundle_identifier(bundle): def parse_boolean(value: str) -> bool: """Takes a string value and attempts to convert to a boolean value.""" - truth_vals = {"true", "t", "yes", "y", "1", "on"} false_vals = {"false", "f", "no", "n", "0", "off"} @@ -356,8 +355,8 @@ def __init__(self, **kwargs): def update(self, data): """Add fields and values from data to BaseConfig instance. - Any existing fields named in data will be overwritten. To merge - data with existing configs, use the `merge_config` function. + Any existing fields named in data will be overwritten. To merge data with + existing configs, use the `merge_config` function. :param data: The new configuration data dictionary. """ @@ -427,12 +426,12 @@ def __repr__(self): class AppConfig(BaseConfig): """Base class for app configuration. - Not instantiated directly. Use ``DraftAppConfig`` for parsed project - configuration (pre-finalization) and ``FinalizedAppConfig`` for - finalized configuration (post-finalization). + Not instantiated directly. Use ``DraftAppConfig`` for parsed project configuration + (pre-finalization) and ``FinalizedAppConfig`` for finalized configuration (post- + finalization). - Provides shared properties (``module_name``, ``bundle_name``, etc.) - and identity (``__eq__``/``__hash__`` based on ``app_name``). + Provides shared properties (``module_name``, ``bundle_name``, etc.) and identity + (``__eq__``/``__hash__`` based on ``app_name``). """ app_name: str @@ -552,8 +551,8 @@ def all_sources(self) -> list[str]: def main_module(self): """The path to the main module for the app. - In normal operation, this is ``app.module_name``; however, - in test mode, it is prefixed with ``tests.``. + In normal operation, this is ``app.module_name``; however, in test mode, it is + prefixed with ``tests.``. """ if self.test_mode: return f"tests.{self.module_name}" @@ -705,9 +704,8 @@ def __init__( class FinalizedAppConfig(AppConfig): """An AppConfig that has been through platform finalization. - Constructed by ``finalize_app_config()``; holds runtime attributes - (``test_mode``, ``debugger``, etc.) that are not part of the parsed - project configuration. + Constructed by ``finalize_app_config()``; holds runtime attributes (``test_mode``, + ``debugger``, etc.) that are not part of the parsed project configuration. """ def __init__( @@ -800,8 +798,8 @@ def _write_temp_license(base_path: Path, app_name: str, text: str) -> str | None """Write a temporary license file based on license text if needed. If the license text is more than one line, write a temporary file into the build - folder, and return that path. If the license text is a single line, no file - will be written, and `None` will be returned. + folder, and return that path. If the license text is a single line, no file will be + written, and `None` will be returned. :param base_path: The project base directory (parent of `pyproject.toml`), used to read license files and write temporary license text files. @@ -830,8 +828,8 @@ def _normalize_pep639_license_config( :param config: The fully-merged config dict (mutated in place). :param app_name: The app name. - :param base_path: The project base directory (parent of `pyproject.toml`), used - to read license files and write temporary license text files. + :param base_path: The project base directory (parent of `pyproject.toml`), used to + read license files and write temporary license text files. :param console: The Briefcase `Console` object used for warning output. """ raw_license = config["license"] @@ -924,16 +922,19 @@ def _normalize_pep621_license_text_config( tmp_license_file = _write_temp_license(base_path, app_name, license_text) if tmp_license_file: license_files = [tmp_license_file] - warning.append(""" - The contents of `license.text` will be used as the contents of the - license file. This may not be correct, and should be verified. -""") + warning.append("""The contents of `license.text` will be used as the contents of + the license file. + + This may not be correct, and should be verified. + """) else: license_files = [] - warning.append(""" - Your project will not have a value for `license-files`. This will - cause problems packaging for some platforms. -""") + warning.append( + """Your project will not have a value for `license-files`. + + This will cause problems packaging for some platforms. + """ + ) warning.append(f""" Update your configuration to put the full license text in a file and use @@ -966,8 +967,8 @@ def _normalize_pep621_license_file_config( :param config: The fully-merged config dict (mutated in place). :param app_name: The app name. - :param base_path: The project base directory (parent of `pyproject.toml`), used - to read license files and write temporary license text files. + :param base_path: The project base directory (parent of `pyproject.toml`), used to + read license files and write temporary license text files. :param console: The Briefcase `Console` object used for warning output. """ license_file = config["license"]["file"] @@ -1003,10 +1004,13 @@ def _normalize_pep621_license_file_config( # Can't identify SPDX for license license = "" spdx_id = "LicenseRef-UnknownLicense" - warning.append(""" - A license SPDX expression could not be identified from the license file. - The license has been set to 'LicenseRef-UnknownLicense' - """) + warning.append( + """A license SPDX expression could not be identified from the + license file. + + The license has been set to 'LicenseRef-UnknownLicense' + """ + ) warning.append(f""" Update your configuration to use PEP 639 format: @@ -1083,16 +1087,21 @@ def _normalize_pre_pep621_license_config( tmp_license_file = _write_temp_license(base_path, app_name, license_text) if tmp_license_file: license_files = [tmp_license_file] - warning.append(""" - The contents of `license` will be used as the contents of the license - file. This may not be correct, and should be verified. -""") + warning.append( + """The contents of `license` will be used as the contents of the + license file. + + This may not be correct, and should be verified. + """ + ) else: license_files = [] - warning.append(""" - Your project will not have a value for `license-files`. This will - cause problems packaging for some platforms. -""") + warning.append( + """Your project will not have a value for `license-files`. + + This will cause problems packaging for some platforms. + """ + ) warning.append(f""" Update your configuration to put the full license text in a file and use @@ -1210,7 +1219,6 @@ def normalize_license_config( def _core_metadata_to_pep621(pep621_key, metadata): """Retrieve PEP621 metadata values from a Core metadata message.""" - match pep621_key: case "authors" | "maintainers": addresses = metadata.get_all(f"{pep621_key.rstrip('s')}-email") @@ -1234,7 +1242,6 @@ def _core_metadata_to_pep621(pep621_key, metadata): def resolve_dynamic_pep621_config(base_path, dynamic, console): """Resolve dynamic PEP621 metadata using the project's configured build backend.""" - try: with console.wait_bar("Evaluating dynamic project metadata..."): metadata = project_wheel_metadata(base_path, isolated=True) @@ -1262,7 +1269,6 @@ def resolve_dynamic_pep621_config(base_path, dynamic, console): def merge_pep621_config(global_config, pep621_config): """Merge a PEP621 configuration into a Briefcase configuration.""" - if requires_python := pep621_config.get("requires-python"): global_config["requires_python"] = requires_python diff --git a/src/briefcase/console.py b/src/briefcase/console.py index 603f6f027d..ba1ddb147b 100644 --- a/src/briefcase/console.py +++ b/src/briefcase/console.py @@ -315,8 +315,9 @@ def warning_banner( message: str | None = None, width: int = 80, ) -> str: - """The title or message can be provided as a single or as multiline string. Any - common leading whitespace from each line will be removed. + """The title or message can be provided as a single or as multiline string. + + Any common leading whitespace from each line will be removed. To separate text into paragraphs you can use: - blank line; @@ -486,10 +487,10 @@ def configure_stdlib_logging(self, logger_name: str): When a third party tool written in Python uses the stdlib logging for their logging, it may provide an abstraction to enable the logging in the console or - may require a handler to be added externally. Either way, the default handler - to write to the console, i.e. logging.StreamHandler, will bypass Rich logging - and therefore not be included in the logfile. To avoid this issue, this will - add a handler that specifically writes to the console through Rich. + may require a handler to be added externally. Either way, the default handler to + write to the console, i.e. logging.StreamHandler, will bypass Rich logging and + therefore not be included in the logfile. To avoid this issue, this will add a + handler that specifically writes to the console through Rich. :param logger_name: Name of the logger the third party tool uses. Typically, this is the package name or path to a submodule for the package. Since @@ -703,17 +704,16 @@ def wait_bar( """Activates the Wait Bar as a context manager. If the Wait Bar is already active, then its message is updated for the new - context. Once the new context is complete, the previous Wait Bar message - is restored. + context. Once the new context is complete, the previous Wait Bar message is + restored. :param message: text explaining what is being awaited; should end in '...' :param done_message: text appended to the message after exiting - :param transient: if True, remove bar and message from screen after exiting; - if False (default), the message will remain on the screen without pulsing - bar. + :param transient: if True, remove bar and message from screen after exiting; if + False (default), the message will remain on the screen without pulsing bar. :param markup: whether to interpret Rich styling markup in the message; if True, the message must already be escaped; defaults False. - :returns: Keep-alive spinner to notify user Briefcase is still waiting + :returns: Keep-alive spinner to notify user Briefcase is still waiting """ is_wait_bar_disabled = not self.is_interactive show_outcome_message = message and (is_wait_bar_disabled or not transient) @@ -1102,7 +1102,6 @@ def boolean_question( attempt to parse into bool :returns: The user's chosen answer or none if closed without input """ - self.divider(title=description) if override_value is not None: diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index 9513e5ef02..bb812eff7f 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -271,16 +271,14 @@ def verify_install( if sdk_source_env == "ANDROID_SDK_ROOT": tools.console.warning_banner( "Using Android SDK from ANDROID_SDK_ROOT", - """ - Briefcase is using the Android SDK specified by - the ANDROID_SDK_ROOT environment variable. + """Briefcase is using the Android SDK specified by the + ANDROID_SDK_ROOT environment variable. - Android has deprecated ANDROID_SDK_ROOT in favor of the - ANDROID_HOME environment variable. + Android has deprecated ANDROID_SDK_ROOT in favor of the + ANDROID_HOME environment variable. - Update your environment configuration to set - ANDROID_HOME instead of ANDROID_SDK_ROOT to ensure - future compatibility. + Update your environment configuration to set ANDROID_HOME + instead of ANDROID_SDK_ROOT to ensure future compatibility. """, ) elif sdk.cmdline_tools_path.parent.exists(): @@ -576,12 +574,9 @@ def verify_license(self): license_path = self.root_path / "licenses/android-sdk-license" if license_path.exists(): return - self.tools.console.info( - """ -The Android tools provided by Google have license terms that you must accept -before you may use those tools. -""" + """The Android tools provided by Google have license terms that you must + accept before you may use those tools.""" ) try: # Using subprocess.run() with no I/O redirection so the user sees @@ -604,11 +599,11 @@ def verify_license(self): if not license_path.exists(): raise BriefcaseCommandError( - """\ -You did not accept the Android SDK licenses. Please re-run the briefcase command -and accept the Android SDK license when prompted. You may need an Internet -connection. -""" + r"""\ You did not accept the Android SDK licenses. + + Please re-run the briefcase command and accept the Android SDK license + when prompted. You may need an Internet connection. + """ ) def verify_emulator(self): @@ -1027,10 +1022,10 @@ def select_target_device( choice = next(iter(choices)) else: raise BriefcaseCommandError( - """\ -Input has been disabled; can't select a device to target. -Use the -d/--device option to explicitly specify the device to use. -""" + r"""\ Input has been disabled; can't select a device to target. + + Use the -d/--device option to explicitly specify the device to use. + """ ) from e # Process the user's choice @@ -1387,10 +1382,11 @@ def start_emulator( prefix=self.name, ) self.tools.console.info( + """If the emulator opened after pressing CTRL+C, then leave the + emulator open and run Briefcase again. + + The running emulator can then be selected from the list. """ -If the emulator opened after pressing CTRL+C, then leave the emulator open and -run Briefcase again. The running emulator can then be selected from the list. -""" ) self.tools.console.info(general_error_msg) @@ -1415,8 +1411,8 @@ def __init__(self, tools: ToolCache, device: str): def avd_name(self) -> str | None: """Get the AVD name for the device. - :returns: The AVD name for the device; or ``None`` if the device isn't - an emulator + :returns: The AVD name for the device; or ``None`` if the device isn't an + emulator """ try: output = self.run("emu", "avd", "name", quiet=1) @@ -1447,17 +1443,18 @@ def has_booted(self) -> bool: ) from e def run(self, *arguments: SubprocessArgT, quiet: int = 0) -> str: - """Run a command on a device using Android debug bridge, `adb`. The device name - is mandatory to ensure clarity in the case of multiple attached devices. + """Run a command on a device using Android debug bridge, `adb`. + + The device name is mandatory to ensure clarity in the case of multiple attached + devices. :param arguments: List of strings to pass to `adb` as arguments. - :param quiet: Should the invocation of this command be silent, and - *not* appear in the logs? This should almost always be 0; - however, for some calls (most notably, calls that are called - frequently to evaluate the status of another process), logging can - be turned off so that log output isn't corrupted by thousands of - polling calls. - :returns: `adb` output on success; raises an exception on failure. + :param quiet: Should the invocation of this command be silent, and *not* appear + in the logs? This should almost always be 0; however, for some calls (most + notably, calls that are called frequently to evaluate the status of another + process), logging can be turned off so that log output isn't corrupted by + thousands of polling calls. + :returns:`adb` output on success; raises an exception on failure. """ # The ADB integration operates on the basis of running commands before # checking that they are valid, then parsing output to notice errors. @@ -1485,7 +1482,7 @@ def install_apk(self, apk_path: str | Path): """Install an APK file on an Android device. :param apk_path: The path of the Android APK file to install. - :returns: `None` on success; raises an exception on failure. + :returns:`None` on success; raises an exception on failure. """ try: self.run("install", "-r", apk_path) @@ -1498,7 +1495,7 @@ def force_stop_app(self, package: str): """Force-stop an app, specified as a package name. :param package: The name of the Android package, e.g., com.username.myapp. - :returns: `None` on success; raises an exception on failure. + :returns:`None` on success; raises an exception on failure. """ # In my testing, `force-stop` exits with status code 0 (success) so long # as you pass a package name, even if the package does not exist, or the @@ -1514,9 +1511,9 @@ def revoke_permission(self, package: str, permission: str) -> None: """Revoke a runtime permission for an app, specified as a package name. :param package: The name of the Android package, e.g., com.username.myapp. - :param permission: The name of the Android permission to revoke, - e.g., android.permission.BLUETOOTH_SCAN. - :returns: `None` on success; raises an exception on failure. + :param permission: The name of the Android permission to revoke, e.g., + android.permission.BLUETOOTH_SCAN. + :returns:`None` on success; raises an exception on failure. """ try: self.run("shell", "pm", "revoke", package, permission) @@ -1531,15 +1528,15 @@ def start_app( ): """Start an app, specified as a package name & activity name. - If you have an APK file, and you are not sure of the package or activity - name, you can find it using `aapt dump badging filename.apk` and looking - for "package" and "launchable-activity" in the output. + If you have an APK file, and you are not sure of the package or activity name, + you can find it using `aapt dump badging filename.apk` and looking for "package" + and "launchable-activity" in the output. :param package: The name of the Android package, e.g., com.username.myapp. :param activity: The activity of the APK to start. :param passthrough: Arguments to pass to the app. :param env: Environment variables to pass to the app. - :returns: `None` on success; raises an exception on failure. + :returns:`None` on success; raises an exception on failure. """ try: # `am start` also accepts string array extras, but we pass the arguments as @@ -1737,8 +1734,7 @@ def pidof(self, package: str, **kwargs) -> str | None: :param package: The package ID for the application (e.g., ``org.beeware.tutorial``) - :returns: The PID of the given app as a string, or None if it isn't - running. + :returns: The PID of the given app as a string, or None if it isn't running. """ # The pidof command is available since API level 24. The level 23 emulator image # also includes it, but it doesn't work correctly (it returns all processes). diff --git a/src/briefcase/integrations/base.py b/src/briefcase/integrations/base.py index 753ff9f817..020d77054c 100644 --- a/src/briefcase/integrations/base.py +++ b/src/briefcase/integrations/base.py @@ -243,8 +243,8 @@ def system_encoding(self) -> str: """The character encoding for the system's locale. This locale API tries to determine the system's default encoding and generally - works on typically configured systems; although, there are potential pitfalls - in certain situations...so, this is best-effort. + works on typically configured systems; although, there are potential pitfalls in + certain situations...so, this is best-effort. This API is used over getpreferredencoding() to avoid respecting Python's UTF-8 mode; the system may not be using UTF-8 even if Python is configured to use it. diff --git a/src/briefcase/integrations/docker.py b/src/briefcase/integrations/docker.py index db93b71b40..541ed02fac 100644 --- a/src/briefcase/integrations/docker.py +++ b/src/briefcase/integrations/docker.py @@ -558,11 +558,12 @@ def x11_passthrough( self._x11_write_xauth_file(DISPLAY, xauth_file_path, proxy_display_num) except XauthDatabaseCreationFailure: self.tools.console.warning( - """\ -An X11 authentication database could not be created for the display. + r"""\ An X11 authentication database could not be created for the + display. -Briefcase will proceed, but if access to the display is rejected, this may be why. -""" + Briefcase will proceed, but if access to the display is rejected, + this may be why. + """ ) else: # Add the xauth database to the container @@ -615,8 +616,8 @@ def _x11_tcp_proxy(self, DISPLAY: str) -> tuple[subprocess.Popen, int]: The proxy is bound to 0.0.0.0 and as such will be reachable on all network interfaces defined for the host. Ideally, the proxy would only need to bind to the network bridge defined by Docker, usually `docker0`, but Docker Desktop is - not able to use a shared network interface bridge as Docker Engine does since - it runs containers inside a Linux VM. Instead, it attaches the host's primary + not able to use a shared network interface bridge as Docker Engine does since it + runs containers inside a Linux VM. Instead, it attaches the host's primary network interface as a device on a virtual network interface bridge created by the VM. Because of this, the proxy must be exposed to the network at large to also be exposed within the container. diff --git a/src/briefcase/integrations/file.py b/src/briefcase/integrations/file.py index 2909189414..426587251f 100644 --- a/src/briefcase/integrations/file.py +++ b/src/briefcase/integrations/file.py @@ -169,8 +169,9 @@ def unpack_archive( ) def download(self, url: str, download_path: Path, role: str | None = None) -> Path: - """Download a given URL, caching it. If it has already been downloaded, return - the value that has been cached. + """Download a given URL, caching it. + + If it has already been downloaded, return the value that has been cached. This is a utility method used to obtain assets used by the installation process. The cached filename will be the filename portion of the URL, appended to the @@ -285,12 +286,12 @@ def download(self, url: str, download_path: Path, role: str | None = None) -> Pa def _fetch_and_write_content(self, response: httpx.Response, filename: Path): """Write the content from the httpx Response to file. - The data is initially written in to a temporary file in the Briefcase - cache. This avoids partially downloaded files masquerading as complete - downloads in later Briefcase runs. The temporary file is only moved - to ``filename`` if the download is successful; otherwise, it is deleted. + The data is initially written in to a temporary file in the Briefcase cache. + This avoids partially downloaded files masquerading as complete downloads in + later Briefcase runs. The temporary file is only moved to ``filename`` if the + download is successful; otherwise, it is deleted. - :param response: ``httpx.Response`` + :param response:``httpx.Response`` :param filename: full filesystem path to save data """ # `temp_file` is used in the `finally` block, so make sure it's assigned diff --git a/src/briefcase/integrations/flatpak.py b/src/briefcase/integrations/flatpak.py index f1a70bb7a5..5548c5ce7d 100644 --- a/src/briefcase/integrations/flatpak.py +++ b/src/briefcase/integrations/flatpak.py @@ -45,40 +45,40 @@ def verify_install(cls, tools: ToolCache, **kwargs) -> Flatpak: raise ValueError(f"Unexpected tool name {parts[0]}") from None except (ValueError, IndexError): tools.console.warning( - """\ -************************************************************************* -** WARNING: Unable to determine the version of Flatpak ** -************************************************************************* + r"""\ *************************************************************** + ********** ** WARNING: Unable to determine the version of Flatpak + ** ***************************************************************** + ******** - Briefcase will proceed, assuming everything is OK. If you - experience problems, this is almost certainly the cause of those - problems. + Briefcase will proceed, assuming everything is OK. If you + experience problems, this is almost certainly the cause of those + problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - flatpak --version + flatpak --version - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) except OSError as e: raise BriefcaseCommandError( - """\ -Briefcase requires the Flatpak toolchain, but it does not appear to be installed. + r"""\ Briefcase requires the Flatpak toolchain, but it does not appear to + be installed. -Instructions for installing the Flatpak toolchain can be found at: + Instructions for installing the Flatpak toolchain can be found at: - https://flatpak.org/setup/ + https://flatpak.org/setup/ -You must install both flatpak and flatpak-builder. -""" + You must install both flatpak and flatpak-builder. + """ ) from e except subprocess.CalledProcessError as e: raise BriefcaseCommandError("Unable to invoke flatpak.") from e @@ -103,41 +103,40 @@ def verify_install(cls, tools: ToolCache, **kwargs) -> Flatpak: raise ValueError(f"Unexpected tool name {parts[0]}") except (ValueError, IndexError): tools.console.warning( - """\ -************************************************************************* -** WARNING: Unable to determine the version of flatpak-builder ** -************************************************************************* + r"""\ *************************************************************** + ********** ** WARNING: Unable to determine the version of flatpak- + builder ** ************************************************* + ************************ - Briefcase will proceed, assuming everything is OK. If you - experience problems, this is almost certainly the cause of those - problems. + Briefcase will proceed, assuming everything is OK. If you + experience problems, this is almost certainly the cause of those + problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - flatpak-builder --version + flatpak-builder --version - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) except OSError as e: raise BriefcaseCommandError( - """\ -Briefcase requires the full Flatpak development toolchain, but flatpak-builder -does not appear to be installed. + r"""\ Briefcase requires the full Flatpak development toolchain, but + flatpak-builder does not appear to be installed. -Instructions for installing the Flatpak toolchain can be found at: + Instructions for installing the Flatpak toolchain can be found at: - https://flatpak.org/setup/ + https://flatpak.org/setup/ -You must install both flatpak and flatpak-builder. -""" + You must install both flatpak and flatpak-builder. + """ ) from e except subprocess.CalledProcessError as e: raise BriefcaseCommandError("Unable to invoke flatpak-builder.") from e diff --git a/src/briefcase/integrations/java.py b/src/briefcase/integrations/java.py index b0942378e6..30fb588c04 100644 --- a/src/briefcase/integrations/java.py +++ b/src/briefcase/integrations/java.py @@ -86,19 +86,19 @@ def version_from_path(cls, tools: ToolCache, java_path: str | Path) -> str: def verify_install(cls, tools: ToolCache, install: bool = True, **kwargs) -> JDK: """Verify that a Java JDK exists. - If ``JAVA_HOME`` is set, try that version. If it is a JRE, or its *not* - a Java JDK, download one. + If ``JAVA_HOME`` is set, try that version. If it is a JRE, or its *not* a Java + JDK, download one. - On macOS, also try invoking /usr/libexec/java_home. If that location - points to a Java JDK, use it. + On macOS, also try invoking /usr/libexec/java_home. If that location points to a + Java JDK, use it. - Otherwise, download a JDK from OpenJDK and unpack it into the - briefcase data directory. + Otherwise, download a JDK from OpenJDK and unpack it into the briefcase data + directory. :param tools: ToolCache of available tools :param install: Should the tool be installed if it is not found? - :returns: A valid Java JDK wrapper. If a JDK is not available, and was - not installed, raises MissingToolError. + :returns: A valid Java JDK wrapper. If a JDK is not available, and was not + installed, raises MissingToolError. """ # short circuit since already verified and available if hasattr(tools, "java"): diff --git a/src/briefcase/integrations/subprocess.py b/src/briefcase/integrations/subprocess.py index 66ce7b8a6b..c9860993b9 100644 --- a/src/briefcase/integrations/subprocess.py +++ b/src/briefcase/integrations/subprocess.py @@ -67,14 +67,16 @@ def get_process_id_by_command( command: str = "", console: Console | None = None, ) -> int | None: - """Find a Process ID (PID) a by its command. If multiple processes are found, then - the most recently created process ID is returned. + """Find a Process ID (PID) a by its command. + + If multiple processes are found, then the most recently created process ID is + returned. :param command_list: list of a command's fully qualified path and its arguments. - :param command: a partial or complete fully-qualified filepath to a command. - This is primarily intended for use on macOS where the `open` command - takes a filepath to a directory for an application; therefore, the actual - running process will be running a command within that directory. + :param command: a partial or complete fully-qualified filepath to a command. This is + primarily intended for use on macOS where the `open` command takes a filepath to + a directory for an application; therefore, the actual running process will be + running a command within that directory. :param console: optional console to show messages about process matching to users :returns: PID if found else None """ @@ -163,8 +165,8 @@ def __init__( :param console: console for printing to console :param capture_output: Retain process output in ``output_queue`` via a ``queue.Queue`` instead of printing to console - :param filter_func: a callable that will be invoked on every line of output - that is streamed; see ``Subprocess.stream_output`` for details + :param filter_func: a callable that will be invoked on every line of output that + is streamed; see ``Subprocess.stream_output`` for details """ super().__init__(name=f"{label} output streamer", daemon=True) @@ -296,6 +298,7 @@ def __init__(self, tools: ToolCache): def prepare(self): """Perform any environment preparation required to execute processes.""" + # This is a no-op; the native subprocess environment is ready-to-use. @contextlib.contextmanager @@ -310,11 +313,11 @@ def run_app_context(self, subprocess_kwargs: dict[str, Any]) -> dict[str, Any]: def full_env(self, overrides: dict[str, str | None] | None) -> dict[str, str]: """Generate the full environment in which the command will run. - If an env var in `overrides` is set to `None`, then that env var - will be altogether absent in the returned environment. + If an env var in `overrides` is set to `None`, then that env var will be + altogether absent in the returned environment. - :param overrides: The environment passed to the subprocess call; - can be `None` if there are no explicit environment changes. + :param overrides: The environment passed to the subprocess call; can be `None` + if there are no explicit environment changes. """ env = self.tools.os.environ.copy() if overrides: @@ -536,7 +539,6 @@ def run( :raises ValueError: if a filter function is provided when in non-streaming mode. :returns: ``CompletedProcess`` for invoked process """ - # Stream the output unless the caller explicitly disables it. When a # caller sets stream_output=False, then ensure_console_is_safe() will # disable any dynamic console elements while the command runs. @@ -740,8 +742,9 @@ def stream_output( stop_func: Callable[[], bool] = lambda: False, filter_func: Callable[[str], Iterator[str]] | None = None, ): - """Stream the output of a Popen process until the process exits. If the user - sends CTRL+C, the process will be terminated. + """Stream the output of a Popen process until the process exits. + + If the user sends CTRL+C, the process will be terminated. This is useful for starting a process via Popen such as tailing a log file, then initiating a non-blocking process that populates that log, and finally streaming @@ -861,8 +864,8 @@ def _log_cwd(self, cwd: str | Path | None): def _log_environment(self, overrides: dict[str, str] | None): """Log the environment variables overrides prior to command execution. - :param overrides: The explicit environment passed to the subprocess call; - can be `None` if there are no explicit environment changes. + :param overrides: The explicit environment passed to the subprocess call; can be + `None` if there are no explicit environment changes. """ if overrides: self._log("Environment Overrides:") diff --git a/src/briefcase/integrations/virtual_environment.py b/src/briefcase/integrations/virtual_environment.py index b13a8cf9c4..161394bdc3 100644 --- a/src/briefcase/integrations/virtual_environment.py +++ b/src/briefcase/integrations/virtual_environment.py @@ -22,7 +22,6 @@ def bin_dir(self) -> Path: :returns: The ``/bin`` (``\\Scripts`` on Windows) directory of the venv. """ - return self.venv_path / ("Scripts" if os.name == "nt" else "bin") @property @@ -101,7 +100,6 @@ def full_env(self, overrides: dict[str, str | None] | None) -> dict[str, str]: environment variables :returns: environment mapping for the venv with overrides applied. """ - if overrides: env = overrides.copy() else: @@ -299,8 +297,9 @@ def create( isolated: bool = True, recreate: bool = False, ) -> VenvEnvironment | NoOpEnvironment: - """Return a environment context for the requested isolation settings. Creates - either a virtual environment context or a no-op context. + """Return a environment context for the requested isolation settings. + + Creates either a virtual environment context or a no-op context. :param tools: The tools instance :param venv_path: Path for the virtual environment diff --git a/src/briefcase/integrations/visualstudio.py b/src/briefcase/integrations/visualstudio.py index e42d13f961..e3d25cecc8 100644 --- a/src/briefcase/integrations/visualstudio.py +++ b/src/briefcase/integrations/visualstudio.py @@ -40,8 +40,8 @@ def msbuild_path(self) -> Path: def install_metadata(self) -> dict[str, str | int | bool] | None: """Metadata about the Visual Studio install. - Will be ``None`` if MSBuild the path to MSBuild has been provided - explicitly, or is on the path. + Will be ``None`` if MSBuild the path to MSBuild has been provided explicitly, or + is on the path. Otherwise, a dictionary containing the install details from VSWhere. """ diff --git a/src/briefcase/integrations/xcode.py b/src/briefcase/integrations/xcode.py index f719640674..abb4a9829d 100644 --- a/src/briefcase/integrations/xcode.py +++ b/src/briefcase/integrations/xcode.py @@ -33,10 +33,9 @@ def verify_install( """Verify that Xcode and the command line developer tools are installed and ready for use. - We need Xcode, *and* the Xcode Command Line Tools. A completely clean - machine will have neither Xcode *nor* the Command Line Tools. However, - it's possible to install Xcode and *not* install the command line tools, - and vice versa. + We need Xcode, *and* the Xcode Command Line Tools. A completely clean machine + will have neither Xcode *nor* the Command Line Tools. However, it's possible to + install Xcode and *not* install the command line tools, and vice versa. We also need to ensure that an adequate version of Xcode is available. @@ -45,9 +44,9 @@ def verify_install( Lastly, we ensure that the iOS simulator is installed. :param tools: ToolCache of available tools - :param min_version: The minimum allowed version of Xcode, specified as a - tuple of integers (e.g., (11, 2, 1)). Default: ``None``, meaning there - is no minimum version. + :param min_version: The minimum allowed version of Xcode, specified as a tuple + of integers (e.g., (11, 2, 1)). Default: ``None``, meaning there is no + minimum version. """ # short circuit since already verified and available if hasattr(tools, "xcode"): @@ -69,13 +68,13 @@ def ensure_xcode_is_installed( """Determine if Xcode is installed; and if so, that it meets minimum version requirements. - Raises an exception if Xcode isn't installed, or if the version of Xcode - that is installed doesn't meet the minimum requirement. + Raises an exception if Xcode isn't installed, or if the version of Xcode that is + installed doesn't meet the minimum requirement. :param tools: ToolCache of available tools - :param min_version: The minimum allowed version of Xcode, specified as a - tuple of integers (e.g., (11, 2, 1)). Default: ``None``, meaning there - is no minimum version. + :param min_version: The minimum allowed version of Xcode, specified as a tuple + of integers (e.g., (11, 2, 1)). Default: ``None``, meaning there is no + minimum version. :param xcode_location: The location where we expect to find an Xcode install. Used for testing; defaults to ``/Applications/Xcode.app``. """ @@ -86,16 +85,15 @@ def ensure_xcode_is_installed( tools.subprocess.check_output(["xcode-select", "-p"]) except subprocess.CalledProcessError as e: raise BriefcaseCommandError( - """\ -Could not find an Xcode installation. + r"""\ Could not find an Xcode installation. -To select an existing Xcode installation, run: + To select an existing Xcode installation, run: - $ sudo xcode-select --switch path/to/Xcode.app + $ sudo xcode-select --switch path/to/Xcode.app -or install Xcode from the macOS App Store. Once you have installed Xcode, -you can re-run Briefcase. -""" + or install Xcode from the macOS App Store. Once you have installed + Xcode, you can re-run Briefcase. + """ ) from e try: @@ -135,28 +133,27 @@ def ensure_xcode_is_installed( else: # Version number is acceptable return - tools.console.warning( - """ -************************************************************************* -** WARNING: Unable to determine the version of Xcode that is installed ** -************************************************************************* + """***************************************************************** + ******** ** WARNING: Unable to determine the version of Xcode that + is installed ** **************************************************** + ********************* - Briefcase will proceed, assuming everything is OK. If you experience - problems, this is almost certainly the cause of those problems. + Briefcase will proceed, assuming everything is OK. If you experience + problems, this is almost certainly the cause of those problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - $ xcodebuild -version + $ xcodebuild -version - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) except subprocess.CalledProcessError as e: @@ -177,32 +174,30 @@ def ensure_xcode_is_installed( a full Xcode install. Xcode can be downloaded from the macOS App Store at . """ - raise BriefcaseCommandError( preamble - + """ -Or, to use a version of Xcode installed in a non-default location: + + """Or, to use a version of Xcode installed in a non-default + location: - $ sudo xcode-select --switch /path/to/Xcode.app + $ sudo xcode-select --switch /path/to/Xcode.app -and then re-run Briefcase. -""" + and then re-run Briefcase. + """ ) from e else: tools.subprocess.output_error(e) raise BriefcaseCommandError( - """\ -An Xcode install appears to exist, but Briefcase was unable to -determine the current Xcode version. Running: + r"""\ An Xcode install appears to exist, but Briefcase was unable to + determine the current Xcode version. Running: - $ xcodebuild -version + $ xcodebuild -version -should return the current Xcode version, but it raised an error. + should return the current Xcode version, but it raised an error. -You may need to re-install Xcode. Re-run Briefcase once that -installation is complete. -""" + You may need to re-install Xcode. Re-run Briefcase once that + installation is complete. + """ ) from e @@ -256,36 +251,36 @@ def ensure_command_line_tools_are_installed(cls, tools: ToolCache): try: tools.subprocess.check_output(["xcode-select", "--install"], quiet=1) raise BriefcaseCommandError( - """\ -The command line developer tools are not installed. + r"""\ The command line developer tools are not installed. -You should be shown a dialog prompting you to install them. Select "Install" -to continue, and re-run Briefcase once that installation is complete. -""" + You should be shown a dialog prompting you to install them. Select + "Install" to continue, and re-run Briefcase once that installation is + complete. + """ ) except subprocess.CalledProcessError as e: if e.returncode != 1: tools.console.warning( - """ -************************************************************************* -** WARNING: Unable to determine if Xcode is installed ** -************************************************************************* + """***************************************************************** + ******** ** WARNING: Unable to determine if Xcode is installed + ** ***************************************************************** + ******** - Briefcase will proceed, assuming everything is OK. If you experience - problems, this is almost certainly the cause of those problems. + Briefcase will proceed, assuming everything is OK. If you experience + problems, this is almost certainly the cause of those problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - $ xcode-select --install + $ xcode-select --install - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) @classmethod @@ -302,18 +297,17 @@ def confirm_xcode_license_accepted(cls, tools: ToolCache): except subprocess.CalledProcessError as e: if e.returncode == 69: tools.console.info( - """ -Use of Xcode and the iOS developer tools are covered by a license that must be -accepted before you can use those tools. + """Use of Xcode and the iOS developer tools are covered by a license + that must be accepted before you can use those tools. -You can accept these licenses by starting Xcode and clicking "Accept"; or, you -can run this command and accept the license when prompted: + You can accept these licenses by starting Xcode and clicking "Accept"; or, you + can run this command and accept the license when prompted: - $ sudo xcodebuild -license + $ sudo xcodebuild -license -Briefcase will try to run this command now. You will need to enter your -password (Briefcase will not store this password anywhere). -""" + Briefcase will try to run this command now. You will need to enter your + password (Briefcase will not store this password anywhere). + """ ) try: tools.subprocess.run( @@ -326,68 +320,70 @@ def confirm_xcode_license_accepted(cls, tools: ToolCache): # status code 69 - license not accepted. if e.returncode == 1: raise BriefcaseCommandError( - """\ -Briefcase was unable to run the Xcode licensing tool. This may be because you -did not enter your password correctly, or because your account does not have -administrator privileges on this computer. + r"""\ Briefcase was unable to run the Xcode licensing tool. + This may be because you did not enter your password + correctly, or because your account does not have + administrator privileges on this computer. -You need to accept the Xcode license before Briefcase can package your app. -""" + You need to accept the Xcode license before Briefcase can + package your app. + """ ) from e elif e.returncode == 69: raise BriefcaseCommandError( - """\ -Xcode license has not been accepted. Briefcase cannot continue. + r"""\ Xcode license has not been accepted. Briefcase cannot + continue. -You need to accept the Xcode license before Briefcase can package your app. -""" + You need to accept the Xcode license before Briefcase can + package your app. + """ ) from e else: tools.console.warning( - """ -************************************************************************* -** WARNING: Unable to determine if the Xcode license has been accepted ** -************************************************************************* + """********************************************************* + **************** ** WARNING: Unable to determine if the + Xcode license has been accepted ** ************************* + ************************************************ - Briefcase will proceed, assuming everything is OK. If you experience - problems, this is almost certainly the cause of those problems. + Briefcase will proceed, assuming everything is OK. If you experience + problems, this is almost certainly the cause of those problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - $ sudo xcodebuild -license + $ sudo xcodebuild -license - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) else: tools.subprocess.output_error(e) tools.console.warning( - """ -************************************************************************* -** WARNING: Unable to determine if the Xcode license has been accepted ** -************************************************************************* + """***************************************************************** + ******** ** WARNING: Unable to determine if the Xcode license has + been accepted ** *************************************************** + ********************** - Briefcase will proceed, assuming everything is OK. If you experience - problems, this is almost certainly the cause of those problems. + Briefcase will proceed, assuming everything is OK. If you experience + problems, this is almost certainly the cause of those problems. - Please report this as a bug at: + Please report this as a bug at: - https://github.com/beeware/briefcase/issues/new + https://github.com/beeware/briefcase/issues/new - In your report, please including the output from running: + In your report, please including the output from running: - $ /usr/bin/clang --version + $ /usr/bin/clang --version - from the command prompt. + from the command prompt. -************************************************************************* -""" + ************************************************************************* + """ ) @@ -509,8 +505,8 @@ def get_identities(tools: ToolCache, policy: str | None = None) -> dict[str, str """Obtain a set of valid identities for the given policy. :param tools: ToolCache of available tools - :param policy: The identity policy to evaluate (e.g., ``codesigning``). - If None, no policy filtering is applied. + :param policy: The identity policy to evaluate (e.g., ``codesigning``). If None, no + policy filtering is applied. """ try: output = tools.subprocess.check_output( diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py index e82deb63a4..c15e1263cc 100644 --- a/src/briefcase/platforms/android/gradle.py +++ b/src/briefcase/platforms/android/gradle.py @@ -39,10 +39,9 @@ def safe_formal_name(name): """Converts the name into a safe name on Android. - Certain characters (``/\\:<>"?*|``) can't be used as app names - on Android; ``!`` causes problems with Android build tooling. - Also ensure that trailing, leading, and consecutive whitespace - caused by removing punctuation is collapsed. + Certain characters (``/\\:<>"?*|``) can't be used as app names on Android; ``!`` + causes problems with Android build tooling. Also ensure that trailing, leading, and + consecutive whitespace caused by removing punctuation is collapsed. :param name: The candidate name :returns: The safe version of the name. @@ -61,14 +60,13 @@ def safe_formal_name(name): def android_log_clean_filter(line): """Filter an ADB log to extract the Python-generated message content. - Any system or stub messages are ignored; all logging prefixes are stripped. - Python code is identified as coming from the ``python.stdout`` + Any system or stub messages are ignored; all logging prefixes are stripped. Python + code is identified as coming from the ``python.stdout`` :param line: The raw line from the system log - :returns: A tuple, containing (a) the log line, stripped of any system - logging context, and (b) a boolean indicating if the message should be - included for analysis purposes (i.e., it's Python content, not a system - message). + :returns: A tuple, containing (a) the log line, stripped of any system logging + context, and (b) a boolean indicating if the message should be included for + analysis purposes (i.e., it's Python content, not a system message). """ match = ANDROID_LOG_PREFIX_REGEX.match(line) if match: @@ -194,7 +192,8 @@ def output_format_template_context(self, app: FinalizedAppConfig): try: dependencies = app.build_gradle_dependencies except AttributeError: - self.console.warning(""" + self.console.warning( + """ ************************************************************************* ** WARNING: App does not define build_gradle_dependencies ** ************************************************************************* @@ -218,7 +217,8 @@ def output_format_template_context(self, app: FinalizedAppConfig): ************************************************************************* -""") +""" + ) dependencies = [ "androidx.appcompat:appcompat:1.0.2", "androidx.constraintlayout:constraintlayout:1.1.3", @@ -473,12 +473,12 @@ def run_app( :param app: The config object for the app :param passthrough: The list of arguments to pass to the app - :param device_or_avd: The device to target. If ``None``, the user will - be asked to re-run the command selecting a specific device. + :param device_or_avd: The device to target. If ``None``, the user will be asked + to re-run the command selecting a specific device. :param extra_emulator_args: Any additional arguments to pass to the emulator. :param shutdown_on_exit: Should the emulator be shut down on exit? - :param revoke_permissions: A list of permissions to revoke before launching - the app. + :param revoke_permissions: A list of permissions to revoke before launching the + app. :param forward_ports: A list of ports to forward for the app. :param reverse_ports: A list of ports to reversed for the app. """ diff --git a/src/briefcase/platforms/iOS/xcode.py b/src/briefcase/platforms/iOS/xcode.py index 39a367214f..2423ff710b 100644 --- a/src/briefcase/platforms/iOS/xcode.py +++ b/src/briefcase/platforms/iOS/xcode.py @@ -57,24 +57,25 @@ def binary_path(self, app): def distribution_path(self, app): # This path won't ever be *generated*, as distribution artefacts # can't be generated on iOS. - raise NoDistributionArtefact(""" -************************************************************************* -** WARNING: No distributable artefact has been generated ** -************************************************************************* + raise NoDistributionArtefact("""************************************************ + ************************* ** WARNING: No + distributable artefact has been generated + ** ************************************************ + ************************* - Briefcase has not generated a standalone iOS artefact, as iOS apps - must be published through Xcode. + Briefcase has not generated a standalone iOS artefact, as iOS apps + must be published through Xcode. - To open Xcode for your iOS project, run: + To open Xcode for your iOS project, run: - briefcase open iOS + briefcase open iOS - and use Xcode's app distribution workflow described at: + and use Xcode's app distribution workflow described at: - https://briefcase.readthedocs.io/en/stable/reference/platforms/iOS/xcode.html#ios-deploy + https://briefcase.readthedocs.io/en/stable/reference/platforms/iOS/xcode.html#ios-deploy -************************************************************************* -""") + ************************************************************************* + """) class iOSXcodeMixin(iOSXcodePassiveMixin): @@ -549,8 +550,8 @@ def run_app( :param app: The config object for the app :param passthrough: The list of arguments to pass to the app - :param udid: The device UDID to target. If ``None``, the user will - be asked to select a device at runtime. + :param udid: The device UDID to target. If ``None``, the user will be asked to + select a device at runtime. """ try: udid, iOS_version, device = self.select_target_device(udid) diff --git a/src/briefcase/platforms/linux/appimage.py b/src/briefcase/platforms/linux/appimage.py index 27a00232c0..b5734eca3d 100644 --- a/src/briefcase/platforms/linux/appimage.py +++ b/src/briefcase/platforms/linux/appimage.py @@ -95,33 +95,35 @@ def clone_options(self, command): def finalize_app_config(self, app: DraftAppConfig, **kwargs) -> FinalizedAppConfig: """If we're *not* using Docker, warn the user about portability.""" if not self.use_docker: - self.console.warning("""\ -************************************************************************* -** WARNING: Building a Local AppImage! ** -************************************************************************* - - You are building an AppImage outside Docker. The resulting AppImage - will work, but will not be as portable as a Docker-based AppImage. - Any `manylinux` setting will be ignored. - -************************************************************************* -""") - - self.console.warning("""\ -************************************************************************* -** WARNING: Use of AppImage is not recommended! ** -************************************************************************* - - Briefcase supports AppImage in a best-effort capacity. It has proven - to be highly unreliable as a distribution platform. AppImages cannot - use pre-compiled binary wheels, and has significant problems with - most commonly used GUI toolkits (including GTK and PySide). - - Consider using system packages or Flatpak for Linux app - distribution. - -************************************************************************* -""") + self.console.warning(r"""\ ************************************************** + *********************** ** WARNING: Building a Local + AppImage! ** ********** + ******************************************************* + ******** + + You are building an AppImage outside Docker. The resulting AppImage + will work, but will not be as portable as a Docker-based AppImage. + Any `manylinux` setting will be ignored. + + ************************************************************************* + """) + self.console.warning( + r"""\ ****************************************************** + ******************* ** WARNING: Use of AppImage is not + recommended! ** ******************** + ***************************************************** + + Briefcase supports AppImage in a best-effort capacity. It has proven + to be highly unreliable as a distribution platform. AppImages cannot + use pre-compiled binary wheels, and has significant problems with + most commonly used GUI toolkits (including GTK and PySide). + + Consider using system packages or Flatpak for Linux app + distribution. + + ************************************************************************* + """ + ) return super().finalize_app_config(app, **kwargs) @@ -225,22 +227,24 @@ def _cleanup_app_support_package(self, support_path): # On Windows, the support path is co-mingled with app content. # This means updating the support package is imperfect. # Warn the user that there could be problems. - self.console.warning(""" -************************************************************************* -** WARNING: Support package update may be imperfect ** -************************************************************************* - - Support packages in Linux AppImages are overlaid with app content, - so it isn't possible to remove all old support files before - installing new ones. - - Briefcase will unpack the new support package without cleaning up - existing support package content. This *should* work; however, - ensure a reproducible release artefacts, it is advisable to - perform a clean app build before release. - -************************************************************************* -""") + self.console.warning( + """******************************************************** + ***************** ** WARNING: Support package update may be + imperfect ** *************************** + ********************************************** + + Support packages in Linux AppImages are overlaid with app content, + so it isn't possible to remove all old support files before + installing new ones. + + Briefcase will unpack the new support package without cleaning up + existing support package content. This *should* work; however, + ensure a reproducible release artefacts, it is advisable to + perform a clean app build before release. + + ************************************************************************* + """ + ) class LinuxAppImageUpdateCommand(LinuxAppImageCreateCommand, UpdateCommand): diff --git a/src/briefcase/platforms/linux/flatpak.py b/src/briefcase/platforms/linux/flatpak.py index 66f2a37d35..7b845b61b1 100644 --- a/src/briefcase/platforms/linux/flatpak.py +++ b/src/briefcase/platforms/linux/flatpak.py @@ -68,34 +68,38 @@ def flatpak_runtime(self, app): try: return app.flatpak_runtime except AttributeError as e: - raise BriefcaseConfigError("""\ -The App does not specify the Flatpak runtime to use. + raise BriefcaseConfigError(r"""\ The App does not specify the Flatpak runtime + to use. -Your application configuration must provide values for -`flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. -""") from e + Your application configuration must provide values for + `flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. + """) from e def flatpak_runtime_version(self, app): try: return app.flatpak_runtime_version except AttributeError as e: - raise BriefcaseConfigError("""\ -The App does not specify the version of the Flatpak runtime to use. + raise BriefcaseConfigError( + r"""\ The App does not specify the version of the + Flatpak runtime to use. -Your application configuration must provide values for -`flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. -""") from e + Your application configuration must provide values for + `flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. + """ + ) from e def flatpak_sdk(self, app): try: return app.flatpak_sdk except AttributeError as e: - raise BriefcaseConfigError("""\ -The App does not specify the Flatpak SDK to use. - -Your application configuration must provide values for -`flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. -""") from e + raise BriefcaseConfigError( + r"""\ The App does not specify the Flatpak SDK to + use. + + Your application configuration must provide values for + `flatpak_sdk`, `flatpak_runtime`, and `flatpak_runtime_version`. + """ + ) from e class LinuxFlatpakCreateCommand(LinuxFlatpakMixin, CreateCommand): diff --git a/src/briefcase/platforms/linux/system.py b/src/briefcase/platforms/linux/system.py index 615bc10e6f..784e2981b4 100644 --- a/src/briefcase/platforms/linux/system.py +++ b/src/briefcase/platforms/linux/system.py @@ -277,12 +277,12 @@ def _deb_devirtualize(self, package: str) -> str: Provides` definition (although there may be multiple versions of that single package name), and doesn't have any `Provides` definitions. (e.g., `make` reverse-provides `make-guile`; but actually provides `make`, so it's not a - virtual package; `mail-transport-agent` returns multiple *different* - reverse-provides, so it can't be devirtualized). + virtual package; `mail-transport-agent` returns multiple *different* reverse- + provides, so it can't be devirtualized). :param package: The possibly virtualized package name - :returns: The devirtualized package name, or `None` if the package isn't - a virtual package + :returns: The devirtualized package name, or `None` if the package isn't a + virtual package """ devirtualized = None try: @@ -321,11 +321,11 @@ def _system_requirement_tools(self, app: LinuxSystemAppConfig): requirements. :param app: The app being built. - :returns: A triple containing (0) The list of package names that must - be installed at a bare minimum; (1) the arguments for the command - used to verify the existence of a package on a system, and (2) - the command used to install packages. All three values are `None` - if the system cannot be identified. + :returns: A triple containing (0) The list of package names that must be + installed at a bare minimum; (1) the arguments for the command used to + verify the existence of a package on a system, and (2) the command used to + install packages. All three values are `None` if the system cannot be + identified. """ if app.target_vendor_base == DEBIAN: base_system_packages = [ @@ -394,18 +394,19 @@ def verify_system_packages(self, app: LinuxSystemAppConfig): ) = self._system_requirement_tools(app) if not (system_verify and self.tools.shutil.which(system_verify[0])): - self.console.warning(""" -************************************************************************* -** WARNING: Can't verify system packages ** -************************************************************************* - - Briefcase doesn't know how to verify the installation of system - packages on your Linux distribution. If you have any problems - building this app, ensure that the packages listed in the app's - `system_requires` setting have been installed. - -************************************************************************* -""") + self.console.warning("""**************************************************** + ********************* ** WARNING: Can't verify system + packages ** ************* + ******************************************************* + ***** + + Briefcase doesn't know how to verify the installation of system + packages on your Linux distribution. If you have any problems + building this app, ensure that the packages listed in the app's + `system_requires` setting have been installed. + + ************************************************************************* + """) return # Run a check for each package listed in the app's system_requires, @@ -533,17 +534,24 @@ def _finalize_target_image(self, app): and self.tools.docker.is_user_mapped and self.tools.host_os != "Darwin" ): - raise BriefcaseCommandError("""\ -Briefcase cannot use this Docker installation to target Arch Linux since the -tools to build packages for Arch cannot be run as root. - -The Docker available to Briefcase requires the use of the root user in -containers to maintain accurate file permissions of the build artefacts. - -This most likely means you're using Docker Desktop or rootless Docker. - -Install Docker Engine and try again or run Briefcase on an Arch host system. -""") + raise BriefcaseCommandError( + r"""\ Briefcase cannot use this Docker + installation to target Arch Linux since the + tools to build packages for Arch cannot be + run as root. + + The Docker available to Briefcase requires + the use of the root user in containers to + maintain accurate file permissions of the + build artefacts. + + This most likely means you're using Docker + Desktop or rootless Docker. + + Install Docker Engine and try again or run + Briefcase on an Arch host system. + """ + ) else: super()._finalize_target_image(app) @@ -639,14 +647,14 @@ def verify_docker_python(self, app: LinuxSystemAppConfig): """Verify that the version of Python being used to build the app in Docker is compatible with the version being used to run Briefcase. - Will raise an exception if the Python version is fundamentally - incompatible (i.e., if Briefcase doesn't support it); any other version - discrepancy will log a warning, but continue. + Will raise an exception if the Python version is fundamentally incompatible + (i.e., if Briefcase doesn't support it); any other version discrepancy will log + a warning, but continue. Requires that the app tools have been verified. - As a side effect of verifying Python, the `python_version_tag` will be - updated to reflect the *actual* python version, not just a generic "3". + As a side effect of verifying Python, the `python_version_tag` will be updated + to reflect the *actual* python version, not just a generic "3". :param app: The application being built """ @@ -867,24 +875,33 @@ def build_app(self, app: FinalizedAppConfig, **kwargs): f"\n{separator}\n".join(parts), encoding="utf-8" ) else: - raise BriefcaseCommandError("""\ -Your project does not include any license files. + raise BriefcaseCommandError( + r"""\ Your project does not include any license + files. -Ensure your `pyproject.toml` is in PEP 639 format and specifies at least -one file in the `license-files` setting. - """) + Ensure your `pyproject.toml` is in PEP 639 + format and specifies at least one file in the + `license-files` setting. + """ + ) with self.console.wait_bar("Installing changelog..."): changelog = find_changelog_filename(self.base_path) if changelog is None: - raise BriefcaseCommandError("""\ -Your project does not contain a changelog file with a known file name. You -must provide a changelog file in the same directory as your `pyproject.toml`, -with a known changelog file name (one of 'CHANGELOG', 'HISTORY', 'NEWS' or -'RELEASES'; the file may have an extension of '.md', '.rst', or '.txt', or have -no extension). -""") + raise BriefcaseCommandError( + r"""\ Your project does not contain a + changelog file with a known file name. + + You must provide a changelog file in the + same directory as your `pyproject.toml`, + with a known changelog file name (one of + 'CHANGELOG', 'HISTORY', 'NEWS' or + 'RELEASES'; the file may have an extension + of '.md', '.rst', or '.txt', or have no + extension). + """ + ) changelog_source = self.base_path / changelog @@ -1294,13 +1311,19 @@ def _package_rpm( changelog = find_changelog_filename(self.base_path) if changelog is None: - raise BriefcaseCommandError("""\ -Your project does not contain a changelog file with a known file name. You -must provide a changelog file in the same directory as your `pyproject.toml`, -with a known changelog file name (one of 'CHANGELOG', 'HISTORY', 'NEWS' or -'RELEASES'; the file may have an extension of '.md', '.rst', or '.txt', or have -no extension). -""") + raise BriefcaseCommandError( + r"""\ Your project does not contain a + changelog file with a known file name. + + You must provide a changelog file in the + same directory as your `pyproject.toml`, + with a known changelog file name (one of + 'CHANGELOG', 'HISTORY', 'NEWS' or + 'RELEASES'; the file may have an extension + of '.md', '.rst', or '.txt', or have no + extension). + """ + ) # Write the changelog content f.write((self.base_path / changelog).read_text(encoding="utf-8")) diff --git a/src/briefcase/platforms/macOS/__init__.py b/src/briefcase/platforms/macOS/__init__.py index d00cd429c1..8a4003b67e 100644 --- a/src/briefcase/platforms/macOS/__init__.py +++ b/src/briefcase/platforms/macOS/__init__.py @@ -152,9 +152,10 @@ def verify_not_on_icloud(self, app: FinalizedAppConfig, cleanup=False): """ if self.is_icloud_synced(self.binary_path(app)): msg = [ - """\ -Your project is in a folder that is synchronized with iCloud. This interferes -with the operation of macOS code signing.""" + r"""\ Your project is in a folder that is synchronized with iCloud. + + This interferes with the operation of macOS code signing. + """ ] if cleanup: self.tools.shutil.rmtree(self.bundle_path(app)) @@ -993,9 +994,10 @@ def ditto_archive( Although the archive format is ".zip", we can't use standard Zip tools, as they don't preserve UTF-8 encoding on all resources. Instead, we need to use `ditto`, which is provided as part of macOS developer tooling. See - https://forums.developer.apple.com/forums/thread/116831 and - https://developer.apple.com/library/archive/technotes/tn2206/_index.html for - more details. + https://forums.developer.apple.com/forums/thread/116831 + and + https://developer.apple.com/library/archive/technotes/tn2206/_index.html + for more details. :param app_filename: The filename of the app to archive :param archive_filename: The filename of the archive to produce @@ -1094,23 +1096,24 @@ def submit_notarization(self, app, identity: SigningIdentity) -> str: $ xcrun notarytool store-credentials --team-id {identity.team_id} {identity.profile} """) - - self.console.warning(""" -The notarization process uses credentials stored on your system Keychain. -You need to do this once for each signing certificate you use. - -The credentials are authenticated and stored using your Apple ID, using -an app-specific Apple ID password. To generate an app-specific Apple ID -password: - - 1. Sign into https://appleid.apple.com; - 2. In the 'Sign-in and Security' section, click 'App-Specific Passwords'; - 3. Click on the '+' icon. You will need to provide an identifying name - for the password. You can pick any name that makes sense to you - the - name is only there so you can identify passwords. 'Briefcase' would be - one possible name. - 4. Record the password somewhere safe. -""") + self.console.warning( + """The notarization process uses credentials + stored on your system Keychain. You need to do + this once for each signing certificate you use. + + The credentials are authenticated and stored using your Apple ID, using + an app-specific Apple ID password. To generate an app-specific Apple ID + password: + + 1. Sign into https://appleid.apple.com; + 2. In the 'Sign-in and Security' section, click 'App-Specific Passwords'; + 3. Click on the '+' icon. You will need to provide an identifying name + for the password. You can pick any name that makes sense to you - the + name is only there so you can identify passwords. 'Briefcase' would be + one possible name. + 4. Record the password somewhere safe. + """ + ) try: self.tools.subprocess.run( [ @@ -1388,23 +1391,25 @@ def package_app( raise BriefcaseCommandError( "Can't notarize an app with an ad-hoc signing identity" ) - self.console.warning(""" -************************************************************************* -** WARNING: Signing with an ad-hoc identity ** -************************************************************************* - - This app is being signed with an ad-hoc identity. The resulting - app will run on this computer, but will not run on anyone else's - computer. - - To generate an app that can be distributed to others, you must - obtain an application distribution certificate from Apple, and - select the developer identity associated with that certificate - when running 'briefcase package'. - -************************************************************************* - -""") + self.console.warning( + """**************************************************** + ********************* ** WARNING: Signing with an ad- + hoc identity ** ************ + ******************************************************* + ****** + + This app is being signed with an ad-hoc identity. The resulting + app will run on this computer, but will not run on anyone else's + computer. + + To generate an app that can be distributed to others, you must + obtain an application distribution certificate from Apple, and + select the developer identity associated with that certificate + when running 'briefcase package'. + + ************************************************************************* + """ + ) self.console.info("Signing app with ad-hoc identity...") else: # If we're signing, and notarization isn't explicitly disabled, diff --git a/src/briefcase/platforms/macOS/filters.py b/src/briefcase/platforms/macOS/filters.py index 46833b0e3b..c85945b23d 100644 --- a/src/briefcase/platforms/macOS/filters.py +++ b/src/briefcase/platforms/macOS/filters.py @@ -15,10 +15,10 @@ def macOS_log_clean_filter(line): Any system or stub messages are ignored; all logging prefixes are stripped. :param line: The raw line from the system log - :returns: A tuple, containing (a) the log line, stripped of any system - logging context, and (b) a boolean indicating if the message should be - included for analysis purposes (i.e., it's Python content, not a system - message). Returns a single ``None`` if the line should be dumped. + :returns: A tuple, containing (a) the log line, stripped of any system logging + context, and (b) a boolean indicating if the message should be included for + analysis purposes (i.e., it's Python content, not a system message). Returns a + single ``None`` if the line should be dumped. """ if line.startswith( ( diff --git a/src/briefcase/platforms/web/static.py b/src/briefcase/platforms/web/static.py index 08a7d9146b..0df027ed09 100644 --- a/src/briefcase/platforms/web/static.py +++ b/src/briefcase/platforms/web/static.py @@ -772,9 +772,9 @@ class StaticWebDevCommand(StaticWebMixin, DevCommand): def venv_name(self) -> str: """Returns the name of the virtual environment directory. - The web environment uses a different venv because a web deployment has - different requirements to a desktop deployment. The name is the same as the - desktop deployment, but with `.web` appended. + The web environment uses a different venv because a web deployment has different + requirements to a desktop deployment. The name is the same as the desktop + deployment, but with `.web` appended. :returns: Name for virtual environment directory """ diff --git a/src/briefcase/platforms/windows/__init__.py b/src/briefcase/platforms/windows/__init__.py index 4df865ff4a..6e1ac761b2 100644 --- a/src/briefcase/platforms/windows/__init__.py +++ b/src/briefcase/platforms/windows/__init__.py @@ -130,11 +130,14 @@ def verify_host(self): # 64bit Python is required to ensure 64bit wheels are installed/created # for the app if self.tools.is_32bit_python: - raise UnsupportedHostError("""\ -Windows applications cannot be built using a 32bit version of Python. + raise UnsupportedHostError( + r"""\ Windows applications cannot be built using a + 32bit version of Python. -Install a 64bit version of Python and run Briefcase again. -""") + Install a 64bit version of Python and run + Briefcase again. + """ + ) class WindowsCreateCommand(CreateCommand): @@ -231,22 +234,24 @@ def _cleanup_app_support_package(self, support_path): # On Windows, the support path is co-mingled with app content. # This means updating the support package is imperfect. # Warn the user that there could be problems. - self.console.warning(""" -************************************************************************* -** WARNING: Support package update may be imperfect ** -************************************************************************* - - Support packages in Windows apps are overlaid with app content, - so it isn't possible to remove all old support files before - installing new ones. - - Briefcase will unpack the new support package without cleaning up - existing support package content. This *should* work; however, - ensure a reproducible release artefacts, it is advisable to - perform a clean app build before release. - -************************************************************************* -""") + self.console.warning( + """******************************************************** + ***************** ** WARNING: Support package update may be + imperfect ** *************************** + ********************************************** + + Support packages in Windows apps are overlaid with app content, + so it isn't possible to remove all old support files before + installing new ones. + + Briefcase will unpack the new support package without cleaning up + existing support package content. This *should* work; however, + ensure a reproducible release artefacts, it is advisable to + perform a clean app build before release. + + ************************************************************************* + """ + ) def install_license(self, app: FinalizedAppConfig): """Install the license for the project as a single RTF document. @@ -269,12 +274,15 @@ def install_license(self, app: FinalizedAppConfig): ] if len(app.license_files) == 0: - raise BriefcaseCommandError("""\ -Your project does not include any license files. + raise BriefcaseCommandError( + r"""\ Your project does not include any license + files. -Ensure your `pyproject.toml` is in PEP 639 format and specifies at least -one file in the `license-files` setting. -""") + Ensure your `pyproject.toml` is in PEP 639 + format and specifies at least one file in the + `license-files` setting. + """ + ) elif len(app.license_files) == 1: license_file = self.base_path / app.license_files[0] if license_file.suffix == ".rtf": @@ -501,7 +509,6 @@ def sign_file( timestamp_digest: str, ): """Sign a file.""" - if not re.fullmatch(r"^[0-9a-f]{40}$", identity, flags=re.IGNORECASE): raise BriefcaseCommandError( f"Codesigning identify {identity!r} must be a " @@ -567,24 +574,26 @@ def package_app( :param timestamp_url: Timestamp authority server to use in code signing. :param timestamp_digest: Hashing algorithm to request from the timestamp server. """ - if adhoc_sign: sign_app = False elif identity: sign_app = True else: sign_app = False - self.console.warning(""" -************************************************************************* -** WARNING: No signing identity provided ** -************************************************************************* - - Briefcase will not sign the app. To provide a signing identity, - use the `--identity` option; or, to explicitly disable signing, - use `--adhoc-sign`. - -************************************************************************* -""") + self.console.warning( + """**************************************************** + ********************* ** WARNING: No signing identity + provided ** ************* + ******************************************************* + ***** + + Briefcase will not sign the app. To provide a signing identity, + use the `--identity` option; or, to explicitly disable signing, + use `--adhoc-sign`. + + ************************************************************************* + """ + ) if sign_app: self.console.info("Signing App...", prefix=app.app_name) @@ -641,7 +650,6 @@ def _package_msi(self, app): def _package_zip(self, app): """Package the app as simple zip file.""" - self.console.info("Building zip file...", prefix=app.app_name) with self.console.wait_bar("Packing..."): source = self.package_path(app) diff --git a/tests/commands/base/test_end_of_life.py b/tests/commands/base/test_end_of_life.py index 4d780f31b5..9d16711b60 100644 --- a/tests/commands/base/test_end_of_life.py +++ b/tests/commands/base/test_end_of_life.py @@ -29,7 +29,6 @@ def test_valid_python_version( minor_version, today, is_valid, base_command, monkeypatch ): """A warning is produced if the Python version is past its EOL.""" - version_info = _create_version_info(3, minor_version) sys_mock = mock.MagicMock(wraps=sys) sys_mock.version_info = version_info diff --git a/tests/commands/base/test_finalize.py b/tests/commands/base/test_finalize.py index 95f08e53ee..c31d435e76 100644 --- a/tests/commands/base/test_finalize.py +++ b/tests/commands/base/test_finalize.py @@ -124,7 +124,6 @@ def test_finalize_all_repeat(base_command, first_app, second_app): def test_finalize_single_repeat(base_command, first_app, second_app): """Multiple calls to finalize verifies host & tools multiple times, but finalizes app "config once.""" - # Finalize app twice. This is an approximation of what happens # when a command chain is executed; create, update, build and run will # all finalize; create will finalize the app config, each command will diff --git a/tests/commands/base/test_verify_requires_python.py b/tests/commands/base/test_verify_requires_python.py index 2cc7251bd0..1be85c09cd 100644 --- a/tests/commands/base/test_verify_requires_python.py +++ b/tests/commands/base/test_verify_requires_python.py @@ -18,7 +18,6 @@ def _get_global_config(requires_python): def test_no_requires_python(base_command, my_app): """If requires-python isn't set, no verification is necessary.""" - base_command.global_config = _get_global_config(requires_python=None) base_command.verify_required_python(my_app) @@ -39,7 +38,6 @@ def test_no_requires_python(base_command, my_app): def test_requires_python_met(base_command, my_app, requires_python): """Validation passes if requires-python specifies a version compatible with the running interpreter.""" - base_command.global_config = _get_global_config( requires_python.format(current=platform.python_version()) ) @@ -63,7 +61,6 @@ def test_requires_python_met(base_command, my_app, requires_python): def test_requires_python_unmet(base_command, my_app, requires_python): """Validation fails if requires-python specifies a version incompatible with the running interpreter.""" - base_command.global_config = _get_global_config(requires_python) with pytest.raises(UnsupportedPythonVersion): @@ -72,7 +69,6 @@ def test_requires_python_unmet(base_command, my_app, requires_python): def test_requires_python_invalid_specifier(base_command, my_app): """Validation fails if requires-python is not a valid specifier.""" - base_command.global_config = _get_global_config(requires_python="0") with pytest.raises(BriefcaseConfigError, match="Invalid requires-python"): diff --git a/tests/commands/convert/test__find_changelog_filename.py b/tests/commands/convert/test__find_changelog_filename.py index ae338b2af8..b5243cdc16 100644 --- a/tests/commands/convert/test__find_changelog_filename.py +++ b/tests/commands/convert/test__find_changelog_filename.py @@ -34,7 +34,6 @@ def test_has_changelog(tmp_path, changelog_filename): def test_multiple_changefile(tmp_path): """If there's more than one changelog, only one is found.""" - changelog_file1 = tmp_path / "base_path/NEWS.txt" changelog_file2 = tmp_path / "base_path/CHANGELOG.md" create_file(changelog_file1, "First App Changelog") diff --git a/tests/commands/create/test_cleanup_app_support_package.py b/tests/commands/create/test_cleanup_app_support_package.py index 68759770d1..7c4de0c862 100644 --- a/tests/commands/create/test_cleanup_app_support_package.py +++ b/tests/commands/create/test_cleanup_app_support_package.py @@ -33,7 +33,6 @@ def test_cleanup_support_package( app_requirements_path_index, ): """If a support package already exists, it can be cleaned up.""" - # Mock an existing support file create_file(support_path / "old/trash.txt", "Old support file") diff --git a/tests/commands/create/test_create_app.py b/tests/commands/create/test_create_app.py index 44f0ba562a..5491a7c6ff 100644 --- a/tests/commands/create/test_create_app.py +++ b/tests/commands/create/test_create_app.py @@ -163,7 +163,6 @@ def test_create_existing_app_input_disabled(tracking_create_command, tmp_path): def test_create_app_not_supported(tracking_create_command, tmp_path): """If the supported attribute is false, the command will terminate with an error message.""" - with pytest.raises(UnsupportedPlatform): tracking_create_command.create_app( DraftAppConfig( diff --git a/tests/commands/create/test_generate_app_template.py b/tests/commands/create/test_generate_app_template.py index 8f1a8f853a..174ac1bdd8 100644 --- a/tests/commands/create/test_generate_app_template.py +++ b/tests/commands/create/test_generate_app_template.py @@ -741,7 +741,6 @@ def test_cookiecutter_undefined_variable_in_template( ): """If the cookiecutter template has an undefined variable, a breifcase configuration error is raised.""" - cookiecutter_exception_message = "undefined document type" create_command.tools.cookiecutter.side_effect = ( cookiecutter_exceptions.UndefinedVariableInTemplate( diff --git a/tests/commands/create/test_install_app_code.py b/tests/commands/create/test_install_app_code.py index c5db1e4424..6f5ea162b4 100644 --- a/tests/commands/create/test_install_app_code.py +++ b/tests/commands/create/test_install_app_code.py @@ -796,7 +796,6 @@ def test_dist_info_with_missing_optional_fields( app_requirements_path_index, ): """Dist-info is created correctly when optional app fields are set to None.""" - myapp.url = None myapp.author = None myapp.author_email = None diff --git a/tests/commands/create/test_install_app_requirements.py b/tests/commands/create/test_install_app_requirements.py index 86f9fc3270..0368db1465 100644 --- a/tests/commands/create/test_install_app_requirements.py +++ b/tests/commands/create/test_install_app_requirements.py @@ -40,8 +40,8 @@ def mock_now(monkeypatch): def create_installation_artefacts(app_packages_path, packages): """Utility method for generating a function that will mock installation artefacts. - Creates a function that when invoked, creates a dummy ``__init__.py`` - and ``__main__.py`` for each package named in ``packages``. + Creates a function that when invoked, creates a dummy ``__init__.py`` and + ``__main__.py`` for each package named in ``packages``. :param app_packages_path: The pathlib object where app packages will be installed :param packages: A list of package names to mock. diff --git a/tests/commands/create/test_install_app_support_package.py b/tests/commands/create/test_install_app_support_package.py index 6768b8154a..4ca238d529 100644 --- a/tests/commands/create/test_install_app_support_package.py +++ b/tests/commands/create/test_install_app_support_package.py @@ -208,7 +208,6 @@ def test_support_package_url_with_invalid_custom_support_package_url( app_requirements_path_index, ): """Invalid URL for a custom support package raises MissingNetworkResourceError.""" - # Provide an custom support URL url = "https://example.com/custom/support.zip" myapp.support_package = url diff --git a/tests/commands/create/test_install_stub_binary.py b/tests/commands/create/test_install_stub_binary.py index e73a593af0..0ad53150cb 100644 --- a/tests/commands/create/test_install_stub_binary.py +++ b/tests/commands/create/test_install_stub_binary.py @@ -462,7 +462,6 @@ def test_install_custom_stub_binary_with_invalid_url( stub_binary_revision_path_index, ): """Invalid URL for a custom stub binary raises MissingNetworkResourceError.""" - # Provide a custom stub binary URL url = "https://example.com/custom/stub.zip" myapp.stub_binary = url diff --git a/tests/commands/new/test_build_app_context.py b/tests/commands/new/test_build_app_context.py index 648e3cffc6..2fc2411b6e 100644 --- a/tests/commands/new/test_build_app_context.py +++ b/tests/commands/new/test_build_app_context.py @@ -5,7 +5,6 @@ def test_question_sequence(new_command): """Questions are asked, a context is constructed.""" - # Prime answers for all the questions. new_command.console.values = [ "My Application", # formal name @@ -42,7 +41,6 @@ def test_question_sequence(new_command): def test_question_sequence_with_overrides(new_command): """Overrides can be used to set the answers for questions.""" - # Prime answers for none of the questions. new_command.console.values = [] @@ -79,7 +77,6 @@ def test_question_sequence_with_overrides(new_command): def test_question_sequence_with_bad_license_override(new_command): """A bad override for license uses user input instead.""" - # Prime answers for all the questions. new_command.console.values = [ "4", # license @@ -118,7 +115,6 @@ def test_question_sequence_with_bad_license_override(new_command): def test_question_sequence_with_no_user_input(new_command): """If no user input is provided, all user inputs are taken as default.""" - new_command.console.input_enabled = False context = new_command.build_app_context(project_overrides={}) diff --git a/tests/commands/new/test_build_gui_context.py b/tests/commands/new/test_build_gui_context.py index f412cdc327..6284f9e0af 100644 --- a/tests/commands/new/test_build_gui_context.py +++ b/tests/commands/new/test_build_gui_context.py @@ -11,7 +11,6 @@ def test_toga_bootstrap(new_command): """Context can be requested from the Toga bootstrap.""" - context = new_command.build_gui_context( TogaGuiBootstrap( new_command.console, @@ -47,12 +46,10 @@ def startup(self): def main(): return {{ cookiecutter.class_name }}() ''', - "app_start_source": """\ -from {{ cookiecutter.module_name }}.app import main + "app_start_source": r"""\ from {{ cookiecutter.module_name }}.app import main. -if __name__ == "__main__": - main().main_loop() -""", + if __name__ == "__main__": main().main_loop() + """, "pyproject_table_briefcase_app_extra_content": """ requires = [ ] @@ -238,7 +235,6 @@ def main(): def test_console_bootstrap(new_command): """Context can be requested from the Console bootstrap.""" - context = new_command.build_gui_context( ConsoleBootstrap( new_command.console, @@ -347,7 +343,6 @@ def main(): def test_pyside6_bootstrap(new_command): """Context can be requested from the PySide6 bootstrap.""" - context = new_command.build_gui_context( PySide6GuiBootstrap( new_command.console, @@ -496,7 +491,6 @@ def main(): def test_pygame_bootstrap(new_command): """Context can be requested from the Pygame bootstrap.""" - context = new_command.build_gui_context( PygameGuiBootstrap( new_command.console, @@ -634,7 +628,6 @@ def main(): def test_no_bootstrap(new_command): """The empty bootstrap is used if no bootstrap is selected.""" - context = new_command.build_gui_context( EmptyBootstrap( new_command.console, diff --git a/tests/commands/new/test_call.py b/tests/commands/new/test_call.py index ecb61ddd60..ba7f65e22c 100644 --- a/tests/commands/new/test_call.py +++ b/tests/commands/new/test_call.py @@ -37,7 +37,6 @@ def test_parse_config(new_command): ) def test_new_app(new_command, cmdline, overrides): """A new application can be created.""" - # Configure no command line options options, _ = new_command.parse_options(cmdline) diff --git a/tests/commands/new/test_select_bootstrap.py b/tests/commands/new/test_select_bootstrap.py index 3969dcd3ad..0bef38e21c 100644 --- a/tests/commands/new/test_select_bootstrap.py +++ b/tests/commands/new/test_select_bootstrap.py @@ -171,7 +171,6 @@ def _metadata(package_name): def test_question_sequence_with_overrides(new_command, monkeypatch): """The answer to the bootstrap question can be overridden.""" - bootstrap_class = new_command.select_bootstrap( project_overrides={"bootstrap": "Toga"} ) @@ -181,7 +180,6 @@ def test_question_sequence_with_overrides(new_command, monkeypatch): def test_question_sequence_with_bad_bootstrap_override(new_command, monkeypatch): """A bad override for the bootstrap uses user input instead.""" - # Simulate user selecting Toga from the menu after bad override. new_command.console.values = [ "1", # Toga @@ -196,7 +194,6 @@ def test_question_sequence_with_bad_bootstrap_override(new_command, monkeypatch) def test_question_sequence_with_no_user_input(new_command): """If no user input is provided, all user inputs are taken as default.""" - new_command.console.input_enabled = False bootstrap_class = new_command.select_bootstrap(project_overrides={}) diff --git a/tests/commands/package/test_call.py b/tests/commands/package/test_call.py index 4280182fed..dc3d4ae6f1 100644 --- a/tests/commands/package/test_call.py +++ b/tests/commands/package/test_call.py @@ -247,7 +247,6 @@ def test_adhoc_sign_args_package_two_app( ): """If there are multiple apps,and an --adhoc argument, package signs all apps using ad-hoc identity.""" - package_command.apps = { # Add the first app "first": first_app, diff --git a/tests/commands/update/test_update_app.py b/tests/commands/update/test_update_app.py index f8d7de6fcd..4785d548d3 100644 --- a/tests/commands/update/test_update_app.py +++ b/tests/commands/update/test_update_app.py @@ -31,7 +31,6 @@ def test_update_app(update_command, first_app, tmp_path): def test_update_non_existing_app(update_command, tmp_path): """If the app hasn't been generated yet, it won't be created.""" - update_command.update_app( update_command.apps["first"], update_requirements=False, diff --git a/tests/commands/upgrade/test_call.py b/tests/commands/upgrade/test_call.py index cb58ee73ef..ee75d6bb03 100644 --- a/tests/commands/upgrade/test_call.py +++ b/tests/commands/upgrade/test_call.py @@ -272,7 +272,6 @@ def test_upgrade_mixed_tools(upgrade_command, mock_tool_registry, capsys): def test_unknown_tool(upgrade_command, mock_tool_registry, capsys): """An upgrade attempt for an unknown tool raises an error.""" - with pytest.raises( UpgradeToolError, match=r"Briefcase does not know how to manage unknown_tool_1, unknown_tool_2\.", diff --git a/tests/config/test_parse_config.py b/tests/config/test_parse_config.py index 91f197534e..8f276992ff 100644 --- a/tests/config/test_parse_config.py +++ b/tests/config/test_parse_config.py @@ -28,11 +28,7 @@ def test_no_briefcase_section(tmp_path): """If the config file doesn't contain a briefcase tool section, raise an error.""" config_file = create_file( tmp_path / "pyproject.toml", - """ - [tool.section] - name="value" - number=42 - """, + """[tool.section] name="value" number=42.""", ) with pytest.raises(BriefcaseConfigError, match=r"No tool\.briefcase section"): @@ -48,11 +44,7 @@ def test_no_apps(tmp_path): """If the config file doesn't contain at least one briefcase app, raise an error.""" config_file = create_file( tmp_path / "pyproject.toml", - """ - [tool.briefcase] - name="value" - number=42 - """, + """[tool.briefcase] name="value" number=42.""", ) with pytest.raises(BriefcaseConfigError, match="No Briefcase apps defined"): @@ -111,14 +103,9 @@ def test_multiple_minimal_apps(tmp_path): """The configuration can contain multiple apps without an explicit tool header.""" config_file = create_file( tmp_path / "pyproject.toml", - """ - [tool.briefcase.app.first] - number=37 - license="MIT" + """[tool.briefcase.app.first] number=37 license="MIT". - [tool.briefcase.app.second] - number=42 - license="BSD-3-Clause" + [tool.briefcase.app.second] number=42 license="BSD-3-Clause" """, ) @@ -1390,8 +1377,7 @@ def test_no_license_key(tmp_path): """An app without a license key gets dummy values and a warning.""" config_file = create_file( tmp_path / "pyproject.toml", - """ - [tool.briefcase] + """[tool.briefcase] [tool.briefcase.app.my_app] """, diff --git a/tests/config/test_validate_document_type_config.py b/tests/config/test_validate_document_type_config.py index 732517b030..0d8bd96a27 100644 --- a/tests/config/test_validate_document_type_config.py +++ b/tests/config/test_validate_document_type_config.py @@ -90,7 +90,6 @@ def test_validate_document_missing_description(valid_document): ) def test_validate_document_invalid_description(invalid_description, valid_document): """Invalid description values raise an error when validating document types.""" - valid_document["description"] = invalid_description # Failure raises an exception with pytest.raises( @@ -102,7 +101,6 @@ def test_validate_document_invalid_description(invalid_description, valid_docume def test_validate_document_missing_url(valid_document): """If a document type is missing a URL, an exception is raised.""" - del valid_document["url"] # Failure raises an exception with pytest.raises( @@ -122,7 +120,6 @@ def test_validate_document_missing_url(valid_document): ) def test_validate_document_invalid_url(invalid_url, valid_document): """Invalid URL values raise an error when validating document types.""" - valid_document["url"] = invalid_url # Failure raises an exception with pytest.raises( @@ -134,7 +131,6 @@ def test_validate_document_invalid_url(invalid_url, valid_document): def test_validate_document_missing_extension(valid_document): """If a document type is missing an extension, an exception is raised.""" - del valid_document["extension"] # Failure raises an exception with pytest.raises( diff --git a/tests/conftest.py b/tests/conftest.py index 3494c3583e..9f024cc59e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,6 @@ def pytest_sessionstart(session): """Ensure that tests don't use a color console.""" - os.environ["TERM"] = "dumb" os.environ["NO_COLOR"] = "1" try: diff --git a/tests/console/Console/test_release_console_control.py b/tests/console/Console/test_release_console_control.py index 850b22dc8f..027ec72162 100644 --- a/tests/console/Console/test_release_console_control.py +++ b/tests/console/Console/test_release_console_control.py @@ -3,7 +3,6 @@ def test_console_is_controlled(console): """Console control is released and then restored.""" - console.is_console_controlled = True with console.release_console_control(): diff --git a/tests/console/Console/test_text_question.py b/tests/console/Console/test_text_question.py index 96e109121b..9c4580e470 100644 --- a/tests/console/Console/test_text_question.py +++ b/tests/console/Console/test_text_question.py @@ -112,7 +112,6 @@ def test_input_with_default(console): def test_input_disabled(disabled_console): """If input is disabled, the default is returned.""" - value = disabled_console.text_question( intro="Some introduction", description="My variable", diff --git a/tests/console/Console/test_warning_banner.py b/tests/console/Console/test_warning_banner.py index 650dbcf063..fc7d172ed6 100644 --- a/tests/console/Console/test_warning_banner.py +++ b/tests/console/Console/test_warning_banner.py @@ -25,25 +25,25 @@ TEST_TITLE, TEST_MESSAGE, 80, - """ -******************************************************************************** -** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are inconsistent ** -******************************************************************************** + """************************************************************************* + ******* ** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are + inconsistent ** **************************************************** + **************************** - The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to - different paths: + The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to + different paths: - ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py - ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py + ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py + ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py - Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by - ANDROID_HOME. + Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by + ANDROID_HOME. - You should update your environment configuration to either not set - ANDROID_SDK_ROOT, or set both environment variables to the same path. + You should update your environment configuration to either not set + ANDROID_SDK_ROOT, or set both environment variables to the same path. -******************************************************************************** -""", + ******************************************************************************** + """, id="80-char", ), # Wrap to 60 chars @@ -51,27 +51,26 @@ TEST_TITLE, TEST_MESSAGE, 60, - """ -************************************************************ -** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are ** -** inconsistent ** -************************************************************ + """************************************************************ ** + WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are ** ** + inconsistent ** + ************************************************************ - The ANDROID_HOME and ANDROID_SDK_ROOT environment - variables are set to different paths: + The ANDROID_HOME and ANDROID_SDK_ROOT environment + variables are set to different paths: - ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py - ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py + ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py + ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py - Briefcase will ignore ANDROID_SDK_ROOT and only use the - path specified by ANDROID_HOME. + Briefcase will ignore ANDROID_SDK_ROOT and only use the + path specified by ANDROID_HOME. - You should update your environment configuration to - either not set ANDROID_SDK_ROOT, or set both environment - variables to the same path. + You should update your environment configuration to + either not set ANDROID_SDK_ROOT, or set both environment + variables to the same path. -************************************************************ -""", + ************************************************************ + """, id="60-char", ), # Wrap to 120 chars @@ -79,23 +78,24 @@ TEST_TITLE, TEST_MESSAGE, 120, - """ -************************************************************************************************************************ -** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are inconsistent ** -************************************************************************************************************************ + """************************************************************************* + *********************************************** ** + WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are inconsistent + ** ************************************************************************* + *********************************************** - The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to different paths: + The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to different paths: - ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py - ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py + ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py + ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py - Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by ANDROID_HOME. + Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by ANDROID_HOME. - You should update your environment configuration to either not set ANDROID_SDK_ROOT, or set both environment - variables to the same path. + You should update your environment configuration to either not set ANDROID_SDK_ROOT, or set both environment + variables to the same path. -************************************************************************************************************************ -""", # noqa: E501 + ************************************************************************************************************************ + """, # noqa: E501 id="120-char", ), # Default width (80) without title @@ -103,34 +103,33 @@ None, TEST_MESSAGE, 80, - """ -******************************************************************************** + """************************************************************************* + ******* - The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to - different paths: + The ANDROID_HOME and ANDROID_SDK_ROOT environment variables are set to + different paths: - ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py - ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py + ANDROID_HOME: /briefcase/tests/console/Console/test_warning_banner.py + ANDROID_SDK_ROOT: /home/anton/briefcase/tests/console/Console/test_warning_banner.py - Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by - ANDROID_HOME. + Briefcase will ignore ANDROID_SDK_ROOT and only use the path specified by + ANDROID_HOME. - You should update your environment configuration to either not set - ANDROID_SDK_ROOT, or set both environment variables to the same path. + You should update your environment configuration to either not set + ANDROID_SDK_ROOT, or set both environment variables to the same path. -******************************************************************************** -""", + ******************************************************************************** + """, id="no-title", ), pytest.param( TEST_TITLE, None, 80, - """ -******************************************************************************** -** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are inconsistent ** -******************************************************************************** -""", + """************************************************************************* + ******* ** WARNING: ANDROID_HOME and ANDROID_SDK_ROOT are + inconsistent ** **************************************************** + ****************************""", id="title-only", ), # Message and title lengths equal to box width @@ -138,38 +137,32 @@ "Length of ............. width", "Length of message is equal to box width.", 44, - """ -******************************************** -** WARNING: Length of ............. width ** -******************************************** + """******************************************** ** WARNING: Length of + ............. width ** ******************************************** - Length of message is equal to box width. + Length of message is equal to box width. -******************************************** -""", + ******************************************** + """, id="exact-width", ), pytest.param( "Length+ of ............. width", "Length+ of message is equal to box width.", 44, - """ -******************************************** -** WARNING: Length+ of ............. ** -** width ** -******************************************** + """******************************************** ** WARNING: Length+ of + ............. ** ** width ** + ******************************************** - Length+ of message is equal to box - width. + Length+ of message is equal to box width. -******************************************** -""", + ******************************************** + """, id="1-char-wrap", ), pytest.param( None, - """ - Start text with literal + """Start text with literal. More text @@ -182,7 +175,6 @@ A single literal. Text following a literal, followed by a blank line. - """, 40, """ @@ -211,7 +203,6 @@ ) def test_warning_banner(console, title, message, width, expected, capsys): """Test warning_banner with various inputs.""" - # call the function console.warning_banner(title=title, message=message, width=width) # capture console output diff --git a/tests/console/test_Log.py b/tests/console/test_Log.py index 2e1a650a12..1ef04a2743 100644 --- a/tests/console/test_Log.py +++ b/tests/console/test_Log.py @@ -89,7 +89,6 @@ def test_log_level( def test_info_logging(console, capsys): """The info level logging only includes info logs.""" - console.info("info") console.verbose("verbose") console.debug("debug") diff --git a/tests/integrations/android_sdk/AndroidSDK/test_avd_config.py b/tests/integrations/android_sdk/AndroidSDK/test_avd_config.py index 78160cfd9d..1e71edbb8c 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_avd_config.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_avd_config.py @@ -8,17 +8,12 @@ def test_avd_config(mock_tools, android_sdk, tmp_path): # * a key whose value explicitly contains an equals sign. with config_file.open("w", encoding="utf-8") as f: f.write( - """ -avd.ini.encoding=UTF-8 -hw.device.manufacturer=Google -hw.device.name=pixel -weird.key=good=bad + """avd.ini.encoding=UTF-8 hw.device.manufacturer=Google hw.device.name=pixel + weird.key=good=bad. -PlayStore.enabled=no -avd.name=testDevice -disk.cachePartition=yes -disk.cachePartition.size=37MB -""" + PlayStore.enabled=no avd.name=testDevice disk.cachePartition=yes + disk.cachePartition.size=37MB + """ ) assert android_sdk.avd_config("testDevice") == { diff --git a/tests/integrations/android_sdk/AndroidSDK/test_list_installed_images.py b/tests/integrations/android_sdk/AndroidSDK/test_list_installed_images.py index a95ec1f63b..520ce1d4be 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_list_installed_images.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_list_installed_images.py @@ -7,7 +7,6 @@ def test_list_installed_system_images(mock_tools, android_sdk): """Returns a set of installed system image package identifiers.""" - mock_tools.subprocess.check_output.return_value = ( "Installed packages:\n" " Path | Version | Description | Location\n" diff --git a/tests/integrations/android_sdk/AndroidSDK/test_select_target_device.py b/tests/integrations/android_sdk/AndroidSDK/test_select_target_device.py index 7660247b5d..9310c6cff0 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_select_target_device.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_select_target_device.py @@ -52,7 +52,6 @@ def mock_adb(device_id): def test_explicit_device(mock_tools, android_sdk): """If the user explicitly names a physical device, it is returned.""" - # Select device with an explicit device ID device, name, avd = android_sdk.select_target_device("KABCDABCDA1513") @@ -68,7 +67,6 @@ def test_explicit_device(mock_tools, android_sdk): def test_explicit_unauthorized_device(mock_tools, android_sdk): """If the user explicitly names an unauthorized physical device, an error is raised.""" - # Select unauthorized device with an explicit device ID with pytest.raises(AndroidDeviceNotAuthorized): android_sdk.select_target_device("041234567892009a") @@ -79,7 +77,6 @@ def test_explicit_unauthorized_device(mock_tools, android_sdk): def test_explicit_running_emulator_by_id(mock_tools, android_sdk): """If the user explicitly names a running emulator by device ID, it is selected.""" - # Select emulator with an explicit device ID device, name, avd = android_sdk.select_target_device("emulator-5554") @@ -94,7 +91,6 @@ def test_explicit_running_emulator_by_id(mock_tools, android_sdk): def test_explicit_running_emulator_by_avd(mock_tools, android_sdk): """If the user explicitly names a running emulator by AVD, it is selected.""" - # Select emulator with an explicit device ID device, name, avd = android_sdk.select_target_device("@runningEmulator") @@ -109,7 +105,6 @@ def test_explicit_running_emulator_by_avd(mock_tools, android_sdk): def test_explicit_idle_emulator(mock_tools, android_sdk): """If the user explicitly names an idle emulator by AVD, it is selected.""" - # Select emulator with an explicit device ID device, name, avd = android_sdk.select_target_device("@idleEmulator") @@ -124,7 +119,6 @@ def test_explicit_idle_emulator(mock_tools, android_sdk): def test_explicit_invalid_device(mock_tools, android_sdk): """If the user explicitly names a non-existet device, an error is raised.""" - # Select emulator with an invalid device ID with pytest.raises(InvalidDeviceError): _device, _name, _avd = android_sdk.select_target_device("deadbeefcafe") @@ -135,7 +129,6 @@ def test_explicit_invalid_device(mock_tools, android_sdk): def test_explicit_invalid_avd(mock_tools, android_sdk): """If the user explicitly names a non-existent device, an error is raised.""" - # Select emulator with an invalid AVD with pytest.raises(InvalidDeviceError): _device, _name, _avd = android_sdk.select_target_device("@invalidEmulator") diff --git a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py index ab71607b02..cc907d5f26 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_start_emulator.py @@ -33,7 +33,6 @@ def mock_adb(device): def test_invalid_emulator(android_sdk): """Attempting to start an invalid emulator raises an error.""" - with pytest.raises(InvalidDeviceError): android_sdk.start_emulator("no-such-avd") diff --git a/tests/integrations/android_sdk/AndroidSDK/test_update_emulator_config.py b/tests/integrations/android_sdk/AndroidSDK/test_update_emulator_config.py index 4921b3a6f5..b2d655b450 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_update_emulator_config.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_update_emulator_config.py @@ -12,17 +12,12 @@ def test_device(tmp_path): # * a key whose value explicitly contains an equals sign. with config_file.open("w", encoding="utf-8") as f: f.write( - """ -avd.ini.encoding=UTF-8 -hw.device.manufacturer=Google -hw.device.name=pixel -weird.key=good=bad + """avd.ini.encoding=UTF-8 hw.device.manufacturer=Google hw.device.name=pixel + weird.key=good=bad. -PlayStore.enabled=no -avd.name=beePhone -disk.cachePartition=yes -disk.cachePartition.size=42M -""" + PlayStore.enabled=no avd.name=beePhone disk.cachePartition=yes + disk.cachePartition.size=42M + """ ) return config_file @@ -41,19 +36,12 @@ def test_update_existing(android_sdk, test_device): with test_device.open(encoding="utf-8") as f: content = f.read() - # Keys have been updated, order is preserved. assert ( content - == """avd.ini.encoding=UTF-8 -hw.device.manufacturer=Google -hw.device.name=pixel -weird.key=good=bad -PlayStore.enabled=no -avd.name=testDevice -disk.cachePartition=yes -disk.cachePartition.size=37MB -""" + == """avd.ini.encoding=UTF-8 hw.device.manufacturer=Google hw.device.name=pixel + weird.key=good=bad PlayStore.enabled=no avd.name=testDevice + disk.cachePartition=yes disk.cachePartition.size=37MB.""" ) @@ -70,20 +58,12 @@ def test_new_content(android_sdk, test_device): with test_device.open(encoding="utf-8") as f: content = f.read() - # New keys are appended to the end of the file # Newlines have been dropped assert ( content - == """avd.ini.encoding=UTF-8 -hw.device.manufacturer=Google -hw.device.name=pixel -weird.key=good=bad -PlayStore.enabled=no -avd.name=beePhone -disk.cachePartition=yes -disk.cachePartition.size=42M -skin.name=pixel_3a -skin.path=skins/pixel_3a -""" + == """avd.ini.encoding=UTF-8 hw.device.manufacturer=Google hw.device.name=pixel + weird.key=good=bad PlayStore.enabled=no avd.name=beePhone + disk.cachePartition=yes disk.cachePartition.size=42M skin.name=pixel_3a + skin.path=skins/pixel_3a.""" ) diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify.py b/tests/integrations/android_sdk/AndroidSDK/test_verify.py index b425bcb597..a49dd2209e 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_verify.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_verify.py @@ -415,7 +415,6 @@ def test_user_provided_sdk_with_latest_cmdline_tools( def test_consistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): """If the user's environment specifies an invalid Android SDK in both ANDROID_HOME and ANDROID_SDK_ROOT, they are ignored.""" - # Create `sdkmanager` and the license file # for the *briefcase* managed version of the SDK. android_sdk_root_path = tmp_path / "tools/android_sdk" @@ -450,7 +449,6 @@ def test_consistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): def test_inconsistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): """If the user's environment specifies an invalid Android SDK in both ANDROID_HOME and ANDROID_SDK_ROOT...and they are both different, they are ignored.""" - # Create `sdkmanager` and the license file # for the *briefcase* managed version of the SDK. android_sdk_root_path = tmp_path / "tools/android_sdk" @@ -679,7 +677,6 @@ def test_download_sdk_legacy_install(mock_tools, tmp_path): def test_no_install(mock_tools, tmp_path): """If an SDK is not available, and install is not requested, an error is raised.""" - # Call `verify()` with pytest.raises(MissingToolError): AndroidSDK.verify(mock_tools, install=False) diff --git a/tests/integrations/docker/test_DockerAppContext__Popen.py b/tests/integrations/docker/test_DockerAppContext__Popen.py index b984149657..b3812357c4 100644 --- a/tests/integrations/docker/test_DockerAppContext__Popen.py +++ b/tests/integrations/docker/test_DockerAppContext__Popen.py @@ -12,7 +12,6 @@ @pytest.mark.usefixtures("mock_docker_app_context") def test_simple_call(mock_tools, my_app, tmp_path, sub_kw, capsys): """A simple call will be invoked.""" - process = mock_tools[my_app].app_context.Popen(["hello", "world"]) assert process is mock_tools._popen_process diff --git a/tests/integrations/docker/test_DockerAppContext__run.py b/tests/integrations/docker/test_DockerAppContext__run.py index 379d816988..7e66587d42 100644 --- a/tests/integrations/docker/test_DockerAppContext__run.py +++ b/tests/integrations/docker/test_DockerAppContext__run.py @@ -13,7 +13,6 @@ @pytest.mark.usefixtures("mock_docker_app_context") def test_simple_call(mock_tools, my_app, tmp_path, sub_stream_kw, capsys): """A simple call will be invoked.""" - mock_tools[my_app].app_context.run(["hello", "world"]) mock_tools[my_app].app_context._dockerize_args.assert_called_once_with( @@ -74,7 +73,6 @@ def test_call_with_extra_kwargs( ): """Extra keyword arguments are passed through as-is; env modifications are converted.""" - mock_tools[my_app].app_context.run( ["hello", "world"], encoding="ISO-42", diff --git a/tests/integrations/docker/test_DockerAppContext__verify.py b/tests/integrations/docker/test_DockerAppContext__verify.py index aa023efb3e..b23e6f711a 100644 --- a/tests/integrations/docker/test_DockerAppContext__verify.py +++ b/tests/integrations/docker/test_DockerAppContext__verify.py @@ -49,7 +49,6 @@ def test_unsupported_os(mock_tools, first_app_config, verify_kwargs): @pytest.mark.usefixtures("mock_docker") def test_success(mock_tools, first_app_config, verify_kwargs, sub_stream_kw): """Docker app context is successfully created and prepared.""" - DockerAppContext.verify(mock_tools, first_app_config, **verify_kwargs) assert isinstance(mock_tools[first_app_config].app_context, DockerAppContext) diff --git a/tests/integrations/docker/test_Docker__x11_passthrough.py b/tests/integrations/docker/test_Docker__x11_passthrough.py index 4ff1859994..1c0dfd6794 100644 --- a/tests/integrations/docker/test_Docker__x11_passthrough.py +++ b/tests/integrations/docker/test_Docker__x11_passthrough.py @@ -200,7 +200,6 @@ def test_x11_write_xauth_missing_xauth_bin(mock_tools, tmp_path): def test_x11_write_xauth_get_cookie_fails(mock_tools, xauth_nlist_outcome, tmp_path): """If the attempt to retrieve the current display's cookie fails, an error is raised.""" - mock_tools.subprocess._subprocess.check_output.side_effect = [xauth_nlist_outcome] with pytest.raises( @@ -217,7 +216,6 @@ def test_x11_write_xauth_get_cookie_fails(mock_tools, xauth_nlist_outcome, tmp_p def test_x11_write_xauth_add_new_xauth_fails(mock_tools, tmp_path): """If the attempt to retrieve the current display's cookie fails, an error is raised.""" - mock_tools.subprocess._subprocess.check_output.side_effect = [ # xauth -i nlist :66 XAUTH_LIST_RET_1, @@ -252,7 +250,6 @@ def test_x11_write_xauth_add_new_xauth_fails(mock_tools, tmp_path): def test_x11_write_xauth_retrieve_xauth_fails(mock_tools, tmp_path): """If the attempt to retrieve the target display's xauth list, an error is raised.""" - mock_tools.subprocess._subprocess.check_output.side_effect = [ # xauth -i nlist :66 XAUTH_LIST_RET_1, @@ -279,7 +276,6 @@ def test_x11_write_xauth_retrieve_xauth_fails(mock_tools, tmp_path): def test_x11_write_xauth_merge_xauth_fails(mock_tools, tmp_path): """If the attempt to retrieve the target display's xauth list, an error is raised.""" - mock_tools.subprocess._subprocess.check_output.side_effect = [ # xauth -i nlist :66 XAUTH_LIST_RET_1, @@ -311,7 +307,6 @@ def test_x11_write_xauth_merge_xauth_fails(mock_tools, tmp_path): @pytest.mark.usefixtures("mock_docker") def test_x11_write_xauth_success(mock_tools, tmp_path, sub_check_output_kw): """The attempt to write the xauth database for the target display succeeds.""" - mock_tools.subprocess._subprocess.check_output.side_effect = [ # xauth -i nlist :66 XAUTH_LIST_RET_1, diff --git a/tests/integrations/file/test_File__download.py b/tests/integrations/file/test_File__download.py index c35e62ab7f..8ee1022231 100644 --- a/tests/integrations/file/test_File__download.py +++ b/tests/integrations/file/test_File__download.py @@ -32,8 +32,8 @@ def mock_tools(mock_tools) -> ToolCache: class _IteratorByteSteam(httpx.SyncByteStream): """Shim that satisfies ``httpx.Response`` ``stream`` parameter type. - Cannot be replaced by any ``Iterable[bytes]`` because the base class requires - an explicit finalization method ``close``. + Cannot be replaced by any ``Iterable[bytes]`` because the base class requires an + explicit finalization method ``close``. """ def __init__(self, iterable: Iterable[bytes]) -> None: @@ -251,7 +251,6 @@ def test_already_downloaded(mock_tools): The request is still made to derive the filename, but the content is never streamed. """ - # Create an existing file existing_file = mock_tools.base_path / "something.zip" with existing_file.open("w", encoding="utf-8") as f: diff --git a/tests/integrations/java/test_JDK__upgrade.py b/tests/integrations/java/test_JDK__upgrade.py index fb10ee2f65..9f8cd5bd66 100644 --- a/tests/integrations/java/test_JDK__upgrade.py +++ b/tests/integrations/java/test_JDK__upgrade.py @@ -26,7 +26,6 @@ def mock_tools(mock_tools) -> ToolCache: def test_non_managed_install(mock_tools, tmp_path, capsys): """If the Java install points to a non-managed install, no upgrade is attempted.""" - # Make the installation point to somewhere else. jdk = JDK(mock_tools, java_home=tmp_path / "other-jdk") diff --git a/tests/integrations/java/test_JDK__verify.py b/tests/integrations/java/test_JDK__verify.py index 7245c3db7f..e0c0bdf8b2 100644 --- a/tests/integrations/java/test_JDK__verify.py +++ b/tests/integrations/java/test_JDK__verify.py @@ -203,7 +203,6 @@ def test_macos_provided_overrides_tool_java_home(mock_tools, capsys): def test_valid_provided_java_home(mock_tools, capsys): """If a valid JAVA_HOME is provided, it is used.""" - # Setup explicit JAVA_HOME mock_tools.os.environ = {"JAVA_HOME": "/path/to/java"} diff --git a/tests/integrations/linuxdeploy/test_LinuxDeployURLPlugin__verify.py b/tests/integrations/linuxdeploy/test_LinuxDeployURLPlugin__verify.py index c9b1e0e085..fb7373d4dd 100644 --- a/tests/integrations/linuxdeploy/test_LinuxDeployURLPlugin__verify.py +++ b/tests/integrations/linuxdeploy/test_LinuxDeployURLPlugin__verify.py @@ -52,7 +52,6 @@ def test_verify(mock_tools, tmp_path): def test_download_failure(mock_tools, tmp_path): """A failure downloading a custom URL plugin raises an error.""" - # Mock a successful download mock_tools.file.download.side_effect = NetworkFailure("mock") @@ -75,7 +74,6 @@ def test_download_failure(mock_tools, tmp_path): def test_invalid_plugin_name(mock_tools, tmp_path): """If the URL filename doesn't match the pattern of a linuxdeploy plugin, an error is raised.""" - with pytest.raises(BriefcaseCommandError): LinuxDeployURLPlugin.verify( mock_tools, diff --git a/tests/integrations/linuxdeploy/test_LinuxDeploy__verify_plugins.py b/tests/integrations/linuxdeploy/test_LinuxDeploy__verify_plugins.py index 58965062aa..7bbb53d28e 100644 --- a/tests/integrations/linuxdeploy/test_LinuxDeploy__verify_plugins.py +++ b/tests/integrations/linuxdeploy/test_LinuxDeploy__verify_plugins.py @@ -20,7 +20,6 @@ def test_no_plugins(linuxdeploy, mock_tools, tmp_path): """If there are no plugins, verify is a no-op.""" - plugins = linuxdeploy.verify_plugins([], bundle_path=tmp_path / "bundle") mock_tools.file.download.assert_not_called() @@ -30,7 +29,6 @@ def test_no_plugins(linuxdeploy, mock_tools, tmp_path): def test_gtk_plugin(linuxdeploy, mock_tools, tmp_path): """The GTK plugin can be verified.""" - # Mock a successful download mock_tools.file.download.side_effect = side_effect_create_mock_tool( tmp_path / "tools/linuxdeploy_plugins/gtk/linuxdeploy-plugin-gtk.sh" @@ -50,7 +48,6 @@ def test_gtk_plugin(linuxdeploy, mock_tools, tmp_path): def test_qt_plugin(linuxdeploy, mock_tools, tmp_path): """The Qt plugin can be verified.""" - # Mock a successful download mock_tools.file.download.side_effect = side_effect_create_mock_appimage( tmp_path @@ -77,7 +74,6 @@ def test_qt_plugin(linuxdeploy, mock_tools, tmp_path): def test_custom_url_plugin(linuxdeploy, mock_tools, tmp_path): """A Custom URL plugin can be verified.""" - # Mock a successful download mock_tools.file.download.side_effect = side_effect_create_mock_appimage( tmp_path @@ -113,7 +109,6 @@ def test_custom_url_plugin(linuxdeploy, mock_tools, tmp_path): ) def test_custom_local_file_plugin(linuxdeploy, mock_tools, tmp_path): """A Custom local file plugin can be verified.""" - # Create a local file plugin_path = tmp_path / "path/to/linuxdeploy-plugin-sometool-i386.AppImage" create_mock_appimage(plugin_path) diff --git a/tests/integrations/linuxdeploy/utils.py b/tests/integrations/linuxdeploy/utils.py index 8e62ded17e..5e2739c8fe 100644 --- a/tests/integrations/linuxdeploy/utils.py +++ b/tests/integrations/linuxdeploy/utils.py @@ -16,7 +16,6 @@ def create_mock_appimage(appimage_path: Path, mock_appimage_kind: str = "origina creates a corrupted mock appimage. :returns: The bytes to be patched of the created AppImage. """ - bytes_to_be_patched = None appimage_headers = { diff --git a/tests/integrations/subprocess/test_Subprocess__Popen.py b/tests/integrations/subprocess/test_Subprocess__Popen.py index 62c7d3f538..db3377e545 100644 --- a/tests/integrations/subprocess/test_Subprocess__Popen.py +++ b/tests/integrations/subprocess/test_Subprocess__Popen.py @@ -12,7 +12,6 @@ @pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"]) def test_call(mock_sub, capsys, platform, sub_kw): """A simple call will be invoked.""" - mock_sub.tools.host_os = platform mock_sub.Popen(["hello", "world"]) @@ -22,7 +21,6 @@ def test_call(mock_sub, capsys, platform, sub_kw): def test_call_with_arg(mock_sub, capsys, sub_kw): """Any extra keyword arguments are passed through as-is.""" - mock_sub.Popen(["hello", "world"], extra_kw="extra") mock_sub._subprocess.Popen.assert_called_with( @@ -35,7 +33,6 @@ def test_call_with_arg(mock_sub, capsys, sub_kw): def test_call_with_path_arg(mock_sub, capsys, tmp_path, sub_kw): """Path-based arguments are converted to strings and passed in as-is.""" - mock_sub.Popen(["hello", tmp_path / "location"], cwd=tmp_path / "cwd") mock_sub._subprocess.Popen.assert_called_with( @@ -73,7 +70,6 @@ def test_call_with_start_new_session( sub_kw, ): """start_new_session is passed thru on Linux and macOS but converted for Windows.""" - mock_sub.tools.host_os = platform mock_sub.Popen(["hello", "world"], start_new_session=start_new_session) @@ -108,7 +104,6 @@ def test_call_windows_with_start_new_session_and_creationflags( ): """Creationflags used to simulate start_new_session=True should be merged with any existing flags.""" - mock_sub.tools.host_os = "Windows" # use commented test below when merging creationflags is allowed @@ -183,6 +178,5 @@ def test_debug_call_with_env(mock_sub, capsys, tmp_path, sub_kw): def test_text_eq_true_default_overriding(mock_sub, in_kwargs, kwargs): """If text or universal_newlines is explicitly provided, those should override text=true default and universal_newlines should be converted to text.""" - mock_sub.Popen(["hello", "world"], **in_kwargs) mock_sub._subprocess.Popen.assert_called_with(["hello", "world"], **kwargs) diff --git a/tests/integrations/subprocess/test_Subprocess__check_output.py b/tests/integrations/subprocess/test_Subprocess__check_output.py index 337b7e6270..24b11127da 100644 --- a/tests/integrations/subprocess/test_Subprocess__check_output.py +++ b/tests/integrations/subprocess/test_Subprocess__check_output.py @@ -76,7 +76,6 @@ def test_call( def test_call_with_arg(mock_sub, capsys, sub_check_output_kw): """Any extra keyword arguments are passed through as-is.""" - mock_sub.check_output(["hello", "world"], extra_kw="extra") mock_sub._subprocess.check_output.assert_called_with( @@ -89,7 +88,6 @@ def test_call_with_arg(mock_sub, capsys, sub_check_output_kw): def test_call_with_path_arg(mock_sub, capsys, tmp_path, sub_check_output_kw): """Path-based arguments are converted to strings and passed in as-is.""" - mock_sub.check_output(["hello", tmp_path / "location"], cwd=tmp_path / "cwd") mock_sub._subprocess.check_output.assert_called_with( @@ -172,7 +170,6 @@ def test_call_with_start_new_session( sub_check_output_kw, ): """start_new_session is passed thru on Linux and macOS but converted for Windows.""" - mock_sub.tools.host_os = platform mock_sub.check_output(["hello", "world"], start_new_session=start_new_session) @@ -209,7 +206,6 @@ def test_call_windows_with_start_new_session_and_creationflags( ): """Creationflags used to simulate start_new_session=True should be merged with any existing flags.""" - mock_sub.tools.host_os = "Windows" # use commented test below when merging creationflags is allowed diff --git a/tests/integrations/subprocess/test_Subprocess__parse_output.py b/tests/integrations/subprocess/test_Subprocess__parse_output.py index c8816fcdbf..16baff6468 100644 --- a/tests/integrations/subprocess/test_Subprocess__parse_output.py +++ b/tests/integrations/subprocess/test_Subprocess__parse_output.py @@ -29,7 +29,6 @@ def third_line_parser(data): def test_call(mock_sub, capsys, sub_check_output_kw): """A simple call to check_output will be invoked.""" - output = mock_sub.parse_output(splitlines_parser, ["hello", "world"]) mock_sub._subprocess.check_output.assert_called_with( @@ -42,7 +41,6 @@ def test_call(mock_sub, capsys, sub_check_output_kw): def test_call_with_arg(mock_sub, capsys, sub_check_output_kw): """Any extra keyword arguments are passed through as-is to check_output.""" - output = mock_sub.parse_output( splitlines_parser, ["hello", "world"], @@ -60,7 +58,6 @@ def test_call_with_arg(mock_sub, capsys, sub_check_output_kw): def test_call_with_parser_success(mock_sub, capsys, sub_check_output_kw): """Parser returns expected portion of check_output's output.""" - output = mock_sub.parse_output(second_line_parser, ["hello", "world"]) mock_sub._subprocess.check_output.assert_called_with( @@ -73,7 +70,6 @@ def test_call_with_parser_success(mock_sub, capsys, sub_check_output_kw): def test_call_with_parser_error(mock_sub, capsys, sub_check_output_kw): """Parser errors on output from check_output.""" - with pytest.raises( CommandOutputParseError, match="Unable to parse command output: Input does not contain 3 lines", @@ -114,7 +110,6 @@ def test_call_with_parser_error(mock_sub, capsys, sub_check_output_kw): def test_text_eq_true_default_overriding(mock_sub, in_kwargs, kwargs): """If text or universal_newlines is explicitly provided, those should override text=true default and universal_newlines should be converted to text.""" - mock_sub.parse_output(splitlines_parser, ["hello", "world"], **in_kwargs) mock_sub._subprocess.check_output.assert_called_with( diff --git a/tests/integrations/subprocess/test_Subprocess__prepare.py b/tests/integrations/subprocess/test_Subprocess__prepare.py index 2bb7c1dcae..f4b8f4fb7b 100644 --- a/tests/integrations/subprocess/test_Subprocess__prepare.py +++ b/tests/integrations/subprocess/test_Subprocess__prepare.py @@ -1,6 +1,5 @@ def test_build_image(mock_sub): """Building an image is a no-op.""" - mock_sub.prepare() assert mock_sub._subprocess.run.call_count == 0 diff --git a/tests/integrations/subprocess/test_Subprocess__run__controlled_console.py b/tests/integrations/subprocess/test_Subprocess__run__controlled_console.py index 6a0a433f06..fb7a2db062 100644 --- a/tests/integrations/subprocess/test_Subprocess__run__controlled_console.py +++ b/tests/integrations/subprocess/test_Subprocess__run__controlled_console.py @@ -10,7 +10,6 @@ def test_call(mock_sub, sub_stream_kw, sleep_zero, capsys): """A simple call will be invoked.""" - with mock_sub.tools.console.wait_bar(): mock_sub.run(["hello", "world"]) @@ -27,7 +26,6 @@ def test_call(mock_sub, sub_stream_kw, sleep_zero, capsys): def test_call_with_arg(mock_sub, sub_stream_kw, sleep_zero, capsys): """Any extra keyword arguments are passed through as-is.""" - with mock_sub.tools.console.wait_bar(): mock_sub.run(["hello", "world"], extra_kw="extra") diff --git a/tests/integrations/subprocess/test_Subprocess__run__stream_output__False.py b/tests/integrations/subprocess/test_Subprocess__run__stream_output__False.py index 3b3c59d335..badb5d21ea 100644 --- a/tests/integrations/subprocess/test_Subprocess__run__stream_output__False.py +++ b/tests/integrations/subprocess/test_Subprocess__run__stream_output__False.py @@ -13,7 +13,6 @@ @pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"]) def test_call(mock_sub, capsys, platform, sub_kw): """A simple call will be invoked.""" - mock_sub.tools.sys.platform = platform mock_sub.run(["hello", "world"], stream_output=False) @@ -23,7 +22,6 @@ def test_call(mock_sub, capsys, platform, sub_kw): def test_call_with_arg(mock_sub, capsys, sub_kw): """Any extra keyword arguments are passed through as-is.""" - mock_sub.run(["hello", "world"], extra_kw="extra", stream_output=False) mock_sub._subprocess.run.assert_called_with( @@ -36,7 +34,6 @@ def test_call_with_arg(mock_sub, capsys, sub_kw): def test_call_with_path_arg(mock_sub, capsys, tmp_path, sub_kw): """Path-based arguments are converted to strings and passed in as-is.""" - mock_sub.run( ["hello", tmp_path / "location"], cwd=tmp_path / "cwd", @@ -78,7 +75,6 @@ def test_call_with_start_new_session( sub_kw, ): """start_new_session is passed thru on Linux and macOS but converted for Windows.""" - mock_sub.tools.host_os = platform mock_sub.run( ["hello", "world"], @@ -116,7 +112,6 @@ def test_call_windows_with_start_new_session_and_creationflags( ): """Creationflags used to simulate start_new_session=True should be merged with any existing flags.""" - mock_sub.tools.host_os = "Windows" # use commented test below when merging creationflags is allowed @@ -233,7 +228,6 @@ def test_text_eq_true_default_overriding(mock_sub, in_kwargs, kwargs): def test_call_with_filter_func(mock_sub, capsys, sub_kw): """Providing a filter function when not streaming raises an exception.""" - with pytest.raises( ValueError, match=r"Cannot apply a filter to non-streamed output", diff --git a/tests/integrations/subprocess/test_Subprocess__run__stream_output__True.py b/tests/integrations/subprocess/test_Subprocess__run__stream_output__True.py index 4a86da637d..5ff0071a40 100644 --- a/tests/integrations/subprocess/test_Subprocess__run__stream_output__True.py +++ b/tests/integrations/subprocess/test_Subprocess__run__stream_output__True.py @@ -14,7 +14,6 @@ @pytest.mark.parametrize("platform", ["Linux", "Darwin", "Windows"]) def test_call(mock_sub, capsys, platform, sub_stream_kw, sleep_zero): """A simple call will be invoked.""" - mock_sub.tools.sys.platform = platform mock_sub.run(["hello", "world"]) @@ -31,7 +30,6 @@ def test_call(mock_sub, capsys, platform, sub_stream_kw, sleep_zero): def test_call_with_arg(mock_sub, capsys, sub_stream_kw, sleep_zero): """Any extra keyword arguments are passed through as-is.""" - mock_sub.run(["hello", "world"], extra_kw="extra") mock_sub._subprocess.Popen.assert_called_with( @@ -51,7 +49,6 @@ def test_call_with_arg(mock_sub, capsys, sub_stream_kw, sleep_zero): def test_call_with_path_arg(mock_sub, capsys, tmp_path, sub_stream_kw, sleep_zero): """Path-based arguments are converted to strings and passed in as-is.""" - mock_sub.run(["hello", tmp_path / "location"], cwd=tmp_path / "cwd") mock_sub._subprocess.Popen.assert_called_with( @@ -97,7 +94,6 @@ def test_call_with_start_new_session( sleep_zero, ): """start_new_session is passed thru on Linux and macOS but converted for Windows.""" - mock_sub.tools.host_os = platform mock_sub.run(["hello", "world"], start_new_session=start_new_session) @@ -141,7 +137,6 @@ def test_call_windows_with_start_new_session_and_creationflags( ): """Creationflags used to simulate start_new_session=True should be merged with any existing flags.""" - mock_sub.tools.host_os = "Windows" # use commented test below when merging creationflags is allowed diff --git a/tests/integrations/subprocess/test_Subprocess__stream_output.py b/tests/integrations/subprocess/test_Subprocess__stream_output.py index 9d0220bc30..5546169fe5 100644 --- a/tests/integrations/subprocess/test_Subprocess__stream_output.py +++ b/tests/integrations/subprocess/test_Subprocess__stream_output.py @@ -49,7 +49,6 @@ def test_output_debug(mock_sub, streaming_process, sleep_zero, capsys): def test_keyboard_interrupt(mock_sub, streaming_process, capsys): """KeyboardInterrupt is suppressed if user sends CTRL+C and all output is printed.""" - send_ctrl_c = mock.MagicMock() send_ctrl_c.side_effect = [False, KeyboardInterrupt] @@ -108,7 +107,6 @@ def test_stop_func(mock_sub, streaming_process, stop_func_ret_val, sleep_zero, c def test_stuck_streamer(mock_sub, streaming_process, sleep_zero, monkeypatch, capsys): """Following a KeyboardInterrupt, output streaming returns even if the output streamer becomes stuck.""" - # Mock time.time() to return times that monotonically increase by 1s # every time it is invoked. This allows us to simulate the progress of # time much faster than the actual calls to time.sleep() would. diff --git a/tests/integrations/virtual_environment/test_NoOpVenvContext__clean.py b/tests/integrations/virtual_environment/test_NoOpVenvContext__clean.py index 26d064b228..cd086e3a3e 100644 --- a/tests/integrations/virtual_environment/test_NoOpVenvContext__clean.py +++ b/tests/integrations/virtual_environment/test_NoOpVenvContext__clean.py @@ -19,7 +19,6 @@ def test_clean(mock_tools, venv_path): def test_clean_non_existent(mock_tools, venv_path): """It's possible to clean a venv that doesn't exist.""" - context = NoOpVenvContext(mock_tools, venv_path) # Marker file doesn't exist diff --git a/tests/integrations/virtual_environment/test_VenvContext__check_output.py b/tests/integrations/virtual_environment/test_VenvContext__check_output.py index 74c992ea80..7d9607b805 100644 --- a/tests/integrations/virtual_environment/test_VenvContext__check_output.py +++ b/tests/integrations/virtual_environment/test_VenvContext__check_output.py @@ -43,7 +43,6 @@ def test_check_output_environment_handling( def test_check_output_kwargs_env_extraction(venv_context, mock_subprocess_setup): """check_output extracts env from kwargs without modifying the original kwargs.""" - mocks = mock_subprocess_setup original_kwargs = { diff --git a/tests/integrations/virtual_environment/test_VenvContext__clean.py b/tests/integrations/virtual_environment/test_VenvContext__clean.py index 4e70032d0c..e9e5976dba 100644 --- a/tests/integrations/virtual_environment/test_VenvContext__clean.py +++ b/tests/integrations/virtual_environment/test_VenvContext__clean.py @@ -22,7 +22,6 @@ def test_clean(mock_tools, venv_path): def test_clean_non_existent(mock_tools, venv_path): """It's possible to clean a venv that doesn't exist.""" - context = VenvContext(mock_tools, venv_path) # venv doesn't exist before clean diff --git a/tests/integrations/virtual_environment/test_VenvContext__recreate.py b/tests/integrations/virtual_environment/test_VenvContext__recreate.py index 07a695292d..2daab767ec 100644 --- a/tests/integrations/virtual_environment/test_VenvContext__recreate.py +++ b/tests/integrations/virtual_environment/test_VenvContext__recreate.py @@ -5,7 +5,6 @@ def test_recreate_when_venv_exists(mock_tools, venv_path): """Recreate removes existing venv and creates new one.""" - context = VenvContext(mock_tools, venv_path) context.exists = MagicMock(return_value=True) context.create = MagicMock() @@ -24,7 +23,6 @@ def test_recreate_when_venv_exists(mock_tools, venv_path): def test_recreate_when_venv_missing(mock_tools, venv_path): """Recreate skips removal and creates new venv.""" - context = VenvContext(mock_tools, venv_path) context.exists = MagicMock(return_value=False) context.create = MagicMock() diff --git a/tests/integrations/windows_sdk/test_WindowsSDK__verify.py b/tests/integrations/windows_sdk/test_WindowsSDK__verify.py index 98d4e294ec..0d1af7bd77 100644 --- a/tests/integrations/windows_sdk/test_WindowsSDK__verify.py +++ b/tests/integrations/windows_sdk/test_WindowsSDK__verify.py @@ -41,8 +41,8 @@ def setup_winsdk_install( """Create a mock Windows SDK for the version and arch. :param base_path: base path to create the SDK in; should be pytest's tmp_path. - :param version: SDK version triple, e.g. 1.2.3. The created directory path will include - a servicing version of 0, e.g. base_path/win_sdk/1.2.3.0. + :param version: SDK version triple, e.g. 1.2.3. The created directory path will + include a servicing version of 0, e.g. base_path/win_sdk/1.2.3.0. :param arch: The architecture for the SDK, e.g. amd64 or arm64. :param skip_bins: Do not create mock binaries in `bin` directory. :returns: tuple of path to base of SDK install and version triple diff --git a/tests/integrations/xcode/test_verify_command_line_tools_install.py b/tests/integrations/xcode/test_verify_command_line_tools_install.py index 44f9cf025d..f382e8a81d 100644 --- a/tests/integrations/xcode/test_verify_command_line_tools_install.py +++ b/tests/integrations/xcode/test_verify_command_line_tools_install.py @@ -19,7 +19,6 @@ def test_verify_command_line_tools_install(mock_tools): def test_reverify_command_line_tools_install(mock_tools): """A second call to verify is a no-op.""" - xcode_cli = mock.MagicMock() mock_tools.xcode_cli = xcode_cli diff --git a/tests/integrations/xcode/test_verify_xcode_install.py b/tests/integrations/xcode/test_verify_xcode_install.py index d51aaad3ed..c0474c0aeb 100644 --- a/tests/integrations/xcode/test_verify_xcode_install.py +++ b/tests/integrations/xcode/test_verify_xcode_install.py @@ -22,7 +22,6 @@ def test_verify_xcode_install(mock_tools): def test_reverify_xcode_install(mock_tools): """A second call to verify is a no-op.""" - xcode = mock.MagicMock() mock_tools.xcode = xcode diff --git a/tests/platforms/android/gradle/conftest.py b/tests/platforms/android/gradle/conftest.py index 6b72b1db7c..4c6c388013 100644 --- a/tests/platforms/android/gradle/conftest.py +++ b/tests/platforms/android/gradle/conftest.py @@ -53,7 +53,6 @@ def first_app_generated(first_app_config, tmp_path): extract_packages_path = "app/extract-packages.txt" """, ) - create_file( tmp_path / "base_path" diff --git a/tests/platforms/iOS/xcode/test_create.py b/tests/platforms/iOS/xcode/test_create.py index 26de81acfa..38690fd44f 100644 --- a/tests/platforms/iOS/xcode/test_create.py +++ b/tests/platforms/iOS/xcode/test_create.py @@ -237,7 +237,6 @@ def test_legacy_support_format( def test_min_os_version(create_command, first_app_generated, tmp_path): """If a minimum iOS version is specified, it is used for wheel installs.""" - # Hard code the current architecture for testing. We only install simulator # requirements for the current platform. create_command.tools.host_arch = "wonky" diff --git a/tests/platforms/linux/appimage/test_build.py b/tests/platforms/linux/appimage/test_build.py index cb6d0ba050..df841e75ea 100644 --- a/tests/platforms/linux/appimage/test_build.py +++ b/tests/platforms/linux/appimage/test_build.py @@ -89,7 +89,6 @@ def build_command(dummy_console, tmp_path, first_app_config): def test_verify_tools_wrong_platform(build_command): """If we're not on Linux, the build fails.""" - build_command.tools.host_os = "TestOS" build_command.build_app = mock.MagicMock() build_command.tools.file.download = mock.MagicMock() @@ -318,7 +317,6 @@ def test_build_appimage_in_docker( monkeypatch, ): """A Linux app can be packaged as an AppImage in a docker container.""" - # Enable docker, and move to a non-Linux OS. build_command.tools.host_os = "TestOS" build_command.use_docker = True diff --git a/tests/platforms/linux/appimage/test_package.py b/tests/platforms/linux/appimage/test_package.py index ee55e72711..0371af536e 100644 --- a/tests/platforms/linux/appimage/test_package.py +++ b/tests/platforms/linux/appimage/test_package.py @@ -23,7 +23,6 @@ def package_command(dummy_console, tmp_path, first_app_config): def test_package_app(package_command, first_app_config, tmp_path): """An AppImage can be packaged.""" - # Create the app binary create_file( tmp_path diff --git a/tests/platforms/linux/system/test_build.py b/tests/platforms/linux/system/test_build.py index d3e6cbd69b..53f24fa4ca 100644 --- a/tests/platforms/linux/system/test_build.py +++ b/tests/platforms/linux/system/test_build.py @@ -132,7 +132,6 @@ def test_no_license_files(build_command, first_app, tmp_path): def test_specified_license_file_is_copied(build_command, first_app, tmp_path): """The specified license file is copied if a license file is specified.""" - # Build the app build_command.build_app(first_app) diff --git a/tests/platforms/linux/system/test_mixin___deb_devirtualize.py b/tests/platforms/linux/system/test_mixin___deb_devirtualize.py index 105a6e5f29..6a9cd65a43 100644 --- a/tests/platforms/linux/system/test_mixin___deb_devirtualize.py +++ b/tests/platforms/linux/system/test_mixin___deb_devirtualize.py @@ -154,7 +154,6 @@ def build_command(dummy_console, tmp_path, first_app): ) def test_deb_devirtualize(build_command, cache_output, original, expected): """Debian requirements can be verified.""" - # Mock the effect of checking requirements that are all present build_command.tools.subprocess.check_output.return_value = cache_output @@ -169,7 +168,6 @@ def test_deb_devirtualize(build_command, cache_output, original, expected): def test_deb_devirtualize_fail(build_command): """If Debian devirtualiztaio fails, an error is raised.""" - # Mock the effect of apt-cache failing build_command.tools.subprocess.check_output.side_effect = ( subprocess.CalledProcessError(cmd="apt-cache", returncode=1) diff --git a/tests/platforms/linux/system/test_mixin__verify_system_packages.py b/tests/platforms/linux/system/test_mixin__verify_system_packages.py index d75a2a7613..f6c8fb480f 100644 --- a/tests/platforms/linux/system/test_mixin__verify_system_packages.py +++ b/tests/platforms/linux/system/test_mixin__verify_system_packages.py @@ -208,6 +208,7 @@ def test_packages_installed(build_command, first_app_config, capsys): def test_missing_virtual_packages(build_command, first_app_config, capsys): """If there are missing virtual system packages, an error is raised.""" + # Mock the system requirement tools; there's a base requirement of packages called # "compiler" and "compiler++", plus 3 packages provided by an installation alias # "alias". These packages are verified using "check ", and installed using @@ -266,6 +267,7 @@ def devirtualize(package): def test_virtual_packages_installed(build_command, first_app_config, capsys): """If all required packages are installed, no error is raised.""" + # Mock the system requirement tools; there's a base requirement of # a packaged called "compiler", verified using "check ", and # installed using "system ", and `devirtualize` is used as diff --git a/tests/platforms/linux/system/test_run.py b/tests/platforms/linux/system/test_run.py index 5ceea0762d..361fd9470a 100644 --- a/tests/platforms/linux/system/test_run.py +++ b/tests/platforms/linux/system/test_run.py @@ -232,7 +232,6 @@ def test_supported_host_os_docker( def test_run_gui_app(run_command, first_app, sub_kw, tmp_path): """A bootstrap binary for a GUI app can be started.""" - # Set up tool cache run_command.verify_app_tools(app=first_app) @@ -316,7 +315,6 @@ def test_run_gui_app_passthrough(run_command, first_app, sub_kw, tmp_path): def test_run_gui_app_failed(run_command, first_app, sub_kw, tmp_path): """If there's a problem starting the GUI app, an exception is raised.""" - # Set up tool cache run_command.verify_app_tools(app=first_app) @@ -498,7 +496,6 @@ def test_run_app_docker(run_command, first_app, sub_kw, tmp_path, monkeypatch): @pytest.mark.skipif(sys.platform == "win32", reason="Windows paths can't be dockerized") def test_run_app_failed_docker(run_command, first_app, sub_kw, tmp_path, monkeypatch): """If there's a problem starting the app in Docker, an exception is raised.""" - # Trigger to run in Docker run_command.target_image = first_app.target_image = "best/distro" diff --git a/tests/platforms/linux/test_DockerOpenCommand.py b/tests/platforms/linux/test_DockerOpenCommand.py index 567deffa4e..a3e5afe0a8 100644 --- a/tests/platforms/linux/test_DockerOpenCommand.py +++ b/tests/platforms/linux/test_DockerOpenCommand.py @@ -42,7 +42,6 @@ def open_command(dummy_console, tmp_path): ) def test_open_docker(open_command, first_app_config, tmp_path, monkeypatch): """Open starts a docker session by default.""" - # Enable docker open_command.use_docker = True open_command.extra_docker_build_args = [] diff --git a/tests/platforms/linux/test_LocalRequirementsMixin.py b/tests/platforms/linux/test_LocalRequirementsMixin.py index aa7455be38..9ed8e93291 100644 --- a/tests/platforms/linux/test_LocalRequirementsMixin.py +++ b/tests/platforms/linux/test_LocalRequirementsMixin.py @@ -149,7 +149,6 @@ def other_package(create_command, first_app_config): ) def test_install_app_requirements_in_docker(create_command, first_app_config, tmp_path): """If Docker is in use, a docker context is used to invoke pip.""" - # Install requirements create_command.install_app_requirements(first_app_config) diff --git a/tests/platforms/macOS/app/package/test_package.py b/tests/platforms/macOS/app/package/test_package.py index 4ace779fca..15eaa57bdf 100644 --- a/tests/platforms/macOS/app/package/test_package.py +++ b/tests/platforms/macOS/app/package/test_package.py @@ -34,7 +34,6 @@ def test_effective_format( actual_format, ): """The packaging format varies depending on the app type.""" - first_app_with_binaries.packaging_format = packaging_format first_app_with_binaries.console_app = is_console_app package_command.verify_app(first_app_with_binaries) @@ -49,7 +48,6 @@ def test_console_invalid_formats( packaging_format, ): """Some packaging formats are not valid for console apps.""" - first_app_with_binaries.packaging_format = packaging_format first_app_with_binaries.console_app = True with pytest.raises( @@ -368,7 +366,6 @@ def test_adhoc_sign( def test_notarize_adhoc_signed(package_command, first_app_with_binaries): """A macOS App cannot be notarized if ad-hoc signing is requested.""" - # Package the app without code signing. Use the base command's interface to ensure # the full cleanup process is tested. with pytest.raises( @@ -396,7 +393,6 @@ def test_notarize_adhoc_signed_via_prompt( adhoc_identity, ): """Notarization is rejected if the user selects the adhoc identity.""" - package_command.select_identity.return_value = adhoc_identity # Package the app without code signing. Use the base command's interface to ensure the full @@ -488,7 +484,6 @@ def test_sign_failure( tmp_path, ): """If the signing process can't be completed, an error is raised.""" - # Select a codesigning identity package_command.select_identity.return_value = sekrit_identity diff --git a/tests/platforms/macOS/app/package/test_resume_notarization.py b/tests/platforms/macOS/app/package/test_resume_notarization.py index 4289f1e478..4fa66a3f12 100644 --- a/tests/platforms/macOS/app/package/test_resume_notarization.py +++ b/tests/platforms/macOS/app/package/test_resume_notarization.py @@ -606,7 +606,6 @@ def test_resume_notarize_artefact_missing( sekrit_identity, ): """If the distribution artefact doesn't exist, notarization cannot be resumed.""" - # Attempting to resume notarization when there's no pre-existing artefact raises an # error. with pytest.raises( diff --git a/tests/platforms/macOS/app/test_create.py b/tests/platforms/macOS/app/test_create.py index 63a7b71db8..1d7cabc9f9 100644 --- a/tests/platforms/macOS/app/test_create.py +++ b/tests/platforms/macOS/app/test_create.py @@ -1094,18 +1094,13 @@ def test_install_legacy_support_package( the support package.""" # Hard code the support revision first_app_templated.support_revision = "37" - # Rewrite the app's briefcase.toml to use the legacy paths (i.e., # a support path of Resources/support, and no stdlib_path) create_file( tmp_path / "base_path/build/first-app/macos/app/briefcase.toml", - """ -[paths] -app_packages_path="First App.app/Contents/Resources/app_packages" -support_path="First App.app/Contents/Resources/support" -info_plist_path="First App.app/Contents/Info.plist" -entitlements_path="Entitlements.plist" -""", + """[paths] app_packages_path="First App.app/Contents/Resources/app_packages" + support_path="First App.app/Contents/Resources/support" info_plist_path="First + App.app/Contents/Info.plist" entitlements_path="Entitlements.plist".""", ) bundle_path = tmp_path / "base_path/build/first-app/macos/app" @@ -1253,7 +1248,6 @@ def test_install_app_requirements_error_adds_install_hint_missing_x86_64_wheel( """Install_hint (mentioning a missing x86_64 wheel) is added when RequirementsInstallError is raised by _install_app_requirements in the macOS create command.""" - create_command.tools.host_arch = "x86_64" first_app_templated.min_os_version = "12.0" first_app_templated.requires = ["package-one", "package_two", "packagethree"] @@ -1284,7 +1278,6 @@ def test_install_app_requirements_error_adds_install_hint_missing_arm64_wheel( """Install_hint (mentioning a missing arm64 wheel) is added when RequirementsInstallError is raised by _install_app_requirements in the macOS create command.""" - create_command.tools.host_arch = "x86_64" first_app_templated.min_os_version = "12.0" first_app_templated.requires = ["package-one", "package_two", "packagethree"] diff --git a/tests/platforms/macOS/app/test_signing.py b/tests/platforms/macOS/app/test_signing.py index a8b85dc387..2658fedc90 100644 --- a/tests/platforms/macOS/app/test_signing.py +++ b/tests/platforms/macOS/app/test_signing.py @@ -79,9 +79,9 @@ def sign_call( def mock_codesign(results): """A utility method for generating codesign side effects. - :param results: A single error string; or a list of error strings to be returned - on successive calls. If `None` is included in the list of results, no - error will be raised for that invocation. + :param results: A single error string; or a list of error strings to be returned on + successive calls. If `None` is included in the list of results, no error will be + raised for that invocation. """ def _codesign(args, **kwargs): diff --git a/tests/platforms/macOS/conftest.py b/tests/platforms/macOS/conftest.py index ea58782974..1b074f4913 100644 --- a/tests/platforms/macOS/conftest.py +++ b/tests/platforms/macOS/conftest.py @@ -69,7 +69,6 @@ def first_app_templated(first_app_config, tmp_path): # Create some folders that need to exist. (app_path / "Contents/Resources/app_packages").mkdir(parents=True) (app_path / "Contents/Frameworks").mkdir(parents=True) - # Create an installer Distribution.xml create_file( tmp_path / "base_path/build/first-app/macos/app/installer/Distribution.xml", diff --git a/tests/platforms/macOS/test_AppPackagesMergeMixin__find_binary_packages.py b/tests/platforms/macOS/test_AppPackagesMergeMixin__find_binary_packages.py index a65ae14cc9..7c6ee1c2b0 100644 --- a/tests/platforms/macOS/test_AppPackagesMergeMixin__find_binary_packages.py +++ b/tests/platforms/macOS/test_AppPackagesMergeMixin__find_binary_packages.py @@ -3,7 +3,6 @@ def test_find_binary_packages(dummy_command, tmp_path): """Binary packages can be identified in a app-packages folder.""" - create_installed_package( tmp_path / "app-packages", "pure-package1", @@ -113,7 +112,6 @@ def test_find_binary_packages(dummy_command, tmp_path): def test_find_binary_packages_non_universal(dummy_command, tmp_path): """If no universal wheel format is specified, universal wheels are identified as binary.""" - create_installed_package( tmp_path / "app-packages", "pure-package1", diff --git a/tests/platforms/macOS/xcode/conftest.py b/tests/platforms/macOS/xcode/conftest.py index 8f2fd070e6..b60a38458c 100644 --- a/tests/platforms/macOS/xcode/conftest.py +++ b/tests/platforms/macOS/xcode/conftest.py @@ -8,12 +8,8 @@ def first_app_generated(first_app_config, tmp_path): # Create the briefcase.toml file create_file( tmp_path / "base_path/macos/xcode/First App/briefcase.toml", - """ -[paths] -app_packages_path="app_packages" -support_path="support" -info_plist_path="Info.plist" -""", + """[paths] app_packages_path="app_packages" support_path="support" + info_plist_path="Info.plist".""", ) create_plist_file( diff --git a/tests/platforms/web/static/conftest.py b/tests/platforms/web/static/conftest.py index 831f4c82b5..34a9760fff 100644 --- a/tests/platforms/web/static/conftest.py +++ b/tests/platforms/web/static/conftest.py @@ -9,40 +9,22 @@ def first_app_generated(first_app_config, tmp_path): bundle_path = tmp_path / "base_path/build/first-app/web/static" create_file( bundle_path / "briefcase.toml", - """ -[paths] -app_path="app" -app_requirements_path="requirements.txt" -""", + """[paths] app_path="app" app_requirements_path="requirements.txt".""", ) - # Create index.html with insert markers create_file( bundle_path / "www/index.html", - """ - - - - - - - - -
+ """ +
- - + - - + - - - - -""", + + """, ) - # Create the initial style.css with CSS insert markers create_file( bundle_path / "www/static/css/style.css", diff --git a/tests/platforms/web/static/test_build__process_wheel.py b/tests/platforms/web/static/test_build__process_wheel.py index 61a1535f28..6ba68d30a1 100644 --- a/tests/platforms/web/static/test_build__process_wheel.py +++ b/tests/platforms/web/static/test_build__process_wheel.py @@ -16,7 +16,6 @@ def build_command(dummy_console, tmp_path): def test_process_wheel(build_command, tmp_path): """A wheel can be processed to have CSS content extracted.""" - # Create a wheel with some content wheel_filename = create_wheel( tmp_path, @@ -66,7 +65,6 @@ def test_process_wheel(build_command, tmp_path): def test_process_wheel_no_content(build_command, tmp_path): """A wheel with no resources can be processed.""" - # Create a wheel with some content, but nothing static to be collected wheel_filename = create_wheel( tmp_path, diff --git a/tests/platforms/web/static/test_build_extract_pyscript_config.py b/tests/platforms/web/static/test_build_extract_pyscript_config.py index 8eb51a2d3a..0147906fc0 100644 --- a/tests/platforms/web/static/test_build_extract_pyscript_config.py +++ b/tests/platforms/web/static/test_build_extract_pyscript_config.py @@ -88,7 +88,6 @@ def test_extract_pyscript_config_no_config(build_command, tmp_path): def test_extract_pyscript_config_multiple_config(build_command, tmp_path): """Multiple config.toml supplied by wheels fails.""" - # Mock wheels that both contain config.toml file_set_1 = { "dependency/deploy/config.toml": """ @@ -125,7 +124,6 @@ def test_extract_pyscript_config_multiple_config(build_command, tmp_path): def test_extract_pyscript_config_no_implementation(build_command, tmp_path, capsys): """An app with no "implementation" value defaults to pyscript with a warning.""" - # Mock a wheel with the needed files files = {"dependency/deploy/config.toml": ""} @@ -150,7 +148,6 @@ def test_extract_pyscript_config_implementation_warning( capsys, ): """Briefcase raises a warning if "implementation" value is not pyscript.""" - files = { "dependency/deploy/config.toml": """ implementation = "something-else" @@ -177,7 +174,6 @@ def test_extract_pyscript_config_implementation_warning( def test_extract_pyscript_config_no_pyscript_toml(build_command, tmp_path, capsys): """If no pyscript.toml is supplied by a wheel, function returns a basic config.""" - # Create wheel with no pyscript.toml files = { "dependency/deploy/config.toml": """ @@ -201,7 +197,6 @@ def test_extract_pyscript_config_no_pyscript_toml(build_command, tmp_path, capsy def test_extract_pyscript_config_invalid_wheel_pyscript_toml(build_command, tmp_path): """A wheel with an invalid pyscript.toml raises an error.""" - # Mock a wheel with files files = { "dependency/deploy/config.toml": """ @@ -210,9 +205,7 @@ def test_extract_pyscript_config_invalid_wheel_pyscript_toml(build_command, tmp_ [pyscript] version = "2024.10.1" """, - "dependency/deploy/pyscript.toml": """ -This is not valid toml. -""", + "dependency/deploy/pyscript.toml": """This is not valid toml.""", } wheel_path = _mock_wheel(tmp_path=tmp_path, wheel_name="dependency", files=files) diff --git a/tests/platforms/web/static/test_build_write_inserts.py b/tests/platforms/web/static/test_build_write_inserts.py index c4c5b50e46..03c9b9522e 100644 --- a/tests/platforms/web/static/test_build_write_inserts.py +++ b/tests/platforms/web/static/test_build_write_inserts.py @@ -32,7 +32,6 @@ def write_target_file(base_path, rel_filename, content): def test_write_insert_warn_if_slot_missing(build_command, app_config, monkeypatch): """Warn if insert slot markers are missing from the file.""" - # File without insert markers file_text = "No markers here" target = write_target_file(app_config._path, "index.html", file_text) @@ -52,7 +51,6 @@ def test_write_insert_warn_if_slot_missing(build_command, app_config, monkeypatc def test_write_insert_warn_if_file_missing(build_command, app_config, monkeypatch): """Warn if the target file does not exist.""" - # Capture warnings warnings = [] monkeypatch.setattr(build_command.console, "warning", warnings.append) @@ -67,16 +65,10 @@ def test_write_insert_warn_if_file_missing(build_command, app_config, monkeypatc def test_write_insert_is_idempotent(build_command, app_config): """Inserts should be idempotent when run multiple times.""" - # File with placeholder slot file_text = dedent( - """\ - - - PLACEHOLDER - - - """ + r"""\ PLACEHOLDER + """ ) target = write_target_file(app_config._path, "index.html", file_text) @@ -95,16 +87,10 @@ def test_write_insert_is_idempotent(build_command, app_config): def test_write_insert_slot_name_regex_escaped(build_command, app_config): """Slots containing regex chars should be escaped and matched literally.""" - # File with marker containing regex chars file_text = dedent( - """\ - - - PLACEHOLDER - - - """ + r"""\ PLACEHOLDER + """ ) target = write_target_file(app_config._path, "index.html", file_text) @@ -119,7 +105,6 @@ def test_write_insert_slot_name_regex_escaped(build_command, app_config): def test_write_insert_css_packages_sorted(build_command, app_config): """Multiple CSS contributions should be inserted in sorted order.""" - # File with CSS marker slot file_text = "/*@@ css:start @@*/\nold\n/*@@ css:end @@*/\n" target = write_target_file(app_config._path, "static/css/briefcase.css", file_text) @@ -139,7 +124,6 @@ def test_write_insert_css_packages_sorted(build_command, app_config): def test_write_insert_replaces_all_matches(build_command, app_config): """All matching slots should be replaced, not just the first occurrence.""" - # File with two identical slots file_text = ( "X\n" @@ -161,9 +145,9 @@ def test_write_insert_replaces_all_matches(build_command, app_config): def test_write_insert_handles_html_and_css_markers(build_command, app_config): """HTML and CSS marker styles should both be processed in one file.""" - # File containing both HTML and CSS markers - file_text = dedent("""\ + file_text = dedent( + """\ OLD_HTML @@ -174,7 +158,8 @@ def test_write_insert_handles_html_and_css_markers(build_command, app_config): /*@@ assets:end @@*/ - """) + """ + ) target = write_target_file(app_config._path, "index.html", file_text) # Insert HTML and CSS contributions @@ -194,7 +179,6 @@ def test_write_insert_handles_html_and_css_markers(build_command, app_config): def test_write_insert_preserves_multiline_indent(build_command, app_config): """Inserted multiline content should preserve indentation of markers.""" - # File with indented marker slot file_text = " \n X\n \n" target = write_target_file(app_config._path, "index.html", file_text) @@ -211,7 +195,6 @@ def test_write_insert_preserves_multiline_indent(build_command, app_config): def test_write_insert_ignores_empty_contributions(build_command, app_config): """Empty insert contributions should be ignored (no banner written).""" - # File with CSS marker slot file_text = "/*@@ css:start @@*/\nX\n/*@@ css:end @@*/\n" target = write_target_file(app_config._path, "static/css/briefcase.css", file_text) diff --git a/tests/platforms/web/static/test_package.py b/tests/platforms/web/static/test_package.py index 813ad4b8ed..914e3e836c 100644 --- a/tests/platforms/web/static/test_package.py +++ b/tests/platforms/web/static/test_package.py @@ -26,7 +26,6 @@ def test_default_packaging_format(package_command): def test_package_app(package_command, first_app_built, tmp_path): """An app can be packaged for distribution.""" - package_command.package_app(first_app_built) # The packaged archive exists, and contains all the www files, diff --git a/tests/platforms/windows/app/create/test_create.py b/tests/platforms/windows/app/create/test_create.py index 623cca0cdc..3bf1aa9557 100644 --- a/tests/platforms/windows/app/create/test_create.py +++ b/tests/platforms/windows/app/create/test_create.py @@ -86,7 +86,6 @@ def test_unsupported_32bit_python(create_command): def test_verify_windows_cpu_arch(create_command): """Running through x86_64 emulation on Windows ARM64 will raise an error.""" - # Create a Mock object for the platform module create_command.tools.platform = MagicMock(spec_set=platform) diff --git a/tests/platforms/windows/app/create/test_install_app_resources.py b/tests/platforms/windows/app/create/test_install_app_resources.py index 998ed6f259..48ba1c3102 100644 --- a/tests/platforms/windows/app/create/test_install_app_resources.py +++ b/tests/platforms/windows/app/create/test_install_app_resources.py @@ -18,7 +18,6 @@ def test_minimal_app_resources(create_command, first_app_templated, tmp_path): def test_post_install_script(create_command, first_app_templated, tmp_path): """A post_install script can be provided as extra resources.""" - # Create a post-install script first_app_templated.post_install_script = "scripts/post_install.bat" create_file(tmp_path / "base_path/scripts/post_install.bat", "echo Hello world") @@ -58,7 +57,6 @@ def test_missing_post_install_script(create_command, first_app_templated): def test_pre_uninstall_script(create_command, first_app_templated, tmp_path): """A pre_uninstall script can be provided as extra resources.""" - # Create a post-install script first_app_templated.pre_uninstall_script = "scripts/pre_uninstall.bat" create_file(tmp_path / "base_path/scripts/pre_uninstall.bat", "echo Hello world") diff --git a/tests/platforms/windows/app/create/test_txt_to_rtf.py b/tests/platforms/windows/app/create/test_txt_to_rtf.py index cdedd56fe5..a737f16d92 100644 --- a/tests/platforms/windows/app/create/test_txt_to_rtf.py +++ b/tests/platforms/windows/app/create/test_txt_to_rtf.py @@ -17,11 +17,9 @@ id="empty-doc", ), pytest.param( - """\ - Hello World. + r"""\ Hello World. - This is a 2 paragraph document - with a multi-line paragraph. + This is a 2 paragraph document with a multi-line paragraph. """, """\ {\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Courier;}} @@ -60,20 +58,14 @@ ), pytest.param( ["Hello World."], - """\ - {\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Courier;}} - Hello World.\x20 - }""", + """\\ {\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Courier;}} Hello World.\x20 }""", id="single-element-list", ), pytest.param( ["Apache License text", "MIT License text"], - """\ - {\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Courier;}} - Apache License text\x20 - \\par\\line\\brdrb\\brdrs\\brdrw10\\brsp20\\par\\line - MIT License text\x20 - }""", + """\\ {\\rtf1\\ansi\\deff0 {\\fonttbl {\\f0 Courier;}} Apache License + text\x20 \\par\\line\\brdrb\\brdrs\\brdrw10\\brsp20\\par\\line MIT License + text\x20 }""", id="multi-element-list", ), ], diff --git a/tests/platforms/windows/app/test_build.py b/tests/platforms/windows/app/test_build.py index 2f408225e7..a2d11d55f3 100644 --- a/tests/platforms/windows/app/test_build.py +++ b/tests/platforms/windows/app/test_build.py @@ -335,7 +335,6 @@ def test_build_app_error_remove_signature( def test_build_app_failure(build_command, first_app_templated): """If the stub binary cannot be updated, an error is raised.""" - build_command.tools.subprocess.run.side_effect = subprocess.CalledProcessError( returncode=1, cmd="rcedit-x64.exe", @@ -356,7 +355,6 @@ def test_build_app_with_support_package_update( capsys, ): """If a support package update is performed, the user is warned.""" - # To trigger the app package update logic, we need to invoke the full build # command, and fake being on a verified Windows install with a generated # app. diff --git a/tests/platforms/windows/app/test_package.py b/tests/platforms/windows/app/test_package.py index 31508ffd4b..2035d7a30c 100644 --- a/tests/platforms/windows/app/test_package.py +++ b/tests/platforms/windows/app/test_package.py @@ -209,7 +209,6 @@ def test_package_msi( package_command, first_app_config, external_first_app, tmp_path, kwargs, external ): """A Windows app can be packaged as an MSI.""" - package_command.package_app( external_first_app if external else first_app_config, **kwargs, @@ -250,7 +249,6 @@ def test_package_msi( ) def test_package_zip(package_command_with_files, first_app_config, kwargs, tmp_path): """A Windows app can be packaged as a zip file.""" - first_app_config.packaging_format = "zip" package_command_with_files.package_app(first_app_config, **kwargs) @@ -303,7 +301,6 @@ def test_package_msi_with_codesigning( additional_args, ): """A Windows app can be packaged as an MSI and code signed.""" - package_command.package_app( first_app_config, identity="80ee4c3321122916f5637522451993c2a0a4a56a", @@ -417,7 +414,6 @@ def test_package_zip_with_codesigning( additional_args, ): """In a ZIP package, only the binary will be code signed.""" - first_app_config.packaging_format = "zip" package_command_with_files.package_app( @@ -667,7 +663,6 @@ def test_external_package_zip( tmp_path, ): """In a ZIP package, only the binary will be code signed.""" - external_first_app.packaging_format = "zip" package_command_with_files.package_app( diff --git a/tests/platforms/windows/app/test_run.py b/tests/platforms/windows/app/test_run.py index 99b8da2b84..4ed2ce3722 100644 --- a/tests/platforms/windows/app/test_run.py +++ b/tests/platforms/windows/app/test_run.py @@ -91,7 +91,6 @@ def test_run_gui_app_with_passthrough(run_command, first_app_config, tmp_path): def test_run_gui_app_failed(run_command, first_app_config, tmp_path): """If there's a problem starting the GUI app, an exception is raised.""" - run_command.tools.subprocess.Popen.side_effect = OSError("Some error") with pytest.raises(OSError, match="Some error"): diff --git a/tests/platforms/windows/visualstudio/test_build.py b/tests/platforms/windows/visualstudio/test_build.py index 7ca0327a3d..cdf62f7d00 100644 --- a/tests/platforms/windows/visualstudio/test_build.py +++ b/tests/platforms/windows/visualstudio/test_build.py @@ -82,7 +82,6 @@ def test_build_app(build_command, first_app_config, tool_debug_mode, tmp_path): def test_build_app_failure(build_command, first_app_config, tmp_path): """If the stub binary cannot be updated, an error is raised.""" - build_command.tools.subprocess.run.side_effect = subprocess.CalledProcessError( returncode=1, cmd="MSBuild.exe", diff --git a/tests/platforms/windows/visualstudio/test_package.py b/tests/platforms/windows/visualstudio/test_package.py index 401df44f01..7cf3b009be 100644 --- a/tests/platforms/windows/visualstudio/test_package.py +++ b/tests/platforms/windows/visualstudio/test_package.py @@ -26,7 +26,6 @@ def package_command(dummy_console, tmp_path): def test_package_msi(package_command, first_app_config, tmp_path): """A Windows app can be packaged as an MSI.""" - package_command.package_app(first_app_config) assert package_command.tools.subprocess.run.mock_calls == [ diff --git a/tests/platforms/windows/visualstudio/test_run.py b/tests/platforms/windows/visualstudio/test_run.py index 93d9a01651..0f651390b2 100644 --- a/tests/platforms/windows/visualstudio/test_run.py +++ b/tests/platforms/windows/visualstudio/test_run.py @@ -29,7 +29,6 @@ def run_command(dummy_console, tmp_path): def test_run_app(run_command, first_app_config, tmp_path): """A windows Visual Studio project app can be started.""" - # Set up the log streamer to return a known stream with a good returncode log_popen = mock.MagicMock() run_command.tools.subprocess.Popen.return_value = log_popen @@ -60,7 +59,6 @@ def test_run_app(run_command, first_app_config, tmp_path): def test_run_app_with_args(run_command, first_app_config, tmp_path): """A windows Visual Studio project app can be started with args.""" - # Set up the log streamer to return a known stream with a good returncode log_popen = mock.MagicMock() run_command.tools.subprocess.Popen.return_value = log_popen diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 075c2d872e..9cdde390ff 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -398,7 +398,6 @@ def test_bare_command(macOS_console): @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") def test_linux_default(console): """``briefcase create`` returns the linux create system command on Linux.""" - cmd, options, _overrides = do_cmdline_parse("create", console) assert isinstance(cmd, LinuxSystemCreateCommand) @@ -412,7 +411,6 @@ def test_linux_default(console): @pytest.mark.skipif(sys.platform != "darwin", reason="requires macOS") def test_macOS_default(console): """``briefcase create`` returns the macOS create command on Linux.""" - cmd, options, overrides = do_cmdline_parse("create", console) assert isinstance(cmd, macOSAppCreateCommand) @@ -427,7 +425,6 @@ def test_macOS_default(console): @pytest.mark.skipif(sys.platform != "win32", reason="requires Windows") def test_windows_default(console): """``briefcase create`` returns the Windows create app command on Windows.""" - cmd, options, overrides = do_cmdline_parse("create", console) assert isinstance(cmd, WindowsAppCreateCommand) @@ -648,7 +645,6 @@ def test_unknown_command_options(capsys, macOS_console): ) def test_dev_command_unsupported_platform(cmdline, console): """Unsupported platforms should raise an UnsupportedCommandError.""" - with pytest.raises(UnsupportedCommandError) as e: do_cmdline_parse(cmdline, console) diff --git a/tests/utils.py b/tests/utils.py index 8fffd67385..6caca4cb76 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -51,14 +51,13 @@ def default_rich_prompt(prompt: str) -> str: def create_file(filepath, content, mode="w", chmod=None): """A test utility to create a file with known content. - Ensures that the directory for the file exists, and writes a file with - specific content. + Ensures that the directory for the file exists, and writes a file with specific + content. :param filepath: The path for the file to create :param content: A string containing the content to write. - :param mode: The mode to open the file. This is `w` by default; - use `wb` and provide content as a bitstring if you need to - write a binary file. + :param mode: The mode to open the file. This is `w` by default; use `wb` and provide + content as a bitstring if you need to write a binary file. :param chmod: file permissions to apply :returns: The path to the file that was created. """ @@ -257,8 +256,8 @@ def installed_package_content( :param tag: The installation tag for the package. Defaults to a pure python wheel. :param extra_content: Optional. A list of tuples of ``(path, content)`` that will be added to the wheel. - :param pure: Is the package explicitly pure? If None, defaults to - the tag as an indicator of purity. + :param pure: Is the package explicitly pure? If None, defaults to the tag as an + indicator of purity. """ return ( [ @@ -284,11 +283,11 @@ def create_installed_package( :param package: The name of the package in the wheel. Defaults to ``dummy`` :param version: The version number of the package. Defaults to ``1.2.3`` :param tag: The installation tag for the package. Defaults to a pure python wheel. - :param extra_content: Optional. A list of tuples of ``(path, content)`` or - ``(path, content, chmod)`` that will be added to the wheel. If ``chmod`` is - not specified, default filesystem permissions will be used. - :param pure: Is the package explicitly pure? If None, defaults to - the tag as an indicator of purity. + :param extra_content: Optional. A list of tuples of ``(path, content)`` or ``(path, + content, chmod)`` that will be added to the wheel. If ``chmod`` is not + specified, default filesystem permissions will be used. + :param pure: Is the package explicitly pure? If None, defaults to the tag as an + indicator of purity. """ for entry in installed_package_content( package=package, @@ -319,9 +318,9 @@ def create_wheel( :param package: The name of the package in the wheel. Defaults to ``dummy`` :param version: The version number of the package. Defaults to ``1.2.3`` :param tag: The installation tag for the package. Defaults to a pure python wheel. - :param extra_content: Optional. A list of tuples of ``(path, content)`` or - ``(path, content, chmod)`` that will be added to the wheel. If ``chmod`` is - not specified, default filesystem permissions will be used. + :param extra_content: Optional. A list of tuples of ``(path, content)`` or ``(path, + content, chmod)`` that will be added to the wheel. If ``chmod`` is not + specified, default filesystem permissions will be used. """ wheel_filename = path / f"{package}-{version}-{tag}.whl" From 62e87df3e34b51417b4539172e4b4122f4a35452 Mon Sep 17 00:00:00 2001 From: "Brutus (robot)" Date: Sun, 17 May 2026 21:08:02 +0000 Subject: [PATCH 2/2] Add changenote. --- changes/2815.misc.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/2815.misc.md diff --git a/changes/2815.misc.md b/changes/2815.misc.md new file mode 100644 index 0000000000..052b264c9c --- /dev/null +++ b/changes/2815.misc.md @@ -0,0 +1 @@ +The `pre-commit` hook for `docformatter` was updated to its latest version.