Repository: beeware/briefcase
Pull request #1238 (2023-04-25 22:56:38)

Add python stdlib names and 'main' to reserved words
Moved reserved words constants to a 'constants.py' module and add python standard library names and 'main' to the set of reserved words. Only names from python 3.8-3.11 are included.
Fixes issue #853.

The isort library maintainers have already done the heavy lifting collecting the stdlib names across the various python versions so the set of names here was generated like so:

```python
from isort.stdlibs import py38, py39, py310, py311

stdlib = py38.stdlib | py39.stdlib | py310.stdlib | py311.stdlib
reserved = sorted({'main'} | stdlib)
text = "PYTHON_RESERVED_WORDS = {\n"
for name in badlist:
    text += f'    "{name}",\n'
text += "}\n"
print(text)
```


Patch:
diff --git a/changes/1236.bugfix.rst b/changes/1236.bugfix.rst
deleted file mode 100644
index 0518430a..00000000
--- a/changes/1236.bugfix.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Filenames and directories in rpm package definitions are quoted in order to include files with filenames that include whitespace.
diff --git a/changes/853.bugfix.rst b/changes/853.bugfix.rst
new file mode 100644
index 00000000..74074955
--- /dev/null
+++ b/changes/853.bugfix.rst
@@ -0,0 +1,1 @@
+Add python stdlib names and 'main' to reserved words.
diff --git a/src/briefcase/config.py b/src/briefcase/config.py
index a68a408e..be9a7617 100644
--- a/src/briefcase/config.py
+++ b/src/briefcase/config.py
@@ -11,163 +11,12 @@ except ModuleNotFoundError:
 
 from briefcase.platforms import get_output_formats, get_platforms
 
+from .constants import RESERVED_WORDS
 from .exceptions import BriefcaseConfigError
 
 # PEP508 provides a basic restriction on naming
 PEP508_NAME_RE = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE)
 
-# Javascript reserved keywords:
-# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015
-JAVASCRIPT_RESERVED_WORDS = {
-    "break",
-    "case",
-    "catch",
-    "class",
-    "const",
-    "continue",
-    "debugger",
-    "default",
-    "delete",
-    "do",
-    "else",
-    "export",
-    "extends",
-    "finally",
-    "for",
-    "function",
-    "if",
-    "import",
-    "in",
-    "instanceof",
-    "new",
-    "return",
-    "super",
-    "switch",
-    "this",
-    "throw",
-    "try",
-    "typeof",
-    "var",
-    "void",
-    "while",
-    "with",
-    "yield",
-}
-
-# Java reserved keywords
-# https://en.wikipedia.org/wiki/List_of_Java_keywords
-JAVA_RESERVED_WORDS = {
-    # Keywords
-    "abstract",
-    "assert",
-    "boolean",
-    "break",
-    "byte",
-    "case",
-    "catch",
-    "char",
-    "class",
-    "const",
-    "continue",
-    "default",
-    "do",
-    "double",
-    "else",
-    "enum",
-    "extends",
-    "final",
-    "finally",
-    "float",
-    "for",
-    "goto",
-    "if",
-    "implements",
-    "import",
-    "instanceof",
-    "int",
-    "interface",
-    "long",
-    "native",
-    "new",
-    "package",
-    "private",
-    "protected",
-    "public",
-    "return",
-    "short",
-    "static",
-    "super",
-    "switch",
-    "synchronized",
-    "this",
-    "throw",
-    "throws",
-    "transient",
-    "try",
-    "void",
-    "volatile",
-    "while",
-    # Reserved Identifiers
-    "exports",
-    "module",
-    "non-sealed",
-    "open",
-    "opens",
-    "permits",
-    "provides",
-    "record",
-    "requires",
-    "sealed",
-    "to",
-    "transitive",
-    "uses",
-    "var",
-    "with",
-    "yield",
-    # Reserved Literals
-    "true",
-    "false",
-    "null",
-    # Unused, but reserved.
-    "strictfp",
-}
-
-
-# Names that are illegal as Windows filenames
-# https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
-WINDOWS_RESERVED_WORDS = {
-    "con",
-    "prn",
-    "aux",
-    "nul",
-    "com1",
-    "com2",
-    "com3",
-    "com4",
-    "com5",
-    "com6",
-    "com7",
-    "com8",
-    "com9",
-    "com0",
-    "lpt1",
-    "lpt2",
-    "lpt3",
-    "lpt4",
-    "lpt5",
-    "lpt6",
-    "lpt7",
-    "lpt8",
-    "lpt9",
-    "lpt0",
-}
-
-NON_PYTHON_RESERVED_WORDS = set.union(
-    JAVASCRIPT_RESERVED_WORDS,
-    JAVA_RESERVED_WORDS,
-    WINDOWS_RESERVED_WORDS,
-)
-
 
 def is_valid_pep508_name(app_name):
     """Determine if the name is valid by PEP508 rules."""
