Skip to content

Improve support for PythonKit, and update usage guide #248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 14, 2025
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
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,9 @@ $$(PYTHON_INCLUDE-$(sdk))/pyconfig.h: $$(PYTHON_LIB-$(sdk))
# Copy headers as-is from the first target in the $(sdk) SDK
cp -r $$(PYTHON_INCLUDE-$$(firstword $$(SDK_TARGETS-$(sdk)))) $$(PYTHON_INCLUDE-$(sdk))

# Copy in the modulemap file
cp -r patch/Python/module.modulemap $$(PYTHON_INCLUDE-$(sdk))

# Link the PYTHONHOME version of the headers
mkdir -p $$(PYTHON_INSTALL-$(sdk))/include
ln -si ../Python.framework/Headers $$(PYTHON_INSTALL-$(sdk))/include/python$(PYTHON_VER)
Expand Down Expand Up @@ -582,6 +585,9 @@ $$(PYTHON_XCFRAMEWORK-$(os))/Info.plist: \
# Rewrite the framework to make it standalone
patch/make-relocatable.sh $$(PYTHON_INSTALL_VERSION-macosx) 2>&1 > /dev/null

# Copy in the modulemap file
cp -r patch/Python/module.modulemap $$(PYTHON_FRAMEWORK-macosx)/Headers

# Re-apply the signature on the binaries.
codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f $$(PYTHON_LIB-macosx) \
2>&1 | tee $$(PYTHON_INSTALL-macosx)/python-$(os).codesign.log
Expand Down
36 changes: 15 additions & 21 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ what Briefcase is doing). The steps required are documented in the CPython usage
guides:

* [macOS](https://docs.python.org/3/using/mac.html)
* [iOS](https://docs.python.org/3.14/using/ios.html)
* [iOS](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project)

For tvOS and watchOS, you should be able to broadly follow the instructions in
the iOS guide.
Expand All @@ -29,7 +29,7 @@ the iOS guide.

There are 2 ways to access the Python runtime in your project code.

### Embedded C API.
### Embedded C API

You can use the [Python Embedded C
API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python
Expand All @@ -43,37 +43,31 @@ An alternate approach is to use
[PythonKit](https://github.com/pvieito/PythonKit). PythonKit is a package that
provides a Swift API to running Python code.

To use PythonKit in your project:
To use PythonKit in your project, add the Python Apple Support package to your
project as described above; then:

1. Add PythonKit to your project using the Swift Package manager. See the
PythonKit documentation for details.

2. Create a file called `module.modulemap` inside
`Python.xcframework/macos-arm64_x86_64/Headers/`, containing the following
code:
```
module Python {
umbrella header "Python.h"
export *
link "Python"
}
```

3. In your Swift code, initialize the Python runtime. This should generally be
2. In your Swift code, initialize the Python runtime. This should generally be
done as early as possible in the application's lifecycle, but definitely
needs to be done before you invoke Python code:
needs to be done before you invoke Python code. References to a specific
Python version should reflect the version of Python you are using:
```swift
import Python

guard let stdLibPath = Bundle.main.path(forResource: "python-stdlib", ofType: nil) else { return }
guard let libDynloadPath = Bundle.main.path(forResource: "python-stdlib/lib-dynload", ofType: nil) else { return }
setenv("PYTHONHOME", stdLibPath, 1)
setenv("PYTHONPATH", "\(stdLibPath):\(libDynloadPath)", 1)
guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return }
guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return }
guard let libDynloadPath = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return }
let appPath = Bundle.main.path(forResource: "app", ofType: nil)

setenv("PYTHONHOME", pythonHome, 1)
setenv("PYTHONPATH", [pythonPath, libDynloadPath, appPath].compactMap { $0 }.joined(separator: ":"), 1)
Py_Initialize()
// we now have a Python interpreter ready to be used
```

5. Invoke Python code in your app. For example:
3. Invoke Python code in your app. For example:
```swift
import PythonKit

Expand Down
5 changes: 5 additions & 0 deletions patch/Python/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Python {
umbrella header "Python.h"
export *
link "Python"
}