From 7c6cd96835dddd282b3fde1c9e06f7dd2a5fcc3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Sun, 20 Jul 2025 11:25:32 +0200 Subject: [PATCH 01/30] Enhance app name validation to include additional checks for format and identifiers --- changes/2127.misc.rst | 1 + docs/about/releases.rst | 21 ++++++++ src/briefcase/config.py | 33 ++++++++++-- tests/config/test_is_valid_app_name.py | 74 +++++++++++++++++++++++++- 4 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 changes/2127.misc.rst diff --git a/changes/2127.misc.rst b/changes/2127.misc.rst new file mode 100644 index 000000000..55f0f16ac --- /dev/null +++ b/changes/2127.misc.rst @@ -0,0 +1 @@ +Enhance app name validation to include additional checks for format and identifiers diff --git a/docs/about/releases.rst b/docs/about/releases.rst index 938d2686a..bcad50503 100644 --- a/docs/about/releases.rst +++ b/docs/about/releases.rst @@ -4,6 +4,27 @@ Release History .. towncrier release notes start +0.1.dev4873+g4e670fa.d20250719 (2025-07-19) +=========================================== + +Features +-------- + +* Add ``--forward-port`` and ``--reverse-port`` via ADB when running an Android app (`#2369 `__) + + +Bugfixes +-------- + +* Apps that contain file paths longer than 260 characters can now be included in MSI installers. (`#948 `__) + + +Misc +---- + +* `#2381 `__, `#2387 `__, `#2394 `__ + + 0.3.24 (2025-07-10) =================== diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 6970e7917..b0620b2cc 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -37,7 +37,29 @@ def is_reserved_keyword(app_name): def is_valid_app_name(app_name): - return not is_reserved_keyword(app_name) and is_valid_pep508_name(app_name) + """Determine if the app name is valid. + + :param app_name: The app name to validate. Checks the following: + - It is not a reserved keyword. + - It is a valid PEP508 name. + - Does not start with a number. + - Does not have a period ('.') in the name. + - Does not start or end with a hyphen ('-') or underscore ('_'). + - Is not the string 'None' + :returns: True if the app name is valid; False otherwise. + """ + return all( + [ + is_valid_pep508_name(app_name), + not is_reserved_keyword(app_name), + not app_name[0].isdigit(), + not app_name.startswith(("-", "_")), + not app_name.endswith(("-", "_")), + app_name.replace("-", "_").isidentifier(), + # the string 'None' is not a valid app name + app_name.lower() != "None", + ] + ) def make_class_name(formal_name): @@ -400,9 +422,12 @@ def __init__( if not is_valid_app_name(self.app_name): raise BriefcaseConfigError( f"{self.app_name!r} is not a valid app name.\n\n" - "App names must not be reserved keywords such as 'and', 'for' and 'while'.\n" - "They must also be PEP508 compliant (i.e., they can only include letters,\n" - "numbers, '-' and '_'; must start with a letter; and cannot end with '-' or '_')." + "App names must:\n" + "- Not be reserved keywords (like 'and', 'for', 'while', 'main', 'test', etc.)\n" + "- Be PEP508 compliant (letters, numbers, hyphens, and underscores only)\n" + "- Start with a letter (not a number, hyphen, or underscore)\n" + "- Not end with a hyphen or underscore\n" + "- Be valid Python identifiers when hyphens are replaced with underscores" ) if not is_valid_bundle_identifier(self.bundle): diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index 936824a4f..f892ceece 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -9,9 +9,17 @@ "helloworld", "helloWorld", "hello42world", - "42helloworld", "hello_world", "hello-world", + "myapp", + "my_app", + "my-app", + "app2", + "validname", + "a", # single letter + "abc123", # alphanumeric + "app-with-hyphens", + "app_with_underscores", ], ) def test_is_valid_app_name(name): @@ -34,6 +42,18 @@ def test_is_valid_app_name(name): "main", "socket", "test", + # Additional invalid formats + "my$app", # dollar sign + "app@domain", # at symbol + "app.name", # period + "helloworld_", # ends with underscore + "helloworld-", # ends with hyphen + "2app", # starts with number + "42app", # starts with number + "_", # single underscore + "-", # single hyphen + "1", # single digit + "123", # all digits # ı, İ and K (i.e. 0x212a) are valid ASCII when made lowercase and as such are # accepted by the official PEP 508 regex... but they are rejected here to ensure # compliance with the regex that is used in practice. @@ -45,3 +65,55 @@ def test_is_valid_app_name(name): def test_is_invalid_app_name(name): """Test that invalid app names are rejected.""" assert not is_valid_app_name(name) + + +@pytest.mark.parametrize( + "name", + [ + # Additional Python keywords to verify logic fix + "and", + "or", + "not", + "if", + "else", + "elif", + "for", + "while", + "def", + "class", + "import", + "from", + "break", + "continue", + "return", + "yield", + "try", + "except", + "finally", + "with", + "as", + "lambda", + "global", + "nonlocal", + "assert", + "del", + "raise", + "in", + "is", + "async", + "await", + "True", + "None", + # Case variations of reserved words + "Switch", + "SWITCH", + "FALSE", + "Pass", + "PASS", + "Test", + "TEST", + ], +) +def test_additional_invalid_app_names(name): + """Test additional invalid app names to verify logic fix.""" + assert not is_valid_app_name(name) From cb8d9ee144b6f04a105a924a17794feebfed6680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Sun, 20 Jul 2025 11:37:07 +0200 Subject: [PATCH 02/30] Enhance app name validation to include additional checks for format and identifiers --- src/briefcase/config.py | 2 +- tests/config/test_is_valid_app_name.py | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index b0620b2cc..6781e62e7 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -57,7 +57,7 @@ def is_valid_app_name(app_name): not app_name.endswith(("-", "_")), app_name.replace("-", "_").isidentifier(), # the string 'None' is not a valid app name - app_name.lower() != "None", + app_name != "None", ] ) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index f892ceece..ea4386790 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -60,16 +60,6 @@ def test_is_valid_app_name(name): "helloworld_ı", "İstanbul", "Kelvin", - ], -) -def test_is_invalid_app_name(name): - """Test that invalid app names are rejected.""" - assert not is_valid_app_name(name) - - -@pytest.mark.parametrize( - "name", - [ # Additional Python keywords to verify logic fix "and", "or", @@ -114,6 +104,6 @@ def test_is_invalid_app_name(name): "TEST", ], ) -def test_additional_invalid_app_names(name): - """Test additional invalid app names to verify logic fix.""" +def test_is_invalid_app_name(name): + """Test that invalid app names are rejected.""" assert not is_valid_app_name(name) From e5727c748f68adde8e502d6e3f1f458035c624ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 21 Jul 2025 12:03:33 +0200 Subject: [PATCH 03/30] Update app name validation tests to include cases for names starting with a number --- tests/commands/new/test_validate_app_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/new/test_validate_app_name.py b/tests/commands/new/test_validate_app_name.py index a7426f4c8..475b16496 100644 --- a/tests/commands/new/test_validate_app_name.py +++ b/tests/commands/new/test_validate_app_name.py @@ -7,7 +7,6 @@ "helloworld", "helloWorld", "hello42world", - "42helloworld", # ?? Are we sure this is correct? "hello_world", "hello-world", ], @@ -24,6 +23,7 @@ def test_valid_app_name(new_command, name): "helloworld!", # contains punctuation "_helloworld", # leading underscore "-helloworld", # leading hyphen + "42helloworld", # starting with a number "pass", # python keyword "Pass", # python keyword, but different case usage "PASS", # python keyword, but all upper case From f011119e4160fb57bc064207a0e07063be9f47fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 21 Jul 2025 12:06:09 +0200 Subject: [PATCH 04/30] Update validation message to clarify naming restrictions for Python package names --- src/briefcase/commands/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index 0a61ab8d5..e8749bb11 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -109,7 +109,7 @@ def input_app_name(self, override_value: str | None) -> str: "We need a name that can serve as a machine-readable Python package name for " "your application. This name must be PEP508-compliant - that means the name " "may only contain letters, numbers, hyphens and underscores; it can't contain " - "spaces or punctuation, and it can't start with a hyphen or underscore." + "spaces or punctuation, and it can't start with a hyphen, underscore or a number." ) default = "hello-world" From 982d2ec8edbaa036aea2e7d45282f49501e68657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 21 Jul 2025 12:43:40 +0200 Subject: [PATCH 05/30] dot in app name is not allowed anymore --- tests/commands/convert/test_input_app_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/convert/test_input_app_name.py b/tests/commands/convert/test_input_app_name.py index be0eede4b..e304e40b9 100644 --- a/tests/commands/convert/test_input_app_name.py +++ b/tests/commands/convert/test_input_app_name.py @@ -39,7 +39,7 @@ def test_valid_pep621_app_name(convert_command): def test_pep621_name_is_canonicalized(convert_command): (convert_command.base_path / "pyproject.toml").write_text( - '[project]\nname="test.name"', encoding="utf-8" + '[project]\nname="test_name"', encoding="utf-8" ) assert convert_command.input_app_name(None) == "test-name" From 02509a54c1ca017c0b8d241d978ead6635afd3a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 21 Jul 2025 12:44:22 +0200 Subject: [PATCH 06/30] Remove 'None' from invalid app name checks and add 'none' to constants --- src/briefcase/config.py | 3 --- src/briefcase/constants.py | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 6781e62e7..d89433101 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -45,7 +45,6 @@ def is_valid_app_name(app_name): - Does not start with a number. - Does not have a period ('.') in the name. - Does not start or end with a hyphen ('-') or underscore ('_'). - - Is not the string 'None' :returns: True if the app name is valid; False otherwise. """ return all( @@ -56,8 +55,6 @@ def is_valid_app_name(app_name): not app_name.startswith(("-", "_")), not app_name.endswith(("-", "_")), app_name.replace("-", "_").isidentifier(), - # the string 'None' is not a valid app name - app_name != "None", ] ) diff --git a/src/briefcase/constants.py b/src/briefcase/constants.py index 3c674dd84..6803f9223 100644 --- a/src/briefcase/constants.py +++ b/src/briefcase/constants.py @@ -263,6 +263,7 @@ "nis", "nntplib", "ntpath", + "none", "numbers", "operator", "optparse", From 9d2558636b59a299f374025824bd9b86441faec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20M=C3=A9ndez?= <44614728+egarciamendez@users.noreply.github.com> Date: Tue, 22 Jul 2025 06:59:55 +0200 Subject: [PATCH 07/30] Update config.py Co-authored-by: Russell Keith-Magee --- src/briefcase/config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index d89433101..fa33763a8 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -39,12 +39,13 @@ def is_reserved_keyword(app_name): def is_valid_app_name(app_name): """Determine if the app name is valid. - :param app_name: The app name to validate. Checks the following: + Checks the following: - It is not a reserved keyword. - It is a valid PEP508 name. - Does not start with a number. - Does not have a period ('.') in the name. - - Does not start or end with a hyphen ('-') or underscore ('_'). + + :param app_name: The app name to validate. :returns: True if the app name is valid; False otherwise. """ return all( From 925280f3033657791f87a4080310912f4b825901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 08:33:41 +0200 Subject: [PATCH 08/30] ran towncrier by mistake --- docs/about/releases.rst | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/docs/about/releases.rst b/docs/about/releases.rst index bcad50503..938d2686a 100644 --- a/docs/about/releases.rst +++ b/docs/about/releases.rst @@ -4,27 +4,6 @@ Release History .. towncrier release notes start -0.1.dev4873+g4e670fa.d20250719 (2025-07-19) -=========================================== - -Features --------- - -* Add ``--forward-port`` and ``--reverse-port`` via ADB when running an Android app (`#2369 `__) - - -Bugfixes --------- - -* Apps that contain file paths longer than 260 characters can now be included in MSI installers. (`#948 `__) - - -Misc ----- - -* `#2381 `__, `#2387 `__, `#2394 `__ - - 0.3.24 (2025-07-10) =================== From 7ce356d340b166d12c20df75ddbf8dda7b3d7cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 08:45:41 +0200 Subject: [PATCH 09/30] remove redudant test cases --- tests/config/test_is_valid_app_name.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index ea4386790..ca41c306b 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -11,15 +11,9 @@ "hello42world", "hello_world", "hello-world", - "myapp", - "my_app", - "my-app", - "app2", - "validname", + "helloworld42", "a", # single letter "abc123", # alphanumeric - "app-with-hyphens", - "app_with_underscores", ], ) def test_is_valid_app_name(name): From 615d177fbd31efe13c1598f14636c05e3973640a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 08:48:56 +0200 Subject: [PATCH 10/30] remove redundant test cases --- tests/config/test_is_valid_app_name.py | 34 -------------------------- 1 file changed, 34 deletions(-) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index ca41c306b..fbce2ec79 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -54,40 +54,6 @@ def test_is_valid_app_name(name): "helloworld_ı", "İstanbul", "Kelvin", - # Additional Python keywords to verify logic fix - "and", - "or", - "not", - "if", - "else", - "elif", - "for", - "while", - "def", - "class", - "import", - "from", - "break", - "continue", - "return", - "yield", - "try", - "except", - "finally", - "with", - "as", - "lambda", - "global", - "nonlocal", - "assert", - "del", - "raise", - "in", - "is", - "async", - "await", - "True", - "None", # Case variations of reserved words "Switch", "SWITCH", From aebdaab01664f91eb22046a5075032711e66ce11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 09:56:20 +0200 Subject: [PATCH 11/30] remove redundant test cases --- src/briefcase/config.py | 5 +---- tests/config/test_is_valid_app_name.py | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index fa33763a8..5247302e6 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -52,10 +52,7 @@ def is_valid_app_name(app_name): [ is_valid_pep508_name(app_name), not is_reserved_keyword(app_name), - not app_name[0].isdigit(), - not app_name.startswith(("-", "_")), - not app_name.endswith(("-", "_")), - app_name.replace("-", "_").isidentifier(), + app_name.lower().replace("-", "_").isidentifier(), ] ) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index fbce2ec79..d000aee63 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -24,10 +24,12 @@ def test_is_valid_app_name(name): @pytest.mark.parametrize( "name", [ + # Invalid characters "hello world", "helloworld!", "_helloworld", "-helloworld", + # python reserved words "switch", "pass", "false", From 43ee2627b34be56b7001083ab1ddc016348b39c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 10:02:00 +0200 Subject: [PATCH 12/30] Add 'None' and 'none' to invalid app name test cases --- tests/config/test_is_valid_app_name.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index d000aee63..e744aed6a 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -38,6 +38,8 @@ def test_is_valid_app_name(name): "main", "socket", "test", + "None", + "none", # Additional invalid formats "my$app", # dollar sign "app@domain", # at symbol From aab7c2b8888ebab14b72715ef7b495286ebe479c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 22 Jul 2025 10:13:08 +0200 Subject: [PATCH 13/30] Update test case to use 'test.name' for app name canonicalization --- tests/commands/convert/test_input_app_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/convert/test_input_app_name.py b/tests/commands/convert/test_input_app_name.py index e304e40b9..be0eede4b 100644 --- a/tests/commands/convert/test_input_app_name.py +++ b/tests/commands/convert/test_input_app_name.py @@ -39,7 +39,7 @@ def test_valid_pep621_app_name(convert_command): def test_pep621_name_is_canonicalized(convert_command): (convert_command.base_path / "pyproject.toml").write_text( - '[project]\nname="test_name"', encoding="utf-8" + '[project]\nname="test.name"', encoding="utf-8" ) assert convert_command.input_app_name(None) == "test-name" From cc36b4d5b2c31056ee7c68c526d6c8d4ef94ceb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Thu, 24 Jul 2025 22:20:07 +0200 Subject: [PATCH 14/30] Replace dashes with underscores in canonicalized app name + remove check for valid module name --- src/briefcase/commands/convert.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index e8749bb11..92e229898 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -113,12 +113,8 @@ def input_app_name(self, override_value: str | None) -> str: ) default = "hello-world" - if ( - "name" in self.pep621_data - and is_valid_app_name(self.pep621_data["name"]) - and override_value is None - ): - app_name = canonicalize_name(self.pep621_data["name"]) + if "name" in self.pep621_data and override_value is None: + app_name = canonicalize_name(self.pep621_data["name"]).replace("-", "_") self.console.divider(title="App name") self.console.prompt() self.console.prompt( From 22c50b7292553a22663c85913fc1b0e45e63caed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Thu, 24 Jul 2025 22:31:44 +0200 Subject: [PATCH 15/30] Remove dashes replacement from canonicalized app name in convert.py --- src/briefcase/commands/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index 92e229898..a1f10a9d0 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -114,7 +114,7 @@ def input_app_name(self, override_value: str | None) -> str: default = "hello-world" if "name" in self.pep621_data and override_value is None: - app_name = canonicalize_name(self.pep621_data["name"]).replace("-", "_") + app_name = canonicalize_name(self.pep621_data["name"]) self.console.divider(title="App name") self.console.prompt() self.console.prompt( From cea0991d0e0fa13c7771da35147542c2c8963d30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 5 Aug 2025 13:19:42 +0200 Subject: [PATCH 16/30] Remove checks for starting with a number and period in app name validation --- src/briefcase/config.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 5247302e6..13c43128d 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -42,8 +42,6 @@ def is_valid_app_name(app_name): Checks the following: - It is not a reserved keyword. - It is a valid PEP508 name. - - Does not start with a number. - - Does not have a period ('.') in the name. :param app_name: The app name to validate. :returns: True if the app name is valid; False otherwise. From 37eae5e1e0cc05ba1cd7edf92f00d61124178dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 5 Aug 2025 13:38:10 +0200 Subject: [PATCH 17/30] Update app name validation message to clarify PEP508 compliance --- src/briefcase/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 13c43128d..cee6fb1f1 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -417,7 +417,7 @@ def __init__( f"{self.app_name!r} is not a valid app name.\n\n" "App names must:\n" "- Not be reserved keywords (like 'and', 'for', 'while', 'main', 'test', etc.)\n" - "- Be PEP508 compliant (letters, numbers, hyphens, and underscores only)\n" + "- Only letters, numbers, hyphens, and underscores only\n" "- Start with a letter (not a number, hyphen, or underscore)\n" "- Not end with a hyphen or underscore\n" "- Be valid Python identifiers when hyphens are replaced with underscores" From 820b0b52f7005b7aff43bc03f7470bf4674b099a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Tue, 5 Aug 2025 13:54:43 +0200 Subject: [PATCH 18/30] `none` is daft but legal --- src/briefcase/config.py | 6 +++++- src/briefcase/constants.py | 1 - tests/config/test_is_valid_app_name.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index cee6fb1f1..73f7f589c 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -33,7 +33,11 @@ def is_valid_pep508_name(app_name): def is_reserved_keyword(app_name): """Determine if the name is a reserved keyword.""" - return keyword.iskeyword(app_name.lower()) or app_name.lower() in RESERVED_WORDS + return ( + keyword.iskeyword(app_name.lower()) + or app_name.lower() in RESERVED_WORDS + or keyword.iskeyword(app_name) + ) def is_valid_app_name(app_name): diff --git a/src/briefcase/constants.py b/src/briefcase/constants.py index 6803f9223..3c674dd84 100644 --- a/src/briefcase/constants.py +++ b/src/briefcase/constants.py @@ -263,7 +263,6 @@ "nis", "nntplib", "ntpath", - "none", "numbers", "operator", "optparse", diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index e744aed6a..0973620dd 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -14,6 +14,7 @@ "helloworld42", "a", # single letter "abc123", # alphanumeric + "none", # daft but legal ;-) ], ) def test_is_valid_app_name(name): @@ -39,7 +40,6 @@ def test_is_valid_app_name(name): "socket", "test", "None", - "none", # Additional invalid formats "my$app", # dollar sign "app@domain", # at symbol From fed1fe32bd40d79666dbe12b062a33daf4f230e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 8 Sep 2025 11:38:58 +0200 Subject: [PATCH 19/30] Enhance app name suggestion logic based on PEP508 compliance --- src/briefcase/commands/convert.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index a1f10a9d0..3994b27da 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -122,13 +122,28 @@ def input_app_name(self, override_value: str | None) -> str: ) return app_name - if is_valid_app_name(self.base_path.name): # Directory name is normalised - default = canonicalize_name(self.base_path.name) + canonicalized_name = canonicalize_name(self.base_path.name) + + # Case 1: Project name is usable as-is (e.g., "foobar", "foo-bar") + if ( + is_valid_app_name(self.base_path.name) + and canonicalized_name == self.base_path.name + ): + default = self.base_path.name intro += ( "\n\n" f"Based on your PEP508 formatted directory name, we suggest an " f"app name of '{default}', but you can use another name if you want." ) + # Case 2: Project name could be valid after canonicalization (e.g., "test.name" -> "test-name", "test-app_name" -> "test-app-name") + elif is_valid_app_name(canonicalized_name): + default = canonicalized_name + intro += ( + "\n\n" + f"Based on your PEP508 formatted directory name, we suggest an " + f"app name of '{default}', but you can use another name if you want." + ) + # Case 3: Project name isn't valid, even after canonicalization - fall back to default return self.console.text_question( intro=intro, From bdb07a88cb5f03e50f0d8522b02610b7f52df6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 8 Sep 2025 11:57:25 +0200 Subject: [PATCH 20/30] Refine app name validation to ensure compliance with Python identifier rules and exclude problematic Unicode characters --- src/briefcase/config.py | 20 +++++++++++++++----- tests/config/test_is_valid_app_name.py | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 73f7f589c..688145d73 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -45,16 +45,27 @@ def is_valid_app_name(app_name): Checks the following: - It is not a reserved keyword. - - It is a valid PEP508 name. + - It is a valid Python identifier when hyphens are replaced with underscores. + - It does not start with an underscore (reserved for internal use). + - It does not contain problematic Unicode characters that have edge-case behaviors. :param app_name: The app name to validate. :returns: True if the app name is valid; False otherwise. """ + module_name = app_name.lower().replace("-", "_") + + # ı, İ and K (i.e. 0x212a) are valid ASCII when made lowercase and as such are + # accepted by the official PEP 508 regex... but they are rejected here to ensure + # compliance with the regex that is used in practice. + problematic_unicode = {"\u0131", "\u0130", "\u212a"} # ı, İ, K + if any(char in app_name for char in problematic_unicode): + return False + return all( [ - is_valid_pep508_name(app_name), not is_reserved_keyword(app_name), - app_name.lower().replace("-", "_").isidentifier(), + module_name.isidentifier(), + not module_name.startswith("_"), ] ) @@ -421,9 +432,8 @@ def __init__( f"{self.app_name!r} is not a valid app name.\n\n" "App names must:\n" "- Not be reserved keywords (like 'and', 'for', 'while', 'main', 'test', etc.)\n" - "- Only letters, numbers, hyphens, and underscores only\n" + "- Only contain letters, numbers, hyphens, and underscores\n" "- Start with a letter (not a number, hyphen, or underscore)\n" - "- Not end with a hyphen or underscore\n" "- Be valid Python identifiers when hyphens are replaced with underscores" ) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index 0973620dd..591ae89b9 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -15,6 +15,8 @@ "a", # single letter "abc123", # alphanumeric "none", # daft but legal ;-) + "helloworld_", # ends with underscore (valid Python identifier) + "helloworld-", # ends with hyphen (converts to valid identifier) ], ) def test_is_valid_app_name(name): @@ -44,8 +46,6 @@ def test_is_valid_app_name(name): "my$app", # dollar sign "app@domain", # at symbol "app.name", # period - "helloworld_", # ends with underscore - "helloworld-", # ends with hyphen "2app", # starts with number "42app", # starts with number "_", # single underscore From 79851d89686087403f415beac2808b1f38870591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 8 Sep 2025 13:20:27 +0200 Subject: [PATCH 21/30] Refactor PEP508 name validation by removing regex and updating test cases for valid identifiers --- src/briefcase/config.py | 12 ------------ tests/config/test_AppConfig.py | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 688145d73..06d9e86b3 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -18,18 +18,6 @@ from .constants import RESERVED_WORDS from .exceptions import BriefcaseConfigError -# PEP 508 restricts the naming of modules. The PEP defines a regex that uses -# re.IGNORECASE; but in in practice, packaging uses a version that rolls out the lower -# case, which has very slightly different semantics with non-ASCII characters. This -# definition is the one from -# https://github.com/pypa/packaging/blob/24.0/src/packaging/_tokenizer.py#L80 -PEP508_NAME_RE = re.compile(r"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$") - - -def is_valid_pep508_name(app_name): - """Determine if the name is valid by PEP508 rules.""" - return PEP508_NAME_RE.match(app_name) - def is_reserved_keyword(app_name): """Determine if the name is a reserved keyword.""" diff --git a/tests/config/test_AppConfig.py b/tests/config/test_AppConfig.py index 82a3eaf40..d23e566c2 100644 --- a/tests/config/test_AppConfig.py +++ b/tests/config/test_AppConfig.py @@ -180,6 +180,8 @@ def test_extra_attrs(): "my_app", # contains underscore "myapp2", # ends with digit "my2app", # contains digit + "myApp-", # ends with hyphen (converts to valid identifier) + "myApp_", # ends with underscore (valid Python identifier) ], ) def test_valid_app_name(name): @@ -204,9 +206,7 @@ def test_valid_app_name(name): "myapp!", # end punctuation "my$app", # other punctuation "-myApp", # initial hyphen - "myApp-", # end hyphen "_myApp", # initial underscore - "myApp_", # end underscore ], ) def test_invalid_app_name(name): From 418a77a84955a978cf48ee7084a9bd9b2de5eb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Mon, 8 Sep 2025 13:38:13 +0200 Subject: [PATCH 22/30] Add PEP508 compliant regex for module naming and create local settings file for permissions --- src/briefcase/config.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 06d9e86b3..e76c43c68 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -18,6 +18,13 @@ from .constants import RESERVED_WORDS from .exceptions import BriefcaseConfigError +# PEP 508 restricts the naming of modules. The PEP defines a regex that uses +# re.IGNORECASE; but in in practice, packaging uses a version that rolls out the lower +# case, which has very slightly different semantics with non-ASCII characters. This +# definition is the one from +# https://github.com/pypa/packaging/blob/24.0/src/packaging/_tokenizer.py#L80 +PEP508_NAME_RE = re.compile(r"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$") + def is_reserved_keyword(app_name): """Determine if the name is a reserved keyword.""" From 4495ab459997b576c409f501fc38f09769b9233c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa=20M=C3=A9ndez?= <44614728+egarciamendez@users.noreply.github.com> Date: Tue, 9 Sep 2025 23:23:36 +0200 Subject: [PATCH 23/30] Update test_is_valid_app_name.py Co-authored-by: Russell Keith-Magee --- tests/config/test_is_valid_app_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index 591ae89b9..fc04e4af4 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -14,7 +14,7 @@ "helloworld42", "a", # single letter "abc123", # alphanumeric - "none", # daft but legal ;-) + "none", # `None` is illegal, but when lower case, it isn't a reserved word "helloworld_", # ends with underscore (valid Python identifier) "helloworld-", # ends with hyphen (converts to valid identifier) ], From 849bc651107b90e2e4f53190ad76cb137b2809b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 12:27:24 +0200 Subject: [PATCH 24/30] Replace PEP508 regex with Android emulator name regex for validation --- src/briefcase/config.py | 14 -------------- src/briefcase/integrations/android_sdk.py | 8 ++++++-- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/briefcase/config.py b/src/briefcase/config.py index e76c43c68..6d3662d23 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -18,13 +18,6 @@ from .constants import RESERVED_WORDS from .exceptions import BriefcaseConfigError -# PEP 508 restricts the naming of modules. The PEP defines a regex that uses -# re.IGNORECASE; but in in practice, packaging uses a version that rolls out the lower -# case, which has very slightly different semantics with non-ASCII characters. This -# definition is the one from -# https://github.com/pypa/packaging/blob/24.0/src/packaging/_tokenizer.py#L80 -PEP508_NAME_RE = re.compile(r"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$") - def is_reserved_keyword(app_name): """Determine if the name is a reserved keyword.""" @@ -49,13 +42,6 @@ def is_valid_app_name(app_name): """ module_name = app_name.lower().replace("-", "_") - # ı, İ and K (i.e. 0x212a) are valid ASCII when made lowercase and as such are - # accepted by the official PEP 508 regex... but they are rejected here to ensure - # compliance with the regex that is used in practice. - problematic_unicode = {"\u0131", "\u0130", "\u212a"} # ı, İ, K - if any(char in app_name for char in problematic_unicode): - return False - return all( [ not is_reserved_keyword(app_name), diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index 5d8942431..bac9308ac 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -10,7 +10,6 @@ from datetime import datetime from pathlib import Path -from briefcase.config import PEP508_NAME_RE from briefcase.exceptions import ( BriefcaseCommandError, IncompatibleToolError, @@ -22,12 +21,17 @@ from briefcase.integrations.java import JDK from briefcase.integrations.subprocess import SubprocessArgT +ANDROID_EMULATOR_NAME_RE = re.compile( + r"^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$" +) + + DEVICE_NOT_FOUND = re.compile(r"^error: device '[^']*' not found") def create_avd_validator(emulators): def _validate_avd_name(avd): - if not PEP508_NAME_RE.match(avd): + if not ANDROID_EMULATOR_NAME_RE.match(avd): raise ValueError( "An emulator name may only contain letters, numbers, hyphens and underscores." ) From 05f9444241891825131830c7cf916b23bfcc5a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 12:28:23 +0200 Subject: [PATCH 25/30] Clarify PEP508 compliance requirements in application name guidelines --- src/briefcase/commands/convert.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index 3994b27da..b76f8815a 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -107,9 +107,8 @@ def input_app_name(self, override_value: str | None) -> str: """ intro = ( "We need a name that can serve as a machine-readable Python package name for " - "your application. This name must be PEP508-compliant - that means the name " - "may only contain letters, numbers, hyphens and underscores; it can't contain " - "spaces or punctuation, and it can't start with a hyphen, underscore or a number." + "your application. The name may only contain letters, numbers, hyphens and underscores; " + "it can't contain spaces or punctuation, and it can't start with a hyphen, underscore or a number." ) default = "hello-world" From 29d2aa9494e9f9bc0920a389f69029a9d32cca43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 12:34:30 +0200 Subject: [PATCH 26/30] Improve app name suggestion logic by using directory name directly if valid --- src/briefcase/commands/convert.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index b76f8815a..2eeb15ea6 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -128,18 +128,22 @@ def input_app_name(self, override_value: str | None) -> str: is_valid_app_name(self.base_path.name) and canonicalized_name == self.base_path.name ): + # Directory name is already valid - use it directly without prompting + if override_value is None: + self.console.divider(title="App name") + self.console.prompt() + self.console.prompt( + f"Using directory name as app name: {self.base_path.name!r}" + ) + return self.base_path.name + # If override_value is provided, fall through to prompt with it default = self.base_path.name - intro += ( - "\n\n" - f"Based on your PEP508 formatted directory name, we suggest an " - f"app name of '{default}', but you can use another name if you want." - ) # Case 2: Project name could be valid after canonicalization (e.g., "test.name" -> "test-name", "test-app_name" -> "test-app-name") elif is_valid_app_name(canonicalized_name): default = canonicalized_name intro += ( "\n\n" - f"Based on your PEP508 formatted directory name, we suggest an " + f"Based on your directory name, we suggest an " f"app name of '{default}', but you can use another name if you want." ) # Case 3: Project name isn't valid, even after canonicalization - fall back to default From a4b4704e11084422ac74d59741423901b0bd4c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 12:53:29 +0200 Subject: [PATCH 27/30] Remove invalid Unicode characters from app name validation tests --- tests/config/test_is_valid_app_name.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py index fc04e4af4..8f2ec718e 100644 --- a/tests/config/test_is_valid_app_name.py +++ b/tests/config/test_is_valid_app_name.py @@ -52,12 +52,6 @@ def test_is_valid_app_name(name): "-", # single hyphen "1", # single digit "123", # all digits - # ı, İ and K (i.e. 0x212a) are valid ASCII when made lowercase and as such are - # accepted by the official PEP 508 regex... but they are rejected here to ensure - # compliance with the regex that is used in practice. - "helloworld_ı", - "İstanbul", - "Kelvin", # Case variations of reserved words "Switch", "SWITCH", From 973bcc040dbb18c72eb010d0e6acc1d9ce88f7e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 13:22:55 +0200 Subject: [PATCH 28/30] Enhance app name suggestion logic to use directory name directly when valid --- src/briefcase/commands/convert.py | 6 ++++- tests/commands/convert/test_input_app_name.py | 27 +++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index 2eeb15ea6..f66f28efc 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -122,7 +122,6 @@ def input_app_name(self, override_value: str | None) -> str: return app_name canonicalized_name = canonicalize_name(self.base_path.name) - # Case 1: Project name is usable as-is (e.g., "foobar", "foo-bar") if ( is_valid_app_name(self.base_path.name) @@ -138,6 +137,11 @@ def input_app_name(self, override_value: str | None) -> str: return self.base_path.name # If override_value is provided, fall through to prompt with it default = self.base_path.name + intro += ( + "\n\n" + f"Based on your PEP508 formatted directory name, we suggest an " + f"app name of '{default}', but you can use another name if you want." + ) # Case 2: Project name could be valid after canonicalization (e.g., "test.name" -> "test-name", "test-app_name" -> "test-app-name") elif is_valid_app_name(canonicalized_name): default = canonicalized_name diff --git a/tests/commands/convert/test_input_app_name.py b/tests/commands/convert/test_input_app_name.py index be0eede4b..726e75290 100644 --- a/tests/commands/convert/test_input_app_name.py +++ b/tests/commands/convert/test_input_app_name.py @@ -12,21 +12,20 @@ def test_override_is_used(convert_command): def test_no_pep621_data(convert_command, monkeypatch): - """The app directory is used if there is no PEP621-name.""" - mock_text_question = MagicMock() - monkeypatch.setattr(convert_command.console, "text_question", mock_text_question) + """The app directory is used directly if there is no PEP621-name and the directory name is already valid.""" + mock_divider = MagicMock() + mock_prompt = MagicMock() + monkeypatch.setattr(convert_command.console, "divider", mock_divider) + monkeypatch.setattr(convert_command.console, "prompt", mock_prompt) convert_command.base_path /= "test-app-name" - convert_command.input_app_name(None) - mock_text_question.assert_called_once_with( - intro=PartialMatchString( - "Based on your PEP508 formatted directory name, we suggest an app name of 'test-app-name'" - ), - description="App Name", - default="test-app-name", - validator=convert_command.validate_app_name, - override_value=None, - ) + result = convert_command.input_app_name(None) + + assert result == "test-app-name" + mock_divider.assert_called_once_with(title="App name") + assert mock_prompt.call_count == 2 + mock_prompt.assert_any_call() + mock_prompt.assert_any_call("Using directory name as app name: 'test-app-name'") def test_valid_pep621_app_name(convert_command): @@ -72,7 +71,7 @@ def test_hint_is_canonicalized(convert_command, monkeypatch): mock_text_question.assert_called_once_with( intro=PartialMatchString( - "Based on your PEP508 formatted directory name, we suggest an app name of 'test-app-name'" + "Based on your directory name, we suggest an app name of 'test-app-name'" ), description="App Name", default="test-app-name", From 395729804bb627cf33a934fd0724a51c587bde6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 13:27:36 +0200 Subject: [PATCH 29/30] Enhance app name suggestion logic to use directory name directly when valid --- src/briefcase/commands/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index f66f28efc..b3707a45b 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -148,7 +148,7 @@ def input_app_name(self, override_value: str | None) -> str: intro += ( "\n\n" f"Based on your directory name, we suggest an " - f"app name of '{default}', but you can use another name if you want." + f"app name of '{default}', but you can use another name if you want. " ) # Case 3: Project name isn't valid, even after canonicalization - fall back to default From 32e4277bd151fafb76b58a234ca7a92cf7bd40ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Garc=C3=ADa?= Date: Fri, 19 Sep 2025 13:27:50 +0200 Subject: [PATCH 30/30] Enhance app name suggestion logic to use directory name directly when valid --- src/briefcase/commands/convert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/briefcase/commands/convert.py b/src/briefcase/commands/convert.py index b3707a45b..f66f28efc 100644 --- a/src/briefcase/commands/convert.py +++ b/src/briefcase/commands/convert.py @@ -148,7 +148,7 @@ def input_app_name(self, override_value: str | None) -> str: intro += ( "\n\n" f"Based on your directory name, we suggest an " - f"app name of '{default}', but you can use another name if you want. " + f"app name of '{default}', but you can use another name if you want." ) # Case 3: Project name isn't valid, even after canonicalization - fall back to default