Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/2526.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Comments in ``src/briefcase`` now have a maximum line length of 88 characters (ruff rule ``E501``).
22 changes: 13 additions & 9 deletions src/briefcase/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1090,20 +1090,21 @@ def update_cookiecutter_cache(self, template: str, branch="master"):
self.tools.shutil.rmtree(cached_template)
raise
except self.tools.git.exc.GitError as e:
# The clone failed; to make sure the repo is in a clean state, clean up
# any partial remnants of this initial clone.
# The clone failed; to make sure the repo is in a clean state,
# clean up any partial remnants of this initial clone.
# If we're getting a GitError, we know the directory must exist.
self.tools.shutil.rmtree(cached_template)
git_fatal_message = re.findall(
r"(?<=fatal: ).*?$", e.stderr, flags=re.DOTALL
)
if git_fatal_message:
# GitError captures stderr with single quotes. Because the regex above
# takes everything after git's "fatal" message, we need to strip that final single quote.
# GitError captures stderr with single quotes. Because the regex
# above takes everything after git's "fatal" message, we need to
# strip that final single quote.
hint = git_fatal_message[0].rstrip("'").strip()

# git is inconsistent with capitalisation of the first word of the message
# and about periods at the end of the message.
# git is inconsistent with capitalization of the first word of
# the message and about periods at the end of the message.
hint = f"{hint[0].upper()}{hint[1:]}{'' if hint[-1] == '.' else '.'}"
else:
hint = (
Expand Down Expand Up @@ -1197,9 +1198,11 @@ def _generate_template(self, template, branch, output_path, extra_context):
no_input=True,
output_dir=str(output_path),
checkout=branch,
# Use a copy to prevent changes propagating among tests while test suite is running
# Use a copy to prevent changes propagating among tests
# while the test suite is running
extra_context=extra_context.copy(),
# Store replay data in the Briefcase template cache instead of ~/.cookiecutter_replay
# Store replay data in the Briefcase template cache
# instead of ~/.cookiecutter_replay
default_config={"replay_dir": str(self.template_cache_path(".replay"))},
)
except subprocess.CalledProcessError as e:
Expand Down Expand Up @@ -1233,7 +1236,8 @@ def generate_template(

extra_context = extra_context.copy()
# Additional context that can be used for the Briefcase template pyproject.toml
# header to include the version of Briefcase as well as the source of the template.
# header to include the version of Briefcase as well as the source of the
# template.
extra_context.update(
{
"template_source": template,
Expand Down
3 changes: 2 additions & 1 deletion src/briefcase/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ def __call__(
**options,
) -> dict | None:
# Has the user requested an invalid set of options?
# This can't be done with argparse, because it isn't a simple mutually exclusive group.
# This can't be done with argparse because it isn't
# a simple mutually exclusive group.
if no_update:
if update:
raise BriefcaseCommandError(
Expand Down
10 changes: 6 additions & 4 deletions src/briefcase/commands/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,8 @@ def get_license_hint(self) -> tuple[str | None, str]:
intro = "What license do you want to use for this project's code? "
default = None

# If there is license information in the pyproject.toml file, use that, otherwise check the license file
# If there is license information in the pyproject.toml file, use that,
# otherwise check the license file
if "text" in self.pep621_data.get("license", {}):
default = self.get_license_from_text(self.pep621_data["license"]["text"])
default_source = "the PEP621 formatted pyproject.toml"
Expand Down Expand Up @@ -638,9 +639,10 @@ def merge_or_copy_pyproject(self, briefcase_config_file: Path) -> None:
if pyproject_file.exists():
pep621_pyproject = pyproject_file.read_text(encoding="utf-8")

# The pyproject.toml file in the target directory has no briefcase keys, so it's
# safe to copy-paste the text, and that way also keep formatting and comments.
# We merge it this way to preserve comments in the original pyproject.toml file
# The pyproject.toml file in the target directory has no briefcase keys, so
# it's safe to copy-paste the text, and that way also keep formatting and
# comments. We merge it this way to preserve comments in the original
# pyproject.toml file
briefcase_comment = "# content below this line added by briefcase convert"
merged_pyproject = (
f"{pep621_pyproject}\n\n\n{briefcase_comment}\n{briefcase_pyproject}"
Expand Down
8 changes: 5 additions & 3 deletions src/briefcase/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ def generate_app_template(self, app: AppConfig):
# Ensure the output format is in the case we expect
"format": self.output_format.lower(),
# Properties of the generating environment
# The full Python version string, including minor and dev/a/b/c suffixes (e.g., 3.11.0rc2)
# The full Python version string, including minor and dev/a/b/c suffixes
# (e.g., 3.11.0rc2)
"python_version": platform.python_version(),
# The host architecture
"host_arch": self.tools.host_arch,
Expand Down Expand Up @@ -938,8 +939,9 @@ def create_app(self, app: AppConfig, **options):
self.console.info("Generating application template...", prefix=app.app_name)
self.generate_app_template(app=app)

# External apps (apps that define 'external_package_path') need the packaging metadata
# from the template, but not the app content, dependencies, support package etc.
# External apps (apps that define 'external_package_path') need the packaging
# metadata from the template, but not the app content, dependencies, support
# package, etc.
if app.external_package_path:
self.console.info("Removing generated app content...", prefix=app.app_name)
self.tools.shutil.rmtree(self.bundle_package_path(app))
Expand Down
14 changes: 7 additions & 7 deletions src/briefcase/commands/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ class DevCommand(RunAppMixin, BaseCommand):
output_format = ""
description = "Run a Briefcase project in the dev environment."

# On macOS CoreFoundation/NSApplication will do its own independent parsing of argc/argv.
# This means that whatever we pass to the Python interpreter on start-up will also be
# visible to NSApplication which will interpret things like `-u` (used to make I/O
# unbuffered in CPython) as `-u [URL]` (a request to open a document by URL). This is,
# rather patently, Not Good.
# To avoid this causing unwanted hilarity, we use environment variables to configure the
# Python interpreter rather than command-line options.
# On macOS CoreFoundation/NSApplication will do its own independent parsing of
# argc/argv. This means that whatever we pass to the Python interpreter on start-up
# will also be visible to NSApplication which will interpret things like `-u` (used
# to make I/O unbuffered in CPython) as `-u [URL]` (a request to open a document by
# URL). This is, rather patently, Not Good.
# To avoid this causing unwanted hilarity, we use environment variables to configure
# the Python interpreter rather than command-line options.
DEV_ENVIRONMENT: Mapping[str, str] = {
# Equivalent of passing "-u"
"PYTHONUNBUFFERED": "1",
Expand Down
5 changes: 3 additions & 2 deletions src/briefcase/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ def __call__(self, line):
if filtered is None:
return

# If there's a cleaned line, we can determine if it should be included in analysis
# If there's a cleaned line, we can determine if it should be
# included in analysis
clean_line, included = filtered
else:
# If we don't perform cleaning, we assume all content is potentially
# Python, and should be included in analysis
# Python and should be included in analysis
clean_line = line
included = True

Expand Down
3 changes: 2 additions & 1 deletion src/briefcase/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ def validate_document_type_config(document_type_id, document_type):
else:
uti = None

# If an UTI is provided in LSItemContentTypes, that takes precedence over a MIME type
# If an UTI is provided in LSItemContentTypes,
# that takes precedence over a MIME type
if is_uti_core_type(uti) or ((uti := mime_type_to_uti(mime_type)) is not None):
macOS.setdefault("is_core_type", True)
macOS.setdefault("LSItemContentTypes", [uti])
Expand Down
22 changes: 14 additions & 8 deletions src/briefcase/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ def __init__(
self.save_log = False
# flag set by exceptions to skip writing the log; save_log takes precedence.
self.skip_log = False
# Rich stacktraces of exceptions for logging to file.
# A list of tuples containing a label for the thread context, and the Trace object
# Rich stacktraces of exceptions for logging to a file.
# A list of tuples containing a label for the thread context,
# and the Trace object
self.stacktraces: list[tuple[str, Trace]] = []
# functions to run for additional logging if creating a logfile
self.log_file_extras: list[Callable[[], object]] = []
Expand Down Expand Up @@ -511,13 +512,17 @@ def _build_log(self, command) -> str:
f"Platform: {platform.platform(aliased=True)}\n"
"\n"
f"Python exe: {sys.executable}\n"
# replace line breaks with spaces (use chr(10) since '\n' isn't allowed in f-strings...)
# replace line breaks with spaces
# (use chr(10) since '\n' isn't allowed in f-strings...)
f"Python version: {sys.version.replace(chr(10), ' ')}\n"
# sys.real_prefix was used in older versions of virtualenv.
# sys.base_prefix is always the python exe's original site-specific directory (e.g. /usr/local).
# sys.prefix is updated (from base_prefix's value) to the virtual env's site-specific directory.
# sys.base_prefix is always the python exe's original site-specific
# directory (e.g. /usr/local).
# sys.prefix is updated (from base_prefix's value) to the virtual env's
# site-specific directory.
f"Virtual env: {hasattr(sys, 'real_prefix') or sys.base_prefix != sys.prefix}\n"
# for conda, prefix and base_prefix are likely the same but contain a conda-meta dir.
# for conda, prefix and base_prefix are likely the same
# but contain a conda-meta dir.
f"Conda env: {(Path(sys.prefix) / 'conda-meta').exists()}\n"
"\n"
f"Briefcase: {__version__}\n"
Expand Down Expand Up @@ -561,7 +566,7 @@ def is_color_enabled(self):
the NO_COLOR environment variable; alternatively, the derived color system for
the terminal is influenced by attributes of the platform as well as FORCE_COLOR.
"""
# no_color has precedence since color_system can be set even if color is disabled
# no_color has precedence: color_system can be set even if color is disabled
if self._console_impl.no_color:
return False
else:
Expand Down Expand Up @@ -632,7 +637,8 @@ def wait_bar(
self._wait_bar.start()
yield NotDeadYet(console=self)
except BaseException as e:
# capture BaseException so message is left on the screen even if user sends CTRL+C
# capture BaseException so the message is left on the screen
# even if the user sends CTRL+C
error_message = "aborted" if isinstance(e, KeyboardInterrupt) else "errored"
self.print(
f"{message} {error_message}", markup=markup, show=show_outcome_message
Expand Down
32 changes: 18 additions & 14 deletions src/briefcase/integrations/android_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,9 @@ def verify_install(
"""
)
elif sdk.cmdline_tools_path.parent.exists():
# a cmdline-tools directory exists but the required version isn't installed.
# try to install the required version using the 'latest' version.
# a cmdline-tools directory exists,
# but the required version isn't installed.
# Try to install the required version using the 'latest' version.
if not sdk.install_cmdline_tools():
sdk = None
tools.console.warning(
Expand Down Expand Up @@ -412,8 +413,10 @@ def install(self):
# So, the unpacking process is:
#
# 1. Make a <sdk_path>/cmdline-tools folder
# 2. Unpack the zip file into that folder, creating <sdk_path>/cmdline-tools/cmdline-tools
# 3. Move <sdk_path>/cmdline-tools/cmdline-tools to <sdk_path>/cmdline-tools/<cmdline-tools version>
# 2. Unpack the zip file into that folder, creating
# <sdk_path>/cmdline-tools/cmdline-tools
# 3. Move <sdk_path>/cmdline-tools/cmdline-tools to
# <sdk_path>/cmdline-tools/<cmdline-tools version>

with self.tools.console.wait_bar(
f"Installing Android SDK Command-Line Tools {self.SDK_MANAGER_VER}..."
Expand Down Expand Up @@ -1474,9 +1477,10 @@ def run(self, *arguments: SubprocessArgT, quiet: int = 0) -> str:
[self.tools.android_sdk.adb_path, "-s", self.device, *arguments],
quiet=quiet,
)
# add returns status code 0 in the case of failure. The only tangible evidence
# of failure is the message "Failure [INSTALL_FAILED_OLDER_SDK]" in the,
# console output; so if that message exists in the output, raise an exception.
# add returns status code 0 in the case of failure.
# The only tangible evidence of failure is the message
# "Failure [INSTALL_FAILED_OLDER_SDK]" in the console output;
# so if that message exists in the output, raise an exception.
if "Failure [INSTALL_FAILED_OLDER_SDK]" in output:
raise BriefcaseCommandError(
"Your device doesn't meet the minimum SDK requirements of this app."
Expand Down Expand Up @@ -1529,9 +1533,9 @@ def start_app(self, package: str, activity: str, passthrough: list[str]):
:returns: `None` on success; raises an exception on failure.
"""
try:
# `am start` also accepts string array extras, but we pass the arguments as a
# single JSON string, because JSON deals with edge cases like whitespace and
# escaping in a reliable and well-documented way.
# `am start` also accepts string array extras, but we pass the arguments as
# a single JSON string, because JSON deals with edge cases like whitespace
# and escaping in a reliable and well-documented way.
output = self.run(
"shell",
"am",
Expand Down Expand Up @@ -1718,11 +1722,11 @@ def pidof(self, package: str, **kwargs) -> str | None:
: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).
# 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).
try:
# Exit status is unreliable: some devices (e.g. Nexus 4) return 0 even when no
# process was found.
# Exit status is unreliable: some devices (e.g. Nexus 4) return 0
# even when no process was found.
return self.run("shell", "pidof", "-s", package, **kwargs).strip() or None
except subprocess.CalledProcessError:
return None
Expand Down
3 changes: 2 additions & 1 deletion src/briefcase/integrations/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,8 @@ def _x11_write_xauth_file(
xauth_file_path.unlink(missing_ok=True)
xauth_file_path.touch()

# Create a xauth database for the target display using the current display's cookie
# Create an xauth database for the target display
# using the current display's cookie
try:
self.tools.subprocess.check_output(
[
Expand Down
15 changes: 8 additions & 7 deletions src/briefcase/integrations/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ def download(self, url: str, download_path: Path, role: str | None = None) -> Pa
url=url, status_code=response.status_code
)

# The initial URL might (read: will) go through URL redirects, so
# we need the *final* response. We look at either the `Content-Disposition`
# header, or the final URL, to extract the cache filename.
# The initial URL might go through URL redirects, so we need the *final*
# response. We look at either the `Content-Disposition` header or the
# final URL to extract the cache filename.
cache_full_name = response.url.path
header_value = response.headers.get("Content-Disposition")
if header_value:
Expand Down Expand Up @@ -233,10 +233,11 @@ def download(self, url: str, download_path: Path, role: str | None = None) -> Pa
description = filename.name if filename else url

if isinstance(e, httpx.TooManyRedirects):
# httpx, unlike requests, will not follow redirects indefinitely, and defaults to
# 20 redirects before calling it quits. If the download attempt exceeds 20 redirects,
# Briefcase probably needs to re-evaluate the URLs it is using for that download
# and ideally find a starting point that won't have so many redirects.
# httpx, unlike requests, will not follow redirects indefinitely and
# defaults to 20 redirects before calling it quits. If the download
# attempt exceeds 20 redirects, Briefcase probably needs to re-evaluate
# the URLs it is using for that download and ideally find a starting
# point that won't have so many redirects.
hint = "exceeded redirects when downloading the file.\n\nPlease report this as a bug to Briefcase."
elif isinstance(e, httpx.DecodingError):
hint = "the server sent a malformed response."
Expand Down
3 changes: 2 additions & 1 deletion src/briefcase/integrations/flatpak.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ def run(
flatpak_run_cmd.extend([] if args is None else args)

if self.tools.console.is_deep_debug:
# Must come before bundle identifier; otherwise, it's passed as an arg to app
# Must come before bundle identifier;
# otherwise, it's passed as an arg to app
flatpak_run_cmd.insert(2, "--verbose")

if stream_output:
Expand Down
4 changes: 2 additions & 2 deletions src/briefcase/integrations/java.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ def install(self):
jdk_zip_path.unlink() # Zip file no longer needed once unpacked.

# The tarball will unpack into <briefcase data dir>/tools/jdk-17.0.X+7
# (or whatever name matches the current release).
# We turn this into <briefcase data dir>/tools/java so we have a consistent name.
# (or whatever name matches the current release). We turn this
# into <briefcase data dir>/tools/java so we have a consistent name.
java_unpack_path = (
self.tools.base_path / f"jdk-{self.JDK_RELEASE}+{self.JDK_BUILD}"
)
Expand Down
Loading