Issues with Embedding Python Interpreter in MacOS App Distributed via TestFlight

Hello Apple Community, many thanks in advance for your help.

My macOS app embeds a Python interpreter, compiled from source, including the Python executable and its associated libraries.

The top-level app is built with Xcode 16.1 and it's written 100% in Swift6.

For test purposes we are running the app on MacOS Sequoia 15.0, 15.1 and Sonoma 14.4.

The app can be downloaded via TestFlight and Console app shows the next errors:

Crash Reports

python3.11

Application Specific Signatures: Unable to get bundle identifier for container id python3: Unable to get bundle identifier because Info.plist from code signature information has no value for kCFBundleIdentifierKey.

tccd process error

Prompting policy for hardened runtime; service: kTCCServiceAppleEvents requires entitlement com.apple.security.automation.apple-events but it is missing for accessing={TCCDProcess: identifier=[IDENTIFIER]], pid=62822, auid=502, euid=502, binary_path=[PATH TO SAMPLEAPP]]}, requesting={TCCDProcess: identifier=com.apple.appleeventsd, pid=577, auid=55, euid=55, binary_path=/System/Library/CoreServices/appleeventsd},

The next documents were helping a lot to reach the current state althought sometimes I was not sure how to apply them in this python interpreter context:

Signing a daemon with a restricted entitlement

Embedding a command-line tool in a sandboxed app

XPC Rendezvous, com.apple.security.inherit and LaunchAgent

Placing content in a bundle

There are a lot of details that I will try to explain in the next lines.

Once archived the app, it looks like this:

SampleApp.app
SampleApp.app/Contents
SampleApp.app/Contents/Info.plist
SampleApp.app/Contents/MacOS
SampleApp.app/Contents/MacOS/SampleApp
SampleApp.app/Contents/Resources
SampleApp.app/Contents/Resources/Python.bundle

And this is how Python.bundle looks like:

Python.bundle/Contents
Python.bundle/Contents/Info.plist
Python.bundle/Contents/Resources
Python.bundle/Contents/Resources/bin
Python.bundle/Contents/Resources/bin/python3.11  <- Python executable
Python.bundle/Contents/Resources/lib
Python.bundle/Contents/Resources/lib/python3.11  <- Folder with python libraries

This is the Info.plist associated with Python.bundle:

<dict>
        <key>CFBundleIdentifier</key>
        <string>com.sampleapp.app.Python</string>
        <key>CFBundleName</key>
        <string>Python</string>
        <key>CFBundleVersion</key>
        <string>1.0</string>
        <key>CFBundlePackageType</key>
        <string>BNDL</string>
</dict>

For some reason Bundle Identifier is ignored.

Created a Python target and added to the main app, I selected the Bundle template.

In Python target I made the next customizations:

  • Enabled the Skip Install (SKIP_INSTALL) build setting.
  • Disabled the Code Signing Inject Base Entitlements
  • Added entitlements com.apple.security.inherit to it, with a Boolean value of true.

Tried to set

Other Code Signing Flags (OTHER_CODE_SIGN_FLAGS)

build setting to:

$(inherited) -i $(PRODUCT_BUNDLE_IDENTIFIER)

But I had to remove it because I could not get rid of this error

"-i com.sampleapp.app.Python: No such file or directory"

Created a python.plist and set it in the Packaging Build Settings section. I set Generate Info.plist File to No

In this document:

Embedding a command-line tool in a sandboxed app

Says:

"Add the ToolX executable to that build phase, making sure Code Sign On Copy is checked."

But I could not do it to avoid duplicates, since the bundle itself contains the executable too. I'm not sure how to handle this case.

Tried to add python3.11 executable in the bundle MacOS folder, but bundle executableURL returned nil and I could not use python from the code.

This is how I get Python bundle from code:

static var pythonBundle: Bundle? {
    if let bundlePath = Bundle.main.path(forResource: "Python", ofType: "bundle"),
       let bundle = Bundle(path: bundlePath) {
           return bundle
    }
    return nil
}

Created Python.entitlements with the next key-values:

<key>com.apple.security.app-sandbox</key>
<true/>

and it is used in an Archive Post-action of SampleApp, in order to sign the python executable of Python.bundle as follows:

codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS_PATH" --sign "$DEVELOPER_ID_APPLICATION" "$ARCHIVE_PATH"

The reason of using an Archive Post-action is becauses signing from a Python.bundle Build phase was generating errors related to Sandboxing.

These are the entitlements to codesign SampleApp:

<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.files.user-selected.read-only</key>
	<true/>
	<key>com.apple.security.inherit</key>
	<true/>
</dict>

Most probably I was mixing concepts and it seems created some confusion.

We would really love to get some advice,

Thanks!

Answered by DTS Engineer in 811316022
Because the app is client and server at the same time I added both entitlements

That’s fine. It’s very common for an app to act as both, and hence have both entitlements.

I also have these lines in the app Info.plist to allow access to a non secure endpoint:

Be aware that disabling ATS is only necessary if your app uses APIs that are affected by ATS. That’s basically URLSession and things layered on top of it. Most Python code uses lower-level APIs, like BSD Sockets, and thus ATS isn’t a concern.

Also, adding 127.0.0.1 as an exception domain is weird. AFAIK ATS never applied to loopback networking [1]. Certainly, on macOS 14 I’m able to use URLSession to access http://127.0.0.1:8080 without any ATS exceptions.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] But it’s possible I’ve forgotten something. There’s a distinction between loopback networking and local networking, and the story for local networking is a complex one that’s. See NSAllowsLocalNetworking property for the details.

Accepted Answer
Because the app is client and server at the same time I added both entitlements

That’s fine. It’s very common for an app to act as both, and hence have both entitlements.

I also have these lines in the app Info.plist to allow access to a non secure endpoint:

Be aware that disabling ATS is only necessary if your app uses APIs that are affected by ATS. That’s basically URLSession and things layered on top of it. Most Python code uses lower-level APIs, like BSD Sockets, and thus ATS isn’t a concern.

Also, adding 127.0.0.1 as an exception domain is weird. AFAIK ATS never applied to loopback networking [1]. Certainly, on macOS 14 I’m able to use URLSession to access http://127.0.0.1:8080 without any ATS exceptions.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] But it’s possible I’ve forgotten something. There’s a distinction between loopback networking and local networking, and the story for local networking is a complex one that’s. See NSAllowsLocalNetworking property for the details.

Issues with Embedding Python Interpreter in MacOS App Distributed via TestFlight
 
 
Q