@@ -176,10 +25,7 @@ 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 NON_PYTHON_RESERVED_WORDS
-    )
+    return keyword.iskeyword(app_name.lower()) or app_name.lower() in RESERVED_WORDS
 
 
 def is_valid_app_name(app_name):
diff --git a/src/briefcase/constants.py b/src/briefcase/constants.py
new file mode 100644
index 00000000..3c674dd8
--- /dev/null
+++ b/src/briefcase/constants.py
@@ -0,0 +1,381 @@
+# Javascript reserved keywords:
+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_keywords_as_of_ecmascript_2015
+JAVASCRIPT_RESERVED_WORDS = {
+    "break",
+    "case",
+    "catch",
+    "class",
+    "const",
+    "continue",
+    "debugger",
+    "default",
+    "delete",
+    "do",
+    "else",
+    "export",
+    "extends",
+    "finally",
+    "for",
+    "function",
+    "if",
+    "import",
+    "in",
+    "instanceof",
+    "new",
+    "return",
+    "super",
+    "switch",
+    "this",
+    "throw",
+    "try",
+    "typeof",
+    "var",
+    "void",
+    "while",
+    "with",
+    "yield",
+}
+
+# Java reserved keywords
+# https://en.wikipedia.org/wiki/List_of_Java_keywords
+JAVA_RESERVED_WORDS = {
+    # Keywords
+    "abstract",
+    "assert",
+    "boolean",
+    "break",
+    "byte",
+    "case",
+    "catch",
+    "char",
+    "class",
+    "const",
+    "continue",
+    "default",
+    "do",
+    "double",
+    "else",
+    "enum",
+    "extends",
+    "final",
+    "finally",
+    "float",
+    "for",
+    "goto",
+    "if",
+    "implements",
+    "import",
+    "instanceof",
+    "int",
+    "interface",
+    "long",
+    "native",
+    "new",
+    "package",
+    "private",
+    "protected",
+    "public",
+    "return",
+    "short",
+    "static",
+    "super",
+    "switch",
+    "synchronized",
+    "this",
+    "throw",
+    "throws",
+    "transient",
+    "try",
+    "void",
+    "volatile",
+    "while",
+    # Reserved Identifiers
+    "exports",
+    "module",
+    "non-sealed",
+    "open",
+    "opens",
+    "permits",
+    "provides",
+    "record",
+    "requires",
+    "sealed",
+    "to",
+    "transitive",
+    "uses",
+    "var",
+    "with",
+    "yield",
+    # Reserved Literals
+    "true",
+    "false",
+    "null",
+    # Unused, but reserved.
+    "strictfp",
+}
+
+
+# Names that are illegal as Windows filenames
+# https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+WINDOWS_RESERVED_WORDS = {
+    "con",
+    "prn",
+    "aux",
+    "nul",
+    "com1",
+    "com2",
+    "com3",
+    "com4",
+    "com5",
+    "com6",
+    "com7",
+    "com8",
+    "com9",
+    "com0",
+    "lpt1",
+    "lpt2",
+    "lpt3",
+    "lpt4",
+    "lpt5",
+    "lpt6",
+    "lpt7",
+    "lpt8",
+    "lpt9",
+    "lpt0",
+}
+
+NON_PYTHON_RESERVED_WORDS = set.union(
+    JAVASCRIPT_RESERVED_WORDS,
+    JAVA_RESERVED_WORDS,
+    WINDOWS_RESERVED_WORDS,
+)
+
+# All standard library names for python 3.8 through 3.11,
+# plus 'main' which is reserved for briefcase use.
+PYTHON_RESERVED_WORDS = {
+    "_ast",
+    "_dummy_thread",
+    "_thread",
+    "abc",
+    "aifc",
+    "argparse",
+    "array",
+    "ast",
+    "asynchat",
+    "asyncio",
+    "asyncore",
+    "atexit",
+    "audioop",
+    "base64",
+    "bdb",
+    "binascii",
+    "binhex",
+    "bisect",
+    "builtins",
+    "bz2",
+    "cProfile",
+    "calendar",
+    "cgi",
+    "cgitb",
+    "chunk",
+    "cmath",
+    "cmd",
+    "code",
+    "codecs",
+    "codeop",
+    "collections",
+    "colorsys",
+    "compileall",
+    "concurrent",
+    "configparser",
+    "contextlib",
+    "contextvars",
+    "copy",
+    "copyreg",
+    "crypt",
+    "csv",
+    "ctypes",
+    "curses",
+    "dataclasses",
+    "datetime",
+    "dbm",
+    "decimal",
+    "difflib",
+    "dis",
+    "distutils",
+    "doctest",
+    "dummy_threading",
+    "email",
+    "encodings",
+    "ensurepip",
+    "enum",
+    "errno",
+    "faulthandler",
+    "fcntl",
+    "filecmp",
+    "fileinput",
+    "fnmatch",
+    "formatter",
+    "fractions",
+    "ftplib",
+    "functools",
+    "gc",
+    "getopt",
+    "getpass",
+    "gettext",
+    "glob",
+    "graphlib",
+    "grp",
+    "gzip",
+    "hashlib",
+    "heapq",
+    "hmac",
+    "html",
+    "http",
+    "idlelib",
+    "imaplib",
+    "imghdr",
+    "imp",
+    "importlib",
+    "inspect",
+    "io",
+    "ipaddress",
+    "itertools",
+    "json",
+    "keyword",
+    "lib2to3",
+    "linecache",
+    "locale",
+    "logging",
+    "lzma",
+    "mailbox",
+    "mailcap",
+    "main",
+    "marshal",
+    "math",
+    "mimetypes",
+    "mmap",
+    "modulefinder",
+    "msilib",
+    "msvcrt",
+    "multiprocessing",
+    "netrc",
+    "nis",
+    "nntplib",
+    "ntpath",
+    "numbers",
+    "operator",
+    "optparse",
+    "os",
+    "ossaudiodev",
+    "parser",
+    "pathlib",
+    "pdb",
+    "pickle",
+    "pickletools",
+    "pipes",
+    "pkgutil",
+    "platform",
+    "plistlib",
+    "poplib",
+    "posix",
+    "posixpath",
+    "pprint",
+    "profile",
+    "pstats",
+    "pty",
+    "pwd",
+    "py_compile",
+    "pyclbr",
+    "pydoc",
+    "queue",
+    "quopri",
+    "random",
+    "re",
+    "readline",
+    "reprlib",
+    "resource",
+    "rlcompleter",
+    "runpy",
+    "sched",
+    "secrets",
+    "select",
+    "selectors",
+    "shelve",
+    "shlex",
+    "shutil",
+    "signal",
+    "site",
+    "smtpd",
+    "smtplib",
+    "sndhdr",
+    "socket",
+    "socketserver",
+    "spwd",
+    "sqlite3",
+    "sre",
+    "sre_compile",
+    "sre_constants",
+    "sre_parse",
+    "ssl",
+    "stat",
+    "statistics",
+    "string",
+    "stringprep",
+    "struct",
+    "subprocess",
+    "sunau",
+    "symbol",
+    "symtable",
+    "sys",
+    "sysconfig",
+    "syslog",
+    "tabnanny",
+    "tarfile",
+    "telnetlib",
+    "tempfile",
+    "termios",
+    "test",
+    "textwrap",
+    "threading",
+    "time",
+    "timeit",
+    "tkinter",
+    "token",
+    "tokenize",
+    "tomllib",
+    "trace",
+    "traceback",
+    "tracemalloc",
+    "tty",
+    "turtle",
+    "turtledemo",
+    "types",
+    "typing",
+    "unicodedata",
+    "unittest",
+    "urllib",
+    "uu",
+    "uuid",
+    "venv",
+    "warnings",
+    "wave",
+    "weakref",
+    "webbrowser",
+    "winreg",
+    "winsound",
+    "wsgiref",
+    "xdrlib",
+    "xml",
+    "xmlrpc",
+    "zipapp",
+    "zipfile",
+    "zipimport",
+    "zlib",
+    "zoneinfo",
+}
+
+RESERVED_WORDS = set.union(
+    PYTHON_RESERVED_WORDS,
+    NON_PYTHON_RESERVED_WORDS,
+)
diff --git a/src/briefcase/platforms/linux/system.py b/src/briefcase/platforms/linux/system.py
index 944d6c01..0867eb3d 100644
--- a/src/briefcase/platforms/linux/system.py
+++ b/src/briefcase/platforms/linux/system.py
@@ -981,9 +981,9 @@ class LinuxSystemPackageCommand(LinuxSystemMixin, PackageCommand):
 
                     if filename.is_dir():
                         if app.app_name in path.parts:
