diff --git a/USAGE.md b/USAGE.md index 12bc1dc7..1e681709 100644 --- a/USAGE.md +++ b/USAGE.md @@ -20,16 +20,16 @@ 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. ## Accessing the Python runtime -There are 2 ways to access the Python runtime in your project code. +There are 3 ways to access the Python runtime in your project code. -### Embedded C API. +### Use C/Objective-C/C++ with Embedded C API You can use the [Python Embedded C API](https://docs.python.org/3/extending/embedding.html) to instantiate a Python @@ -37,54 +37,106 @@ interpreter. This is the approach taken by Briefcase; you may find the bootstrap mainline code generated by Briefcase a helpful guide to what is needed to start an interpreter and run Python code. -### PythonKit +### Use Swift with Embedded C API +If you want to use Python framework in Swift, you should do some additional setups. + +1. Edit all `Python.xcframework/*/Python.framework` + 1. Make a `Modules/module.modulemap` file + ```shell + cd Python.xcframework/macos-arm64_x86_64/Python.framework + mkdir Modules + touch Modules/module.modulemap + ``` + `macos-arm64_x86_64` in the cd command, should be changed to your target platform + + 2. Fill `Modules/module.modulemap` with the content: + ``` + framework module Python { + umbrella header "Python.h" + export * + link "Python" + } + ``` + + 3. Edit `Headers/cpython/pyatomic.h`, in the file: + - replace ``cpython/pyatomic_gcc.h`` with ``pyatomic_gcc.h`` + - replace ``cpython/pyatomic_std.h`` with ``pyatomic_std.h`` + - replace ``cpython/pyatomic_msc.h`` with ``pyatomic_msc.h`` + +2. Drag `Python.xcframework` into the root of the macOS/iOS project in Xcode Navigator +3. If you are making an iOS project, do the steps of 3, 6, 7, 8, 9 in the Python offical document [Adding Python to an iOS Project](https://docs.python.org/3/using/ios.html#adding-python-to-an-ios-project). + `iOS/Resources/dylib-Info-template.plist` for the step 3 is [here](https://github.com/python/cpython/blob/bee7bb3310b356e99e3a0f75f23efbc97f1b0a24/iOS/Resources/dylib-Info-template.plist)) + +5. 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: -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. + ```swift + import Foundation + import Python + + func setEnvs() { + #if os(macOS) + print("macOS do not need set envs") + #else + guard let pythonHome = Bundle.main.path(forResource: "python", ofType: nil) else { return } + setenv("PYTHONHOME", pythonHome, 1) + + /* + The PYTHONPATH for the interpreter includes: + the python/lib/python3.X subfolder of your app’s bundle, + the python/lib/python3.X/lib-dynload subfolder of your app’s bundle, and + the app subfolder of your app’s bundle + */ + guard let pythonPath = Bundle.main.path(forResource: "python/lib/python3.13", ofType: nil) else { return } + guard let libDynLoad = Bundle.main.path(forResource: "python/lib/python3.13/lib-dynload", ofType: nil) else { return } + let appPath = Bundle.main.path(forResource: "app", ofType: nil) + setenv("PYTHONPATH", [pythonPath, libDynLoad, appPath].compactMap { $0 }.joined(separator: ":"), 1) + #endif + } + + setEnvs() + Py_Initialize() + let version = String(cString: Py_GetVersion()) + print(version) + // we now have a Python interpreter ready to be used + ``` + +After init Python, you could use Embed C API in Swift directly. -To use PythonKit in your project: +To integrate 3rd party python code and dependencies, you will need to make sure +`PYTHONPATH` contains their paths; once this has been done, you can run +`Python.import("")`. to import that module from inside swift. -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 - done as early as possible in the application's lifecycle, but definitely - needs to be done before you invoke Python code: -```swift -import Python +### Use Swift with `PythonKit` -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) -Py_Initialize() -// we now have a Python interpreter ready to be used -``` +Instead of using Embed C API, an alternate approach is to use +[PythonKit](https://github.com/pvieito/PythonKit). -5. Invoke Python code in your app. For example: -```swift -import PythonKit +PythonKit is a package that +provides Swift friendly API to running Python code. -let sys = Python.import("sys") -print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") -print("Python Encoding: \(sys.getdefaultencoding().upper())") -print("Python Path: \(sys.path)") +To use PythonKit in your project: -_ = Python.import("math") // verifies `lib-dynload` is found and signed successfully -``` +1. Do all steps of "Use Swift with Embedded C API" above +2. Add PythonKit to your project using the Swift Package manager. See the + PythonKit documentation for details. -To integrate 3rd party python code and dependencies, you will need to make sure -`PYTHONPATH` contains their paths; once this has been done, you can run -`Python.import("")`. to import that module from inside swift. +3. Invoke Python code in your app. For example: + ```swift + import Foundation + import Python + + // ... setEnv defines + setEnvs() + Py_Initialize() + + import PythonKit + let sys = Python.import("sys") + print("Python Full Version: \(sys.version)") + print("Python Version: \(sys.version_info.major).\(sys.version_info.minor)") + print("Python Encoding: \(sys.getdefaultencoding().upper())") + print("Python Path: \(sys.path)") + _ = Python.import("math") // verifies `lib-dynload` is found and signed successfully + ```