-                            f.write(f'%dir "/{path}"\n')
+                            f.write(f"%dir /{path}\n")
                     else:
-                        f.write(f'"/{path}"\n')
+                        f.write(f"/{path}\n")
 
                 # Add the changelog content to the bottom of the spec file.
                 f.write("\n%changelog\n")


Test patch:
diff --git a/tests/config/test_is_valid_app_name.py b/tests/config/test_is_valid_app_name.py
index 450323b4..56d7ab49 100644
--- a/tests/config/test_is_valid_app_name.py
+++ b/tests/config/test_is_valid_app_name.py
@@ -31,6 +31,9 @@ def test_is_valid_app_name(name):
         "false",
         "False",
         "YIELD",
+        "main",
+        "socket",
+        "test",
     ],
 )
 def test_is_invalid_app_name(name):
diff --git a/tests/platforms/linux/system/test_package__rpm.py b/tests/platforms/linux/system/test_package__rpm.py
index 534b9a26..ad0d1f08 100644
--- a/tests/platforms/linux/system/test_package__rpm.py
+++ b/tests/platforms/linux/system/test_package__rpm.py
@@ -204,21 +204,21 @@ def test_rpm_package(package_command, first_app_rpm, tmp_path):
                 "cp -r usr %{buildroot}/usr",
                 "",
                 "%files",
-                '"/usr/bin/first-app"',
-                '%dir "/usr/lib/first-app"',
-                '%dir "/usr/lib/first-app/app"',
-                '"/usr/lib/first-app/app/support.so"',
-                '%dir "/usr/lib/first-app/app_packages"',
-                '%dir "/usr/lib/first-app/app_packages/firstlib"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so.1.0"',
-                '%dir "/usr/lib/first-app/app_packages/secondlib"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_a.so"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_b.so"',
-                '%dir "/usr/share/doc/first-app"',
-                '"/usr/share/doc/first-app/UserManual"',
-                '"/usr/share/doc/first-app/license"',
-                '"/usr/share/man/man1/first-app.1.gz"',
+                "/usr/bin/first-app",
+                "%dir /usr/lib/first-app",
+                "%dir /usr/lib/first-app/app",
+                "/usr/lib/first-app/app/support.so",
+                "%dir /usr/lib/first-app/app_packages",
+                "%dir /usr/lib/first-app/app_packages/firstlib",
+                "/usr/lib/first-app/app_packages/firstlib/first.so",
+                "/usr/lib/first-app/app_packages/firstlib/first.so.1.0",
+                "%dir /usr/lib/first-app/app_packages/secondlib",
+                "/usr/lib/first-app/app_packages/secondlib/second_a.so",
+                "/usr/lib/first-app/app_packages/secondlib/second_b.so",
+                "%dir /usr/share/doc/first-app",
+                "/usr/share/doc/first-app/UserManual",
+                "/usr/share/doc/first-app/license",
+                "/usr/share/man/man1/first-app.1.gz",
                 "",
                 "%changelog",
                 "First App Changelog",
@@ -352,21 +352,21 @@ def test_rpm_re_package(package_command, first_app_rpm, tmp_path):
                 "cp -r usr %{buildroot}/usr",
                 "",
                 "%files",
-                '"/usr/bin/first-app"',
-                '%dir "/usr/lib/first-app"',
-                '%dir "/usr/lib/first-app/app"',
-                '"/usr/lib/first-app/app/support.so"',
-                '%dir "/usr/lib/first-app/app_packages"',
-                '%dir "/usr/lib/first-app/app_packages/firstlib"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so.1.0"',
-                '%dir "/usr/lib/first-app/app_packages/secondlib"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_a.so"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_b.so"',
-                '%dir "/usr/share/doc/first-app"',
-                '"/usr/share/doc/first-app/UserManual"',
-                '"/usr/share/doc/first-app/license"',
-                '"/usr/share/man/man1/first-app.1.gz"',
+                "/usr/bin/first-app",
+                "%dir /usr/lib/first-app",
+                "%dir /usr/lib/first-app/app",
+                "/usr/lib/first-app/app/support.so",
+                "%dir /usr/lib/first-app/app_packages",
+                "%dir /usr/lib/first-app/app_packages/firstlib",
+                "/usr/lib/first-app/app_packages/firstlib/first.so",
+                "/usr/lib/first-app/app_packages/firstlib/first.so.1.0",
+                "%dir /usr/lib/first-app/app_packages/secondlib",
+                "/usr/lib/first-app/app_packages/secondlib/second_a.so",
+                "/usr/lib/first-app/app_packages/secondlib/second_b.so",
+                "%dir /usr/share/doc/first-app",
+                "/usr/share/doc/first-app/UserManual",
+                "/usr/share/doc/first-app/license",
+                "/usr/share/man/man1/first-app.1.gz",
                 "",
                 "%changelog",
                 "First App Changelog",
@@ -521,21 +521,21 @@ def test_rpm_package_extra_requirements(package_command, first_app_rpm, tmp_path
                 "cp -r usr %{buildroot}/usr",
                 "",
                 "%files",
-                '"/usr/bin/first-app"',
-                '%dir "/usr/lib/first-app"',
-                '%dir "/usr/lib/first-app/app"',
-                '"/usr/lib/first-app/app/support.so"',
-                '%dir "/usr/lib/first-app/app_packages"',
-                '%dir "/usr/lib/first-app/app_packages/firstlib"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so"',
-                '"/usr/lib/first-app/app_packages/firstlib/first.so.1.0"',
-                '%dir "/usr/lib/first-app/app_packages/secondlib"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_a.so"',
-                '"/usr/lib/first-app/app_packages/secondlib/second_b.so"',
-                '%dir "/usr/share/doc/first-app"',
-                '"/usr/share/doc/first-app/UserManual"',
-                '"/usr/share/doc/first-app/license"',
-                '"/usr/share/man/man1/first-app.1.gz"',
+                "/usr/bin/first-app",
+                "%dir /usr/lib/first-app",
+                "%dir /usr/lib/first-app/app",
+                "/usr/lib/first-app/app/support.so",
+                "%dir /usr/lib/first-app/app_packages",
+                "%dir /usr/lib/first-app/app_packages/firstlib",
+                "/usr/lib/first-app/app_packages/firstlib/first.so",
+                "/usr/lib/first-app/app_packages/firstlib/first.so.1.0",
+                "%dir /usr/lib/first-app/app_packages/secondlib",
+                "/usr/lib/first-app/app_packages/secondlib/second_a.so",
+                "/usr/lib/first-app/app_packages/secondlib/second_b.so",
+                "%dir /usr/share/doc/first-app",
+                "/usr/share/doc/first-app/UserManual",
+                "/usr/share/doc/first-app/license",
+                "/usr/share/man/man1/first-app.1.gz",
                 "",
                 "%changelog",
                 "First App Changelog",