Merge remote-tracking branch 'upstream/master'

This commit is contained in:
John Cote 2026-01-27 20:58:16 -05:00
commit 935c9cd80a
No known key found for this signature in database
GPG Key ID: A5250CCD017DFCA5
377 changed files with 21592 additions and 12032 deletions

View File

@ -20,7 +20,7 @@ jobs:
include:
- profile: ubuntu-bionic-double-standard-unity-makefile
os: ubuntu-20.04
os: ubuntu-22.04
config: Standard
unity: YES
generator: Unix Makefiles
@ -29,9 +29,10 @@ jobs:
python: YES
eigen: NO
double: YES
asan: YES
- profile: ubuntu-bionic-coverage-ninja
os: ubuntu-20.04
os: ubuntu-22.04
config: Coverage
unity: NO
generator: Ninja
@ -40,9 +41,10 @@ jobs:
python: YES
eigen: NO
double: NO
asan: YES
#- profile: macos-coverage-unity-xcode
# os: macOS-13
# os: macOS-15
# config: Coverage
# unity: YES
# generator: Xcode
@ -53,7 +55,7 @@ jobs:
# double: NO
- profile: macos-nometa-standard-makefile
os: macOS-13
os: macOS-15
config: Standard
unity: NO
generator: Unix Makefiles
@ -62,6 +64,7 @@ jobs:
python: YES
eigen: NO
double: NO
asan: YES
- profile: windows-standard-unity-msvc
os: windows-2022
@ -88,7 +91,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 10
@ -110,7 +113,7 @@ jobs:
- name: Set up XCode (macOS)
if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app/Contents/Developer
run: sudo xcode-select -s /Applications/Xcode_16.app/Contents/Developer
- name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
@ -127,7 +130,7 @@ jobs:
- name: Cache dependencies (Windows)
if: runner.os == 'Windows'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: thirdparty
key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.15-r1
@ -144,7 +147,7 @@ jobs:
- name: ccache (non-Windows)
if: runner.os != 'Windows'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ccache
key: ci-cmake-ccache-${{ matrix.profile }}
@ -182,8 +185,13 @@ jobs:
-D HAVE_PYTHON=${{ runner.os != 'Windows' && matrix.python || 'NO' }}
-D HAVE_EIGEN=${{ matrix.eigen }}
-D STDFLOAT_DOUBLE=${{ matrix.double }}
-D ENABLE_ASAN=${{ matrix.asan || 'NO' }}
..
echo "$PWD/${{ matrix.config }}/bin" >> $GITHUB_PATH
echo "$PWD/bin" >> $GITHUB_PATH
- name: Build (no Python)
if: contains(matrix.python, 'NO')
# BEGIN A
@ -193,7 +201,7 @@ jobs:
- name: Setup Python (Python 3.8)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.8'
- name: Configure (Python 3.8)
@ -216,16 +224,17 @@ jobs:
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py38
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
run_pytest ../tests --cov=.
# END B
- name: Setup Python (Python 3.9)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.9'
- name: Configure (Python 3.9)
@ -248,16 +257,17 @@ jobs:
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py39
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
run_pytest ../tests --cov=.
# END B
- name: Setup Python (Python 3.10)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Configure (Python 3.10)
@ -280,16 +290,17 @@ jobs:
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py310
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
run_pytest ../tests --cov=.
# END B
- name: Setup Python (Python 3.11)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Configure (Python 3.11)
@ -312,16 +323,17 @@ jobs:
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py311
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
run_pytest ../tests --cov=.
# END B
- name: Setup Python (Python 3.12)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Configure (Python 3.12)
@ -344,13 +356,26 @@ jobs:
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py312
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
run_pytest ../tests --cov=.
# END B
- name: Show ASan errors
if: failure() && contains(matrix.asan, 'YES')
working-directory: build
run: |
ls -lAh asan.log.* || true
if cat asan.log.* 2>/dev/null | grep -q .; then
echo '### Address Sanitizer Report' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat asan.log.* >> $GITHUB_STEP_SUMMARY 2>/dev/null || echo "No ASan errors found"
echo '```' >> $GITHUB_STEP_SUMMARY
fi
- name: Upload coverage reports
if: always() && matrix.config == 'Coverage'
working-directory: build
@ -364,6 +389,13 @@ jobs:
python -m pip install coverage
python -m coverage combine $(find . -name '.coverage.*')
TOTAL=$(python -m coverage report --format=total)
echo '### Coverage Report' >> $GITHUB_STEP_SUMMARY
echo "<details><summary>Coverage: <b>${TOTAL}%</b></summary>" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
echo '</details>' >> $GITHUB_STEP_SUMMARY
llvm-profdata merge pid-*.profraw -o coverage.profdata
llvm-cov show $(grep -Rl LLVM_PROFILE_FILE . | sed 's/^/-object /') -instr-profile=coverage.profdata > coverage.txt
bash <(curl -s https://codecov.io/bash) -y ../.github/codecov.yml
@ -372,12 +404,12 @@ jobs:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
strategy:
matrix:
os: [ubuntu-20.04, windows-2019, macOS-13]
os: [ubuntu-22.04, windows-2022, macOS-15]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install dependencies (Ubuntu)
if: matrix.os == 'ubuntu-20.04'
if: matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install build-essential bison flex libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev
@ -386,30 +418,30 @@ jobs:
shell: powershell
run: |
$wc = New-Object System.Net.WebClient
$wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win64.zip", "thirdparty-tools.zip")
$wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win64.zip", "thirdparty-tools.zip")
Expand-Archive -Path thirdparty-tools.zip
Move-Item -Path thirdparty-tools/panda3d-1.10.15/thirdparty -Destination .
Move-Item -Path thirdparty-tools/panda3d-1.10.16/thirdparty -Destination .
- name: Get thirdparty packages (macOS)
if: runner.os == 'macOS'
run: |
curl -O https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-mac.tar.gz
tar -xf panda3d-1.10.15-tools-mac.tar.gz
mv panda3d-1.10.15/thirdparty thirdparty
rmdir panda3d-1.10.15
curl -O https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-mac.tar.gz
tar -xf panda3d-1.10.16-tools-mac.tar.gz
mv panda3d-1.10.16/thirdparty thirdparty
rmdir panda3d-1.10.16
(cd thirdparty/darwin-libs-a && rm -rf rocket)
- name: Set up XCode (macOS)
if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app/Contents/Developer
run: sudo xcode-select -s /Applications/Xcode_16.app/Contents/Developer
- name: Set up Python 3.13
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Build Python 3.13
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.13
shell: bash
run: |
@ -417,13 +449,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.12
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Build Python 3.12 (double)
shell: bash
run: |
python makepanda/makepanda.py --override STDFLOAT_DOUBLE=1 --git-commit=${{github.sha}} --outputdir=built_dbl --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --override STDFLOAT_DOUBLE=1 --git-commit=${{github.sha}} --outputdir=built_dbl --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.12 (double)
shell: bash
run: |
@ -435,13 +467,13 @@ jobs:
python makepanda/makepackage.py --verbose --lzma --outputdir=built_dbl
- name: Set up Python 3.11
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Build Python 3.11
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.11
shell: bash
run: |
@ -449,13 +481,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.10
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.10'
- name: Build Python 3.10
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.10
shell: bash
run: |
@ -463,13 +495,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.9
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.9'
- name: Build Python 3.9
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.9
shell: bash
run: |
@ -477,13 +509,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.8
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.8'
- name: Build Python 3.8
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.8
shell: bash
run: |
@ -498,7 +530,7 @@ jobs:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install dependencies
run: |
@ -508,32 +540,32 @@ jobs:
- name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14
with:
version: 3.1.70
version: 4.0.2
actions-cache-folder: 'emsdk-cache'
- name: Restore Python build cache
id: cache-emscripten-python-restore
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
with:
path: ~/python
key: cache-emscripten-python-3.12.7
key: cache-emscripten-python-3.12.8
- name: Build Python 3.12
if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true'
run: |
wget https://www.python.org/ftp/python/3.12.7/Python-3.12.7.tar.xz
tar -xJf Python-3.12.7.tar.xz
(cd Python-3.12.7 && EM_CONFIG=$EMSDK/.emscripten python3 Tools/wasm/wasm_build.py emscripten-browser)
(cd Python-3.12.7/builddir/emscripten-browser && make install DESTDIR=~/python)
cp Python-3.12.7/builddir/emscripten-browser/Modules/_hacl/libHacl_Hash_SHA2.a ~/python/usr/local/lib
cp Python-3.12.7/builddir/emscripten-browser/Modules/_decimal/libmpdec/libmpdec.a ~/python/usr/local/lib
cp Python-3.12.7/builddir/emscripten-browser/Modules/expat/libexpat.a ~/python/usr/local/lib
rm -rf Python-3.12.7
wget https://www.python.org/ftp/python/3.12.8/Python-3.12.8.tar.xz
tar -xJf Python-3.12.8.tar.xz
(cd Python-3.12.8 && EM_CONFIG=$EMSDK/.emscripten python3 Tools/wasm/wasm_build.py emscripten-browser)
(cd Python-3.12.8/builddir/emscripten-browser && make install DESTDIR=~/python)
cp Python-3.12.8/builddir/emscripten-browser/Modules/_hacl/libHacl_Hash_SHA2.a ~/python/usr/local/lib
cp Python-3.12.8/builddir/emscripten-browser/Modules/_decimal/libmpdec/libmpdec.a ~/python/usr/local/lib
cp Python-3.12.8/builddir/emscripten-browser/Modules/expat/libexpat.a ~/python/usr/local/lib
rm -rf Python-3.12.8
- name: Save Python build cache
id: cache-emscripten-python-save
if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
uses: actions/cache/save@v5
with:
path: ~/python
key: ${{ steps.cache-emscripten-python-restore.outputs.cache-primary-key }}
@ -552,3 +584,156 @@ jobs:
shell: bash
run: |
PYTHONHOME=~/python/usr/local PYTHONPATH=built node built/bin/run_tests.js tests
android:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
strategy:
fail-fast: false
matrix:
include:
- android-abi: arm64-v8a
android-arch: arm64
android-triple: aarch64-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: armeabi-v7a
android-arch: arm
android-triple: arm-linux-androideabi
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: x86_64
android-arch: x86_64
android-triple: x86_64-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: x86
android-arch: x86
android-triple: i686-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: 'tools platform-tools platforms;android-21 ndk;${{ matrix.ndk-version }}'
- name: Set up Android NDK
run: |
echo "ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk-version }}" >> $GITHUB_ENV
- uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Restore Python build cache
id: cache-android-python-restore
uses: actions/cache/restore@v5
with:
path: ~/python-prefix
key: cache-android-python-3.13.9-${{ matrix.android-abi }}-ndk${{ matrix.ndk-version }}
- name: Build Python 3.13
if: steps.cache-android-python-restore.outputs.cache-hit != 'true'
run: |
wget https://www.python.org/ftp/python/3.13.9/Python-3.13.9.tar.xz
tar -xJf Python-3.13.9.tar.xz
sed -i.bak "s/aarch64-linux-android/${{ matrix.android-triple }}/" Python-3.13.9/Android/android.py
sed -i.bak "s/ndk_version=[0-9.]\{1,\}/ndk_version=${{ matrix.ndk-version }}/" Python-3.13.9/Android/android-env.sh
(cd Python-3.13.9/Android && python3 android.py build ${{ matrix.android-triple }})
cp -R Python-3.13.9/cross-build/${{ matrix.android-triple }}/prefix ~/python-prefix
rm -rf Python-3.13.9
- name: Save Python build cache
id: cache-android-python-save
if: steps.cache-android-python-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ~/python-prefix
key: ${{ steps.cache-android-python-restore.outputs.cache-primary-key }}
- name: Restore thirdparty build cache
id: cache-android-thirdparty-restore
uses: actions/cache/restore@v5
with:
path: ./thirdparty
key: cache-android-thirdparty-935c80380ca171e08587c570e9dad678d29db3c8-${{ matrix.android-abi }}-ndk${{ matrix.ndk-version }}
- name: Install yasm
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
run: ${{runner.os == 'macOS' && 'brew' || 'sudo apt-get'}} install yasm
- name: Build thirdparty packages
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
run: |
git clone --revision=935c80380ca171e08587c570e9dad678d29db3c8 --depth=1 https://github.com/rdb/panda3d-thirdparty.git thirdparty
cd thirdparty
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DCMAKE_ANDROID_ARCH_ABI=${{ matrix.android-abi }} \
-DCMAKE_ANDROID_ARCH=${{ matrix.android-arch }} \
-DCMAKE_SYSTEM_VERSION=21 \
-DANDROID_ABI=${{ matrix.android-abi }} \
-DANDROID_PLATFORM=android-21 \
-DBUILD_FCOLLADA=OFF \
-DBUILD_OPENSSL=OFF \
-DBUILD_VRPN=OFF \
-DBUILD_ARTOOLKIT=OFF
cmake --build build --config Release -j4
rm -rf build
- name: Save thirdparty build cache
id: cache-android-thirdparty-save
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ./thirdparty
key: ${{ steps.cache-android-thirdparty-restore.outputs.cache-primary-key }}
- name: Build for Android
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --target android-21 --python-incdir=~/python-prefix/include --python-libdir=~/python-prefix/lib --arch ${{ matrix.android-arch }} --everything --no-openssl --no-tinydisplay --no-pandatool --verbose --threads=4
- name: Install test dependencies
shell: bash
run: |
python -m pip install -t ~/python-prefix/lib/python3.13/site-packages -r requirements-test.txt
- name: Enable KVM group perms
if: startsWith(matrix.runner, 'ubuntu')
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Setup Android Emulator and Run Tests
if: startsWith(matrix.android-abi, 'x86')
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 27
arch: ${{ matrix.android-abi }}
target: default
profile: pixel_6
script: |
adb shell "rm -rf /data/local/tmp/panda3d-tests"
adb push built/bin/run_tests /data/local/tmp/panda3d-tests/run_tests
adb shell "mkdir /data/local/tmp/panda3d-tests/lib"
adb push built/lib/*.so /data/local/tmp/panda3d-tests/lib
adb push ~/python-prefix/lib/*.so /data/local/tmp/panda3d-tests/lib
adb push ~/python-prefix/lib/python3.13 /data/local/tmp/panda3d-tests/lib/python3.13
adb push built/panda3d /data/local/tmp/panda3d-tests/lib/python3.13/site-packages/
adb push built/direct /data/local/tmp/panda3d-tests/lib/python3.13/site-packages/
adb push built/models /data/local/tmp/panda3d-tests/models
adb push built/etc /data/local/tmp/panda3d-tests/etc
adb push tests /data/local/tmp/panda3d-tests/tests
adb shell "cd /data/local/tmp/panda3d-tests && LD_LIBRARY_PATH=/data/local/tmp/panda3d-tests/lib PYTHONPATH=/data/local/tmp/panda3d-tests/lib/python3.13/site-packages ./run_tests -v tests"

View File

@ -7,17 +7,17 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.8', '3.11']
python-version: ['3.9', '3.13']
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy==1.4.0
pip install mypy==1.14.1
- name: Run mypy on direct
run: python tests/run_mypy.py

View File

@ -5,10 +5,12 @@ This is a list of all the people who are contributing financially to Panda3D. I
## Bronze Sponsors
[<img src="https://www.panda3d.org/wp-content/uploads/2024/08/Route4MeLogo1185x300-2-1-1024x259.png" alt="Route4Me" height="48">](https://route4me.com/)
[<img src="https://www.panda3d.org/wp-content/uploads/2026/01/black-logo-1-e1768931551108.png" alt="TestMu AI" height="48">](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
* [Daniel Stokes](https://opencollective.com/daniel-stokes)
* [David Rose](https://opencollective.com/david-rose)
* [Route4Me](https://route4me.com/)
* [TestMu AI](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
## Benefactors

View File

@ -109,6 +109,7 @@ option(BUILD_DIRECT "Build the direct source tree." ON)
option(BUILD_PANDATOOL "Build the pandatool source tree." ON)
option(BUILD_CONTRIB "Build the contrib source tree." ON)
option(BUILD_MODELS "Build/install the built-in models." ON)
option(BUILD_TESTS "Build unit test runner." ON)
option(BUILD_TOOLS "Build binary tools." ON)
# Include Panda3D packages
@ -166,17 +167,8 @@ if(BUILD_MODELS)
COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d)
endif()
if(INTERROGATE_PYTHON_INTERFACE)
# If we built the Python interface, run the test suite. Note, we do NOT test
# for pytest before adding this test. If the user doesn't have pytest, we'd
# like for the tests to fail.
# In the Coverage configuration, we also require pytest-cov
add_test(NAME pytest
COMMAND "${PYTHON_EXECUTABLE}" -m pytest "${PROJECT_SOURCE_DIR}/tests"
$<$<CONFIG:Coverage>:--cov=.>
WORKING_DIRECTORY "${PANDA_OUTPUT_DIR}")
if(BUILD_TESTS)
add_subdirectory(tests "${CMAKE_BINARY_DIR}/tests")
endif()
# Generate the Panda3DConfig.cmake file so find_package(Panda3D) works, and

View File

@ -24,7 +24,7 @@ Installing Panda3D
==================
The latest Panda3D SDK can be downloaded from
[this page](https://www.panda3d.org/download/sdk-1-10-15/).
[this page](https://www.panda3d.org/download/sdk-1-10-16/).
If you are familiar with installing Python packages, you can use
the following command:
@ -64,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
[click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
building them from source.
- https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win64.zip
- https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win32.zip
- https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win64.zip
- https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win32.zip
After acquiring these dependencies, you can build Panda3D from the command
prompt using the following command. Change the `--msvc-version` option based
@ -136,7 +136,7 @@ macOS
-----
On macOS, you will need to download a set of precompiled thirdparty packages in order to
compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-mac.tar.gz).
compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-mac.tar.gz).
After placing the thirdparty directory inside the panda3d source directory,
you may build Panda3D using a command like the following:
@ -181,22 +181,21 @@ Although it's possible to build Panda3D on an Android device using the
[termux](https://termux.com/) shell, the recommended route is to cross-compile
.whl files using the SDK and NDK, which can then be used by the `build_apps`
command to build a Python application into an .apk or .aab bundle. You will
need to get the latest thirdparty packages, which can be obtained from the
artifacts page of the last successful run here:
need to get the latest thirdparty packages, which can be obtained from here:
https://github.com/rdb/panda3d-thirdparty/actions?query=branch%3Amain+is%3Asuccess+event%3Apush
https://rdb.name/thirdparty-android.tar.gz
This does not include Python at the moment, which can be extracted from
[this archive](https://rdb.name/thirdparty-android.tar.gz) instead.
This includes a copy of Python 3.13 compiled for Android. You will need to
use Python 3.13 on the host as well.
These commands show how to compile wheels for the supported Android ABIs:
```bash
export ANDROID_SDK_ROOT=/home/rdb/local/android
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-19 --threads 6 --wheel
python3.13 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel
python3.13 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch arm --target android-21 --threads 6 --wheel
python3.13 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel
python3.13 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-21 --threads 6 --wheel
```
It is now possible to use the generated wheels with `build_apps`, as explained
@ -229,8 +228,8 @@ information as possible to help the developers track down the issue, such as
your version of Panda3D, operating system, architecture, and any code and
models that are necessary for the developers to reproduce the issue.
If you're not sure whether you've encountered a bug, feel free to ask about
it in the forums or the IRC channel first.
If you're unsure whether you've encountered a bug, feel free to ask in the [forums](https://discourse.panda3d.org) or the [IRC channel](https://web.libera.chat/#panda3d) before opening an issue.
Supporting the Project
======================
@ -239,9 +238,12 @@ If you would like to support the project financially, visit
[our campaign on OpenCollective](https://opencollective.com/panda3d). Your
contributions help us accelerate the development of Panda3D.
For the list of backers, see the [BACKERS.md](BACKERS.md) file or visit the
[Sponsors page](https://www.panda3d.org/sponsors) on our web site. Thank you
to everyone who has donated!
For the complete list of backers, see the [BACKERS.md](BACKERS.md) file or
visit the [Sponsors page](https://www.panda3d.org/sponsors) on our web site.
Thank you to everyone who has donated!
[<img src="https://www.panda3d.org/wp-content/uploads/2024/08/Route4MeLogo1185x300-2-1-1024x259.png" alt="Route4Me" height="48">](https://route4me.com/)
[<img src="https://www.panda3d.org/wp-content/uploads/2026/01/black-logo-1-e1768931551108.png" alt="TestMu AI" height="48">](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
<a href="https://opencollective.com/panda3d" target="_blank">
<img src="https://opencollective.com/panda3d/contribute/button@2x.png?color=blue" width=300 />

View File

@ -283,7 +283,7 @@ endfunction(interrogate_sources)
#
# Function: add_python_module(module [lib1 [lib2 ...]] [LINK lib1 ...]
# [IMPORT mod1 ...])
# [IMPORT mod1 ...] [INIT func1 ...])
# Uses interrogate to create a Python module. If the LINK keyword is specified,
# the Python module is linked against the specified libraries instead of those
# listed before. The IMPORT keyword makes the output module import another
@ -305,7 +305,7 @@ function(add_python_module module)
set(keyword)
foreach(arg ${ARGN})
if(arg STREQUAL "LINK" OR arg STREQUAL "IMPORT" OR arg STREQUAL "COMPONENT")
if(arg STREQUAL "LINK" OR arg STREQUAL "IMPORT" OR arg STREQUAL "INIT" OR arg STREQUAL "COMPONENT")
set(keyword "${arg}")
elseif(keyword STREQUAL "LINK")
@ -316,6 +316,10 @@ function(add_python_module module)
list(APPEND import_flags "-import" "${arg}")
set(keyword)
elseif(keyword STREQUAL "INIT")
list(APPEND import_flags "-init" "${arg}")
set(keyword)
elseif(keyword STREQUAL "COMPONENT")
set(component "${arg}")
set(keyword)

View File

@ -1,6 +1,7 @@
"""PANDA3D Particle Panel"""
# Import Tkinter, Pmw, and the floater code from this directory tree.
from panda3d.core import ConfigVariableSearchPath, Point2, Point3, Vec3, Vec4
from direct.tkwidgets.AppShell import AppShell
import os, Pmw
from direct.tkwidgets.Dial import AngleDial
@ -17,6 +18,10 @@ from tkinter.filedialog import *
from tkinter.simpledialog import askstring
particlePath = ConfigVariableSearchPath("particle-path",
"The directories to search for particle files to be loaded.")
class ParticlePanel(AppShell):
# Override class variables
appname = 'Particle Panel'
@ -1041,7 +1046,7 @@ class ParticlePanel(AppShell):
def loadParticleEffectFromFile(self):
# Find path to particle directory
pPath = getParticlePath()
pPath = particlePath
if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.':
path = '.'
@ -1070,7 +1075,7 @@ class ParticlePanel(AppShell):
def saveParticleEffectToFile(self):
# Find path to particle directory
pPath = getParticlePath()
pPath = particlePath
if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.':
path = '.'

View File

@ -113,7 +113,7 @@ receive_update(PyObject *distobj, DatagramIterator &di) const {
int field_id = packer.raw_unpack_uint16();
DCField *field = _this->get_field_by_index(field_id);
if (field == nullptr) {
ostringstream strm;
std::ostringstream strm;
strm
<< "Received update for field " << field_id << ", not in class "
<< _this->get_name();

View File

@ -42,25 +42,25 @@ class DirectNotify:
"""
return list(self.__categories.keys())
def getCategory(self, categoryName: str) -> Notifier.Notifier | None:
def getCategory(self, name: str) -> Notifier.Notifier | None:
"""getCategory(self, string)
Return the category with given name if present, None otherwise
"""
return self.__categories.get(categoryName, None)
return self.__categories.get(name, None)
def newCategory(self, categoryName: str, logger: Logger.Logger | None = None) -> Notifier.Notifier:
def newCategory(self, name: str, logger: Logger.Logger | None = None) -> Notifier.Notifier:
"""newCategory(self, string)
Make a new notify category named categoryName. Return new category
Make a new notify category named name. Return new category
if no such category exists, else return existing category
"""
if categoryName not in self.__categories:
self.__categories[categoryName] = Notifier.Notifier(categoryName, logger)
self.setDconfigLevel(categoryName)
notifier = self.getCategory(categoryName)
if name not in self.__categories:
self.__categories[name] = Notifier.Notifier(name, logger)
self.setDconfigLevel(name)
notifier = self.getCategory(name)
assert notifier is not None
return notifier
def setDconfigLevel(self, categoryName: str) -> None:
def setDconfigLevel(self, name: str) -> None:
"""
Check to see if this category has a dconfig variable
to set the notify severity and then set that level. You cannot
@ -71,7 +71,7 @@ class DirectNotify:
# we're running before ShowBase has finished initializing
from panda3d.core import ConfigVariableString
dconfigParam = ("notify-level-" + categoryName)
dconfigParam = ("notify-level-" + name)
cvar = ConfigVariableString(dconfigParam, "")
level = cvar.getValue()
@ -82,11 +82,11 @@ class DirectNotify:
if not level:
level = 'error'
category = self.getCategory(categoryName)
assert category is not None, f'failed to find category: {categoryName!r}'
category = self.getCategory(name)
assert category is not None, f'failed to find category: {name!r}'
# Note - this print statement is making it difficult to
# achieve "no output unless there's an error" operation - Josh
# print ("Setting DirectNotify category: " + categoryName +
# print ("Setting DirectNotify category: " + name +
# " to severity: " + level)
if level == "error":
category.setWarning(False)
@ -106,7 +106,7 @@ class DirectNotify:
category.setDebug(True)
else:
print("DirectNotify: unknown notify level: " + str(level)
+ " for category: " + str(categoryName))
+ " for category: " + str(name))
def setDconfigLevels(self) -> None:
for categoryName in self.getCategories():
@ -129,3 +129,12 @@ class DirectNotify:
def giveNotify(self, cls) -> None:
cls.notify = self.newCategory(cls.__name__)
get_categories = getCategories
get_category = getCategory
new_category = newCategory
set_dconfig_level = setDconfigLevel
set_dconfig_levels = setDconfigLevels
set_verbose = setVerbose
popup_controls = popupControls
give_notify = giveNotify

View File

@ -79,8 +79,8 @@ class LineNodePath(NodePath):
def getVertex(self, index):
return self.lineSegs.getVertex(index)
def getVertexColor(self):
return self.lineSegs.getVertexColor()
def getVertexColor(self, index):
return self.lineSegs.getVertexColor(index)
def drawArrow(self, sv, ev, arrowAngle, arrowLength):
"""

View File

@ -33,7 +33,7 @@ class DirectGrid(NodePath, DirectObject):
self.centerLines = LineNodePath(self.lines)
self.centerLines.lineNode.setName('centerLines')
self.centerLines.setColor(VBase4(1, 0, 0, 0))
self.centerLines.setColor(VBase4(1, 0, 0, 1))
self.centerLines.setThickness(3)
# Small marker to hilight snap-to-grid point

View File

@ -1,4 +1,4 @@
from panda3d.core import VBase4
from panda3d.core import NodePath, VBase4
from direct.task.Task import Task
from direct.task.TaskManagerGlobal import taskMgr
@ -55,7 +55,7 @@ def lerpBackgroundColor(r, g, b, duration):
# Set direct drawing style for an object
# Never light object or draw in wireframe
def useDirectRenderStyle(nodePath, priority = 0):
def useDirectRenderStyle(nodePath: NodePath, priority: int = 0) -> None:
"""
Function to force a node path to use direct render style:
no lighting, and no wireframe

View File

@ -88,6 +88,7 @@ defaultHiddenImports = {
'numpy.core._dtype_ctypes',
'numpy.core._methods',
],
'panda3d.core': ['enum'],
'pandas.compat': ['lzma', 'cmath'],
'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
'plyer': ['plyer.platforms'],
@ -101,6 +102,7 @@ defaultHiddenImports = {
'scipy.stats._stats': ['scipy.special.cython_special'],
'setuptools.monkey': ['setuptools.msvc'],
'shapely._geometry_helpers': ['shapely._geos'],
'jnius': ['jnius_config'],
}
@ -113,7 +115,7 @@ ignoreImports = {
'toml.encoder': ['numpy'],
'py._builtin': ['__builtin__'],
'site': ['android_log'],
'site': ['android_support'],
}
if sys.version_info >= (3, 8):
@ -138,12 +140,23 @@ def getline(filename, lineno, module_globals=None):
return ''
def clearcache():
global cache
cache = {}
cache.clear()
def getlines(filename, module_globals=None):
return []
def _getline_from_code(filename, lineno):
return ''
def _make_key(code):
return (code.co_filename, code.co_qualname, code.co_firstlineno)
def _getlines_from_code(code):
return []
def _source_unavailable(filename):
return True
def checkcache(filename=None):
pass
@ -1236,8 +1249,12 @@ class Freezer:
pass
# Check if any new modules we found have "hidden" imports
for origName in list(self.mf.modules.keys()):
checkHiddenImports = set(self.mf.modules.keys())
while checkHiddenImports:
origName = next(iter(checkHiddenImports))
checkHiddenImports.discard(origName)
hidden = self.hiddenImports.get(origName, [])
preModules = frozenset(self.mf.modules.keys())
for modname in hidden:
if modname.endswith('.*'):
mdefs = self._gatherSubmodules(modname, implicit = True)
@ -1251,6 +1268,9 @@ class Freezer:
self.__loadModule(self.ModuleDef(modname, implicit = True))
except ImportError:
pass
addedModules = set(self.mf.modules.keys())
addedModules -= preModules
checkHiddenImports |= addedModules
# Special case for sysconfig, which depends on a platform-specific
# sysconfigdata module on POSIX systems.
@ -1834,7 +1854,8 @@ class Freezer:
return target
def generateRuntimeFromStub(self, target, stub_file, use_console, fields={},
log_append=False, log_filename_strftime=False):
log_append=False, log_filename_strftime=False,
blob_path=None):
self.__replacePaths()
# We must have a __main__ module to make an exe file.
@ -1969,6 +1990,9 @@ class Freezer:
elif self.platform.endswith('_aarch64') or self.platform.endswith('_arm64'):
# Most arm64 operating systems are configured with 16 KiB pages.
blob_align = 16384
elif self.platform.replace('-', '_') == 'android_x86_64':
# Android nowadays requires 16 KiB pages on 64-bit Intel as well.
blob_align = 16384
else:
# Align to page size, so that it can be mmapped.
blob_align = 4096
@ -1979,29 +2003,33 @@ class Freezer:
pad = (blob_align - (blob_size & (blob_align - 1)))
blob_size += pad
# TODO: Support creating custom sections in universal binaries.
append_blob = True
if self.platform.startswith('macosx') and len(bitnesses) == 1:
# If our deploy-stub has a __PANDA segment, we know we're meant to
# put our blob there rather than attach it to the end.
load_commands = self._parse_macho_load_commands(stub_data)
if b'__PANDA' in load_commands.keys():
append_blob = False
if self.platform.startswith("macosx") and not append_blob:
# Take this time to shift any Mach-O structures around to fit our
# blob. We don't need to worry about aligning the offset since the
# compiler already took care of that when creating the segment.
blob_offset = self._shift_macho_structures(stub_data, load_commands, blob_size)
if blob_path is not None:
# We'll be writing the blob to a separate location.
blob_offset = 0
else:
# Add padding before the blob if necessary.
blob_offset = len(stub_data)
if (blob_offset & (blob_align - 1)) != 0:
pad = (blob_align - (blob_offset & (blob_align - 1)))
stub_data += (b'\0' * pad)
blob_offset += pad
assert (blob_offset % blob_align) == 0
assert blob_offset == len(stub_data)
# TODO: Support creating custom sections in universal binaries.
append_blob = True
if self.platform.startswith('macosx') and len(bitnesses) == 1:
# If our deploy-stub has a __PANDA segment, we know we're meant to
# put our blob there rather than attach it to the end.
load_commands = self._parse_macho_load_commands(stub_data)
if b'__PANDA' in load_commands.keys():
append_blob = False
if self.platform.startswith("macosx") and not append_blob:
# Take this time to shift any Mach-O structures around to fit our
# blob. We don't need to worry about aligning the offset since the
# compiler already took care of that when creating the segment.
blob_offset = self._shift_macho_structures(stub_data, load_commands, blob_size)
else:
# Add padding before the blob if necessary.
blob_offset = len(stub_data)
if (blob_offset & (blob_align - 1)) != 0:
pad = (blob_align - (blob_offset & (blob_align - 1)))
stub_data += (b'\0' * pad)
blob_offset += pad
assert (blob_offset % blob_align) == 0
assert blob_offset == len(stub_data)
# Calculate the offsets for the variables. These are pointers,
# relative to the beginning of the blob.
@ -2087,7 +2115,9 @@ class Freezer:
blob += struct.pack('<Q', blob_offset)
with open(target, 'wb') as f:
if append_blob:
if blob_path is not None:
f.write(stub_data)
elif append_blob:
f.write(stub_data)
assert f.tell() == blob_offset
f.write(blob)
@ -2095,6 +2125,10 @@ class Freezer:
stub_data[blob_offset:blob_offset + blob_size] = blob
f.write(stub_data)
if blob_path is not None:
with open(blob_path, 'wb') as f:
f.write(blob)
os.chmod(target, 0o755)
return target

View File

@ -3,7 +3,7 @@
import xml.etree.ElementTree as ET
from ._proto.targeting_pb2 import Abi
from ._proto.config_pb2 import BundleConfig # pylint: disable=unused-import
from ._proto.config_pb2 import BundleConfig, UncompressNativeLibraries # pylint: disable=unused-import
from ._proto.files_pb2 import NativeLibraries # pylint: disable=unused-import
from ._proto.Resources_pb2 import ResourceTable # pylint: disable=unused-import
from ._proto.Resources_pb2 import XmlNode
@ -174,6 +174,7 @@ ANDROID_ATTRIBUTES = {
'debuggable': bool_resource(0x0101000f),
'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"),
'enabled': bool_resource(0x101000e),
'enableOnBackInvokedCallback': bool_resource(0x0101066c),
'excludeFromRecents': bool_resource(0x1010017),
'exported': bool_resource(0x1010010),
'extractNativeLibs': bool_resource(0x10104ea),
@ -195,8 +196,10 @@ ANDROID_ATTRIBUTES = {
'multiprocess': bool_resource(0x1010013),
'name': str_resource(0x1010003),
'noHistory': bool_resource(0x101022d),
'pageSizeCompat': bool_resource(0x010106ab),
'pathPattern': str_resource(0x101002c),
'preferMinimalPostProcessing': bool_resource(0x101060c),
'resource': ref_resource(0x01010025),
'required': bool_resource(0x101028e),
'resizeableActivity': bool_resource(0x10104f6),
'scheme': str_resource(0x1010027),
@ -220,6 +223,7 @@ class AndroidManifest:
self.root = XmlNode()
self.resource_types = []
self.resources = {}
self.extract_native_libs = None
def parse_xml(self, data):
parser = ET.XMLParser(target=self)
@ -240,6 +244,15 @@ class AndroidManifest:
element = node.element
element.name = tag
if tag == 'application':
value = attribs.get('{http://schemas.android.com/apk/res/android}extractNativeLibs')
if value == 'false':
self.extract_native_libs = False
elif value == 'true':
self.extract_native_libs = True
else:
print(f'Warning: invalid value for android:extractNativeLibs: {value}')
self._stack.append(element)
for key, value in attribs.items():

View File

@ -1,11 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: frameworks/base/tools/aapt2/Configuration.proto
# Protobuf Python Version: 6.33.0
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
33,
0,
'',
'frameworks/base/tools/aapt2/Configuration.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@ -13,754 +24,44 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='frameworks/base/tools/aapt2/Configuration.proto',
package='aapt.pb',
syntax='proto3',
serialized_options=b'\n\020com.android.aapt',
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n/frameworks/base/tools/aapt2/Configuration.proto\x12\x07\x61\x61pt.pb\"\xd9\x14\n\rConfiguration\x12\x0b\n\x03mcc\x18\x01 \x01(\r\x12\x0b\n\x03mnc\x18\x02 \x01(\r\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12@\n\x10layout_direction\x18\x04 \x01(\x0e\x32&.aapt.pb.Configuration.LayoutDirection\x12\x14\n\x0cscreen_width\x18\x05 \x01(\r\x12\x15\n\rscreen_height\x18\x06 \x01(\r\x12\x17\n\x0fscreen_width_dp\x18\x07 \x01(\r\x12\x18\n\x10screen_height_dp\x18\x08 \x01(\r\x12 \n\x18smallest_screen_width_dp\x18\t \x01(\r\x12\x43\n\x12screen_layout_size\x18\n \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutSize\x12\x43\n\x12screen_layout_long\x18\x0b \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutLong\x12\x38\n\x0cscreen_round\x18\x0c \x01(\x0e\x32\".aapt.pb.Configuration.ScreenRound\x12?\n\x10wide_color_gamut\x18\r \x01(\x0e\x32%.aapt.pb.Configuration.WideColorGamut\x12\'\n\x03hdr\x18\x0e \x01(\x0e\x32\x1a.aapt.pb.Configuration.Hdr\x12\x37\n\x0borientation\x18\x0f \x01(\x0e\x32\".aapt.pb.Configuration.Orientation\x12\x37\n\x0cui_mode_type\x18\x10 \x01(\x0e\x32!.aapt.pb.Configuration.UiModeType\x12\x39\n\rui_mode_night\x18\x11 \x01(\x0e\x32\".aapt.pb.Configuration.UiModeNight\x12\x0f\n\x07\x64\x65nsity\x18\x12 \x01(\r\x12\x37\n\x0btouchscreen\x18\x13 \x01(\x0e\x32\".aapt.pb.Configuration.Touchscreen\x12\x36\n\x0bkeys_hidden\x18\x14 \x01(\x0e\x32!.aapt.pb.Configuration.KeysHidden\x12\x31\n\x08keyboard\x18\x15 \x01(\x0e\x32\x1f.aapt.pb.Configuration.Keyboard\x12\x34\n\nnav_hidden\x18\x16 \x01(\x0e\x32 .aapt.pb.Configuration.NavHidden\x12\x35\n\nnavigation\x18\x17 \x01(\x0e\x32!.aapt.pb.Configuration.Navigation\x12\x13\n\x0bsdk_version\x18\x18 \x01(\r\x12\x0f\n\x07product\x18\x19 \x01(\t\"a\n\x0fLayoutDirection\x12\x1a\n\x16LAYOUT_DIRECTION_UNSET\x10\x00\x12\x18\n\x14LAYOUT_DIRECTION_LTR\x10\x01\x12\x18\n\x14LAYOUT_DIRECTION_RTL\x10\x02\"\xaa\x01\n\x10ScreenLayoutSize\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_UNSET\x10\x00\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_SMALL\x10\x01\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_NORMAL\x10\x02\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_LARGE\x10\x03\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_XLARGE\x10\x04\"m\n\x10ScreenLayoutLong\x12\x1c\n\x18SCREEN_LAYOUT_LONG_UNSET\x10\x00\x12\x1b\n\x17SCREEN_LAYOUT_LONG_LONG\x10\x01\x12\x1e\n\x1aSCREEN_LAYOUT_LONG_NOTLONG\x10\x02\"X\n\x0bScreenRound\x12\x16\n\x12SCREEN_ROUND_UNSET\x10\x00\x12\x16\n\x12SCREEN_ROUND_ROUND\x10\x01\x12\x19\n\x15SCREEN_ROUND_NOTROUND\x10\x02\"h\n\x0eWideColorGamut\x12\x1a\n\x16WIDE_COLOR_GAMUT_UNSET\x10\x00\x12\x1b\n\x17WIDE_COLOR_GAMUT_WIDECG\x10\x01\x12\x1d\n\x19WIDE_COLOR_GAMUT_NOWIDECG\x10\x02\"3\n\x03Hdr\x12\r\n\tHDR_UNSET\x10\x00\x12\x0e\n\nHDR_HIGHDR\x10\x01\x12\r\n\tHDR_LOWDR\x10\x02\"h\n\x0bOrientation\x12\x15\n\x11ORIENTATION_UNSET\x10\x00\x12\x14\n\x10ORIENTATION_PORT\x10\x01\x12\x14\n\x10ORIENTATION_LAND\x10\x02\x12\x16\n\x12ORIENTATION_SQUARE\x10\x03\"\xd7\x01\n\nUiModeType\x12\x16\n\x12UI_MODE_TYPE_UNSET\x10\x00\x12\x17\n\x13UI_MODE_TYPE_NORMAL\x10\x01\x12\x15\n\x11UI_MODE_TYPE_DESK\x10\x02\x12\x14\n\x10UI_MODE_TYPE_CAR\x10\x03\x12\x1b\n\x17UI_MODE_TYPE_TELEVISION\x10\x04\x12\x1a\n\x16UI_MODE_TYPE_APPLIANCE\x10\x05\x12\x16\n\x12UI_MODE_TYPE_WATCH\x10\x06\x12\x1a\n\x16UI_MODE_TYPE_VRHEADSET\x10\x07\"[\n\x0bUiModeNight\x12\x17\n\x13UI_MODE_NIGHT_UNSET\x10\x00\x12\x17\n\x13UI_MODE_NIGHT_NIGHT\x10\x01\x12\x1a\n\x16UI_MODE_NIGHT_NOTNIGHT\x10\x02\"m\n\x0bTouchscreen\x12\x15\n\x11TOUCHSCREEN_UNSET\x10\x00\x12\x17\n\x13TOUCHSCREEN_NOTOUCH\x10\x01\x12\x16\n\x12TOUCHSCREEN_STYLUS\x10\x02\x12\x16\n\x12TOUCHSCREEN_FINGER\x10\x03\"v\n\nKeysHidden\x12\x15\n\x11KEYS_HIDDEN_UNSET\x10\x00\x12\x1b\n\x17KEYS_HIDDEN_KEYSEXPOSED\x10\x01\x12\x1a\n\x16KEYS_HIDDEN_KEYSHIDDEN\x10\x02\x12\x18\n\x14KEYS_HIDDEN_KEYSSOFT\x10\x03\"`\n\x08Keyboard\x12\x12\n\x0eKEYBOARD_UNSET\x10\x00\x12\x13\n\x0fKEYBOARD_NOKEYS\x10\x01\x12\x13\n\x0fKEYBOARD_QWERTY\x10\x02\x12\x16\n\x12KEYBOARD_TWELVEKEY\x10\x03\"V\n\tNavHidden\x12\x14\n\x10NAV_HIDDEN_UNSET\x10\x00\x12\x19\n\x15NAV_HIDDEN_NAVEXPOSED\x10\x01\x12\x18\n\x14NAV_HIDDEN_NAVHIDDEN\x10\x02\"}\n\nNavigation\x12\x14\n\x10NAVIGATION_UNSET\x10\x00\x12\x14\n\x10NAVIGATION_NONAV\x10\x01\x12\x13\n\x0fNAVIGATION_DPAD\x10\x02\x12\x18\n\x14NAVIGATION_TRACKBALL\x10\x03\x12\x14\n\x10NAVIGATION_WHEEL\x10\x04\x42\x12\n\x10\x63om.android.aaptb\x06proto3'
)
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/frameworks/base/tools/aapt2/Configuration.proto\x12\x07\x61\x61pt.pb\"\x97\x16\n\rConfiguration\x12\x0b\n\x03mcc\x18\x01 \x01(\r\x12\x0b\n\x03mnc\x18\x02 \x01(\r\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12@\n\x10layout_direction\x18\x04 \x01(\x0e\x32&.aapt.pb.Configuration.LayoutDirection\x12\x14\n\x0cscreen_width\x18\x05 \x01(\r\x12\x15\n\rscreen_height\x18\x06 \x01(\r\x12\x17\n\x0fscreen_width_dp\x18\x07 \x01(\r\x12\x18\n\x10screen_height_dp\x18\x08 \x01(\r\x12 \n\x18smallest_screen_width_dp\x18\t \x01(\r\x12\x43\n\x12screen_layout_size\x18\n \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutSize\x12\x43\n\x12screen_layout_long\x18\x0b \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutLong\x12\x38\n\x0cscreen_round\x18\x0c \x01(\x0e\x32\".aapt.pb.Configuration.ScreenRound\x12?\n\x10wide_color_gamut\x18\r \x01(\x0e\x32%.aapt.pb.Configuration.WideColorGamut\x12\'\n\x03hdr\x18\x0e \x01(\x0e\x32\x1a.aapt.pb.Configuration.Hdr\x12\x37\n\x0borientation\x18\x0f \x01(\x0e\x32\".aapt.pb.Configuration.Orientation\x12\x37\n\x0cui_mode_type\x18\x10 \x01(\x0e\x32!.aapt.pb.Configuration.UiModeType\x12\x39\n\rui_mode_night\x18\x11 \x01(\x0e\x32\".aapt.pb.Configuration.UiModeNight\x12\x0f\n\x07\x64\x65nsity\x18\x12 \x01(\r\x12\x37\n\x0btouchscreen\x18\x13 \x01(\x0e\x32\".aapt.pb.Configuration.Touchscreen\x12\x36\n\x0bkeys_hidden\x18\x14 \x01(\x0e\x32!.aapt.pb.Configuration.KeysHidden\x12\x31\n\x08keyboard\x18\x15 \x01(\x0e\x32\x1f.aapt.pb.Configuration.Keyboard\x12\x34\n\nnav_hidden\x18\x16 \x01(\x0e\x32 .aapt.pb.Configuration.NavHidden\x12\x35\n\nnavigation\x18\x17 \x01(\x0e\x32!.aapt.pb.Configuration.Navigation\x12\x13\n\x0bsdk_version\x18\x18 \x01(\r\x12\x44\n\x12grammatical_gender\x18\x1a \x01(\x0e\x32(.aapt.pb.Configuration.GrammaticalGender\x12\x0f\n\x07product\x18\x19 \x01(\t\"a\n\x0fLayoutDirection\x12\x1a\n\x16LAYOUT_DIRECTION_UNSET\x10\x00\x12\x18\n\x14LAYOUT_DIRECTION_LTR\x10\x01\x12\x18\n\x14LAYOUT_DIRECTION_RTL\x10\x02\"\xaa\x01\n\x10ScreenLayoutSize\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_UNSET\x10\x00\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_SMALL\x10\x01\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_NORMAL\x10\x02\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_LARGE\x10\x03\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_XLARGE\x10\x04\"m\n\x10ScreenLayoutLong\x12\x1c\n\x18SCREEN_LAYOUT_LONG_UNSET\x10\x00\x12\x1b\n\x17SCREEN_LAYOUT_LONG_LONG\x10\x01\x12\x1e\n\x1aSCREEN_LAYOUT_LONG_NOTLONG\x10\x02\"X\n\x0bScreenRound\x12\x16\n\x12SCREEN_ROUND_UNSET\x10\x00\x12\x16\n\x12SCREEN_ROUND_ROUND\x10\x01\x12\x19\n\x15SCREEN_ROUND_NOTROUND\x10\x02\"h\n\x0eWideColorGamut\x12\x1a\n\x16WIDE_COLOR_GAMUT_UNSET\x10\x00\x12\x1b\n\x17WIDE_COLOR_GAMUT_WIDECG\x10\x01\x12\x1d\n\x19WIDE_COLOR_GAMUT_NOWIDECG\x10\x02\"3\n\x03Hdr\x12\r\n\tHDR_UNSET\x10\x00\x12\x0e\n\nHDR_HIGHDR\x10\x01\x12\r\n\tHDR_LOWDR\x10\x02\"h\n\x0bOrientation\x12\x15\n\x11ORIENTATION_UNSET\x10\x00\x12\x14\n\x10ORIENTATION_PORT\x10\x01\x12\x14\n\x10ORIENTATION_LAND\x10\x02\x12\x16\n\x12ORIENTATION_SQUARE\x10\x03\"\xd7\x01\n\nUiModeType\x12\x16\n\x12UI_MODE_TYPE_UNSET\x10\x00\x12\x17\n\x13UI_MODE_TYPE_NORMAL\x10\x01\x12\x15\n\x11UI_MODE_TYPE_DESK\x10\x02\x12\x14\n\x10UI_MODE_TYPE_CAR\x10\x03\x12\x1b\n\x17UI_MODE_TYPE_TELEVISION\x10\x04\x12\x1a\n\x16UI_MODE_TYPE_APPLIANCE\x10\x05\x12\x16\n\x12UI_MODE_TYPE_WATCH\x10\x06\x12\x1a\n\x16UI_MODE_TYPE_VRHEADSET\x10\x07\"[\n\x0bUiModeNight\x12\x17\n\x13UI_MODE_NIGHT_UNSET\x10\x00\x12\x17\n\x13UI_MODE_NIGHT_NIGHT\x10\x01\x12\x1a\n\x16UI_MODE_NIGHT_NOTNIGHT\x10\x02\"m\n\x0bTouchscreen\x12\x15\n\x11TOUCHSCREEN_UNSET\x10\x00\x12\x17\n\x13TOUCHSCREEN_NOTOUCH\x10\x01\x12\x16\n\x12TOUCHSCREEN_STYLUS\x10\x02\x12\x16\n\x12TOUCHSCREEN_FINGER\x10\x03\"v\n\nKeysHidden\x12\x15\n\x11KEYS_HIDDEN_UNSET\x10\x00\x12\x1b\n\x17KEYS_HIDDEN_KEYSEXPOSED\x10\x01\x12\x1a\n\x16KEYS_HIDDEN_KEYSHIDDEN\x10\x02\x12\x18\n\x14KEYS_HIDDEN_KEYSSOFT\x10\x03\"`\n\x08Keyboard\x12\x12\n\x0eKEYBOARD_UNSET\x10\x00\x12\x13\n\x0fKEYBOARD_NOKEYS\x10\x01\x12\x13\n\x0fKEYBOARD_QWERTY\x10\x02\x12\x16\n\x12KEYBOARD_TWELVEKEY\x10\x03\"V\n\tNavHidden\x12\x14\n\x10NAV_HIDDEN_UNSET\x10\x00\x12\x19\n\x15NAV_HIDDEN_NAVEXPOSED\x10\x01\x12\x18\n\x14NAV_HIDDEN_NAVHIDDEN\x10\x02\"}\n\nNavigation\x12\x14\n\x10NAVIGATION_UNSET\x10\x00\x12\x14\n\x10NAVIGATION_NONAV\x10\x01\x12\x13\n\x0fNAVIGATION_DPAD\x10\x02\x12\x18\n\x14NAVIGATION_TRACKBALL\x10\x03\x12\x14\n\x10NAVIGATION_WHEEL\x10\x04\"v\n\x11GrammaticalGender\x12\x14\n\x10GRAM_GENDER_USET\x10\x00\x12\x16\n\x12GRAM_GENDER_NEUTER\x10\x01\x12\x18\n\x14GRAM_GENDER_FEMININE\x10\x02\x12\x19\n\x15GRAM_GENDER_MASCULINE\x10\x03\x42\x12\n\x10\x63om.android.aaptb\x06proto3')
_CONFIGURATION_LAYOUTDIRECTION = _descriptor.EnumDescriptor(
name='LayoutDirection',
full_name='aapt.pb.Configuration.LayoutDirection',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='LAYOUT_DIRECTION_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='LAYOUT_DIRECTION_LTR', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='LAYOUT_DIRECTION_RTL', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1119,
serialized_end=1216,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_LAYOUTDIRECTION)
_CONFIGURATION_SCREENLAYOUTSIZE = _descriptor.EnumDescriptor(
name='ScreenLayoutSize',
full_name='aapt.pb.Configuration.ScreenLayoutSize',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_SMALL', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_NORMAL', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_LARGE', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_XLARGE', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1219,
serialized_end=1389,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENLAYOUTSIZE)
_CONFIGURATION_SCREENLAYOUTLONG = _descriptor.EnumDescriptor(
name='ScreenLayoutLong',
full_name='aapt.pb.Configuration.ScreenLayoutLong',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_LONG', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_NOTLONG', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1391,
serialized_end=1500,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENLAYOUTLONG)
_CONFIGURATION_SCREENROUND = _descriptor.EnumDescriptor(
name='ScreenRound',
full_name='aapt.pb.Configuration.ScreenRound',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_ROUND', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_NOTROUND', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1502,
serialized_end=1590,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENROUND)
_CONFIGURATION_WIDECOLORGAMUT = _descriptor.EnumDescriptor(
name='WideColorGamut',
full_name='aapt.pb.Configuration.WideColorGamut',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_WIDECG', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_NOWIDECG', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1592,
serialized_end=1696,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_WIDECOLORGAMUT)
_CONFIGURATION_HDR = _descriptor.EnumDescriptor(
name='Hdr',
full_name='aapt.pb.Configuration.Hdr',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='HDR_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='HDR_HIGHDR', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='HDR_LOWDR', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1698,
serialized_end=1749,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_HDR)
_CONFIGURATION_ORIENTATION = _descriptor.EnumDescriptor(
name='Orientation',
full_name='aapt.pb.Configuration.Orientation',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='ORIENTATION_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_PORT', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_LAND', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_SQUARE', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1751,
serialized_end=1855,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_ORIENTATION)
_CONFIGURATION_UIMODETYPE = _descriptor.EnumDescriptor(
name='UiModeType',
full_name='aapt.pb.Configuration.UiModeType',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_NORMAL', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_DESK', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_CAR', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_TELEVISION', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_APPLIANCE', index=5, number=5,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_WATCH', index=6, number=6,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_VRHEADSET', index=7, number=7,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1858,
serialized_end=2073,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_UIMODETYPE)
_CONFIGURATION_UIMODENIGHT = _descriptor.EnumDescriptor(
name='UiModeNight',
full_name='aapt.pb.Configuration.UiModeNight',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_NIGHT', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_NOTNIGHT', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2075,
serialized_end=2166,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_UIMODENIGHT)
_CONFIGURATION_TOUCHSCREEN = _descriptor.EnumDescriptor(
name='Touchscreen',
full_name='aapt.pb.Configuration.Touchscreen',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_NOTOUCH', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_STYLUS', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_FINGER', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2168,
serialized_end=2277,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_TOUCHSCREEN)
_CONFIGURATION_KEYSHIDDEN = _descriptor.EnumDescriptor(
name='KeysHidden',
full_name='aapt.pb.Configuration.KeysHidden',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSEXPOSED', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSHIDDEN', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSSOFT', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2279,
serialized_end=2397,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_KEYSHIDDEN)
_CONFIGURATION_KEYBOARD = _descriptor.EnumDescriptor(
name='Keyboard',
full_name='aapt.pb.Configuration.Keyboard',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='KEYBOARD_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_NOKEYS', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_QWERTY', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_TWELVEKEY', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2399,
serialized_end=2495,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_KEYBOARD)
_CONFIGURATION_NAVHIDDEN = _descriptor.EnumDescriptor(
name='NavHidden',
full_name='aapt.pb.Configuration.NavHidden',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_NAVEXPOSED', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_NAVHIDDEN', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2497,
serialized_end=2583,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_NAVHIDDEN)
_CONFIGURATION_NAVIGATION = _descriptor.EnumDescriptor(
name='Navigation',
full_name='aapt.pb.Configuration.Navigation',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='NAVIGATION_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_NONAV', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_DPAD', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_TRACKBALL', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_WHEEL', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2585,
serialized_end=2710,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_NAVIGATION)
_CONFIGURATION = _descriptor.Descriptor(
name='Configuration',
full_name='aapt.pb.Configuration',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='mcc', full_name='aapt.pb.Configuration.mcc', index=0,
number=1, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='mnc', full_name='aapt.pb.Configuration.mnc', index=1,
number=2, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='locale', full_name='aapt.pb.Configuration.locale', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='layout_direction', full_name='aapt.pb.Configuration.layout_direction', index=3,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_width', full_name='aapt.pb.Configuration.screen_width', index=4,
number=5, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_height', full_name='aapt.pb.Configuration.screen_height', index=5,
number=6, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_width_dp', full_name='aapt.pb.Configuration.screen_width_dp', index=6,
number=7, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_height_dp', full_name='aapt.pb.Configuration.screen_height_dp', index=7,
number=8, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='smallest_screen_width_dp', full_name='aapt.pb.Configuration.smallest_screen_width_dp', index=8,
number=9, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_layout_size', full_name='aapt.pb.Configuration.screen_layout_size', index=9,
number=10, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_layout_long', full_name='aapt.pb.Configuration.screen_layout_long', index=10,
number=11, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_round', full_name='aapt.pb.Configuration.screen_round', index=11,
number=12, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='wide_color_gamut', full_name='aapt.pb.Configuration.wide_color_gamut', index=12,
number=13, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='hdr', full_name='aapt.pb.Configuration.hdr', index=13,
number=14, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='orientation', full_name='aapt.pb.Configuration.orientation', index=14,
number=15, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='ui_mode_type', full_name='aapt.pb.Configuration.ui_mode_type', index=15,
number=16, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='ui_mode_night', full_name='aapt.pb.Configuration.ui_mode_night', index=16,
number=17, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='density', full_name='aapt.pb.Configuration.density', index=17,
number=18, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='touchscreen', full_name='aapt.pb.Configuration.touchscreen', index=18,
number=19, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='keys_hidden', full_name='aapt.pb.Configuration.keys_hidden', index=19,
number=20, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='keyboard', full_name='aapt.pb.Configuration.keyboard', index=20,
number=21, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='nav_hidden', full_name='aapt.pb.Configuration.nav_hidden', index=21,
number=22, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='navigation', full_name='aapt.pb.Configuration.navigation', index=22,
number=23, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='sdk_version', full_name='aapt.pb.Configuration.sdk_version', index=23,
number=24, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='product', full_name='aapt.pb.Configuration.product', index=24,
number=25, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
_CONFIGURATION_LAYOUTDIRECTION,
_CONFIGURATION_SCREENLAYOUTSIZE,
_CONFIGURATION_SCREENLAYOUTLONG,
_CONFIGURATION_SCREENROUND,
_CONFIGURATION_WIDECOLORGAMUT,
_CONFIGURATION_HDR,
_CONFIGURATION_ORIENTATION,
_CONFIGURATION_UIMODETYPE,
_CONFIGURATION_UIMODENIGHT,
_CONFIGURATION_TOUCHSCREEN,
_CONFIGURATION_KEYSHIDDEN,
_CONFIGURATION_KEYBOARD,
_CONFIGURATION_NAVHIDDEN,
_CONFIGURATION_NAVIGATION,
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=61,
serialized_end=2710,
)
_CONFIGURATION.fields_by_name['layout_direction'].enum_type = _CONFIGURATION_LAYOUTDIRECTION
_CONFIGURATION.fields_by_name['screen_layout_size'].enum_type = _CONFIGURATION_SCREENLAYOUTSIZE
_CONFIGURATION.fields_by_name['screen_layout_long'].enum_type = _CONFIGURATION_SCREENLAYOUTLONG
_CONFIGURATION.fields_by_name['screen_round'].enum_type = _CONFIGURATION_SCREENROUND
_CONFIGURATION.fields_by_name['wide_color_gamut'].enum_type = _CONFIGURATION_WIDECOLORGAMUT
_CONFIGURATION.fields_by_name['hdr'].enum_type = _CONFIGURATION_HDR
_CONFIGURATION.fields_by_name['orientation'].enum_type = _CONFIGURATION_ORIENTATION
_CONFIGURATION.fields_by_name['ui_mode_type'].enum_type = _CONFIGURATION_UIMODETYPE
_CONFIGURATION.fields_by_name['ui_mode_night'].enum_type = _CONFIGURATION_UIMODENIGHT
_CONFIGURATION.fields_by_name['touchscreen'].enum_type = _CONFIGURATION_TOUCHSCREEN
_CONFIGURATION.fields_by_name['keys_hidden'].enum_type = _CONFIGURATION_KEYSHIDDEN
_CONFIGURATION.fields_by_name['keyboard'].enum_type = _CONFIGURATION_KEYBOARD
_CONFIGURATION.fields_by_name['nav_hidden'].enum_type = _CONFIGURATION_NAVHIDDEN
_CONFIGURATION.fields_by_name['navigation'].enum_type = _CONFIGURATION_NAVIGATION
_CONFIGURATION_LAYOUTDIRECTION.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENLAYOUTSIZE.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENLAYOUTLONG.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENROUND.containing_type = _CONFIGURATION
_CONFIGURATION_WIDECOLORGAMUT.containing_type = _CONFIGURATION
_CONFIGURATION_HDR.containing_type = _CONFIGURATION
_CONFIGURATION_ORIENTATION.containing_type = _CONFIGURATION
_CONFIGURATION_UIMODETYPE.containing_type = _CONFIGURATION
_CONFIGURATION_UIMODENIGHT.containing_type = _CONFIGURATION
_CONFIGURATION_TOUCHSCREEN.containing_type = _CONFIGURATION
_CONFIGURATION_KEYSHIDDEN.containing_type = _CONFIGURATION
_CONFIGURATION_KEYBOARD.containing_type = _CONFIGURATION
_CONFIGURATION_NAVHIDDEN.containing_type = _CONFIGURATION
_CONFIGURATION_NAVIGATION.containing_type = _CONFIGURATION
DESCRIPTOR.message_types_by_name['Configuration'] = _CONFIGURATION
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Configuration = _reflection.GeneratedProtocolMessageType('Configuration', (_message.Message,), {
'DESCRIPTOR' : _CONFIGURATION,
'__module__' : 'frameworks.base.tools.aapt2.Configuration_pb2'
# @@protoc_insertion_point(class_scope:aapt.pb.Configuration)
})
_sym_db.RegisterMessage(Configuration)
DESCRIPTOR._options = None
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'frameworks.base.tools.aapt2.Configuration_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
_globals['DESCRIPTOR']._loaded_options = None
_globals['DESCRIPTOR']._serialized_options = b'\n\020com.android.aapt'
_globals['_CONFIGURATION']._serialized_start=61
_globals['_CONFIGURATION']._serialized_end=2900
_globals['_CONFIGURATION_LAYOUTDIRECTION']._serialized_start=1189
_globals['_CONFIGURATION_LAYOUTDIRECTION']._serialized_end=1286
_globals['_CONFIGURATION_SCREENLAYOUTSIZE']._serialized_start=1289
_globals['_CONFIGURATION_SCREENLAYOUTSIZE']._serialized_end=1459
_globals['_CONFIGURATION_SCREENLAYOUTLONG']._serialized_start=1461
_globals['_CONFIGURATION_SCREENLAYOUTLONG']._serialized_end=1570
_globals['_CONFIGURATION_SCREENROUND']._serialized_start=1572
_globals['_CONFIGURATION_SCREENROUND']._serialized_end=1660
_globals['_CONFIGURATION_WIDECOLORGAMUT']._serialized_start=1662
_globals['_CONFIGURATION_WIDECOLORGAMUT']._serialized_end=1766
_globals['_CONFIGURATION_HDR']._serialized_start=1768
_globals['_CONFIGURATION_HDR']._serialized_end=1819
_globals['_CONFIGURATION_ORIENTATION']._serialized_start=1821
_globals['_CONFIGURATION_ORIENTATION']._serialized_end=1925
_globals['_CONFIGURATION_UIMODETYPE']._serialized_start=1928
_globals['_CONFIGURATION_UIMODETYPE']._serialized_end=2143
_globals['_CONFIGURATION_UIMODENIGHT']._serialized_start=2145
_globals['_CONFIGURATION_UIMODENIGHT']._serialized_end=2236
_globals['_CONFIGURATION_TOUCHSCREEN']._serialized_start=2238
_globals['_CONFIGURATION_TOUCHSCREEN']._serialized_end=2347
_globals['_CONFIGURATION_KEYSHIDDEN']._serialized_start=2349
_globals['_CONFIGURATION_KEYSHIDDEN']._serialized_end=2467
_globals['_CONFIGURATION_KEYBOARD']._serialized_start=2469
_globals['_CONFIGURATION_KEYBOARD']._serialized_end=2565
_globals['_CONFIGURATION_NAVHIDDEN']._serialized_start=2567
_globals['_CONFIGURATION_NAVHIDDEN']._serialized_end=2653
_globals['_CONFIGURATION_NAVIGATION']._serialized_start=2655
_globals['_CONFIGURATION_NAVIGATION']._serialized_end=2780
_globals['_CONFIGURATION_GRAMMATICALGENDER']._serialized_start=2782
_globals['_CONFIGURATION_GRAMMATICALGENDER']._serialized_end=2900
# @@protoc_insertion_point(module_scope)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: files.proto
# Protobuf Python Version: 6.33.0
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
33,
0,
'',
'files.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
@ -14,294 +25,24 @@ _sym_db = _symbol_database.Default()
from . import targeting_pb2 as targeting__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='files.proto',
package='android.bundle',
syntax='proto3',
serialized_options=b'\n\022com.android.bundle',
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x0b\x66iles.proto\x12\x0e\x61ndroid.bundle\x1a\x0ftargeting.proto\"D\n\x06\x41ssets\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedAssetsDirectory\"M\n\x0fNativeLibraries\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedNativeDirectory\"D\n\nApexImages\x12\x30\n\x05image\x18\x01 \x03(\x0b\x32!.android.bundle.TargetedApexImageJ\x04\x08\x02\x10\x03\"d\n\x17TargetedAssetsDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.AssetsDirectoryTargeting\"d\n\x17TargetedNativeDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.NativeDirectoryTargeting\"q\n\x11TargetedApexImage\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x17\n\x0f\x62uild_info_path\x18\x03 \x01(\t\x12\x35\n\ttargeting\x18\x02 \x01(\x0b\x32\".android.bundle.ApexImageTargetingB\x14\n\x12\x63om.android.bundleb\x06proto3'
,
dependencies=[targeting__pb2.DESCRIPTOR,])
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x66iles.proto\x12\x0e\x61ndroid.bundle\x1a\x0ftargeting.proto\"D\n\x06\x41ssets\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedAssetsDirectory\"M\n\x0fNativeLibraries\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedNativeDirectory\"D\n\nApexImages\x12\x30\n\x05image\x18\x01 \x03(\x0b\x32!.android.bundle.TargetedApexImageJ\x04\x08\x02\x10\x03\"d\n\x17TargetedAssetsDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.AssetsDirectoryTargeting\"d\n\x17TargetedNativeDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.NativeDirectoryTargeting\"q\n\x11TargetedApexImage\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x17\n\x0f\x62uild_info_path\x18\x03 \x01(\t\x12\x35\n\ttargeting\x18\x02 \x01(\x0b\x32\".android.bundle.ApexImageTargetingB\x14\n\x12\x63om.android.bundleb\x06proto3')
_ASSETS = _descriptor.Descriptor(
name='Assets',
full_name='android.bundle.Assets',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='directory', full_name='android.bundle.Assets.directory', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=48,
serialized_end=116,
)
_NATIVELIBRARIES = _descriptor.Descriptor(
name='NativeLibraries',
full_name='android.bundle.NativeLibraries',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='directory', full_name='android.bundle.NativeLibraries.directory', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=118,
serialized_end=195,
)
_APEXIMAGES = _descriptor.Descriptor(
name='ApexImages',
full_name='android.bundle.ApexImages',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='image', full_name='android.bundle.ApexImages.image', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=197,
serialized_end=265,
)
_TARGETEDASSETSDIRECTORY = _descriptor.Descriptor(
name='TargetedAssetsDirectory',
full_name='android.bundle.TargetedAssetsDirectory',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedAssetsDirectory.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedAssetsDirectory.targeting', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=267,
serialized_end=367,
)
_TARGETEDNATIVEDIRECTORY = _descriptor.Descriptor(
name='TargetedNativeDirectory',
full_name='android.bundle.TargetedNativeDirectory',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedNativeDirectory.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedNativeDirectory.targeting', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=369,
serialized_end=469,
)
_TARGETEDAPEXIMAGE = _descriptor.Descriptor(
name='TargetedApexImage',
full_name='android.bundle.TargetedApexImage',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedApexImage.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='build_info_path', full_name='android.bundle.TargetedApexImage.build_info_path', index=1,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedApexImage.targeting', index=2,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=471,
serialized_end=584,
)
_ASSETS.fields_by_name['directory'].message_type = _TARGETEDASSETSDIRECTORY
_NATIVELIBRARIES.fields_by_name['directory'].message_type = _TARGETEDNATIVEDIRECTORY
_APEXIMAGES.fields_by_name['image'].message_type = _TARGETEDAPEXIMAGE
_TARGETEDASSETSDIRECTORY.fields_by_name['targeting'].message_type = targeting__pb2._ASSETSDIRECTORYTARGETING
_TARGETEDNATIVEDIRECTORY.fields_by_name['targeting'].message_type = targeting__pb2._NATIVEDIRECTORYTARGETING
_TARGETEDAPEXIMAGE.fields_by_name['targeting'].message_type = targeting__pb2._APEXIMAGETARGETING
DESCRIPTOR.message_types_by_name['Assets'] = _ASSETS
DESCRIPTOR.message_types_by_name['NativeLibraries'] = _NATIVELIBRARIES
DESCRIPTOR.message_types_by_name['ApexImages'] = _APEXIMAGES
DESCRIPTOR.message_types_by_name['TargetedAssetsDirectory'] = _TARGETEDASSETSDIRECTORY
DESCRIPTOR.message_types_by_name['TargetedNativeDirectory'] = _TARGETEDNATIVEDIRECTORY
DESCRIPTOR.message_types_by_name['TargetedApexImage'] = _TARGETEDAPEXIMAGE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Assets = _reflection.GeneratedProtocolMessageType('Assets', (_message.Message,), {
'DESCRIPTOR' : _ASSETS,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.Assets)
})
_sym_db.RegisterMessage(Assets)
NativeLibraries = _reflection.GeneratedProtocolMessageType('NativeLibraries', (_message.Message,), {
'DESCRIPTOR' : _NATIVELIBRARIES,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.NativeLibraries)
})
_sym_db.RegisterMessage(NativeLibraries)
ApexImages = _reflection.GeneratedProtocolMessageType('ApexImages', (_message.Message,), {
'DESCRIPTOR' : _APEXIMAGES,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.ApexImages)
})
_sym_db.RegisterMessage(ApexImages)
TargetedAssetsDirectory = _reflection.GeneratedProtocolMessageType('TargetedAssetsDirectory', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDASSETSDIRECTORY,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedAssetsDirectory)
})
_sym_db.RegisterMessage(TargetedAssetsDirectory)
TargetedNativeDirectory = _reflection.GeneratedProtocolMessageType('TargetedNativeDirectory', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDNATIVEDIRECTORY,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedNativeDirectory)
})
_sym_db.RegisterMessage(TargetedNativeDirectory)
TargetedApexImage = _reflection.GeneratedProtocolMessageType('TargetedApexImage', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDAPEXIMAGE,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedApexImage)
})
_sym_db.RegisterMessage(TargetedApexImage)
DESCRIPTOR._options = None
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'files_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
_globals['DESCRIPTOR']._loaded_options = None
_globals['DESCRIPTOR']._serialized_options = b'\n\022com.android.bundle'
_globals['_ASSETS']._serialized_start=48
_globals['_ASSETS']._serialized_end=116
_globals['_NATIVELIBRARIES']._serialized_start=118
_globals['_NATIVELIBRARIES']._serialized_end=195
_globals['_APEXIMAGES']._serialized_start=197
_globals['_APEXIMAGES']._serialized_end=265
_globals['_TARGETEDASSETSDIRECTORY']._serialized_start=267
_globals['_TARGETEDASSETSDIRECTORY']._serialized_end=367
_globals['_TARGETEDNATIVEDIRECTORY']._serialized_start=369
_globals['_TARGETEDNATIVEDIRECTORY']._serialized_end=469
_globals['_TARGETEDAPEXIMAGE']._serialized_start=471
_globals['_TARGETEDAPEXIMAGE']._serialized_end=584
# @@protoc_insertion_point(module_scope)

File diff suppressed because one or more lines are too long

View File

@ -79,6 +79,10 @@ def _model_to_bam(_build_cmd, srcpath, dstpath):
_register_python_loaders()
if src_fn.get_extension() == 'egg' and zipfile.is_zipfile(src_fn.to_os_specific()):
_build_cmd.warn('Skipping %s as it appears to be a Python .egg archive, was this included by mistake?' % (src_fn.to_os_specific()))
return
loader = p3d.Loader.get_global_ptr()
options = p3d.LoaderOptions(p3d.LoaderOptions.LF_report_errors |
p3d.LoaderOptions.LF_no_ram_cache)
@ -192,14 +196,14 @@ SITE_PY_ANDROID = """
# module.
import sys, os
from importlib import _bootstrap, _bootstrap_external
from android_support import log_write as android_log_write
from android_support import find_library
class AndroidExtensionFinder:
@classmethod
def find_spec(cls, fullname, path=None, target=None):
soname = 'libpy.' + fullname + '.so'
path = os.path.join(sys.platlibdir, soname)
if os.path.exists(path):
path = find_library('py.' + fullname)
if path:
loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
return _bootstrap.ModuleSpec(fullname, loader, origin=path)
@ -210,8 +214,6 @@ sys.meta_path.append(AndroidExtensionFinder)
from _frozen_importlib import _imp, FrozenImporter
from io import RawIOBase, TextIOWrapper
from android_log import write as android_log_write
sys.frozen = True
@ -300,7 +302,8 @@ class build_apps(setuptools.Command):
self.android_version_code = 1
self.android_min_sdk_version = 21
self.android_max_sdk_version = None
self.android_target_sdk_version = 30
self.android_target_sdk_version = 35
self.android_manifest_file = None
self.gui_apps = {}
self.console_apps = {}
self.macos_main_app = None
@ -316,7 +319,10 @@ class build_apps(setuptools.Command):
'win_amd64',
]
if sys.version_info >= (3, 13):
if sys.version_info >= (3, 14):
# This version of Python is only available for 10.15+.
self.platforms[1] = 'macosx_10_15_x86_64'
elif sys.version_info >= (3, 13):
# This version of Python is only available for 10.13+.
self.platforms[1] = 'macosx_10_13_x86_64'
@ -586,6 +592,10 @@ class build_apps(setuptools.Command):
data_dir = os.path.join(build_dir, 'assets')
os.makedirs(data_dir, exist_ok=True)
res_dir = os.path.join(build_dir, 'res')
res_raw_dir = os.path.join(res_dir, 'raw')
os.makedirs(res_raw_dir, exist_ok=True)
for abi in self.android_abis:
lib_dir = os.path.join(build_dir, 'lib', abi)
os.makedirs(lib_dir, exist_ok=True)
@ -602,7 +612,7 @@ class build_apps(setuptools.Command):
# We end up copying the data multiple times to the same
# directory, but that's probably fine for now.
self.build_binaries(platform + suffix, lib_dir, data_dir)
self.build_binaries(platform + suffix, lib_dir, data_dir, res_raw_dir)
# Write out the icons to the res directory.
for appname, icon in self.icon_objects.items():
@ -610,9 +620,9 @@ class build_apps(setuptools.Command):
# Conventional name for icon on Android.
basename = 'ic_launcher.png'
else:
basename = f'ic_{appname}.png'
appname_sane = appname.replace(' ', '_')
basename = f'ic_{appname_sane}.png'
res_dir = os.path.join(build_dir, 'res')
icon.writeSize(48, os.path.join(res_dir, 'mipmap-mdpi-v4', basename))
icon.writeSize(72, os.path.join(res_dir, 'mipmap-hdpi-v4', basename))
icon.writeSize(96, os.path.join(res_dir, 'mipmap-xhdpi-v4', basename))
@ -623,8 +633,17 @@ class build_apps(setuptools.Command):
self.build_assets(platform, data_dir)
# Generate an AndroidManifest.xml
self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml'))
# Generate an AndroidManifest.xml if none was provided
manifest_path = os.path.join(build_dir, 'AndroidManifest.xml')
if self.android_manifest_file:
try:
self.check_android_manifest(self.android_manifest_file)
except Exception as e:
self.announce(f"Failed to use provided manifest file from {self.android_manifest_file}", distutils.log.FATAL)
raise
self.copy(self.android_manifest_file, manifest_path)
else:
self.generate_android_manifest(manifest_path)
else:
self.build_binaries(platform, build_dir, build_dir)
self.build_assets(platform, build_dir)
@ -691,8 +710,14 @@ class build_apps(setuptools.Command):
subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
except:
# Display a more helpful message for these common issues.
if platform.startswith('macosx_10_9_') and sys.version_info >= (3, 13):
new_platform = platform.replace('macosx_10_9_', 'macosx_10_13_')
if platform.startswith('macosx_10_13_') and sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_13_', 'macosx_10_15_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.14.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('macosx_10_9_') and sys.version_info >= (3, 13):
if sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_9_', 'macosx_10_15_')
else:
new_platform = platform.replace('macosx_10_9_', 'macosx_10_13_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.13.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('manylinux2010_') and sys.version_info >= (3, 11):
new_platform = platform.replace('manylinux2010_', 'manylinux2014_')
@ -701,7 +726,9 @@ class build_apps(setuptools.Command):
new_platform = platform.replace('manylinux1_', 'manylinux2014_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.10.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('macosx_10_6_') and sys.version_info >= (3, 8):
if sys.version_info >= (3, 13):
if sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_6_', 'macosx_10_15_')
elif sys.version_info >= (3, 13):
new_platform = platform.replace('macosx_10_6_', 'macosx_10_13_')
else:
new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_')
@ -855,7 +882,7 @@ class build_apps(setuptools.Command):
if category:
application.set('android:appCategory', category)
application.set('android:debuggable', ('false', 'true')[self.android_debuggable])
application.set('android:extractNativeLibs', 'true')
application.set('android:extractNativeLibs', 'false')
application.set('android:hardwareAccelerated', 'true')
app_icon = self.icon_objects.get('*', self.icon_objects.get(self.macos_main_app))
@ -863,6 +890,8 @@ class build_apps(setuptools.Command):
application.set('android:icon', '@mipmap/ic_launcher')
for appname in self.gui_apps:
appname_sane = appname.replace(' ', '_')
activity = ET.SubElement(application, 'activity')
activity.set('android:name', 'org.panda3d.android.PythonActivity')
activity.set('android:label', appname)
@ -871,14 +900,19 @@ class build_apps(setuptools.Command):
activity.set('android:configChanges', 'layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation')
activity.set('android:launchMode', 'singleInstance')
activity.set('android:preferMinimalPostProcessing', 'true')
activity.set('android:exported', 'true')
act_icon = self.icon_objects.get(appname)
if act_icon and act_icon is not app_icon:
activity.set('android:icon', '@mipmap/ic_' + appname)
activity.set('android:icon', '@mipmap/ic_' + appname_sane)
meta_data = ET.SubElement(activity, 'meta-data')
meta_data.set('android:name', 'android.app.lib_name')
meta_data.set('android:value', appname)
meta_data.set('android:value', appname_sane)
meta_data = ET.SubElement(activity, 'meta-data')
meta_data.set('android:name', 'org.panda3d.android.BLOB_RESOURCE')
meta_data.set('android:resource', '@raw/' + appname_sane + '.so')
intent_filter = ET.SubElement(activity, 'intent-filter')
ET.SubElement(intent_filter, 'action').set('android:name', 'android.intent.action.MAIN')
@ -886,10 +920,44 @@ class build_apps(setuptools.Command):
ET.SubElement(intent_filter, 'category').set('android:name', 'android.intent.category.LEANBACK_LAUNCHER')
tree = ET.ElementTree(manifest)
if sys.version_info >= (3, 9):
ET.indent(tree)
with open(path, 'wb') as fh:
tree.write(fh, encoding='utf-8', xml_declaration=True)
def build_binaries(self, platform, binary_dir, data_dir=None):
def check_android_manifest(self, path):
""" Checks that the user-provided manifest file seems OK. """
# This function doesn't aim to check everything as it's the user's
# responsibility, just a basic sanity check, but if we change anything
# in our own generation logic then it would be good to check those
# things here and warn if anything needs to be updated.
import xml.etree.ElementTree as ET
android = '{http://schemas.android.com/apk/res/android}'
tree = ET.parse(path)
root = tree.getroot()
if root.tag != 'manifest':
raise RuntimeError(f"Expected <manifest> in {path}")
if root.attrib['package'] != self.application_id:
raise RuntimeError(f"<manifest> package attribute does not match given application_id {self.application_id}")
apps = root.findall('application')
if len(apps) != 1:
raise RuntimeError("<manifest> must contain exactly one <application>")
application = apps[0]
for activity in application.iter('activity'):
if f'{android}name' not in activity.attrib:
raise RuntimeError("<activity> element must have android:name attribute")
if self.android_target_sdk_version >= 31 and f'{android}exported' not in activity.attrib:
raise RuntimeError("<activity> element must have android:exported attribute when targeting Android API 31+")
def build_binaries(self, platform, binary_dir, data_dir=None, blob_dir=None):
""" Builds the binary data for the given platform. """
use_wheels = True
@ -1085,13 +1153,26 @@ class build_apps(setuptools.Command):
stub_name = 'deploy-stub'
target_name = appname
appname_sane = appname
if platform.startswith('win') or 'macosx' in platform:
if not use_console:
stub_name = 'deploy-stubw'
elif platform.startswith('android'):
if not use_console:
stub_name = 'libdeploy-stubw.so'
target_name = 'lib' + target_name + '.so'
appname_sane = appname.replace(' ', '_')
target_name = 'lib' + appname_sane + '.so'
if use_wheels:
dexfile = os.path.join(binary_dir, '..', '..', 'classes.dex')
self.copy(os.path.join(p3dwhlfn, 'deploy_libs', 'classes.dex'), dexfile)
# Can this wheel load the blob as a raw resource?
with open(dexfile, 'rb') as fh:
supports_blob_resource = b'org.panda3d.android.BLOB_RESOURCE' in fh.read()
assert supports_blob_resource, \
"Please use a newer Panda3D wheel to build for Android using this version of build_apps"
if platform.startswith('win'):
stub_name += '.exe'
@ -1123,6 +1204,14 @@ class build_apps(setuptools.Command):
if not self.log_filename or '%' not in self.log_filename:
use_strftime = False
blob_path = None
if blob_dir is not None:
if platform.startswith('android'):
# Not really a .so file, but it forces bundletool to align it
blob_path = os.path.join(blob_dir, appname_sane + '.so')
else:
blob_path = os.path.join(blob_dir, appname_sane)
target_path = os.path.join(binary_dir, target_name)
freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
'prc_data': prcexport if self.embed_prc_data else None,
@ -1136,7 +1225,7 @@ class build_apps(setuptools.Command):
'prc_executable_args_envvar': None,
'main_dir': None,
'log_filename': self.expand_path(self.log_filename, platform),
}, self.log_append, use_strftime)
}, self.log_append, use_strftime, blob_path)
stub_file.close()
if temp_file:
@ -1251,11 +1340,6 @@ class build_apps(setuptools.Command):
search_path = get_search_path_for(source_path)
self.copy_with_dependencies(source_path, target_path, search_path)
# Copy classes.dex on Android
if use_wheels and platform.startswith('android'):
self.copy(os.path.join(p3dwhlfn, 'deploy_libs', 'classes.dex'),
os.path.join(binary_dir, '..', '..', 'classes.dex'))
# Extract any other data files from dependency packages.
if data_dir is None:
return

View File

@ -207,7 +207,7 @@ def create_aab(command, basename, build_dir):
and use it to convert an .aab into an .apk.
"""
from ._android import AndroidManifest, AbiAlias, BundleConfig, NativeLibraries, ResourceTable
from ._android import AndroidManifest, AbiAlias, BundleConfig, NativeLibraries, ResourceTable, UncompressNativeLibraries
bundle_fn = p3d.Filename.from_os_specific(command.dist_dir) / (basename + '.aab')
build_dir_fn = p3d.Filename.from_os_specific(build_dir)
@ -230,7 +230,13 @@ def create_aab(command, basename, build_dir):
config = BundleConfig()
config.bundletool.version = '1.1.0'
config.optimizations.splits_config.Clear()
config.optimizations.uncompress_native_libraries.enabled = False
if axml.extract_native_libs:
config.optimizations.uncompress_native_libraries.enabled = False
else:
config.optimizations.uncompress_native_libraries.enabled = True
config.optimizations.uncompress_native_libraries.alignment = \
UncompressNativeLibraries.PageAlignment.PAGE_ALIGNMENT_16K
config.compression.uncompressed_glob.append('res/raw/**')
bundle.add_subfile('BundleConfig.pb', p3d.StringStream(config.SerializeToString()), 9)
resources = ResourceTable()
@ -251,13 +257,25 @@ def create_aab(command, basename, build_dir):
entry.entry_id.id = entry_id
entry.name = res_name
for density, tag in (160, 'mdpi'), (240, 'hdpi'), (320, 'xhdpi'), (480, 'xxhdpi'), (640, 'xxxhdpi'):
path = f'res/mipmap-{tag}-v4/{res_name}.png'
if (build_dir_fn / path).exists():
bundle.add_subfile('base/' + path, build_dir_fn / path, 0)
config_value = entry.config_value.add()
config_value.config.density = density
config_value.value.item.file.path = path
if type_name == 'raw':
path = f'res/raw/{res_name}'
if not (build_dir_fn / path).exists():
command.announce(
f'\tRaw resource {path} was not found on disk', distutils.log.ERROR)
return
# These are aligned to page size for mmap.
bundle.add_subfile('base/' + path, build_dir_fn / path, 0, 16384)
config_value = entry.config_value.add()
config_value.value.item.file.path = path
else:
for density, tag in (120, 'ldpi'), (160, 'mdpi'), (240, 'hdpi'), (320, 'xhdpi'), (480, 'xxhdpi'), (640, 'xxxhdpi'):
path = f'res/mipmap-{tag}-v4/{res_name}.png'
if (build_dir_fn / path).exists():
bundle.add_subfile('base/' + path, build_dir_fn / path, 0)
config_value = entry.config_value.add()
config_value.config.density = density
config_value.value.item.file.path = path
bundle.add_subfile('base/resources.pb', p3d.StringStream(resources.SerializeToString()), 9)
@ -273,13 +291,25 @@ def create_aab(command, basename, build_dir):
# Add the classes.dex.
bundle.add_subfile('base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
# Add libraries, compressed.
# Add libraries, compressed, unless extractNativeLibs is false, in which
# case they have to be aligned to page boundaries (16 KiB on 64-bit).
lib_compress = 9 if axml.extract_native_libs else 0
for abi in os.listdir(os.path.join(build_dir, 'lib')):
abi_dir = os.path.join(build_dir, 'lib', abi)
if axml.extract_native_libs:
lib_align = 0
elif '64' in abi:
lib_align = 16384
else:
lib_align = 4096
for lib in os.listdir(abi_dir):
if lib.startswith('lib') and lib.endswith('.so'):
bundle.add_subfile(f'base/lib/{abi}/{lib}', build_dir_fn / 'lib' / abi / lib, 9)
bundle.add_subfile(f'base/lib/{abi}/{lib}',
build_dir_fn / 'lib' / abi / lib,
lib_compress, lib_align)
# Add assets, compressed.
assets_dir = os.path.join(build_dir, 'assets')

View File

@ -48,7 +48,7 @@ class ClockDelta(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory('ClockDelta')
def __init__(self):
def __init__(self) -> None:
self.globalClock = ClockObject.getGlobalClock()
# self.delta is the relative delta from our clock to the

View File

@ -1,4 +1,4 @@
from panda3d.core import DocumentSpec, Filename, HTTPClient, VirtualFileSystem, getModelPath
from panda3d.core import DocumentSpec, Filename, VirtualFileSystem, getModelPath
from panda3d.direct import CConnectionRepository, DCPacker
from direct.task import Task
from direct.task.TaskManagerGlobal import taskMgr
@ -590,6 +590,7 @@ class ConnectionRepository(
if self.http is None:
try:
from panda3d.core import HTTPClient
self.http = HTTPClient()
except Exception:
pass

View File

@ -317,7 +317,7 @@ class DistributedObjectAI(DistributedObjectBase):
# setLocation destroys self._zoneData if we move away to
# a different zone
if self._zoneData is None:
from otp.ai.AIZoneData import AIZoneData # type: ignore[import]
from otp.ai.AIZoneData import AIZoneData # type: ignore[import-not-found]
self._zoneData = AIZoneData(self.air, self.parentId, self.zoneId)
return self._zoneData
@ -515,7 +515,7 @@ class DistributedObjectAI(DistributedObjectBase):
# simultaneously on different lists of avatars, although they
# should have different names.
from otp.ai import Barrier # type: ignore[import]
from otp.ai import Barrier # type: ignore[import-not-found]
context = self.__nextBarrierContext
# We assume the context number is passed as a uint16.
self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

View File

@ -432,7 +432,7 @@ class DistributedObjectUD(DistributedObjectBase):
# simultaneously on different lists of avatars, although they
# should have different names.
from otp.ai import Barrier # type: ignore[import]
from otp.ai import Barrier # type: ignore[import-not-found]
context = self.__nextBarrierContext
# We assume the context number is passed as a uint16.
self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

View File

@ -84,8 +84,8 @@ dclass DistributedSmoothNode: DistributedNode {
suggestResync(uint32 avId, int16 timestampA, int16 timestampB,
int32 serverTimeSec, uint16 serverTimeUSec,
uint16 / 100 uncertainty);
int16 / 100 uncertainty);
returnResync(uint32 avId, int16 timestampB,
int32 serverTimeSec, uint16 serverTimeUSec,
uint16 / 100 uncertainty);
int16 / 100 uncertainty);
};

View File

@ -1,15 +1,19 @@
from __future__ import annotations
__all__ = ["Dtool_ObjectToDict", "Dtool_funcToMethod"]
from collections.abc import Callable
def Dtool_ObjectToDict(cls, name, obj):
cls.DtoolClassDict[name] = obj
def Dtool_funcToMethod(func, cls, method_name=None):
def Dtool_funcToMethod(func: Callable, cls, method_name: str | None = None) -> None:
"""Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
The new method is accessible to any instance immediately."""
func.__func__ = func
func.__self__ = None
func.__func__ = func # type: ignore[attr-defined]
func.__self__ = None # type: ignore[attr-defined]
if not method_name:
method_name = func.__name__
cls.DtoolClassDict[method_name] = func

View File

@ -44,6 +44,7 @@ class DirectButton(DirectFrame):
# Sounds to be used for button events
('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound),
('clickSound', DGG.getDefaultClickSound(), self.setClickSound),
('releaseSound', DGG.getDefaultReleaseSound(), self.setReleaseSound),
# Can only be specified at time of widget contruction
# Do the text/graphics appear to move when the button is clicked
('pressEffect', 1, DGG.INITOPT),
@ -108,6 +109,13 @@ class DirectButton(DirectFrame):
# Pass any extra args to command
self['command'](*self['extraArgs'])
def setRolloverSound(self):
rolloverSound = self['rolloverSound']
if rolloverSound:
self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound)
else:
self.guiItem.clearSound(DGG.ENTER + self.guiId)
def setClickSound(self):
clickSound = self['clickSound']
# Clear out sounds
@ -122,9 +130,16 @@ class DirectButton(DirectFrame):
if DGG.RMB in self['commandButtons']:
self.guiItem.setSound(DGG.B3PRESS + self.guiId, clickSound)
def setRolloverSound(self):
rolloverSound = self['rolloverSound']
if rolloverSound:
self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound)
else:
self.guiItem.clearSound(DGG.ENTER + self.guiId)
def setReleaseSound(self):
releaseSound = self['releaseSound']
# Clear out sounds
self.guiItem.clearSound(DGG.B1RELEASE + self.guiId)
self.guiItem.clearSound(DGG.B2RELEASE + self.guiId)
self.guiItem.clearSound(DGG.B3RELEASE + self.guiId)
if releaseSound:
if DGG.LMB in self['commandButtons']:
self.guiItem.setSound(DGG.B1RELEASE + self.guiId, releaseSound)
if DGG.MMB in self['commandButtons']:
self.guiItem.setSound(DGG.B2RELEASE + self.guiId, releaseSound)
if DGG.RMB in self['commandButtons']:
self.guiItem.setSound(DGG.B3RELEASE + self.guiId, releaseSound)

View File

@ -28,6 +28,7 @@ class DirectCheckBox(DirectButton):
# Sounds to be used for button events
('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound),
('clickSound', DGG.getDefaultClickSound(), self.setClickSound),
('releaseSound', DGG.getDefaultReleaseSound(), self.setReleaseSound),
# Can only be specified at time of widget contruction
# Do the text/graphics appear to move when the button is clicked
('pressEffect', 1, DGG.INITOPT),

View File

@ -17,8 +17,9 @@ from panda3d.core import (
defaultFont = None
defaultFontFunc = TextNode.getDefaultFont
defaultClickSound = None
defaultRolloverSound = None
defaultClickSound = None
defaultReleaseSound = None
defaultDialogGeom = None
defaultDialogRelief = PGFrameStyle.TBevelOut
drawOrder = 100
@ -129,6 +130,13 @@ def setDefaultClickSound(newSound):
global defaultClickSound
defaultClickSound = newSound
def getDefaultReleaseSound():
return defaultReleaseSound
def setDefaultReleaseSound(newSound):
global defaultReleaseSound
defaultReleaseSound = newSound
def getDefaultFont():
global defaultFont
if defaultFont is None:

View File

@ -1,11 +1,14 @@
"""Defines the IntervalManager class as well as the global instance of
this class, ivalMgr."""
from __future__ import annotations
__all__ = ['IntervalManager', 'ivalMgr']
from panda3d.core import EventQueue
from panda3d.direct import CIntervalManager, Dtool_BorrowThisReference
from panda3d.direct import CInterval, CIntervalManager, Dtool_BorrowThisReference
from direct.showbase import EventManager
from . import Interval
import fnmatch
class IntervalManager(CIntervalManager):
@ -15,7 +18,7 @@ class IntervalManager(CIntervalManager):
# the Python extensions is to add support for Python-based
# intervals (like MetaIntervals).
def __init__(self, globalPtr = 0):
def __init__(self, globalPtr: bool = False) -> None:
# Pass globalPtr == 1 to the constructor to trick it into
# "constructing" a Python wrapper around the global
# CIntervalManager object.
@ -28,8 +31,8 @@ class IntervalManager(CIntervalManager):
self.eventQueue = EventQueue()
self.MyEventmanager = EventManager.EventManager(self.eventQueue)
self.setEventQueue(self.eventQueue)
self.ivals = []
self.removedIvals = {}
self.ivals: list[Interval.Interval | CInterval | None] = []
self.removedIvals: dict = {}
def addInterval(self, interval):
index = self.addCInterval(interval, 1)
@ -86,7 +89,7 @@ class IntervalManager(CIntervalManager):
ival.pause()
return len(ivals)
def step(self):
def step(self) -> None:
# This method should be called once per frame to perform all
# of the per-frame processing on the active intervals.
# Call C++ step, then do the Python stuff.
@ -101,7 +104,7 @@ class IntervalManager(CIntervalManager):
CIntervalManager.interrupt(self)
self.__doPythonCallbacks()
def __doPythonCallbacks(self):
def __doPythonCallbacks(self) -> None:
# This method does all of the required Python post-processing
# after performing some C++-level action.
# It is important to call all of the python callbacks on the
@ -137,4 +140,4 @@ class IntervalManager(CIntervalManager):
self.ivals[index] = interval
#: The global IntervalManager object.
ivalMgr = IntervalManager(1)
ivalMgr = IntervalManager(True)

View File

@ -122,7 +122,7 @@ class LerpPosInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
LerpNodePathInterval.privDoEvent(self, t, event)
@ -153,7 +153,7 @@ class LerpHprInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndHpr, self.endHpr)
self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat)
@ -192,7 +192,7 @@ class LerpQuatInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndQuat, self.endQuat)
self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat)
@ -219,7 +219,7 @@ class LerpScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndScale, self.endScale)
self.setupParam(self.setStartScale, self.startScale)
LerpNodePathInterval.privDoEvent(self, t, event)
@ -245,7 +245,7 @@ class LerpShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndShear, self.endShear)
self.setupParam(self.setStartShear, self.startShear)
LerpNodePathInterval.privDoEvent(self, t, event)
@ -280,7 +280,7 @@ class LerpPosHprInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr)
@ -326,7 +326,7 @@ class LerpPosQuatInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat)
@ -365,7 +365,7 @@ class LerpHprScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndHpr, self.endHpr)
self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat)
@ -414,7 +414,7 @@ class LerpQuatScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndQuat, self.endQuat)
self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat)
@ -459,7 +459,7 @@ class LerpPosHprScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr)
@ -516,7 +516,7 @@ class LerpPosQuatScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat)
@ -569,7 +569,7 @@ class LerpPosHprScaleShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr)
@ -636,7 +636,7 @@ class LerpPosQuatScaleShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in
# for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize:
if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat)

View File

@ -12,6 +12,8 @@ Or, you can enable the following variable in your Config.prc::
show-buffers true
"""
from __future__ import annotations
__all__ = ['BufferViewer']
from panda3d.core import (
@ -39,14 +41,19 @@ from direct.task.TaskManagerGlobal import taskMgr
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.DirectObject import DirectObject
import math
from typing import Literal, Union
# The following variables are typing constructs used in annotations
# to succinctly express complex type structures.
_Texture = Union[Texture, GraphicsOutput, Literal['all']]
class BufferViewer(DirectObject):
notify = directNotify.newCategory('BufferViewer')
def __init__(self, win, parent):
def __init__(self, win: GraphicsOutput | None, parent: NodePath) -> None:
"""Access: private. Constructor."""
self.enabled = 0
self.enabled = False
size = ConfigVariableDouble('buffer-viewer-size', '0 0')
self.sizex = size[0]
self.sizey = size[1]
@ -59,23 +66,23 @@ class BufferViewer(DirectObject):
self.win = win
self.engine = GraphicsEngine.getGlobalPtr()
self.renderParent = parent
self.cards = []
self.cards: list[NodePath] = []
self.cardindex = 0
self.cardmaker = CardMaker("cubemaker")
self.cardmaker.setFrame(-1,1,-1,1)
self.task = 0
self.dirty = 1
self.dirty = True
self.accept("render-texture-targets-changed", self.refreshReadout)
if ConfigVariableBool("show-buffers", 0):
self.enable(1)
self.enable(True)
def refreshReadout(self):
def refreshReadout(self) -> None:
"""Force the readout to be refreshed. This is usually invoked
by GraphicsOutput::add_render_texture (via an event handler).
However, it is also possible to invoke it manually. Currently,
the only time I know of that this is necessary is after a
window resize (and I ought to fix that)."""
self.dirty = 1
self.dirty = True
# Call enabled again, mainly to ensure that the task has been
# started.
@ -95,14 +102,14 @@ class BufferViewer(DirectObject):
"""Returns true if the buffer viewer is currently enabled."""
return self.enabled
def enable(self, x):
def enable(self, x: bool) -> None:
"""Turn the buffer viewer on or off. The initial state of the
buffer viewer depends on the Config variable 'show-buffers'."""
if x != 0 and x != 1:
BufferViewer.notify.error('invalid parameter to BufferViewer.enable')
return
self.enabled = x
self.dirty = 1
self.dirty = True
if (x and self.task == 0):
self.task = taskMgr.add(self.maintainReadout, "buffer-viewer-maintain-readout",
priority=1)
@ -218,7 +225,11 @@ class BufferViewer(DirectObject):
self.renderParent = renderParent
self.dirty = 1
def analyzeTextureSet(self, x, set):
def analyzeTextureSet(
self,
x: _Texture | GraphicsEngine | list[_Texture | GraphicsEngine],
set: dict[Texture, int],
) -> None:
"""Access: private. Converts a list of GraphicsObject,
GraphicsEngine, and Texture into a table of Textures."""
@ -240,7 +251,7 @@ class BufferViewer(DirectObject):
else:
return
def makeFrame(self, sizex, sizey):
def makeFrame(self, sizex: int, sizey: int) -> NodePath:
"""Access: private. Each texture card is displayed with
a two-pixel wide frame (a ring of black and a ring of white).
This routine builds the frame geometry. It is necessary to
@ -287,7 +298,7 @@ class BufferViewer(DirectObject):
geomnode.addGeom(geom)
return NodePath(geomnode)
def maintainReadout(self, task):
def maintainReadout(self, task: object) -> int:
"""Access: private. Whenever necessary, rebuilds the entire
display from scratch. This is only done when the configuration
parameters have changed."""
@ -295,7 +306,7 @@ class BufferViewer(DirectObject):
# If nothing has changed, don't update.
if not self.dirty:
return Task.cont
self.dirty = 0
self.dirty = False
# Delete the old set of cards.
for card in self.cards:
@ -308,8 +319,8 @@ class BufferViewer(DirectObject):
return Task.done
# Generate the include and exclude sets.
exclude = {}
include = {}
exclude: dict[Texture, int] = {}
include: dict[Texture, int] = {}
self.analyzeTextureSet(self.exclude, exclude)
self.analyzeTextureSet(self.include, include)
@ -407,6 +418,7 @@ class BufferViewer(DirectObject):
bordersize = 4.0
assert self.win is not None
if float(self.sizex) == 0.0 and float(self.sizey) == 0.0:
sizey = int(0.4266666667 * self.win.getYSize())
sizex = (sizey * aspectx) // aspecty

View File

@ -1,8 +1,14 @@
"""Defines the DirectObject class, a convenient class to inherit from if the
object needs to be able to respond to events."""
from __future__ import annotations
__all__ = ['DirectObject']
from typing import Callable
from panda3d.core import AsyncTask
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.task.TaskManagerGlobal import taskMgr
from .MessengerGlobal import messenger
@ -12,6 +18,9 @@ class DirectObject:
"""
This is the class that all Direct/SAL classes should inherit from
"""
_MSGRmessengerId: tuple[str, int]
_taskList: dict[int, AsyncTask]
#def __del__(self):
# This next line is useful for debugging leaks
#print "Destructing: ", self.__class__.__name__
@ -19,16 +28,16 @@ class DirectObject:
# Wrapper functions to have a cleaner, more object oriented approach to
# the messenger functionality.
def accept(self, event, method, extraArgs=[]):
return messenger.accept(event, self, method, extraArgs, 1)
def accept(self, event: str, method: Callable, extraArgs: list = []) -> None:
return messenger.accept(event, self, method, extraArgs, True)
def acceptOnce(self, event, method, extraArgs=[]):
return messenger.accept(event, self, method, extraArgs, 0)
def ignore(self, event):
def ignore(self, event: str) -> None:
return messenger.ignore(event, self)
def ignoreAll(self):
def ignoreAll(self) -> None:
return messenger.ignoreAll(self)
def isAccepting(self, event):
@ -41,7 +50,7 @@ class DirectObject:
return messenger.isIgnoring(event, self)
#This function must be used if you want a managed task
def addTask(self, *args, **kwargs):
def addTask(self, *args, **kwargs) -> AsyncTask:
if not hasattr(self, "_taskList"):
self._taskList = {}
kwargs['owner'] = self

View File

@ -1,21 +1,33 @@
"""Contains the EventManager class. See :mod:`.EventManagerGlobal` for the
global eventMgr instance."""
from __future__ import annotations
__all__ = ['EventManager']
from typing import Any
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.directnotify.Notifier import Notifier
from direct.task.TaskManagerGlobal import taskMgr
from direct.showbase.MessengerGlobal import messenger
from panda3d.core import PStatCollector, EventQueue, EventHandler
from panda3d.core import ConfigVariableBool
from panda3d.core import (
ConfigVariableBool,
Event,
EventHandler,
EventParameter,
EventQueue,
PStatCollector,
PythonTask,
)
class EventManager:
notify = None
notify: Notifier | None = None
def __init__(self, eventQueue = None):
def __init__(self, eventQueue: EventQueue | None = None) -> None:
"""
Create a C++ event queue and handler
"""
@ -24,11 +36,11 @@ class EventManager:
EventManager.notify = directNotify.newCategory("EventManager")
self.eventQueue = eventQueue
self.eventHandler = None
self.eventHandler: EventHandler | None = None
self._wantPstats = ConfigVariableBool('pstats-eventmanager', False)
def doEvents(self):
def doEvents(self) -> None:
"""
Process all the events on the C++ event queue
"""
@ -38,12 +50,13 @@ class EventManager:
processFunc = self.processEventPstats
else:
processFunc = self.processEvent
assert self.eventQueue is not None
isEmptyFunc = self.eventQueue.isQueueEmpty
dequeueFunc = self.eventQueue.dequeueEvent
while not isEmptyFunc():
processFunc(dequeueFunc())
def eventLoopTask(self, task):
def eventLoopTask(self, task: PythonTask) -> int:
"""
Process all the events on the C++ event queue
"""
@ -51,7 +64,7 @@ class EventManager:
messenger.send("event-loop-done")
return task.cont
def parseEventParameter(self, eventParameter):
def parseEventParameter(self, eventParameter: EventParameter) -> Any:
"""
Extract the actual data from the eventParameter
"""
@ -63,6 +76,8 @@ class EventManager:
return eventParameter.getStringValue()
elif eventParameter.isWstring():
return eventParameter.getWstringValue()
elif hasattr(eventParameter, 'isBytes') and eventParameter.isBytes():
return eventParameter.getBytesValue()
elif eventParameter.isTypedRefCount():
return eventParameter.getTypedRefCountValue()
elif eventParameter.isEmpty():
@ -72,7 +87,7 @@ class EventManager:
# which will be downcast to that type.
return eventParameter.getPtr()
def processEvent(self, event):
def processEvent(self, event: Event) -> None:
"""
Process a C++ event
Duplicate any changes in processEventPstats
@ -89,6 +104,7 @@ class EventManager:
paramList.append(eventParameterData)
# Do not print the new frame debug, it is too noisy!
assert EventManager.notify is not None
if EventManager.notify.getDebug() and eventName != 'NewFrame':
EventManager.notify.debug('received C++ event named: ' + eventName +
' parameters: ' + repr(paramList))
@ -106,9 +122,10 @@ class EventManager:
else:
# An unnamed event from C++ is probably a bad thing
assert EventManager.notify is not None
EventManager.notify.warning('unnamed event in processEvent')
def processEventPstats(self, event):
def processEventPstats(self, event: Event) -> None:
"""
Process a C++ event with pstats tracking
Duplicate any changes in processEvent
@ -125,6 +142,7 @@ class EventManager:
paramList.append(eventParameterData)
# Do not print the new frame debug, it is too noisy!
assert EventManager.notify is not None
if EventManager.notify.getDebug() and eventName != 'NewFrame':
EventManager.notify.debug('received C++ event named: ' + eventName +
' parameters: ' + repr(paramList))
@ -156,9 +174,10 @@ class EventManager:
else:
# An unnamed event from C++ is probably a bad thing
assert EventManager.notify is not None
EventManager.notify.warning('unnamed event in processEvent')
def restart(self):
def restart(self) -> None:
if self.eventQueue is None:
self.eventQueue = EventQueue.getGlobalEventQueue()
@ -173,7 +192,7 @@ class EventManager:
taskMgr.add(self.eventLoopTask, 'eventManager')
def shutdown(self):
def shutdown(self) -> None:
taskMgr.remove('eventManager')
# Flush the event queue. We do this after removing the task

View File

@ -35,7 +35,7 @@ def _varDump__init__(self, *args, **kArgs):
sReentry = 0
def _varDump__print(exc):
def _varDump__print(exc) -> None:
global sReentry
global notify
if sReentry > 0:
@ -176,7 +176,7 @@ def _excepthookDumpVars(eType, eValue, tb):
oldExcepthook(eType, eValue, origTb)
def install(log, upload):
def install(log: bool, upload: bool) -> None:
"""Installs the exception hook."""
global oldExcepthook
global wantStackDumpLog
@ -192,8 +192,8 @@ def install(log, upload):
# thrown by the interpreter don't get created until the
# stack has been unwound and an except block has been reached
if not hasattr(Exception, '_moved__init__'):
Exception._moved__init__ = Exception.__init__
Exception.__init__ = _varDump__init__
Exception._moved__init__ = Exception.__init__ # type: ignore[attr-defined]
Exception.__init__ = _varDump__init__ # type: ignore[method-assign]
else:
if sys.excepthook is not _excepthookDumpVars:
oldExcepthook = sys.excepthook

View File

@ -1,5 +1,7 @@
"""Contains utility classes for debugging memory leaks."""
from __future__ import annotations
__all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger']
from direct.directnotify.DirectNotifyGlobal import directNotify
@ -10,6 +12,7 @@ from direct.showbase.JobManagerGlobal import jobMgr
from direct.showbase.MessengerGlobal import messenger
from panda3d.core import ConfigVariableBool
import gc
from collections.abc import Callable
GarbageCycleCountAnnounceEvent = 'announceGarbageCycleDesc2num'
@ -41,9 +44,21 @@ class GarbageReport(Job):
If you just want to dump the report to the log, use GarbageLogger."""
notify = directNotify.newCategory("GarbageReport")
def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True,
threaded=False, doneCallback=None, autoDestroy=False, priority=None,
safeMode=False, delOnly=False, collect=True):
def __init__(
self,
name: str,
log: bool = True,
verbose: bool = False,
fullReport: bool = False,
findCycles: bool = True,
threaded: bool = False,
doneCallback: Callable[[GarbageReport], object] | None = None,
autoDestroy: bool = False,
priority: int | None = None,
safeMode: bool = False,
delOnly: bool = False,
collect: bool = True
) -> None:
# if autoDestroy is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy()
# if threaded is True, processing will be performed over multiple frames
@ -399,7 +414,7 @@ class GarbageReport(Job):
if self._args.autoDestroy:
self.destroy()
def destroy(self):
def destroy(self) -> None:
#print 'GarbageReport.destroy'
del self._args
del self.garbage
@ -417,13 +432,13 @@ class GarbageReport(Job):
del self._reportStr
Job.destroy(self)
def getNumCycles(self):
def getNumCycles(self) -> int:
# if the job hasn't run yet, we don't have a numCycles yet
return self.numCycles
def getDesc2numDict(self):
def getDesc2numDict(self) -> dict[str, int]:
# dict of python-syntax leak -> number of that type of leak
desc2num = {}
desc2num: dict[str, int] = {}
for cycleBySyntax in self.cyclesBySyntax:
desc2num.setdefault(cycleBySyntax, 0)
desc2num[cycleBySyntax] += 1
@ -563,7 +578,7 @@ class _CFGLGlobals:
LastNumCycles = 0
def checkForGarbageLeaks():
def checkForGarbageLeaks() -> int:
gc.collect()
numGarbage = len(gc.garbage)
if numGarbage > 0 and ConfigVariableBool('auto-garbage-logging', False):
@ -576,6 +591,7 @@ def checkForGarbageLeaks():
messenger.send(GarbageCycleCountAnnounceEvent, [gr.getDesc2numDict()])
gr.destroy()
notify = directNotify.newCategory("GarbageDetect")
func: Callable[[str], object]
if ConfigVariableBool('allow-garbage-cycles', True):
func = notify.warning
else:
@ -584,7 +600,8 @@ def checkForGarbageLeaks():
return numGarbage
def b_checkForGarbageLeaks(wantReply=False):
def b_checkForGarbageLeaks(wantReply: bool = False) -> int:
from direct.showbase.ShowBaseGlobal import base, __dev__
if not __dev__:
return 0
# does a garbage collect on the client and the AI
@ -592,10 +609,10 @@ def b_checkForGarbageLeaks(wantReply=False):
# logs leak info and terminates (if configured to do so)
try:
# if this is the client, tell the AI to check for leaks too
base.cr.timeManager
base.cr.timeManager # type: ignore[attr-defined]
except Exception:
pass
else:
if base.cr.timeManager:
base.cr.timeManager.d_checkForGarbageLeaks(wantReply=wantReply)
if base.cr.timeManager: # type: ignore[attr-defined]
base.cr.timeManager.d_checkForGarbageLeaks(wantReply=wantReply) # type: ignore[attr-defined]
return checkForGarbageLeaks()

View File

@ -1,3 +1,7 @@
from __future__ import annotations
from collections.abc import Iterator
from panda3d.core import ConfigVariableBool, ConfigVariableDouble, ClockObject
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.task.TaskManagerGlobal import taskMgr
@ -17,28 +21,28 @@ class JobManager:
# there's one task for the JobManager, all jobs run in this task
TaskName = 'jobManager'
def __init__(self, timeslice=None):
def __init__(self, timeslice: float | None = None) -> None:
# how long do we run per frame
self._timeslice = timeslice
# store the jobs in these structures to allow fast lookup by various keys
# priority -> jobId -> job
self._pri2jobId2job = {}
self._pri2jobId2job: dict[int, dict[int, Job]] = {}
# priority -> chronological list of jobIds
self._pri2jobIds = {}
self._pri2jobIds: dict[int, list[int]] = {}
# jobId -> priority
self._jobId2pri = {}
self._jobId2pri: dict[int, int] = {}
# how many timeslices to give each job; this is used to efficiently implement
# the relative job priorities
self._jobId2timeslices = {}
self._jobId2timeslices: dict[int, int] = {}
# how much time did the job use beyond the allotted timeslice, used to balance
# out CPU usage
self._jobId2overflowTime = {}
self._useOverflowTime = None
self._jobId2overflowTime: dict[int, float] = {}
self._useOverflowTime: bool | None = None
# this is a generator that we use to give high-priority jobs more timeslices,
# it yields jobIds in a sequence that includes high-priority jobIds more often
# than low-priority
self._jobIdGenerator = None
self._highestPriority = Job.Priorities.Normal
self._jobIdGenerator: Iterator[int] | None = None
self._highestPriority: int = Job.Priorities.Normal # type: ignore[attr-defined]
def destroy(self):
taskMgr.remove(JobManager.TaskName)

View File

@ -2,9 +2,13 @@
sound, music, shaders and fonts from disk.
"""
from __future__ import annotations
__all__ = ['Loader']
from panda3d.core import (
AsyncTask,
AudioManager,
ConfigVariableBool,
Filename,
FontPool,
@ -13,6 +17,7 @@ from panda3d.core import (
ModelFlattenRequest,
ModelNode,
ModelPool,
MovieAudio,
NodePath,
PandaNode,
SamplerState,
@ -24,13 +29,20 @@ from panda3d.core import (
from panda3d.core import Loader as PandaLoader
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.DirectObject import DirectObject
from . import ShowBase
import warnings
import sys
import os
from typing import Any, Callable, Iterable, Union
# Type aliases for loadModel, loadSound, etc. annotations
_ModelPath = Union[str, Filename, os.PathLike]
_SoundPath = Union[str, Filename, os.PathLike, MovieAudio]
# You can specify a phaseChecker callback to check
# a modelPath to see if it is being loaded in the correct
# phase
phaseChecker = None
phaseChecker: Callable[[_ModelPath, LoaderOptions], object] | None = None
class Loader(DirectObject):
@ -49,16 +61,23 @@ class Loader(DirectObject):
# This indicates that this class behaves like a Future.
_asyncio_future_blocking = False
def __init__(self, loader, numObjects, gotList, callback, extraArgs):
def __init__(
self,
loader: Loader | None,
numObjects: int,
gotList: bool,
callback: Callable[..., object] | None,
extraArgs: list,
) -> None:
self._loader = loader
self.objects = [None] * numObjects
self.objects: list[Any | None] = [None] * numObjects
self.gotList = gotList
self.callback = callback
self.extraArgs = extraArgs
self.requests = set()
self.requestList = []
self.requests: set[AsyncTask] = set()
self.requestList: list[AsyncTask] = []
def gotObject(self, index, object):
def gotObject(self, index: int, object) -> None:
self.objects[index] = object
if not self.requests:
@ -79,7 +98,7 @@ class Loader(DirectObject):
self.requests = None
self.requestList = None
def cancelled(self):
def cancelled(self) -> bool:
"Returns true if the request was cancelled."
return self.requestList is None
@ -87,7 +106,7 @@ class Loader(DirectObject):
"Returns true if all the requests were finished or cancelled."
return not self.requests
def result(self):
def result(self) -> Any:
"Returns the results, suspending the thread to wait if necessary."
for r in list(self.requests):
r.wait()
@ -126,26 +145,26 @@ class Loader(DirectObject):
yield await req
# special methods
def __init__(self, base):
def __init__(self, base: ShowBase.ShowBase | None = None) -> None:
self.base = base
self.loader = PandaLoader.getGlobalPtr()
self._requests = {}
self._requests: dict[AsyncTask, tuple[Loader._Callback, int]] = {}
self.hook = "async_loader_%s" % (Loader.loaderIndex)
Loader.loaderIndex += 1
self.accept(self.hook, self.__gotAsyncObject)
self._loadPythonFileTypes()
def destroy(self):
def destroy(self) -> None:
self.ignore(self.hook)
self.loader.stopThreads()
del self.base
del self.loader
def _init_base(self, base: ShowBase.ShowBase) -> None:
self.base = base
self.accept(self.hook, self.__gotAsyncObject)
@classmethod
def _loadPythonFileTypes(cls):
def _loadPythonFileTypes(cls) -> None:
if cls._loadedPythonFileTypes:
return
@ -168,10 +187,18 @@ class Loader(DirectObject):
cls._loadedPythonFileTypes = True
# model loading funcs
def loadModel(self, modelPath, loaderOptions = None, noCache = None,
allowInstance = False, okMissing = None,
callback = None, extraArgs = [], priority = None,
blocking = None):
def loadModel(
self,
modelPath: _ModelPath | list[_ModelPath] | tuple[_ModelPath, ...] | set[_ModelPath],
loaderOptions: LoaderOptions | None = None,
noCache: bool | None = None,
allowInstance: bool = False,
okMissing: bool | None = None,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
priority: int | None = None,
blocking: bool | None = None,
) -> Any:
"""
Attempts to load a model or models from one or more relative
pathnames. If the input modelPath is a string (a single model
@ -229,6 +256,10 @@ class Loader(DirectObject):
"""
assert Loader.notify.debug("Loading model: %s" % (modelPath,))
if not self._loadedPythonFileTypes:
self._loadPythonFileTypes()
if loaderOptions is None:
loaderOptions = LoaderOptions()
else:
@ -251,6 +282,7 @@ class Loader(DirectObject):
if allowInstance:
loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFAllowInstance)
modelList: Iterable[_ModelPath]
if not isinstance(modelPath, (tuple, list, set)):
# We were given a single model pathname.
modelList = [modelPath]
@ -416,6 +448,9 @@ class Loader(DirectObject):
a callback is used, the model is saved asynchronously, and the
true/false status is passed to the callback function. """
if not self._loadedPythonFileTypes:
self._loadPythonFileTypes()
if loaderOptions is None:
loaderOptions = LoaderOptions()
else:
@ -942,7 +977,13 @@ class Loader(DirectObject):
TexturePool.releaseTexture(texture)
# sound loading funcs
def loadSfx(self, *args, **kw):
def loadSfx(
self,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifically designated as a
"sound effect" file (that is, uses the sfxManager to load the
sound). There is no distinction between sound effect files
@ -952,11 +993,18 @@ class Loader(DirectObject):
independently of the other group."""
# showbase-created sfxManager should always be at front of list
assert self.base is not None
if self.base.sfxManagerList:
return self.loadSound(self.base.sfxManagerList[0], *args, **kw)
return self.loadSound(self.base.sfxManagerList[0], soundPath, positional, callback, extraArgs)
return None
def loadMusic(self, *args, **kw):
def loadMusic(
self,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifically designated as a
"music" file (that is, uses the musicManager to load the
sound). There is no distinction between sound effect files
@ -964,13 +1012,20 @@ class Loader(DirectObject):
to load the sound file, but this distinction allows the sound
effects and/or the music files to be adjusted as a group,
independently of the other group."""
assert self.base is not None
if self.base.musicManager:
return self.loadSound(self.base.musicManager, *args, **kw)
return self.loadSound(self.base.musicManager, soundPath, positional, callback, extraArgs)
else:
return None
def loadSound(self, manager, soundPath, positional = False,
callback = None, extraArgs = []):
def loadSound(
self,
manager: AudioManager,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifying the particular
AudioManager that should be used to load them. The soundPath
may be either a single filename, or a list of filenames. If a
@ -980,6 +1035,7 @@ class Loader(DirectObject):
from panda3d.core import AudioLoadRequest
soundList: Iterable[_SoundPath]
if not isinstance(soundPath, (tuple, list, set)):
# We were given a single sound pathname or a MovieAudio instance.
soundList = [soundPath]
@ -1105,7 +1161,7 @@ class Loader(DirectObject):
else:
callback(*(origModelList + extraArgs))
def __gotAsyncObject(self, request):
def __gotAsyncObject(self, request: AsyncTask) -> None:
"""A model or sound file or some such thing has just been
loaded asynchronously by the sub-thread. Add it to the list
of loaded objects, and call the appropriate callback when it's

View File

@ -2,19 +2,40 @@
:ref:`event handling <event-handlers>` that happens on the Python side.
"""
from __future__ import annotations
__all__ = ['Messenger']
import types
from collections.abc import Callable
from typing import Protocol
# These can be replaced with their builtin counterparts once support for Python 3.8 is dropped.
from typing import Dict, Tuple
from panda3d.core import AsyncTask
from direct.stdpy.threading import Lock
from direct.directnotify import DirectNotifyGlobal
from .PythonUtil import safeRepr
import types
# The following variables are typing constructs used in annotations
# to succinctly express complex type structures.
_ObjMsgrId = Tuple[str, int]
_CallbackInfo = list # [Callable, list, bool]
_ListenerObject = list # [int, DirectObject]
_AcceptorDict = Dict[_ObjMsgrId, _CallbackInfo]
_EventTuple = Tuple[_AcceptorDict, str, list, bool]
class _HasMessengerID(Protocol):
_MSGRmessengerId: _ObjMsgrId
class Messenger:
notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
def __init__(self):
def __init__(self) -> None:
"""
One is keyed off the event name. It has the following structure::
@ -34,16 +55,16 @@ class Messenger:
{'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
"""
# eventName->objMsgrId->callbackInfo
self.__callbacks = {}
self.__callbacks: dict[str, _AcceptorDict] = {}
# objMsgrId->set(eventName)
self.__objectEvents = {}
self.__objectEvents: dict[_ObjMsgrId, dict[str, None]] = {}
self._messengerIdGen = 0
# objMsgrId->listenerObject
self._id2object = {}
self._id2object: dict[_ObjMsgrId, _ListenerObject] = {}
# A mapping of taskChain -> eventList, used for sending events
# across task chains (and therefore across threads).
self._eventQueuesByTaskChain = {}
self._eventQueuesByTaskChain: dict[str, list[_EventTuple]] = {}
# This protects the data structures within this object from
# multithreaded access.
@ -51,7 +72,7 @@ class Messenger:
if __debug__:
self.__isWatching=0
self.__watching={}
self.__watching: dict[str, bool] = {}
# I'd like this to be in the __debug__, but I fear that someone will
# want this in a release build. If you're sure that that will not be
# then please remove this comment and put the quiet/verbose stuff
@ -62,7 +83,7 @@ class Messenger:
'collisionLoopFinished':1,
} # see def quiet()
def _getMessengerId(self, object):
def _getMessengerId(self, object: _HasMessengerID) -> _ObjMsgrId:
# TODO: allocate this id in DirectObject.__init__ and get derived
# classes to call down (speed optimization, assuming objects
# accept/ignore more than once over their lifetime)
@ -73,7 +94,7 @@ class Messenger:
self._messengerIdGen += 1
return object._MSGRmessengerId
def _storeObject(self, object):
def _storeObject(self, object: _HasMessengerID) -> None:
# store reference-counted reference to object in case we need to
# retrieve it later. assumes lock is held.
id = self._getMessengerId(object)
@ -82,7 +103,7 @@ class Messenger:
else:
self._id2object[id][0] += 1
def _getObject(self, id):
def _getObject(self, id: _ObjMsgrId) -> _HasMessengerID:
return self._id2object[id][1]
def _getObjects(self):
@ -101,7 +122,7 @@ class Messenger:
def _getEvents(self):
return list(self.__callbacks.keys())
def _releaseObject(self, object):
def _releaseObject(self, object: _HasMessengerID) -> None:
# assumes lock is held.
id = self._getMessengerId(object)
if id in self._id2object:
@ -117,7 +138,14 @@ class Messenger:
from .EventManagerGlobal import eventMgr
return eventMgr.eventHandler.get_future(event)
def accept(self, event, object, method, extraArgs=[], persistent=1):
def accept(
self,
event: str,
object: _HasMessengerID,
method: Callable,
extraArgs: list = [],
persistent: bool = True,
) -> None:
""" accept(self, string, DirectObject, Function, List, Boolean)
Make this object accept this event. When the event is
@ -174,7 +202,7 @@ class Messenger:
finally:
self.lock.release()
def ignore(self, event, object):
def ignore(self, event: str, object: _HasMessengerID) -> None:
""" ignore(self, string, DirectObject)
Make this object no longer respond to this event.
It is safe to call even if it was not already accepting
@ -208,7 +236,7 @@ class Messenger:
finally:
self.lock.release()
def ignoreAll(self, object):
def ignoreAll(self, object: _HasMessengerID) -> None:
"""
Make this object no longer respond to any events it was accepting
Useful for cleanup
@ -283,7 +311,7 @@ class Messenger:
"""
return not self.isAccepting(event, object)
def send(self, event, sentArgs=[], taskChain=None):
def send(self, event: str, sentArgs: list = [], taskChain: str | None = None) -> None:
"""
Send this event, optionally passing in arguments.
@ -305,12 +333,12 @@ class Messenger:
self.lock.acquire()
try:
foundWatch = 0
foundWatch = False
if __debug__:
if self.__isWatching:
for i in self.__watching:
if str(event).find(i) >= 0:
foundWatch = 1
foundWatch = True
break
acceptorDict = self.__callbacks.get(event)
if not acceptorDict:
@ -336,7 +364,7 @@ class Messenger:
finally:
self.lock.release()
def __taskChainDispatch(self, taskChain, task):
def __taskChainDispatch(self, taskChain: str, task: AsyncTask) -> int:
""" This task is spawned each time an event is sent across
task chains. Its job is to empty the task events on the queue
for this particular task chain. This guarantees that events
@ -365,7 +393,13 @@ class Messenger:
return task.done
def __dispatch(self, acceptorDict, event, sentArgs, foundWatch):
def __dispatch(
self,
acceptorDict: _AcceptorDict,
event: str,
sentArgs: list,
foundWatch: bool,
) -> None:
for id in list(acceptorDict.keys()):
# We have to make this apparently redundant check, because
# it is possible that one object removes its own hooks
@ -562,7 +596,7 @@ class Messenger:
break
return matches
def __methodRepr(self, method):
def __methodRepr(self, method: object) -> str:
"""
return string version of class.method or method.
"""

View File

@ -1,7 +1,11 @@
"""Contains the OnScreenDebug class."""
from __future__ import annotations
__all__ = ['OnScreenDebug']
from typing import Any
from panda3d.core import (
ConfigVariableBool,
ConfigVariableDouble,
@ -18,13 +22,13 @@ class OnScreenDebug:
enabled = ConfigVariableBool("on-screen-debug-enabled", False)
def __init__(self):
self.onScreenText = None
def __init__(self) -> None:
self.onScreenText: OnscreenText.OnscreenText | None = None
self.frame = 0
self.text = ""
self.data = {}
self.data: dict[str, tuple[int, Any]] = {}
def load(self):
def load(self) -> None:
if self.onScreenText:
return
@ -40,6 +44,7 @@ class OnScreenDebug:
fgColor.setW(ConfigVariableDouble("on-screen-debug-fg-alpha", 0.85).value)
bgColor.setW(ConfigVariableDouble("on-screen-debug-bg-alpha", 0.85).value)
from direct.showbase.ShowBaseGlobal import base
font = base.loader.loadFont(fontPath)
if not font.isValid():
print("failed to load OnScreenDebug font %s" % fontPath)
@ -47,15 +52,16 @@ class OnScreenDebug:
self.onScreenText = OnscreenText.OnscreenText(
parent = base.a2dTopLeft, pos = (0.0, -0.1),
fg=fgColor, bg=bgColor, scale = (fontScale, fontScale, 0.0),
align = TextNode.ALeft, mayChange = 1, font = font)
align = TextNode.ALeft, mayChange = True, font = font)
# Make sure readout is never lit or drawn in wireframe
DirectUtil.useDirectRenderStyle(self.onScreenText)
def render(self):
def render(self) -> None:
if not self.enabled:
return
if not self.onScreenText:
self.load()
assert self.onScreenText is not None
self.onScreenText.clearText()
for k, v in sorted(self.data.items()):
if v[0] == self.frame:
@ -75,7 +81,7 @@ class OnScreenDebug:
self.onScreenText.appendText(self.text)
self.frame += 1
def clear(self):
def clear(self) -> None:
self.text = ""
if self.onScreenText:
self.onScreenText.clearText()

View File

@ -41,13 +41,17 @@ import time
import builtins
import importlib
import functools
from typing import Callable
from collections.abc import Callable, Container, Iterable, Mapping
from typing import Any, Generic, TypeVar
__report_indent = 3
from panda3d.core import ConfigVariableBool, ConfigVariableString, ConfigFlags
from panda3d.core import ClockObject
_T = TypeVar('_T')
_KT = TypeVar('_KT')
_VT = TypeVar('_VT')
## with one integer positional arg, this uses about 4/5 of the memory of the Functor class below
#def Functor(function, *args, **kArgs):
@ -100,9 +104,9 @@ class Functor:
return s
class Stack:
def __init__(self):
self.__list = []
class Stack(Generic[_T]):
def __init__(self) -> None:
self.__list: list[_T] = []
def push(self, item):
self.__list.append(item)
@ -229,7 +233,7 @@ if __debug__:
#-----------------------------------------------------------------------------
def traceFunctionCall(frame):
def traceFunctionCall(frame: types.FrameType) -> str:
"""
return a string that shows the call frame with calling arguments.
e.g.
@ -270,7 +274,7 @@ if __debug__:
r+="*** undefined ***"
return r+')'
def traceParentCall():
def traceParentCall() -> str:
return traceFunctionCall(sys._getframe(2))
def printThisCall():
@ -411,7 +415,7 @@ def list2dict(L, value=None):
return dict([(k, value) for k in L])
def listToIndex2item(L):
def listToIndex2item(L: Iterable[_VT]) -> dict[int, _VT]:
"""converts list to dict of list index->list item"""
d = {}
for i, item in enumerate(L):
@ -422,7 +426,7 @@ def listToIndex2item(L):
assert listToIndex2item(['a','b']) == {0: 'a', 1: 'b',}
def listToItem2index(L):
def listToItem2index(L: Iterable[_KT]) -> dict[_KT, int]:
"""converts list to dict of list item->list index
This is lossy if there are duplicate list items"""
d = {}
@ -451,7 +455,7 @@ def invertDict(D, lossy=False):
return n
def invertDictLossless(D):
def invertDictLossless(D: Mapping[_KT, _VT]) -> dict[_VT, list[_KT]]:
"""similar to invertDict, but values of new dict are lists of keys from
old dict. No information is lost.
@ -459,7 +463,7 @@ def invertDictLossless(D):
>>> invertDictLossless(old)
{1: ['key1'], 2: ['key2', 'keyA']}
"""
n = {}
n: dict[_VT, list[_KT]] = {}
for key, value in D.items():
n.setdefault(value, [])
n[value].append(key)
@ -706,7 +710,7 @@ if __debug__:
movedDumpFuncs: list[Callable] = []
movedLoadFuncs: list[Callable] = []
profileFilenames = set()
profileFilenameList = Stack()
profileFilenameList = Stack[str]()
profileFilename2file = {}
profileFilename2marshalData = {}
@ -998,7 +1002,7 @@ def lineupPos(i, num, spacing):
return pos - ((float(spacing) * (num-1))/2.)
def formatElapsedSeconds(seconds):
def formatElapsedSeconds(seconds: float) -> str:
"""
Returns a string of the form "mm:ss" or "hh:mm:ss" or "n days",
representing the indicated elapsed time in seconds.
@ -1278,7 +1282,7 @@ def randInt32(rng=random.random):
class SerialNumGen:
"""generates serial numbers"""
def __init__(self, start=None):
def __init__(self, start: int | None = None) -> None:
if start is None:
start = 0
self.__counter = start-1
@ -1305,7 +1309,7 @@ class SerialMaskedGen(SerialNumGen):
_serialGen = SerialNumGen()
def serialNum():
def serialNum() -> int:
return _serialGen.next()
@ -1404,14 +1408,16 @@ def _getSafeReprNotify():
return safeReprNotify
def safeRepr(obj):
def safeRepr(obj: object) -> str:
global dtoolSuperBase
if dtoolSuperBase is None:
_getDtoolSuperBase()
assert dtoolSuperBase is not None
global safeReprNotify
if safeReprNotify is None:
_getSafeReprNotify()
assert safeReprNotify is not None
if isinstance(obj, dtoolSuperBase):
# repr of C++ object could crash, particularly if the object has been deleted
@ -1443,7 +1449,12 @@ def safeReprTypeOnFail(obj):
return '<** FAILED REPR OF %s instance at %s **>' % (obj.__class__.__name__, hex(id(obj)))
def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
def fastRepr(
obj: object,
maxLen: int = 200,
strFactor: int = 10,
_visitedIds: set[int] | None = None,
) -> str:
""" caps the length of iterable types, so very large objects will print faster.
also prevents infinite recursion """
try:
@ -1452,9 +1463,9 @@ def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
if id(obj) in _visitedIds:
return '<ALREADY-VISITED %s>' % itype(obj)
if type(obj) in (tuple, list):
assert isinstance(obj, (tuple, list))
s = ''
s += {tuple: '(',
list: '[',}[type(obj)]
s += '(' if type(obj) == tuple else '['
if maxLen is not None and len(obj) > maxLen:
o = obj[:maxLen]
ellips = '...'
@ -1467,8 +1478,7 @@ def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
s += ', '
_visitedIds.remove(id(obj))
s += ellips
s += {tuple: ')',
list: ']',}[type(obj)]
s += ')' if type(obj) == tuple else ']'
return s
elif type(obj) is dict:
s = '{'
@ -1588,7 +1598,7 @@ class ScratchPad:
class Sync:
_SeriesGen = SerialNumGen()
def __init__(self, name, other=None):
def __init__(self, name: str, other: Sync | None = None) -> None:
self._name = name
if other is None:
self._series = self._SeriesGen.next()
@ -1620,7 +1630,7 @@ class Sync:
self._name, self._series, self._value)
def itype(obj):
def itype(obj: object) -> type | str:
# version of type that gives more complete information about instance types
global dtoolSuperBase
t = type(obj)
@ -1628,6 +1638,7 @@ def itype(obj):
# check if this is a C++ object
if dtoolSuperBase is None:
_getDtoolSuperBase()
assert dtoolSuperBase is not None
if isinstance(obj, dtoolSuperBase):
return "<type 'instance' of %s>" % (obj.__class__)
return t
@ -1972,7 +1983,13 @@ def pstatcollect(scope, level = None):
__report_indent = 0
def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigParam = []):
def report(
types: Container[str] = [],
prefix: str = '',
xform: Callable[[Any], object] | None = None,
notifyFunc: Callable[[str], object] | None = None,
dConfigParam: str | list[str] | tuple[str, ...] = [],
) -> Callable[[_T], _T]:
"""
This is a decorator generating function. Use is similar to
a @decorator, except you must be sure to call it as a function.
@ -2031,7 +2048,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
return f
try:
if not __dev__ and not ConfigVariableBool('force-reports', False):
if not __dev__ and not ConfigVariableBool('force-reports', False): # type: ignore[name-defined]
return decorator
# determine whether we should use the decorator
@ -2041,6 +2058,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
if not dConfigParam:
doPrint = True
else:
dConfigParams: list[str] | tuple[str, ...]
if not isinstance(dConfigParam, (list,tuple)):
dConfigParams = (dConfigParam,)
else:
@ -2070,7 +2088,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
globalClockDelta = importlib.import_module("direct.distributed.ClockDelta").globalClockDelta
def decorator(f):
def decorator(f): # type: ignore[no-redef]
def wrap(*args, **kwargs):
if args:
rArgs = [args[0].__class__.__name__ + ', ']

View File

@ -62,6 +62,7 @@ from panda3d.core import (
DepthTestAttrib,
DepthWriteAttrib,
DriveInterface,
EventQueue,
ExecutionEnvironment,
Filename,
FisheyeMaker,
@ -114,7 +115,7 @@ from panda3d.core import (
WindowProperties,
getModelPath,
)
from panda3d.direct import throw_new_frame, init_app_for_gui
from panda3d.direct import init_app_for_gui
from panda3d.direct import storeAccessibilityShortcutKeys, allowAccessibilityShortcutKeys
from . import DConfig
@ -177,6 +178,10 @@ class ShowBase(DirectObject.DirectObject):
aspect2d: NodePath
pixel2d: NodePath
a2dTopLeft: NodePath
cluster: Any | None
def __init__(self, fStartDirect: bool = True, windowType: str | None = None) -> None:
"""Opens a window, sets up a 3-D and several 2-D scene graphs, and
everything else needed to render the scene graph to the window.
@ -380,7 +385,7 @@ class ShowBase(DirectObject.DirectObject):
#: yourself every frame.
self.cTrav: CollisionTraverser | Literal[0] = 0
self.shadowTrav: CollisionTraverser | Literal[0] = 0
self.cTravStack = Stack()
self.cTravStack = Stack[CollisionTraverser]()
# Ditto for an AppTraverser.
self.appTrav: Any | Literal[0] = 0
@ -436,9 +441,9 @@ class ShowBase(DirectObject.DirectObject):
self.useTrackball()
#: `.Loader.Loader` object.
self.loader = Loader.Loader(self)
self.loader: Loader.Loader = ShowBaseGlobal.loader
self.loader._init_base(self)
self.graphicsEngine.setDefaultLoader(self.loader.loader)
ShowBaseGlobal.loader = self.loader
#: The global event manager, as imported from `.EventManagerGlobal`.
self.eventMgr = eventMgr
@ -652,7 +657,7 @@ class ShowBase(DirectObject.DirectObject):
def getExitErrorCode(self):
return 0
def printEnvDebugInfo(self):
def printEnvDebugInfo(self) -> None:
"""Print some information about the environment that we are running
in. Stuff like the model paths and other paths. Feel free to
add stuff to this.
@ -706,7 +711,8 @@ class ShowBase(DirectObject.DirectObject):
allowAccessibilityShortcutKeys(True)
self.__disabledStickyKeys = False
self.__directObject.ignoreAll()
if hasattr(self, '__directObject'):
self.__directObject.ignoreAll()
self.ignoreAll()
self.shutdown()
@ -1208,7 +1214,7 @@ class ShowBase(DirectObject.DirectObject):
self.taskMgr.remove('clientSleep')
self.taskMgr.add(self.__sleepCycleTask, 'clientSleep', sort = 55)
def __sleepCycleTask(self, task):
def __sleepCycleTask(self, task: object) -> int:
Thread.sleep(self.clientSleep)
#time.sleep(self.clientSleep)
return Task.cont
@ -1444,7 +1450,7 @@ class ShowBase(DirectObject.DirectObject):
self.__configAspectRatio = aspectRatio
self.adjustWindowAspectRatio(self.getAspectRatio())
def getAspectRatio(self, win = None):
def getAspectRatio(self, win: GraphicsOutput | None = None) -> float:
# Returns the actual aspect ratio of the indicated (or main
# window), or the default aspect ratio if there is not yet a
# main window.
@ -1453,7 +1459,7 @@ class ShowBase(DirectObject.DirectObject):
if self.__configAspectRatio:
return self.__configAspectRatio
aspectRatio = 1
aspectRatio: float = 1
if win is None:
win = self.win
@ -1476,7 +1482,7 @@ class ShowBase(DirectObject.DirectObject):
return aspectRatio
def getSize(self, win = None):
def getSize(self, win: GraphicsOutput | None = None) -> tuple[int, int]:
"""
Returns the actual size of the indicated (or main window), or the
default size if there is not yet a main window.
@ -2176,7 +2182,7 @@ class ShowBase(DirectObject.DirectObject):
music.setLoop(looping)
music.play()
def __resetPrevTransform(self, state):
def __resetPrevTransform(self, state: object) -> int:
# Clear out the previous velocity deltas now, after we have
# rendered (the previous frame). We do this after the render,
# so that we have a chance to draw a representation of spheres
@ -2187,7 +2193,7 @@ class ShowBase(DirectObject.DirectObject):
PandaNode.resetAllPrevTransform()
return Task.cont
def __dataLoop(self, state):
def __dataLoop(self, state: object) -> int:
# Check if there were newly connected devices.
self.devices.update()
@ -2197,7 +2203,7 @@ class ShowBase(DirectObject.DirectObject):
self.dgTrav.traverse(self.dataRootNode)
return Task.cont
def __ivalLoop(self, state):
def __ivalLoop(self, state: object) -> int:
# Execute all intervals in the global ivalMgr.
IntervalManager.ivalMgr.step()
return Task.cont
@ -2215,7 +2221,7 @@ class ShowBase(DirectObject.DirectObject):
self.shadowTrav.traverse(self.render)
return Task.cont
def __collisionLoop(self, state):
def __collisionLoop(self, state: object) -> int:
# run the collision traversal if we have a
# CollisionTraverser set.
if self.cTrav:
@ -2227,14 +2233,14 @@ class ShowBase(DirectObject.DirectObject):
messenger.send("collisionLoopFinished")
return Task.cont
def __audioLoop(self, state):
def __audioLoop(self, state: object) -> int:
if self.musicManager is not None:
self.musicManager.update()
for x in self.sfxManagerList:
x.update()
return Task.cont
def __garbageCollectStates(self, state):
def __garbageCollectStates(self, state: object) -> int:
""" This task is started only when we have
garbage-collect-states set in the Config.prc file, in which
case we're responsible for taking out Panda's garbage from
@ -2245,7 +2251,7 @@ class ShowBase(DirectObject.DirectObject):
RenderState.garbageCollect()
return Task.cont
def __igLoop(self, state):
def __igLoop(self, state: object) -> int:
if __debug__:
# We render the watch variables for the onScreenDebug as soon
# as we reasonably can before the renderFrame().
@ -2280,12 +2286,11 @@ class ShowBase(DirectObject.DirectObject):
# now until someone complains.
time.sleep(0.1)
# Lerp stuff needs this event, and it must be generated in
# C++, not in Python.
throw_new_frame()
# Lerp stuff needs this event, thrown directly on the C++ queue.
EventQueue.getGlobalEventQueue().queueEvent("NewFrame")
return Task.cont
def __igLoopSync(self, state):
def __igLoopSync(self, state: object) -> int:
if __debug__:
# We render the watch variables for the onScreenDebug as soon
# as we reasonably can before the renderFrame().
@ -2294,6 +2299,7 @@ class ShowBase(DirectObject.DirectObject):
if self.recorder:
self.recorder.recordFrame()
assert self.cluster is not None
self.cluster.collectData()
# Finally, render the frame.
@ -2323,12 +2329,12 @@ class ShowBase(DirectObject.DirectObject):
time.sleep(0.1)
self.graphicsEngine.readyFlip()
assert self.cluster is not None
self.cluster.waitForFlipCommand()
self.graphicsEngine.flipFrame()
# Lerp stuff needs this event, and it must be generated in
# C++, not in Python.
throw_new_frame()
# Lerp stuff needs this event, thrown directly on the C++ queue.
EventQueue.getGlobalEventQueue().queueEvent("NewFrame")
return Task.cont
def restart(self, clusterSync: bool = False, cluster=None) -> None:
@ -2753,7 +2759,7 @@ class ShowBase(DirectObject.DirectObject):
self.oobeVis.reparentTo(self.camera)
self.oobeMode = 1
def __oobeButton(self, suffix, button):
def __oobeButton(self, suffix: str, button: str) -> None:
if button.startswith('mouse'):
# Eat mouse buttons.
return
@ -3073,7 +3079,7 @@ class ShowBase(DirectObject.DirectObject):
else:
return Task.cont
def windowEvent(self, win):
def windowEvent(self, win: GraphicsOutput) -> None:
if win != self.win:
# This event isn't about our window.
return
@ -3092,9 +3098,9 @@ class ShowBase(DirectObject.DirectObject):
self.userExit()
if properties.getForeground() and not self.mainWinForeground:
self.mainWinForeground = 1
self.mainWinForeground = True
elif not properties.getForeground() and self.mainWinForeground:
self.mainWinForeground = 0
self.mainWinForeground = False
if __debug__:
if self.__autoGarbageLogging:
GarbageReport.b_checkForGarbageLeaks()
@ -3102,12 +3108,12 @@ class ShowBase(DirectObject.DirectObject):
if properties.getMinimized() and not self.mainWinMinimized:
# If the main window is minimized, throw an event to
# stop the music.
self.mainWinMinimized = 1
self.mainWinMinimized = True
messenger.send('PandaPaused')
elif not properties.getMinimized() and self.mainWinMinimized:
# If the main window is restored, throw an event to
# restart the music.
self.mainWinMinimized = 0
self.mainWinMinimized = False
messenger.send('PandaRestarted')
# If we have not forced the aspect ratio, let's see if it has
@ -3125,7 +3131,7 @@ class ShowBase(DirectObject.DirectObject):
if self.wantRender2dp:
self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
def adjustWindowAspectRatio(self, aspectRatio):
def adjustWindowAspectRatio(self, aspectRatio: float) -> None:
""" This function is normally called internally by
`windowEvent()`, but it may also be called to explicitly adjust
the aspect ratio of the render/render2d DisplayRegion, by a
@ -3512,6 +3518,7 @@ class ShowBase(DirectObject.DirectObject):
remove_camera_frustum = removeCameraFrustum
save_cube_map = saveCubeMap
save_sphere_map = saveSphereMap
user_exit = userExit
start_wx = startWx
start_tk = startTk
start_direct = startDirect

View File

@ -62,7 +62,8 @@ aspect2d = render2d.attachNewNode(PGTop("aspect2d"))
#: A dummy scene graph that is not being rendered by anything.
hidden = NodePath("hidden")
loader: Loader
#: The global Loader instance for models, textures, etc.
loader = Loader()
# Set direct notify categories now that we have config
directNotify.setDconfigLevels()

View File

@ -5,477 +5,267 @@ Calling the :func:`register()` function to register the import hooks should be
sufficient to enable this functionality.
"""
__all__ = ['register', 'sharedPackages',
'reloadSharedPackage', 'reloadSharedPackages']
from __future__ import annotations
from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
from direct.stdpy.file import open
__all__ = ['register']
from panda3d.core import Filename, VirtualFile, VirtualFileSystem, VirtualFileMountSystem
from panda3d.core import OFileStream, copy_stream
import sys
import marshal
import imp
import types
import _imp
import atexit
from importlib.abc import Loader, SourceLoader
from importlib.util import MAGIC_NUMBER, decode_source
from importlib.machinery import ModuleSpec, EXTENSION_SUFFIXES, BYTECODE_SUFFIXES
from types import ModuleType
from typing import Any
#: The sharedPackages dictionary lists all of the "shared packages",
#: special Python packages that automatically span multiple directories
#: via magic in the VFSImporter. You can make a package "shared"
#: simply by adding its name into this dictionary (and then calling
#: reloadSharedPackages() if it's already been imported).
#:
#: When a package name is in this dictionary at import time, *all*
#: instances of the package are located along sys.path, and merged into
#: a single Python module with a __path__ setting that represents the
#: union. Thus, you can have a direct.showbase.foo in your own
#: application, and loading it won't shadow the system
#: direct.showbase.ShowBase which is in a different directory on disk.
sharedPackages = {}
vfs = VirtualFileSystem.getGlobalPtr()
compiledExtensions = ['pyc', 'pyo']
if not __debug__:
# In optimized mode, we prefer loading .pyo files over .pyc files.
# We implement that by reversing the extension names.
compiledExtensions = ['pyo', 'pyc']
vfs = VirtualFileSystem.get_global_ptr()
class VFSImporter:
def _make_spec(fullname: str, loader: VFSLoader, *, is_package: bool) -> ModuleSpec:
filename = loader._vfile.get_filename()
spec = ModuleSpec(fullname, loader, origin=filename.to_os_specific(), is_package=is_package)
if is_package:
assert spec.submodule_search_locations is not None
spec.submodule_search_locations.append(Filename(filename.get_dirname()).to_os_specific())
spec.has_location = True
return spec
class VFSFinder:
""" This class serves as a Python importer to support loading
Python .py and .pyc/.pyo files from Panda's Virtual File System,
which allows loading Python source files from mounted .mf files
(among other places). """
def __init__(self, path):
if isinstance(path, Filename):
self.dir_path = Filename(path)
else:
self.dir_path = Filename.fromOsSpecific(path)
def __init__(self, path: str) -> None:
self.path = path
def find_module(self, fullname, path = None):
if path is None:
dir_path = self.dir_path
else:
dir_path = path
#print >>sys.stderr, "find_module(%s), dir_path = %s" % (fullname, dir_path)
def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None:
#print(f"find_spec({fullname}), dir_path = {dir_path}", file=sys.stderr)
basename = fullname.split('.')[-1]
path = Filename(dir_path, basename)
filename = Filename(Filename.from_os_specific(self.path), basename)
loader: VFSLoader
# First, look for Python files.
filename = Filename(path)
filename.setExtension('py')
vfile = vfs.getFile(filename, True)
vfile = vfs.get_file(filename + '.py', True)
if vfile:
return VFSLoader(dir_path, vfile, filename,
desc=('.py', 'r', imp.PY_SOURCE))
loader = VFSSourceLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=False)
# If there's no .py file, but there's a .pyc file, load that
# anyway.
for ext in compiledExtensions:
filename = Filename(path)
filename.setExtension(ext)
vfile = vfs.getFile(filename, True)
for suffix in BYTECODE_SUFFIXES:
vfile = vfs.get_file(filename + suffix, True)
if vfile:
return VFSLoader(dir_path, vfile, filename,
desc=('.'+ext, 'rb', imp.PY_COMPILED))
loader = VFSCompiledLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=False)
# Look for a C/C++ extension module.
for desc in imp.get_suffixes():
if desc[2] != imp.C_EXTENSION:
continue
filename = Filename(path + desc[0])
vfile = vfs.getFile(filename, True)
for suffix in EXTENSION_SUFFIXES:
vfile = vfs.get_file(filename + suffix, True)
if vfile:
return VFSLoader(dir_path, vfile, filename, desc=desc)
loader = VFSExtensionLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=False)
# Finally, consider a package, i.e. a directory containing
# __init__.py.
filename = Filename(path, '__init__.py')
vfile = vfs.getFile(filename, True)
# Consider a package, i.e. a directory containing __init__.py.
init_filename = Filename(filename, '__init__.py')
vfile = vfs.get_file(init_filename, True)
if vfile:
return VFSLoader(dir_path, vfile, filename, packagePath=path,
desc=('.py', 'r', imp.PY_SOURCE))
for ext in compiledExtensions:
filename = Filename(path, '__init__.' + ext)
vfile = vfs.getFile(filename, True)
if vfile:
return VFSLoader(dir_path, vfile, filename, packagePath=path,
desc=('.'+ext, 'rb', imp.PY_COMPILED))
loader = VFSSourceLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=True)
#print >>sys.stderr, "not found."
for suffix in BYTECODE_SUFFIXES:
init_filename = Filename(filename, '__init__' + suffix)
vfile = vfs.get_file(init_filename, True)
if vfile:
loader = VFSCompiledLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=True)
# Consider a namespace package.
if vfs.is_directory(filename):
spec = ModuleSpec(fullname, VFSNamespaceLoader(), is_package=True)
assert spec.submodule_search_locations is not None
spec.submodule_search_locations.append(filename.to_os_specific())
return spec
#print("not found.", file=sys.stderr)
return None
class VFSLoader:
""" The second part of VFSImporter, this is created for a
particular .py file or directory. """
def __init__(self, dir_path, vfile, filename, desc, packagePath=None):
self.dir_path = dir_path
self.timestamp = None
if vfile:
self.timestamp = vfile.getTimestamp()
self.filename = filename
self.desc = desc
self.packagePath = packagePath
def load_module(self, fullname, loadingShared = False):
#print >>sys.stderr, "load_module(%s), dir_path = %s, filename = %s" % (fullname, self.dir_path, self.filename)
if self.desc[2] == imp.PY_FROZEN:
return self._import_frozen_module(fullname)
if self.desc[2] == imp.C_EXTENSION:
return self._import_extension_module(fullname)
# Check if this is a child of a shared package.
if not loadingShared and self.packagePath and '.' in fullname:
parentname = fullname.rsplit('.', 1)[0]
if parentname in sharedPackages:
# It is. That means it's a shared package too.
parent = sys.modules[parentname]
path = getattr(parent, '__path__', None)
importer = VFSSharedImporter()
sharedPackages[fullname] = True
loader = importer.find_module(fullname, path = path)
assert loader
return loader.load_module(fullname)
code = self._read_code()
if not code:
raise ImportError('No Python code in %s' % (fullname))
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = self.filename.toOsSpecific()
mod.__loader__ = self
if self.packagePath:
mod.__path__ = [self.packagePath.toOsSpecific()]
#print >> sys.stderr, "loaded %s, path = %s" % (fullname, mod.__path__)
exec(code, mod.__dict__)
return sys.modules[fullname]
def getdata(self, path):
path = Filename(self.dir_path, Filename.fromOsSpecific(path))
vfile = vfs.getFile(path)
if not vfile:
raise IOError("Could not find '%s'" % (path))
return vfile.readFile(True)
class VFSLoader(Loader):
def __init__(self, fullname: str, vfile: VirtualFile) -> None:
self.name = fullname
self._vfile = vfile
def is_package(self, fullname):
return bool(self.packagePath)
if fullname is not None and self.name != fullname:
raise ImportError
filename = self._vfile.get_filename().get_basename()
filename_base = filename.rsplit('.', 1)[0]
tail_name = fullname.rpartition('.')[2]
return filename_base == '__init__' and tail_name != '__init__'
def create_module(self, spec: ModuleSpec) -> ModuleType | None:
"""Use default semantics for module creation."""
def exec_module(self, module: ModuleType) -> None:
"""Execute the module."""
code = self.get_code(module.__name__) # type: ignore[attr-defined]
exec(code, module.__dict__)
def get_filename(self, fullname: str) -> str:
if fullname is not None and self.name != fullname:
raise ImportError
return self._vfile.get_filename().to_os_specific()
@staticmethod
def get_data(path: str) -> bytes:
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return vfile.read_file(True)
else:
raise OSError
@staticmethod
def path_stats(path: str) -> dict[str, Any]:
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return {'mtime': vfile.get_timestamp(), 'size': vfile.get_file_size()}
else:
raise OSError
@staticmethod
def path_mtime(path):
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return vfile.get_timestamp()
else:
raise OSError
class VFSSourceLoader(VFSLoader, SourceLoader): # type: ignore[misc]
def get_source(self, fullname):
if fullname is not None and self.name != fullname:
raise ImportError
return decode_source(self._vfile.read_file(True))
class VFSCompiledLoader(VFSLoader):
def get_code(self, fullname):
return self._read_code()
if fullname is not None and self.name != fullname:
raise ImportError
vfile = self._vfile
data = vfile.read_file(True)
if data[:4] != MAGIC_NUMBER:
raise ImportError("Bad magic number in %s" % (vfile))
return marshal.loads(data[16:])
def get_source(self, fullname):
return self._read_source()
return None
def get_filename(self, fullname):
return self.filename.toOsSpecific()
def _read_source(self):
""" Returns the Python source for this file, if it is
available, or None if it is not. May raise IOError. """
if self.desc[2] == imp.PY_COMPILED or \
self.desc[2] == imp.C_EXTENSION:
return None
filename = Filename(self.filename)
filename.setExtension('py')
filename.setText()
# Use the tokenize module to detect the encoding.
import tokenize
fh = open(self.filename, 'rb')
encoding, lines = tokenize.detect_encoding(fh.readline)
return (b''.join(lines) + fh.read()).decode(encoding)
def _import_extension_module(self, fullname):
""" Loads the binary shared object as a Python module, and
returns it. """
vfile = vfs.getFile(self.filename, False)
class VFSExtensionLoader(VFSLoader):
def create_module(self, spec):
vfile = self._vfile
filename = vfile.get_filename()
# We can only import an extension module if it already exists on
# disk. This means if it's a truly virtual file that has no
# on-disk equivalent, we have to write it to a temporary file
# first.
if hasattr(vfile, 'getMount') and \
isinstance(vfile.getMount(), VirtualFileMountSystem):
if isinstance(vfile.get_mount(), VirtualFileMountSystem):
# It's a real file.
filename = self.filename
elif self.filename.exists():
pass
elif filename.exists():
# It's a virtual file, but it's shadowing a real file in
# the same directory. Assume they're the same, and load
# the real one.
filename = self.filename
else:
# It's a virtual file with no real-world existence. Dump
# it to disk. TODO: clean up this filename.
filename = Filename.temporary('', self.filename.getBasenameWoExtension(),
'.' + self.filename.getExtension(),
type = Filename.TDso)
filename.setExtension(self.filename.getExtension())
filename.setBinary()
sin = vfile.openReadFile(True)
sout = OFileStream()
if not filename.openWrite(sout):
raise IOError
if not copyStream(sin, sout):
raise IOError
vfile.closeReadFile(sin)
del sout
module = imp.load_module(fullname, None, filename.toOsSpecific(),
self.desc)
module.__file__ = self.filename.toOsSpecific()
return module
def _import_frozen_module(self, fullname):
""" Imports the frozen module without messing around with
searching any more. """
#print >>sys.stderr, "importing frozen %s" % (fullname)
module = imp.load_module(fullname, None, fullname,
('', '', imp.PY_FROZEN))
module.__path__ = []
return module
def _read_code(self):
""" Returns the Python compiled code object for this file, if
it is available, or None if it is not. May raise IOError,
ValueError, SyntaxError, or a number of other errors generated
by the low-level system. """
if self.desc[2] == imp.PY_COMPILED:
# It's a pyc file; just read it directly.
pycVfile = vfs.getFile(self.filename, False)
if pycVfile:
return self._loadPyc(pycVfile, None)
raise IOError('Could not read %s' % (self.filename))
elif self.desc[2] == imp.C_EXTENSION:
return None
# It's a .py file (or an __init__.py file; same thing). Read
# the .pyc file if it is available and current; otherwise read
# the .py file and compile it.
t_pyc = None
for ext in compiledExtensions:
pycFilename = Filename(self.filename)
pycFilename.setExtension(ext)
pycVfile = vfs.getFile(pycFilename, False)
if pycVfile:
t_pyc = pycVfile.getTimestamp()
break
code = None
if t_pyc and t_pyc >= self.timestamp:
try:
code = self._loadPyc(pycVfile, self.timestamp)
except ValueError:
code = None
if not code:
source = self._read_source()
filename = Filename(self.filename)
filename.setExtension('py')
code = self._compile(filename, source)
return code
def _loadPyc(self, vfile, timestamp):
""" Reads and returns the marshal data from a .pyc file.
Raises ValueError if there is a problem. """
code = None
data = vfile.readFile(True)
if data[:4] != imp.get_magic():
raise ValueError("Bad magic number in %s" % (vfile))
t = int.from_bytes(data[4:8], 'little')
data = data[12:]
if not timestamp or t == timestamp:
return marshal.loads(data)
else:
raise ValueError("Timestamp wrong on %s" % (vfile))
def _compile(self, filename, source):
""" Compiles the Python source code to a code object and
attempts to write it to an appropriate .pyc file. May raise
SyntaxError or other errors generated by the compiler. """
if source and source[-1] != '\n':
source = source + '\n'
code = compile(source, filename.toOsSpecific(), 'exec')
# try to cache the compiled code
pycFilename = Filename(filename)
pycFilename.setExtension(compiledExtensions[0])
try:
f = open(pycFilename.toOsSpecific(), 'wb')
except IOError:
pass
else:
f.write(imp.get_magic())
f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little'))
f.write(b'\0\0\0\0')
f.write(marshal.dumps(code))
f.close()
return code
class VFSSharedImporter:
""" This is a special importer that is added onto the meta_path
list, so that it is called before sys.path is traversed. It uses
special logic to load one of the "shared" packages, by searching
the entire sys.path for all instances of this shared package, and
merging them. """
def __init__(self):
pass
def find_module(self, fullname, path = None, reload = False):
#print >>sys.stderr, "shared find_module(%s), path = %s" % (fullname, path)
if fullname not in sharedPackages:
# Not a shared package; fall back to normal import.
return None
if path is None:
path = sys.path
excludePaths = []
if reload:
# If reload is true, we are simply reloading the module,
# looking for new paths to add.
mod = sys.modules[fullname]
excludePaths = getattr(mod, '_vfs_shared_path', None)
if excludePaths is None:
# If there isn't a _vfs_shared_path symbol already,
# the module must have been loaded through
# conventional means. Try to guess which path it was
# found on.
d = self.getLoadedDirname(mod)
excludePaths = [d]
loaders = []
for dir in path:
if dir in excludePaths:
continue
importer = sys.path_importer_cache.get(dir, None)
if importer is None:
try:
importer = VFSImporter(dir)
except ImportError:
continue
sys.path_importer_cache[dir] = importer
# It's a virtual file with no real-world existence. Dump
# it to disk.
ext = filename.get_extension()
tmp_filename = Filename.temporary('', filename.get_basename_wo_extension(),
'.' + ext,
type = Filename.T_dso)
tmp_filename.set_extension(ext)
tmp_filename.set_binary()
sin = vfile.open_read_file(True)
try:
loader = importer.find_module(fullname)
if not loader:
continue
except ImportError:
continue
sout = OFileStream()
if not tmp_filename.open_write(sout):
raise IOError
if not copy_stream(sin, sout):
raise IOError
finally:
vfile.close_read_file(sin)
del sout
loaders.append(loader)
# Delete when the process ends.
atexit.register(tmp_filename.unlink)
if not loaders:
return None
return VFSSharedLoader(loaders, reload = reload)
# Make a dummy spec to pass to create_dynamic with the path to
# our temporary file.
spec = ModuleSpec(spec.name, spec.loader,
origin=tmp_filename.to_os_specific(),
is_package=False)
def getLoadedDirname(self, mod):
""" Returns the directory name that the indicated
conventionally-loaded module must have been loaded from. """
module = _imp.create_dynamic(spec)
module.__file__ = filename.to_os_specific()
return module
if not getattr(mod, '__file__', None):
return None
def exec_module(self, module):
_imp.exec_dynamic(module)
fullname = mod.__name__
dirname = Filename.fromOsSpecific(mod.__file__).getDirname()
def is_package(self, fullname):
return False
parentname = None
basename = fullname
if '.' in fullname:
parentname, basename = fullname.rsplit('.', 1)
def get_code(self, fullname):
return None
path = None
if parentname:
parent = sys.modules[parentname]
path = parent.__path__
if path is None:
path = sys.path
for dir in path:
pdir = str(Filename.fromOsSpecific(dir))
if pdir + '/' + basename == dirname:
# We found it!
return dir
# Couldn't figure it out.
def get_source(self, fullname):
return None
class VFSSharedLoader:
""" The second part of VFSSharedImporter, this imports a list of
packages and combines them. """
class VFSNamespaceLoader(Loader):
def create_module(self, spec: ModuleSpec) -> ModuleType | None:
"""Use default semantics for module creation."""
def __init__(self, loaders, reload):
self.loaders = loaders
self.reload = reload
def exec_module(self, module: ModuleType) -> None:
pass
def load_module(self, fullname):
#print >>sys.stderr, "shared load_module(%s), loaders = %s" % (fullname, map(lambda l: l.dir_path, self.loaders))
def is_package(self, fullname):
return True
mod = None
message = None
path = []
vfs_shared_path = []
if self.reload:
mod = sys.modules[fullname]
path = mod.__path__ or []
if path == fullname:
# Work around Python bug setting __path__ of frozen modules.
path = []
vfs_shared_path = getattr(mod, '_vfs_shared_path', [])
def get_source(self, fullname):
return ''
for loader in self.loaders:
try:
mod = loader.load_module(fullname, loadingShared = True)
except ImportError:
etype, evalue, etraceback = sys.exc_info()
print("%s on %s: %s" % (etype.__name__, fullname, evalue))
if not message:
message = '%s: %s' % (fullname, evalue)
continue
for dir in getattr(mod, '__path__', []):
if dir not in path:
path.append(dir)
def get_code(self, fullname):
return compile('', '<string>', 'exec', dont_inherit=True)
if mod is None:
# If all of them failed to load, raise ImportError.
raise ImportError(message)
# If at least one of them loaded successfully, return the
# union of loaded modules.
mod.__path__ = path
mod.__package__ = fullname
# Also set this special symbol, which records that this is a
# shared package, and also lists the paths we have already
# loaded.
mod._vfs_shared_path = vfs_shared_path + [l.dir_path for l in self.loaders]
return mod
def _path_hook(entry: str) -> VFSFinder:
# If this is a directory in the VFS, create a VFSFinder for this entry.
vfile = vfs.get_file(Filename.from_os_specific(entry), False)
if vfile and vfile.is_directory() and not isinstance(vfile.get_mount(), VirtualFileMountSystem):
return VFSFinder(entry)
else:
raise ImportError
_registered = False
def register():
""" Register the VFSImporter on the path_hooks, if it has not
def register() -> None:
""" Register the VFSFinder on the path_hooks, if it has not
already been registered, so that future Python import statements
will vector through here (and therefore will take advantage of
Panda's virtual file system). """
@ -483,55 +273,9 @@ def register():
global _registered
if not _registered:
_registered = True
sys.path_hooks.insert(0, VFSImporter)
sys.meta_path.insert(0, VFSSharedImporter())
sys.path_hooks.insert(0, _path_hook)
# Blow away the importer cache, so we'll come back through the
# VFSImporter for every folder in the future, even those
# VFSFinder for every folder in the future, even those
# folders that previously were loaded directly.
sys.path_importer_cache = {}
def reloadSharedPackage(mod):
""" Reloads the specific module as a shared package, adding any
new directories that might have appeared on the search path. """
fullname = mod.__name__
path = None
if '.' in fullname:
parentname = fullname.rsplit('.', 1)[0]
parent = sys.modules[parentname]
path = parent.__path__
importer = VFSSharedImporter()
loader = importer.find_module(fullname, path = path, reload = True)
if loader:
loader.load_module(fullname)
# Also force any child packages to become shared packages, if
# they aren't already.
for basename, child in list(mod.__dict__.items()):
if isinstance(child, types.ModuleType):
childname = child.__name__
if childname == fullname + '.' + basename and \
hasattr(child, '__path__') and \
childname not in sharedPackages:
sharedPackages[childname] = True
reloadSharedPackage(child)
def reloadSharedPackages():
""" Walks through the sharedPackages list, and forces a reload of
any modules on that list that have already been loaded. This
allows new directories to be added to the search path. """
#print >> sys.stderr, "reloadSharedPackages, path = %s, sharedPackages = %s" % (sys.path, sharedPackages.keys())
# Sort the list, just to make sure parent packages are reloaded
# before child packages are.
for fullname in sorted(sharedPackages.keys()):
mod = sys.modules.get(fullname, None)
if not mod:
continue
reloadSharedPackage(mod)

View File

@ -45,15 +45,6 @@ ConfigureDef(config_showbase);
ConfigureFn(config_showbase) {
}
ConfigVariableSearchPath particle_path
("particle-path",
PRC_DESC("The directories to search for particle files to be loaded."));
ConfigVariableSearchPath &
get_particle_path() {
return particle_path;
}
// Throw the "NewFrame" event in the C++ world. Some of the lerp code depends
// on receiving this.
void

View File

@ -22,8 +22,6 @@
#include "animControl.h"
#include "pointerTo.h"
#include "dconfig.h"
#include "dSearchPath.h"
#include "configVariableSearchPath.h"
#include "nodePath.h"
ConfigureDecl(config_showbase, EXPCL_DIRECT_SHOWBASE, EXPTP_DIRECT_SHOWBASE);
@ -34,8 +32,6 @@ class GraphicsEngine;
BEGIN_PUBLISH
EXPCL_DIRECT_SHOWBASE ConfigVariableSearchPath &get_particle_path();
EXPCL_DIRECT_SHOWBASE void throw_new_frame();
EXPCL_DIRECT_SHOWBASE void init_app_for_gui();

View File

@ -5,6 +5,8 @@ in some compilation models, Panda's threading constructs are
incompatible with the OS-provided threads used by Python's thread
module. """
from __future__ import annotations
__all__ = [
'error', 'LockType',
'start_new_thread',
@ -19,6 +21,9 @@ __all__ = [
from panda3d import core
import sys
from collections.abc import Callable, Iterable, Mapping
from typing import Any
if sys.platform == "win32":
TIMEOUT_MAX = float(0xffffffff // 1000)
else:
@ -41,12 +46,12 @@ class LockType:
allows a different thread to release the lock than the one that
acquired it. """
def __init__(self):
def __init__(self) -> None:
self.__lock = core.Mutex('PythonLock')
self.__cvar = core.ConditionVar(self.__lock)
self.__locked = False
def acquire(self, waitflag = 1, timeout = -1):
def acquire(self, waitflag: bool = True, timeout: float = -1) -> bool:
self.__lock.acquire()
try:
if self.__locked and not waitflag:
@ -65,7 +70,7 @@ class LockType:
finally:
self.__lock.release()
def release(self):
def release(self) -> None:
self.__lock.acquire()
try:
if not self.__locked:
@ -90,18 +95,23 @@ class LockType:
_counter = 0
def _newname(template="Thread-%d"):
def _newname(template: str = "Thread-%d") -> str:
global _counter
_counter = _counter + 1
return template % _counter
_threads = {}
_threads: dict[int, tuple[core.Thread, dict[int, dict[str, Any]], Any | None]] = {}
_nextThreadId = 0
_threadsLock = core.Mutex('thread._threadsLock')
def start_new_thread(function, args, kwargs = {}, name = None):
def start_new_thread(
function: Callable[..., object],
args: Iterable[Any],
kwargs: Mapping[str, Any] = {},
name: str | None = None,
) -> int:
def threadFunc(threadId, function = function, args = args, kwargs = kwargs):
try:
try:
@ -132,7 +142,7 @@ def start_new_thread(function, args, kwargs = {}, name = None):
_threadsLock.release()
def _add_thread(thread, wrapper):
def _add_thread(thread: core.Thread, wrapper: Any) -> int:
""" Adds the indicated core.Thread object, with the indicated Python
wrapper, to the thread list. Returns the new thread ID. """
@ -150,7 +160,7 @@ def _add_thread(thread, wrapper):
_threadsLock.release()
def _get_thread_wrapper(thread, wrapperClass):
def _get_thread_wrapper(thread: core.Thread, wrapperClass: Callable[[core.Thread, int], Any]) -> Any:
""" Returns the thread wrapper for the indicated thread. If there
is not one, creates an instance of the indicated wrapperClass
instead. """
@ -222,7 +232,7 @@ def _get_thread_locals(thread, i):
_threadsLock.release()
def _remove_thread_id(threadId):
def _remove_thread_id(threadId: int) -> None:
""" Removes the thread with the indicated ID from the thread list. """
# On interpreter shutdown, Python may set module globals to None.
@ -250,11 +260,11 @@ def exit():
raise SystemExit
def allocate_lock():
def allocate_lock() -> LockType:
return LockType()
def get_ident():
def get_ident() -> int:
return core.Thread.getCurrentThread().this

View File

@ -21,12 +21,17 @@ easier to use and understand.
It is permissible to mix-and-match both threading and threading2
within the same application. """
from __future__ import annotations
from panda3d import core
from direct.stdpy import thread as _thread
import sys as _sys
import weakref
from collections.abc import Callable, Iterable, Mapping
from typing import Any, NoReturn
__all__ = [
'Thread',
'Lock', 'RLock',
@ -54,10 +59,14 @@ class ThreadBase:
""" A base class for both Thread and ExternalThread in this
module. """
def __init__(self):
name: str
ident: int
daemon: bool
def __init__(self) -> None:
pass
def getName(self):
def getName(self) -> str:
return self.name
def isDaemon(self):
@ -92,7 +101,15 @@ class Thread(ThreadBase):
object. The wrapper is designed to emulate Python's own
threading.Thread object. """
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None):
def __init__(
self,
group: None = None,
target: Callable[..., object] | None = None,
name: str | None = None,
args: Iterable[Any] = (),
kwargs: Mapping[str, Any] = {},
daemon: bool | None = None,
) -> None:
ThreadBase.__init__(self)
assert group is None
@ -131,7 +148,7 @@ class Thread(ThreadBase):
isAlive = is_alive
def start(self):
def start(self) -> None:
thread = self.__thread
if thread is None or thread.is_started():
raise RuntimeError
@ -147,7 +164,7 @@ class Thread(ThreadBase):
self.__target(*self.__args, **self.__kwargs)
def join(self, timeout = None):
def join(self, timeout: float | None = None) -> None:
# We don't support a timed join here, sorry.
assert timeout is None
thread = self.__thread
@ -157,7 +174,7 @@ class Thread(ThreadBase):
self.__thread = None
_thread._remove_thread_id(self.ident)
def setName(self, name):
def setName(self, name: str) -> None:
self.__dict__['name'] = name
self.__thread.setName(name)
@ -166,7 +183,7 @@ class ExternalThread(ThreadBase):
""" Returned for a Thread object that wasn't created by this
interface. """
def __init__(self, extThread, threadId):
def __init__(self, extThread: core.Thread, threadId: int) -> None:
ThreadBase.__init__(self)
self.__thread = extThread
@ -196,7 +213,7 @@ class ExternalThread(ThreadBase):
class MainThread(ExternalThread):
""" Returned for the MainThread object. """
def __init__(self, extThread, threadId):
def __init__(self, extThread: core.Thread, threadId: int) -> None:
ExternalThread.__init__(self, extThread, threadId)
self.__dict__['daemon'] = False
@ -206,7 +223,7 @@ class Lock(core.Mutex):
The wrapper is designed to emulate Python's own threading.Lock
object. """
def __init__(self, name = "PythonLock"):
def __init__(self, name: str = "PythonLock") -> None:
core.Mutex.__init__(self, name)
@ -224,7 +241,7 @@ class Condition(core.ConditionVar):
object. The wrapper is designed to emulate Python's own
threading.Condition object. """
def __init__(self, lock = None):
def __init__(self, lock: Lock | RLock | None = None) -> None:
if not lock:
lock = Lock()
@ -241,7 +258,7 @@ class Condition(core.ConditionVar):
def release(self):
self.__lock.release()
def wait(self, timeout = None):
def wait(self, timeout: float | None = None) -> None:
if timeout is None:
core.ConditionVar.wait(self)
else:
@ -373,8 +390,9 @@ class Timer(Thread):
self.finished.set()
def _create_thread_wrapper(t, threadId):
def _create_thread_wrapper(t: core.Thread, threadId: int) -> ExternalThread:
""" Creates a thread wrapper for the indicated external thread. """
pyt: ExternalThread
if isinstance(t, core.MainThread):
pyt = MainThread(t, threadId)
else:
@ -383,7 +401,7 @@ def _create_thread_wrapper(t, threadId):
return pyt
def current_thread():
def current_thread() -> ThreadBase:
t = core.Thread.getCurrentThread()
return _thread._get_thread_wrapper(t, _create_thread_wrapper)
@ -430,5 +448,5 @@ def setprofile(func):
_setprofile_func = func
def stack_size(size = None):
def stack_size(size: object = None) -> NoReturn:
raise ThreadError

View File

@ -13,6 +13,8 @@ to import Panda's thread reimplementation instead of the system thread
module, and so it is therefore layered on top of Panda's thread
implementation. """
from __future__ import annotations
import sys as _sys
import atexit as _atexit
@ -21,8 +23,10 @@ from direct.stdpy.thread import stack_size, _newname, _local as local
from panda3d import core
_sleep = core.Thread.sleep
from collections.abc import Callable, Iterable, Mapping
from time import time as _time
from traceback import format_exc as _format_exc
from typing import Any
__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
'enumerate', 'main_thread', 'TIMEOUT_MAX',
@ -51,12 +55,12 @@ if __debug__:
class _Verbose(object):
def __init__(self, verbose=None):
def __init__(self, verbose: bool | None = None) -> None:
if verbose is None:
verbose = _VERBOSE
self.__verbose = verbose
def _note(self, format, *args):
def _note(self, format: str, *args: Any) -> None:
if self.__verbose:
format = format % args
format = "%s: %s\n" % (
@ -66,9 +70,9 @@ if __debug__:
else:
# Disable this when using "python -O"
class _Verbose(object): # type: ignore[no-redef]
def __init__(self, verbose=None):
def __init__(self, verbose: bool | None = None) -> None:
pass
def _note(self, *args):
def _note(self, *args) -> None:
pass
# Support for profile and trace hooks
@ -88,15 +92,15 @@ def settrace(func):
Lock = _allocate_lock
def RLock(*args, **kwargs):
return _RLock(*args, **kwargs)
def RLock(verbose: bool | None = None) -> _RLock:
return _RLock(verbose)
class _RLock(_Verbose):
def __init__(self, verbose=None):
def __init__(self, verbose: bool | None = None) -> None:
_Verbose.__init__(self, verbose)
self.__block = _allocate_lock()
self.__owner = None
self.__owner: Thread | None = None
self.__count = 0
def __repr__(self):
@ -105,13 +109,13 @@ class _RLock(_Verbose):
self.__owner and self.__owner.getName(),
self.__count)
def acquire(self, blocking=1):
def acquire(self, blocking: bool = True) -> bool:
me = currentThread()
if self.__owner is me:
self.__count = self.__count + 1
if __debug__:
self._note("%s.acquire(%s): recursive success", self, blocking)
return 1
return True
rc = self.__block.acquire(blocking)
if rc:
self.__owner = me
@ -125,7 +129,7 @@ class _RLock(_Verbose):
__enter__ = acquire
def release(self):
def release(self) -> None:
me = currentThread()
assert self.__owner is me, "release() of un-acquire()d lock"
self.__count = count = self.__count - 1
@ -163,12 +167,12 @@ class _RLock(_Verbose):
return self.__owner is currentThread()
def Condition(*args, **kwargs):
return _Condition(*args, **kwargs)
def Condition(lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> _Condition:
return _Condition(lock, verbose)
class _Condition(_Verbose):
def __init__(self, lock=None, verbose=None):
def __init__(self, lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> None:
_Verbose.__init__(self, verbose)
if lock is None:
lock = RLock()
@ -180,18 +184,18 @@ class _Condition(_Verbose):
# these override the default implementations (which just call
# release() and acquire() on the lock). Ditto for _is_owned().
try:
self._release_save = lock._release_save
self._release_save = lock._release_save # type: ignore[method-assign, union-attr]
except AttributeError:
pass
try:
self._acquire_restore = lock._acquire_restore
self._acquire_restore = lock._acquire_restore # type: ignore[method-assign, union-attr]
except AttributeError:
pass
try:
self._is_owned = lock._is_owned
self._is_owned = lock._is_owned # type: ignore[method-assign, union-attr]
except AttributeError:
pass
self.__waiters = []
self.__waiters: list[_thread.LockType] = []
def __enter__(self):
return self.__lock.__enter__()
@ -202,22 +206,22 @@ class _Condition(_Verbose):
def __repr__(self):
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
def _release_save(self): # pylint: disable=method-hidden
def _release_save(self) -> Any: # pylint: disable=method-hidden
self.__lock.release() # No state to save
def _acquire_restore(self, x): # pylint: disable=method-hidden
def _acquire_restore(self, x) -> None: # pylint: disable=method-hidden
self.__lock.acquire() # Ignore saved state
def _is_owned(self): # pylint: disable=method-hidden
def _is_owned(self) -> bool: # pylint: disable=method-hidden
# Return True if lock is owned by currentThread.
# This method is called only if __lock doesn't have _is_owned().
if self.__lock.acquire(0):
if self.__lock.acquire(False):
self.__lock.release()
return False
else:
return True
def wait(self, timeout=None):
def wait(self, timeout: float | None = None) -> None:
assert self._is_owned(), "wait() of un-acquire()d lock"
waiter = _allocate_lock()
waiter.acquire()
@ -237,7 +241,7 @@ class _Condition(_Verbose):
endtime = _time() + timeout
delay = 0.0005 # 500 us -> initial delay of 1 ms
while True:
gotit = waiter.acquire(0)
gotit = waiter.acquire(False)
if gotit:
break
remaining = endtime - _time()
@ -258,7 +262,7 @@ class _Condition(_Verbose):
finally:
self._acquire_restore(saved_state)
def notify(self, n=1):
def notify(self, n: int = 1) -> None:
assert self._is_owned(), "notify() of un-acquire()d lock"
__waiters = self.__waiters
waiters = __waiters[:n]
@ -275,7 +279,7 @@ class _Condition(_Verbose):
except ValueError:
pass
def notifyAll(self):
def notifyAll(self) -> None:
self.notify(len(self.__waiters))
@ -381,7 +385,7 @@ class _Event(_Verbose):
# Active thread administration
_active_limbo_lock = _allocate_lock()
_active = {} # maps thread id to Thread object
_active: dict[int, Thread] = {} # maps thread id to Thread object
_limbo = {}
@ -400,8 +404,16 @@ class Thread(_Verbose):
# Protected by _active_limbo_lock.
__registered_atexit = False
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, verbose=None, daemon=None):
def __init__(
self,
group: None = None,
target: Callable[..., object] | None = None,
name: object = None,
args: Iterable[Any] = (),
kwargs: Mapping[str, Any] | None = None,
verbose: bool | None = None,
daemon: bool | None = None,
) -> None:
assert group is None, "group argument must be None for now"
_Verbose.__init__(self, verbose)
if kwargs is None:
@ -422,7 +434,7 @@ class Thread(_Verbose):
# sys.exc_info since it can be changed between instances
self.__stderr = _sys.stderr
def _set_daemon(self):
def _set_daemon(self) -> bool:
# Overridden in _MainThread and _DummyThread
return currentThread().isDaemon()
@ -437,7 +449,7 @@ class Thread(_Verbose):
status = status + " daemon"
return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
def start(self):
def start(self) -> None:
assert self.__initialized, "Thread.__init__() not called"
assert not self.__started, "thread already started"
if __debug__:
@ -457,11 +469,11 @@ class Thread(_Verbose):
self.__started = True
_sleep(0.000001) # 1 usec, to let the thread run (Solaris hack)
def run(self):
def run(self) -> None:
if self.__target:
self.__target(*self.__args, **self.__kwargs)
def __bootstrap(self):
def __bootstrap(self) -> None:
try:
self.__started = True
_active_limbo_lock.acquire()
@ -497,7 +509,7 @@ class Thread(_Verbose):
# Do the best job possible w/o a huge amt. of code to
# approximate a traceback (code ideas from
# Lib/traceback.py)
exc_type, exc_value, exc_tb = self.__exc_info()
exc_type, exc_value, exc_tb = self.__exc_info() # type: ignore[misc]
try:
self.__stderr.write("Exception in thread " + self.getName() +
" (most likely raised during interpreter shutdown):\n")
@ -523,13 +535,13 @@ class Thread(_Verbose):
except:
pass
def __stop(self):
def __stop(self) -> None:
self.__block.acquire()
self.__stopped = True
self.__block.notifyAll()
self.__block.release()
def __delete(self):
def __delete(self) -> None:
"Remove current thread from the dict of currently running threads."
# Notes about running with dummy_thread:
@ -563,7 +575,7 @@ class Thread(_Verbose):
finally:
_active_limbo_lock.release()
def join(self, timeout=None):
def join(self, timeout: float | None = None) -> None:
assert self.__initialized, "Thread.__init__() not called"
assert self.__started, "cannot join thread before it is started"
assert self is not currentThread(), "cannot join current thread"
@ -592,11 +604,11 @@ class Thread(_Verbose):
finally:
self.__block.release()
def getName(self):
def getName(self) -> str:
assert self.__initialized, "Thread.__init__() not called"
return self.__name
def setName(self, name):
def setName(self, name: object) -> None:
assert self.__initialized, "Thread.__init__() not called"
self.__name = str(name)
@ -606,7 +618,7 @@ class Thread(_Verbose):
isAlive = is_alive
def isDaemon(self):
def isDaemon(self) -> bool:
assert self.__initialized, "Thread.__init__() not called"
return self.__daemonic
@ -654,7 +666,7 @@ class _Timer(Thread):
class _MainThread(Thread):
def __init__(self):
def __init__(self) -> None:
Thread.__init__(self, name="MainThread")
self._Thread__started = True
_active_limbo_lock.acquire()
@ -688,13 +700,13 @@ class _MainThread(Thread):
class _DummyThread(Thread):
def __init__(self):
def __init__(self) -> None:
Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True)
# Thread.__block consumes an OS-level locking primitive, which
# can never be used by a _DummyThread. Since a _DummyThread
# instance is immortal, that's bad, so release this resource.
del self._Thread__block
del self._Thread__block # type: ignore[attr-defined]
self._Thread__started = True
_active_limbo_lock.acquire()
@ -710,7 +722,7 @@ class _DummyThread(Thread):
# Global API functions
def current_thread():
def current_thread() -> Thread:
try:
return _active[get_ident()]
except KeyError:

View File

@ -28,7 +28,7 @@ if hasattr(sys, 'getandroidapilevel'):
signal = None
else:
try:
import _signal as signal # type: ignore[import, no-redef]
import _signal as signal # type: ignore[import-not-found, no-redef]
except ImportError:
signal = None

View File

@ -48,7 +48,7 @@ def inspectorFor(anObject):
### initializing
def initializeInspectorMap():
def initializeInspectorMap() -> None:
global _InspectorMap
notFinishedTypes = ['BufferType', 'EllipsisType', 'FrameType', 'TracebackType', 'XRangeType']

View File

@ -3,7 +3,7 @@
__all__ = ['ParticlePanel']
# Import Tkinter, Pmw, and the floater code from this directory tree.
from panda3d.core import ColorBlendAttrib, Filename, Point2, Point3, Vec3, Vec4, getModelPath
from panda3d.core import ColorBlendAttrib, ConfigVariableSearchPath, Filename, Point2, Point3, Vec3, Vec4, getModelPath
from panda3d.physics import (
BaseParticleEmitter,
BaseParticleRenderer,
@ -36,7 +36,6 @@ from panda3d.physics import (
SpriteParticleRenderer,
TangentRingEmitter,
)
from panda3d.direct import getParticlePath
from direct.tkwidgets.AppShell import AppShell
from direct.tkwidgets import Dial
from direct.tkwidgets import Floater
@ -53,6 +52,10 @@ import os
import tkinter as tk
particlePath = ConfigVariableSearchPath("particle-path",
"The directories to search for particle files to be loaded.")
class ParticlePanel(AppShell):
# Override class variables
appname = 'Particle Panel'
@ -1275,7 +1278,7 @@ class ParticlePanel(AppShell):
def loadParticleEffectFromFile(self):
# Find path to particle directory
pPath = getParticlePath()
pPath = particlePath
if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.':
path = '.'
@ -1303,7 +1306,7 @@ class ParticlePanel(AppShell):
def saveParticleEffectToFile(self):
# Find path to particle directory
pPath = getParticlePath()
pPath = particlePath
if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.':
path = '.'
@ -2872,6 +2875,12 @@ class ParticlePanel(AppShell):
if type == 'FT_ONE_OVER_R_CUBED':
#f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED)
f.setFalloffType(2)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE':
f.setFalloffType(3)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED':
f.setFalloffType(4)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE_CUBED':
f.setFalloffType(5)
def setForceCenter(vec, f = force):
f.setForceCenter(Point3(vec[0], vec[1], vec[2]))
@ -2886,7 +2895,10 @@ class ParticlePanel(AppShell):
'Set force falloff type',
('FT_ONE_OVER_R',
'FT_ONE_OVER_R_SQUARED',
'FT_ONE_OVER_R_CUBED'),
'FT_ONE_OVER_R_CUBED',
'FT_ONE_OVER_R_OVER_DISTANCE',
'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED',
'FT_ONE_OVER_R_OVER_DISTANCE_CUBED'),
command = setFalloffType)
self.getWidget(pageName, forceName + ' Falloff').configure(
label_width = 16)
@ -2897,6 +2909,12 @@ class ParticlePanel(AppShell):
var.set('FT_ONE_OVER_R_SQUARED')
elif falloff == LinearDistanceForce.FTONEOVERRCUBED:
var.set('FT_ONE_OVER_R_CUBED')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCE:
var.set('FT_ONE_OVER_R_OVER_DISTANCE')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCESQUARED:
var.set('FT_ONE_OVER_R_OVER_DISTANCE_SQUARED')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCECUBED:
var.set('FT_ONE_OVER_R_OVER_DISTANCE_CUBED')
vec = force.getForceCenter()
self.createVector3Entry(frame, pageName, forceName + ' Center',
'Set center of force',

View File

@ -1,3 +1,32 @@
----------------------- RELEASE 1.10.16 -----------------------
This maintenance release fixes some minor defects and stability issues.
* OpenAL: Support non-default coordinate systems when playing 3D audio
* Tasks: Coroutine detect now also handles coroutine subclass to support Nuitka
* Tasks: Now properly handles generators without send()
* Windows: Fixes a hang when adjusting Z-order in some situations
* Fix SparseArray methods get_lowest_off_bit() and get_lowest_on_bit()
* Fix linecache error when distributing for newer Python versions
* Fix `await AsyncFuture.gather()` returning first item instead of tuple (#1738)
* Fix use-after-free in collision system with transform cache disabled (#1733)
* Egg: Add limited forward compatibility for metallic-roughness textures
* OpenGL: fix error blitting depth texture on macOS (#1719)
* OpenGL: fix SSBO not being rebound if deleted and recreated immediately
* OpenGL: fix SSBO support not being detected in certain situations
* GLSL: Add p3d_MetallicRoughnessTexture input mapped to M_metallic_roughness
* X11: Add config variable to enable detection of autorepeat key events (#1735)
* GUI: Fix bug with PGSliderBar dragging (#1722)
* Minor thread safety things for free-threaded Python builds
* Add forward compatibility for bam version 6.46
* Fixes a harmless buffer overflow in pdtoa
* Fix compilation issues with SDL version of tinydisplay (#1708)
* bam2egg: Fix issue when having more than two tags (#1725)
* Fix "Detected leak for ... which interrogate cannot delete." error (#1743)
* Fix PythonCallbackObject crash upon destruction in some cases
* PStats: Fix crash when receiving frames out of order
* PandaFramework::close_framework() now clears task manager of tasks
----------------------- RELEASE 1.10.15 -----------------------
This release adds support for Python 3.13, and fixes some significant bugs.

View File

@ -51,6 +51,16 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
endif()
# AddressSanitizer
if(ENABLE_ASAN)
if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GNU)")
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
else()
message(FATAL_ERROR "ENABLE_ASAN requires GCC or Clang")
endif()
endif()
# Panda3D is now a C++14 project.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

View File

@ -265,7 +265,7 @@ if(BUILD_INTERROGATE)
panda3d-interrogate
GIT_REPOSITORY https://github.com/panda3d/interrogate.git
GIT_TAG 03418d6d7ddda7fb99abf27230aa42d1d8bd607e
GIT_TAG 7cf2550d2c8d95b8c268aa4bb0b5602e85a086dc
PREFIX ${_interrogate_dir}
CMAKE_ARGS
@ -425,6 +425,14 @@ endif()
unset(_prefer_mimalloc)
#
# Sanitizers
#
option(ENABLE_ASAN
"Enable AddressSanitizer for detecting memory errors such as
buffer overflows, use-after-free, etc. Requires GCC or Clang." OFF)
#
# This section relates to mobile-device/phone support and options
#

View File

@ -133,6 +133,7 @@ check_include_file_cxx(dirent.h PHAVE_DIRENT_H)
check_include_file_cxx(ucontext.h PHAVE_UCONTEXT_H) #TODO doesn't work on OSX, use sys/ucontext.h
check_include_file_cxx(linux/input.h PHAVE_LINUX_INPUT_H)
check_include_file_cxx(stdint.h PHAVE_STDINT_H)
check_include_file_cxx(execinfo.h PHAVE_EXECINFO_H)
# Do we have Posix threads?
#set(HAVE_POSIX_THREADS ${CMAKE_USE_PTHREADS_INIT})

View File

@ -30,7 +30,7 @@ struct AtomicAdjust {
#include "atomicAdjustDummyImpl.h"
typedef AtomicAdjustDummyImpl AtomicAdjust;
#elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (defined(__clang__) && (__clang_major__ >= 3))
#elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (defined(__clang__) && (__clang_major__ >= 3) && !defined(WIN32_VC) )
// GCC 4.7 and above has built-in __atomic functions for atomic operations.
// Clang 3.0 and above also supports them.

View File

@ -88,7 +88,14 @@ allocate(size_t size, TypeHandle type_handle) {
return ptr;
#else // USE_DELETED_CHAIN
return PANDA_MALLOC_SINGLE(_buffer_size);
void *ptr = PANDA_MALLOC_SINGLE(_buffer_size);
#ifdef DO_MEMORY_USAGE
type_handle.inc_memory_usage(TypeHandle::MC_singleton, _buffer_size);
#endif // DO_MEMORY_USAGE
return ptr;
#endif // USE_DELETED_CHAIN
}
@ -133,6 +140,11 @@ deallocate(void *ptr, TypeHandle type_handle) {
_lock.unlock();
#else // USE_DELETED_CHAIN
#ifdef DO_MEMORY_USAGE
type_handle.dec_memory_usage(TypeHandle::MC_singleton, _buffer_size);
#endif // DO_MEMORY_USAGE
PANDA_FREE_SINGLE(ptr);
#endif // USE_DELETED_CHAIN
}
@ -155,8 +167,9 @@ get_deleted_chain(size_t buffer_size) {
static MutexImpl lock;
lock.lock();
static std::set<DeletedBufferChain> deleted_chains;
DeletedBufferChain *result = (DeletedBufferChain *)&*deleted_chains.insert(DeletedBufferChain(buffer_size)).first;
alignas(std::set<DeletedBufferChain>) static char storage[sizeof(std::set<DeletedBufferChain>)];
static auto *deleted_chains = new (storage) std::set<DeletedBufferChain>;
DeletedBufferChain *result = (DeletedBufferChain *)&*deleted_chains->insert(DeletedBufferChain(buffer_size)).first;
lock.unlock();
return result;
}

View File

@ -122,6 +122,50 @@ public:
#define ALLOC_DELETED_CHAIN_DEF(Type) \
DeletedChain< Type > Type::_deleted_chain;
#elif defined(DO_MEMORY_USAGE)
#define ALLOC_DELETED_CHAIN(Type) \
inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \
void *ptr = PANDA_MALLOC_SINGLE(size); \
get_type_handle(Type).inc_memory_usage(TypeHandle::MC_singleton, sizeof(Type)); \
return ptr; \
} \
inline void *operator new(size_t size, void *ptr) { \
(void) size; \
return ptr; \
} \
inline void operator delete(void *ptr) { \
if (ptr != nullptr) { \
get_type_handle(Type).dec_memory_usage(TypeHandle::MC_singleton, sizeof(Type)); \
PANDA_FREE_SINGLE(ptr); \
} \
} \
inline void operator delete(void *, void *) { \
} \
inline void *operator new[](size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \
void *ptr = PANDA_MALLOC_SINGLE(size); \
get_type_handle(Type).inc_memory_usage(TypeHandle::MC_array, sizeof(Type)); \
return ptr; \
} \
inline void *operator new[](size_t size, void *ptr) { \
(void) size; \
return ptr; \
} \
inline void operator delete[](void *ptr) { \
if (ptr != nullptr) { \
get_type_handle(Type).dec_memory_usage(TypeHandle::MC_array, sizeof(Type)); \
PANDA_FREE_SINGLE(ptr); \
} \
} \
inline void operator delete[](void *, void *) { \
} \
inline static bool validate_ptr(const void *ptr) { \
return (ptr != nullptr); \
}
#define ALLOC_DELETED_CHAIN_DECL(Type) ALLOC_DELETED_CHAIN(Type)
#define ALLOC_DELETED_CHAIN_DEF(Type)
#else // USE_DELETED_CHAIN
#define ALLOC_DELETED_CHAIN(Type) \

View File

@ -96,6 +96,9 @@
#elif defined(__arm__)
#define DTOOL_PLATFORM "linux_arm"
#elif defined(__riscv)
#define DTOOL_PLATFORM "linux_riscv"
#elif defined(__ppc__)
#define DTOOL_PLATFORM "linux_ppc"

View File

@ -618,6 +618,9 @@ determine_page_size() const {
_page_size = (size_t)sysinfo.dwPageSize;
#elif defined(ANDROID)
_page_size = getpagesize();
#else
// Posix case.
_page_size = sysconf(_SC_PAGESIZE);

View File

@ -454,6 +454,12 @@ patomic_notify_one(volatile uint32_t *value) {
#elif defined(_WIN32)
_patomic_wake_one_func((void *)value);
#elif defined(__APPLE__)
#ifndef __arm64__
if (UNLIKELY(__ulock_wake == nullptr || __ulock_wait == nullptr)) {
_patomic_notify_all(value);
return;
}
#endif
__ulock_wake(UL_COMPARE_AND_WAIT, (void *)value, 0);
#elif defined(HAVE_POSIX_THREADS)
_patomic_notify_all(value);
@ -472,6 +478,12 @@ patomic_notify_all(volatile uint32_t *value) {
#elif defined(_WIN32)
_patomic_wake_all_func((void *)value);
#elif defined(__APPLE__)
#ifndef __arm64__
if (UNLIKELY(__ulock_wake == nullptr || __ulock_wait == nullptr)) {
_patomic_notify_all(value);
return;
}
#endif
__ulock_wake(UL_COMPARE_AND_WAIT | ULF_WAKE_ALL, (void *)value, 0);
#elif defined(HAVE_POSIX_THREADS)
_patomic_notify_all(value);

View File

@ -125,7 +125,7 @@ initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
return emulated_wait(addr, cmp, size, timeout);
}
#elif !defined(CPPPARSER) && !defined(__linux__) && !defined(__APPLE__) && defined(HAVE_POSIX_THREADS)
#elif !defined(CPPPARSER) && !defined(__linux__) && (!defined(__APPLE__) || !defined(__arm64__)) && defined(HAVE_POSIX_THREADS)
// Same as above, but using pthreads.
struct alignas(64) WaitTableEntry {

View File

@ -36,9 +36,6 @@
// Undocumented API, see https://outerproduct.net/futex-dictionary.html
#define UL_COMPARE_AND_WAIT 1
#define ULF_WAKE_ALL 0x00000100
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout);
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value);
#endif
#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
@ -173,9 +170,17 @@ ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value);
EXPCL_DTOOL_DTOOLBASE extern BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD);
EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_one_func)(PVOID);
EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_all_func)(PVOID);
#elif !defined(__linux__) && !defined(__APPLE__) && defined(HAVE_POSIX_THREADS)
#elif defined(__APPLE__) && defined(__arm64__)
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout);
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value);
#elif !defined(__linux__) && defined(HAVE_POSIX_THREADS)
EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old);
EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value);
#ifdef __APPLE__
// Use conditionally since we can't count on support before 10.12.
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout) __attribute__((weak_import));
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value) __attribute__((weak_import));
#endif
#endif
#include "patomic.I"

View File

@ -252,7 +252,7 @@ inline static unsigned CountDecimalDigit32(uint32_t n) {
}
inline static void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 0, 0, 0, 0, 0 };
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 0, 0, 0, 0, 0, 0 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);

View File

@ -46,13 +46,16 @@ public:
typedef std::vector<Type, allocator> base_class;
typedef typename base_class::size_type size_type;
explicit pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { }
pvector() : base_class(allocator(get_type_handle(pvector<Type>))) { }
explicit pvector(TypeHandle type_handle) : base_class(allocator(type_handle)) { }
pvector(const pvector<Type> &copy) : base_class(copy) { }
pvector(pvector<Type> &&from) noexcept : base_class(std::move(from)) {};
explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { }
explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { }
pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { }
pvector(std::initializer_list<Type> init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator(type_handle)) { }
explicit pvector(size_type n, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(n, Type(), allocator(type_handle)) { }
explicit pvector(size_type n, const Type &value, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(n, value, allocator(type_handle)) { }
pvector(const Type *begin, const Type *end, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(allocator(type_handle)) {
this->insert(this->end(), begin, end);
}
pvector(std::initializer_list<Type> init, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(std::move(init), allocator(type_handle)) { }
pvector<Type> &operator =(const pvector<Type> &copy) {
base_class::operator =(copy);

View File

@ -26,6 +26,7 @@ TypeHandle double_type_handle;
TypeHandle float_type_handle;
TypeHandle string_type_handle;
TypeHandle wstring_type_handle;
TypeHandle vector_uchar_type_handle;
TypeHandle long_p_type_handle;
TypeHandle int_p_type_handle;
@ -59,6 +60,7 @@ void init_system_type_handles() {
register_type(float_type_handle, "float");
register_type(string_type_handle, "string");
register_type(wstring_type_handle, "wstring");
register_type(vector_uchar_type_handle, "vector_uchar");
register_type(int_p_type_handle, "int*");
register_type(short_p_type_handle, "short*");

View File

@ -19,6 +19,9 @@
#include "typeHandle.h"
#include "typeRegistry.h"
template<class T>
class pvector;
/**
* This inline function is just a convenient way to call
* TypeRegistry::register_type(), along with zero to four
@ -88,6 +91,7 @@ extern TypeHandle EXPCL_DTOOL_DTOOLBASE double_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE float_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE string_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE wstring_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE vector_uchar_type_handle;
extern TypeHandle long_p_type_handle;
extern TypeHandle int_p_type_handle;
@ -175,6 +179,16 @@ INLINE TypeHandle _get_type_handle(const std::wstring *) {
return wstring_type_handle;
}
template<class T>
INLINE TypeHandle _get_type_handle(const pvector<T> *) {
return pvector_type_handle;
}
template<>
INLINE TypeHandle _get_type_handle(const pvector<unsigned char> *) {
return vector_uchar_type_handle;
}
template<>
INLINE TypeHandle _get_type_handle(const long * const *) {
return long_p_type_handle;

View File

@ -69,6 +69,8 @@ set(P3DTOOLUTIL_IGATEEXT
globPattern_ext.h
iostream_ext.cxx
iostream_ext.h
pyenv_init.cxx
pyenv_init.h
textEncoder_ext.cxx
textEncoder_ext.h
)

View File

@ -0,0 +1,50 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file console_preamble.js
* @author rdb
* @date 2025-02-03
*/
if (ENVIRONMENT_IS_NODE) {
Module["preInit"] = Module["preInit"] || [];
Module["preInit"].push(function() {
if (typeof process === "object" && typeof process.env === "object") {
// These are made up by emscripten if we don't set them to undefined
ENV['USER'] = undefined;
ENV['LOGNAME'] = undefined;
ENV['PATH'] = undefined;
ENV['PWD'] = undefined;
ENV['HOME'] = undefined;
ENV['LANG'] = undefined;
ENV['_'] = undefined;
for (var variable in process.env) {
ENV[variable] = process.env[variable];
}
}
addOnPreMain(function preloadNodeEnv() {
var sp = stackSave();
var set_binary_name = wasmExports["_set_binary_name"];
if (set_binary_name && typeof __filename === "string") {
set_binary_name(stringToUTF8OnStack(__filename));
}
var set_env_var = wasmExports["_set_env_var"];
if (set_env_var) {
for (var variable in ENV) {
var value = ENV[variable];
if (value !== undefined) {
set_env_var(stringToUTF8OnStack(variable), stringToUTF8OnStack(value));
}
}
}
stackRestore(sp);
});
});
}

View File

@ -125,12 +125,18 @@ static const char *const libp3dtool_filenames[] = {
#if defined(__EMSCRIPTEN__) && !defined(CPPPARSER)
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value) {
_set_env_var(const char *var, const char *value) {
ExecutionEnvironment *ptr = ExecutionEnvironment::get_ptr();
ptr->_variables[std::string(var)] = std::string(value);
}
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_binary_name(const char *path) {
ExecutionEnvironment::set_binary_name(std::string(path));
}
#endif
// Linux with GNU libc does have global argvargc variables, but we can't
// Linux with GNU libc does have global argv/argc variables, but we can't
// safely access them at stat init time--at least, not in libc5. (It does seem
// to work with glibc2, however.)
@ -584,17 +590,10 @@ read_environment_variables() {
}
}
#elif defined(__EMSCRIPTEN__)
// We only have environment variables if we're running in node.js.
#ifndef CPPPARSER
EM_ASM({
if (typeof process === 'object' && typeof process.env === 'object') {
for (var variable in process.env) {
__set_env_var($0, stringToUTF8OnStack(variable),
stringToUTF8OnStack(process.env[variable]));
}
}
}, this);
#endif
// The environment variables get loaded in by the .js file before main()
// using the _set_env_var exported function, defined above. Trying to load
// env vars at static init time otherwise makes some optimizations more
// difficult, notably wasm-ctor-eval/wizer.
#elif defined(HAVE_PROC_SELF_ENVIRON)
// In some cases, we may have a file called procselfenviron that may be read

View File

@ -22,10 +22,10 @@
#include <map>
#if defined(__EMSCRIPTEN__) && !defined(CPPPARSER)
class ExecutionEnvironment;
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value);
_set_env_var(const char *var, const char *value);
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_binary_name(const char *path);
#endif
/**
@ -98,7 +98,7 @@ private:
static ExecutionEnvironment *_global_ptr;
#ifdef __EMSCRIPTEN__
friend void ::_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value);
friend void ::_set_env_var(const char *var, const char *value);
#endif
};

View File

@ -67,7 +67,7 @@ __init__(PyObject *path) {
return;
}
PyObject *path_str = PyObject_CallFunctionObjArgs(fspath, path, nullptr);
PyObject *path_str = PyObject_CallOneArg(fspath, path);
Py_DECREF(fspath);
if (path_str == nullptr) {
return;

View File

@ -134,8 +134,9 @@ readinto(PyObject *b) {
Py_buffer view;
if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG) == -1) {
PyErr_SetString(PyExc_TypeError,
"write() requires a contiguous, read-write bytes-like object");
PyErr_Format(PyExc_TypeError,
"readinto() requires a contiguous, read-write bytes-like object, not '%.100s'",
Py_TYPE(b)->tp_name);
return 0;
}
@ -260,7 +261,9 @@ write(PyObject *b) {
Py_buffer view;
if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG_RO) == -1) {
PyErr_SetString(PyExc_TypeError, "write() requires a contiguous buffer");
PyErr_Format(PyExc_TypeError,
"write() requires a bytes-like object, not '%.100s'",
Py_TYPE(b)->tp_name);
return;
}

View File

@ -22,6 +22,9 @@
#include <iostream>
#include "py_panda.h"
using std::ostream;
using std::istream;
/**
* These classes define the extension methods for istream and ostream, which
* are called instead of any C++ methods with the same prototype.

View File

@ -19,11 +19,15 @@ using std::string;
static Filename resolve_dso(const DSearchPath &path, const Filename &filename) {
if (filename.is_local()) {
if ((path.get_num_directories()==1)&&(path.get_directory(0)=="<auto>")) {
#ifdef ANDROID
return filename;
#else
// This is a special case, meaning to search in the same directory in
// which libp3dtool.dll, or the exe, was started from.
Filename dtoolpath = ExecutionEnvironment::get_dtool_name();
DSearchPath spath(dtoolpath.get_dirname());
return spath.find_file(filename);
#endif
} else {
return path.find_file(filename);
}
@ -119,7 +123,13 @@ get_dso_symbol(void *handle, const string &name) {
void *
load_dso(const DSearchPath &path, const Filename &filename) {
Filename abspath = resolve_dso(path, filename);
#ifdef ANDROID
// We just try to load it on Android, because we can't verify right now
// whether it might just be an unextracted library.
if (abspath.empty()) {
#else
if (!abspath.is_regular_file()) {
#endif
// Make sure the error flag is cleared, to prevent a subsequent call to
// load_dso_error() from returning a previously stored error.
dlerror();

View File

@ -1,4 +1,5 @@
#include "filename_ext.cxx"
#include "globPattern_ext.cxx"
#include "iostream_ext.cxx"
#include "pyenv_init.cxx"
#include "textEncoder_ext.cxx"

View File

@ -0,0 +1,80 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pyenv_init.cxx
* @author rdb
* @date 2025-02-02
*/
#include "pyenv_init.h"
#include "py_panda.h"
#include "executionEnvironment.h"
/**
* Called when panda3d.core is initialized, does some initialization specific
* to the Python environment.
*/
void
pyenv_init() {
// MAIN_DIR needs to be set very early; this seems like a convenient place
// to do that. Perhaps we'll find a better place for this in the future.
static bool initialized_main_dir = false;
if (!initialized_main_dir) {
/*if (interrogatedb_cat.is_debug()) {
// Good opportunity to print this out once, at startup.
interrogatedb_cat.debug()
<< "Python " << version << "\n";
}*/
if (!ExecutionEnvironment::has_environment_variable("MAIN_DIR")) {
// Grab the __main__ module and extract its __file__ attribute.
Filename main_dir;
PyObject *main_module = PyImport_ImportModule("__main__");
PyObject *file_attr = nullptr;
if (main_module != nullptr) {
file_attr = PyObject_GetAttrString(main_module, "__file__");
} else {
std::cerr << "Warning: unable to import __main__\n";
}
if (file_attr == nullptr) {
// Must be running in the interactive interpreter. Use the CWD.
main_dir = ExecutionEnvironment::get_cwd();
} else {
#if PY_MAJOR_VERSION >= 3
Py_ssize_t length;
wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length);
if (buffer != nullptr) {
main_dir = Filename::from_os_specific_w(std::wstring(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
PyMem_Free(buffer);
}
#else
char *buffer;
Py_ssize_t length;
if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) {
main_dir = Filename::from_os_specific(std::string(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
}
#endif
else {
std::cerr << "Invalid string for __main__.__file__\n";
}
}
ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific());
PyErr_Clear();
}
initialized_main_dir = true;
}
// Also, while we are at it, initialize the thread swap hook.
#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
global_thread_state_swap = PyThreadState_Swap;
#endif
}

View File

@ -0,0 +1,14 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pyenv_init.h
* @author rdb
* @date 2025-02-02
*/
extern "C" void pyenv_init();

View File

@ -94,7 +94,7 @@ encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
* given encoding.
*/
PyObject *Extension<TextEncoder>::
encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) {
encode_wtext(const std::wstring &wtext, TextEncoder::Encoding encoding) {
std::string value = TextEncoder::encode_wtext(wtext, encoding);
return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
}

View File

@ -19,3 +19,25 @@
#define NAME vector_uchar
#include "vector_src.cxx"
/**
* Writes a hex representation of the bytes to the output stream.
*/
std::ostream &
operator << (std::ostream &out, const vector_uchar &data) {
static const char nibble_to_hex[] = "0123456789abcdef";
if (data.empty()) {
return out;
}
out << nibble_to_hex[(data[0] & 0xf0) >> 4];
out << nibble_to_hex[data[0] & 0xf];
for (size_t i = 1; i < data.size(); ++i) {
out << ' ';
out << nibble_to_hex[(data[i] & 0xf0) >> 4];
out << nibble_to_hex[data[i] & 0xf];
}
return out;
}

View File

@ -30,4 +30,7 @@
#include "vector_src.h"
EXPCL_DTOOL_DTOOLUTIL std::ostream &
operator << (std::ostream &out, const vector_uchar &data);
#endif

View File

@ -2,7 +2,6 @@ set(P3IGATERUNTIME_HEADERS
interrogate_request.h
py_compat.h
py_panda.h py_panda.I
py_wrappers.h
)
install(FILES ${P3IGATERUNTIME_HEADERS} COMPONENT CoreDevel DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d)

View File

@ -44,12 +44,12 @@ EXPCL_INTERROGATEDB void interrogate_request_database(const char *database_filen
/* The more sophisticated interface uses these structures. */
typedef struct {
typedef struct InterrogateUniqueNameDef {
const char *name;
int index_offset;
} InterrogateUniqueNameDef;
typedef struct {
typedef struct InterrogateModuleDef {
int file_identifier;
const char *library_name;

View File

@ -219,6 +219,14 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
}
#endif
#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET
# define PY_VECTORCALL_ARGUMENTS_OFFSET (((size_t)1) << (8 * sizeof(size_t) - 1))
#endif
#if PY_VERSION_HEX < 0x030800B1
# define PyVectorcall_NARGS(n) ((n) & ~PY_VECTORCALL_ARGUMENTS_OFFSET)
#endif
/* Python 3.9 */
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
@ -229,6 +237,10 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
# define PyCFunction_CheckExact(op) (Py_IS_TYPE(op, &PyCFunction_Type))
#endif
#if PY_VERSION_HEX < 0x030900A4
# define PyObject_Vectorcall(callable, args, nargsf, kwnames) (_PyObject_Vectorcall((callable), (args), (nargsf), (kwnames)))
#endif
#if PY_VERSION_HEX < 0x03090000
INLINE PyObject *PyObject_CallNoArgs(PyObject *func) {
#if PY_VERSION_HEX >= 0x03080000
@ -368,6 +380,12 @@ PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) {
# define Py_END_CRITICAL_SECTION2() }
#endif
/* Python 3.14 */
#if PY_VERSION_HEX < 0x030E00A8
# define PyUnstable_Object_IsUniquelyReferenced(op) (Py_REFCNT((op)) == 1)
#endif
/* Other Python implementations */
#endif // HAVE_PYTHON

View File

@ -59,63 +59,6 @@ DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &target_c
return false;
}
/**
* Function to create a hash from a wrapped Python object.
*/
INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self) {
if (self != nullptr && DtoolInstance_Check(self)) {
return (Py_hash_t)(intptr_t)DtoolInstance_VOID_PTR(self);
}
return -1;
}
/**
* Python 2-style comparison function that compares objects by pointer.
*/
INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2) {
void *v1_this = DtoolInstance_Check(v1) ? DtoolInstance_VOID_PTR(v1) : nullptr;
void *v2_this = DtoolInstance_Check(v2) ? DtoolInstance_VOID_PTR(v2) : nullptr;
if (v1_this != nullptr && v2_this != nullptr) {
return (v1_this > v2_this) - (v1_this < v2_this);
} else {
return (v1 > v2) - (v1 < v2);
}
}
/**
* Rich comparison function that compares objects by pointer.
*/
INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op) {
int cmpval = DtoolInstance_ComparePointers(v1, v2);
Py_RETURN_RICHCOMPARE(cmpval, 0, op);
}
/**
* Utility function for assigning a PyObject pointer while managing refcounts.
*/
ALWAYS_INLINE void
Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value) {
PyObject *prev_value = ptr;
if (prev_value != value) {
ptr = Py_XNewRef(value);
Py_XDECREF(prev_value);
}
}
/**
* Converts the enum value to a C long.
*/
INLINE long Dtool_EnumValue_AsLong(PyObject *value) {
PyObject *val = PyObject_GetAttrString(value, "value");
if (val != nullptr) {
long as_long = PyLongOrInt_AS_LONG(val);
Py_DECREF(val);
return as_long;
} else {
return -1;
}
}
/**
* These functions wrap a pointer for a class that defines get_type_handle().
*/
@ -167,23 +110,6 @@ DTool_PyInit_Finalize(PyObject *self, void *local_this, Dtool_PyTypedObject *typ
return 0;
}
/**
* Checks that the tuple is empty.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args) {
return PyTuple_GET_SIZE(args) == 0;
}
/**
* Checks that the tuple is empty, and that the dict is empty or NULL.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) {
return PyTuple_GET_SIZE(args) == 0 &&
(kwds == nullptr || PyDict_GET_SIZE(kwds) == 0);
}
/**
* The following functions wrap an arbitrary C++ value into a PyObject.
*/
@ -324,7 +250,8 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) {
return value;
}
ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value) {
template<class Allocator>
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char, Allocator> &value) {
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
#else

View File

@ -8,15 +8,14 @@
#include <set>
#include <map>
#include <string>
#include <vector>
#ifdef USE_DEBUG_PYTHON
#define Py_DEBUG
#endif
#include "pnotify.h"
#include "vector_uchar.h"
#include "register_type.h"
#include "interrogate_request.h"
#if defined(HAVE_PYTHON) && !defined(CPPPARSER)
@ -24,8 +23,6 @@
#include "py_compat.h"
#include <structmember.h>
using namespace std;
// this is tempory .. untill this is glued better into the panda build system
#if defined(_WIN32) && !defined(LINK_ALL_STATIC)
@ -86,83 +83,6 @@ struct Dtool_PyTypedObject {
CoerceFunction _Dtool_Coerce;
};
// This is now simply a forward declaration. The actual definition is created
// by the code generator.
#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \
extern Dtool_PyTypedObject Dtool_##CLASS_NAME;
// More Macro(s) to Implement class functions.. Usually used if C++ needs type
// information
#define Define_Dtool_new(CLASS_NAME,CNAME)\
static PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds) {\
(void) args; (void) kwds;\
PyObject *self = type->tp_alloc(type, 0);\
((Dtool_PyInstDef *)self)->_signature = PY_PANDA_SIGNATURE;\
((Dtool_PyInstDef *)self)->_My_Type = &Dtool_##CLASS_NAME;\
return self;\
}
// The following used to be in the above macro, but it doesn't seem to be
// necessary as tp_alloc memsets the object to 0.
// ((Dtool_PyInstDef *)self)->_ptr_to_object = NULL;
// ((Dtool_PyInstDef *)self)->_memory_rules = false;
// ((Dtool_PyInstDef *)self)->_is_const = false;
// Delete functions..
#ifdef NDEBUG
#define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
Py_TYPE(self)->tp_free(self);\
}
#else // NDEBUG
#define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
std::cerr << "Detected leak for " << #CLASS_NAME \
<< " which interrogate cannot delete.\n"; \
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#endif // NDEBUG
#define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
delete (CNAME *)DtoolInstance_VOID_PTR(self);\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
unref_delete((CNAME *)DtoolInstance_VOID_PTR(self));\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
unref_delete((ReferenceCount *)(CNAME *)DtoolInstance_VOID_PTR(self));\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_Simple_FreeInstance(CLASS_NAME, CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
((Dtool_InstDef_##CLASS_NAME *)self)->_value.~##CLASS_NAME();\
Py_TYPE(self)->tp_free(self);\
}
// Extract the PyTypeObject pointer corresponding to a Dtool_PyTypedObject.
#define Dtool_GetPyTypeObject(type) (&(type)->_PyType)
@ -178,31 +98,12 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
#define DtoolInstance_INIT_PTR(obj, ptr) { ((Dtool_PyInstDef *)obj)->_ptr_to_object = (void*)(ptr); }
#define DtoolInstance_UPCAST(obj, type) (((Dtool_PyInstDef *)(obj))->_My_Type->_Dtool_UpcastInterface((obj), &(type)))
// ** HACK ** allert.. Need to keep a runtime type dictionary ... that is
// forward declared of typed object. We rely on the fact that typed objects
// are uniquly defined by an integer.
#if PY_VERSION_HEX >= 0x030d0000
class Dtool_TypeMap : public std::map<std::string, Dtool_PyTypedObject *> {
public:
PyMutex _lock { 0 };
};
#else
typedef std::map<std::string, Dtool_PyTypedObject *> Dtool_TypeMap;
#endif
EXPCL_PYPANDA Dtool_TypeMap *Dtool_GetGlobalTypeMap();
class DtoolProxy {
public:
mutable PyObject *_self;
TypeHandle _type;
};
EXPCL_PYPANDA void DtoolProxy_Init(DtoolProxy *proxy, PyObject *self,
Dtool_PyTypedObject &classdef,
TypeRegistry::PythonWrapFunc *wrap_func);
/**
*/
@ -218,23 +119,11 @@ EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into);
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef);
INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self);
INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2);
INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op);
// Functions related to error reporting.
EXPCL_PYPANDA bool _Dtool_CheckErrorOccurred();
#ifdef NDEBUG
#define Dtool_CheckErrorOccurred() (UNLIKELY(PyErr_Occurred() != nullptr))
#else
#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred()))
#endif
EXPCL_PYPANDA PyObject *Dtool_Raise_AssertionError();
EXPCL_PYPANDA PyObject *Dtool_Raise_TypeError(const char *message);
EXPCL_PYPANDA PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name);
EXPCL_PYPANDA PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute);
EXPCL_PYPANDA int Dtool_Raise_CantDeleteAttributeError(const char *attribute);
EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError();
EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError(const char *message);
@ -250,31 +139,6 @@ EXPCL_PYPANDA int _Dtool_Raise_BadArgumentsError_Int(const char *message);
#define Dtool_Raise_BadArgumentsError_Int(x) _Dtool_Raise_BadArgumentsError_Int(x)
#endif
// These functions are similar to Dtool_WrapValue, except that they also
// contain code for checking assertions and exceptions when compiling with
// NDEBUG mode on.
EXPCL_PYPANDA PyObject *_Dtool_Return_None();
EXPCL_PYPANDA PyObject *Dtool_Return_Bool(bool value);
EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value);
#ifdef NDEBUG
#define Dtool_Return_None() (LIKELY(PyErr_Occurred() == nullptr) ? (Py_NewRef(Py_None)) : nullptr)
#define Dtool_Return(value) (LIKELY(PyErr_Occurred() == nullptr) ? value : nullptr)
#else
#define Dtool_Return_None() _Dtool_Return_None()
#define Dtool_Return(value) _Dtool_Return(value)
#endif
ALWAYS_INLINE void Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value);
/**
* Wrapper around Python 3.4's enum library, which does not have a C API.
*/
EXPCL_PYPANDA PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names,
const char *module = nullptr);
INLINE long Dtool_EnumValue_AsLong(PyObject *value);
/**
*/
@ -291,102 +155,9 @@ template<class T> INLINE PyObject *DTool_CreatePyInstance(T *obj, bool memory_ru
template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(const T *obj, bool memory_rules);
template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(T *obj, bool memory_rules);
// Macro(s) class definition .. Used to allocate storage and init some values
// for a Dtool Py Type object.
// struct Dtool_PyTypedObject Dtool_##CLASS_NAME;
#define Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
extern struct Dtool_PyTypedObject Dtool_##CLASS_NAME;\
static int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\
static PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);
#define Define_Module_Class(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_Class_Private(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_ClassRef_Private(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_ClassRef(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
// The finalizer for simple instances.
INLINE int DTool_PyInit_Finalize(PyObject *self, void *This, Dtool_PyTypedObject *type, bool memory_rules, bool is_const);
// A heler function to glu methed definition together .. that can not be done
// at code generation time becouse of multiple generation passes in
// interigate..
typedef std::map<std::string, PyMethodDef *> MethodDefmap;
// We need a way to runtime merge compile units into a python "Module" .. this
// is done with the fallowing structors and code.. along with the support of
// interigate_module
struct Dtool_TypeDef {
const char *const name;
Dtool_PyTypedObject *type;
};
struct LibraryDef {
PyMethodDef *const _methods;
const Dtool_TypeDef *const _types;
Dtool_TypeDef *const _external_types;
const InterrogateModuleDef *const _module_def;
};
#if PY_MAJOR_VERSION >= 3
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def);
#else
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename);
#endif
// HACK.... Be carefull Dtool_BorrowThisReference This function can be used to
// grab the "THIS" pointer from an object and use it Required to support fom
// historical inharatence in the for of "is this instance of"..
EXPCL_PYPANDA PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args);
#define DTOOL_PyObject_HashPointer DtoolInstance_HashPointer
#define DTOOL_PyObject_ComparePointers DtoolInstance_ComparePointers
EXPCL_PYPANDA PyObject *
copy_from_make_copy(PyObject *self, PyObject *noargs);
EXPCL_PYPANDA PyObject *
copy_from_copy_constructor(PyObject *self, PyObject *noargs);
EXPCL_PYPANDA PyObject *
map_deepcopy_to_copy(PyObject *self, PyObject *args);
/**
* These functions check whether the arguments passed to a function conform to
* certain expectations.
*/
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args);
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds);
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds);
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds);
/**
* These functions convert a C++ value into the corresponding Python object.
* This used to be generated by the code generator, but it seems more reliable
@ -411,7 +182,8 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(char value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(std::nullptr_t);
ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value);
template<class Allocator>
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char, Allocator> &value);
#if PY_MAJOR_VERSION >= 0x02060000
ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);
@ -422,9 +194,12 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::pair<T1, T2> &value);
EXPCL_PYPANDA Dtool_PyTypedObject *Dtool_GetSuperBase();
#include "py_panda.I"
/**
* Creates a Python generator object from a next() function.
*/
EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
#include "py_wrappers.h"
#include "py_panda.I"
#endif // HAVE_PYTHON && !CPPPARSER

View File

@ -1,61 +0,0 @@
/**
* @file py_wrappers.h
* @author rdb
* @date 2017-11-26
*/
#ifndef PY_WRAPPERS_H
#define PY_WRAPPERS_H
#include "py_panda.h"
#ifdef HAVE_PYTHON
/**
* These classes are returned from properties that require a subscript
* interface, ie. something.children[i] = 3.
*/
struct Dtool_WrapperBase {
PyObject_HEAD;
PyObject *_self;
const char *_name;
};
struct Dtool_SequenceWrapper {
Dtool_WrapperBase _base;
lenfunc _len_func;
ssizeargfunc _getitem_func;
};
struct Dtool_MutableSequenceWrapper {
Dtool_WrapperBase _base;
lenfunc _len_func;
ssizeargfunc _getitem_func;
ssizeobjargproc _setitem_func;
PyObject *(*_insert_func)(PyObject *, size_t, PyObject *);
};
struct Dtool_MappingWrapper {
union {
Dtool_WrapperBase _base;
Dtool_SequenceWrapper _keys;
};
binaryfunc _getitem_func;
objobjargproc _setitem_func;
};
struct Dtool_GeneratorWrapper {
Dtool_WrapperBase _base;
iternextfunc _iternext_func;
};
EXPCL_PYPANDA Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
EXPCL_PYPANDA PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
#endif // HAVE_PYTHON
#endif // PY_WRAPPERS_H

View File

View File

@ -52,4 +52,9 @@ PyObject _Py_FalseStruct;
typedef void *visitproc;
typedef enum {
PyRefTracer_CREATE = 0,
PyRefTracer_DESTROY = 1,
} PyRefTracerEvent;
#endif // PYTHON_H

View File

@ -0,0 +1 @@
#include <xmmintrin.h>

View File

@ -0,0 +1,29 @@
#pragma once
#include <initializer_list>
namespace std {
class random_device;
class seed_seq;
template<class IntType = int> class uniform_int_distribution;
template<class RealType = double> class uniform_real_distribution;
class bernoulli_distribution;
template<class IntType = int> class binomial_distribution;
template<class IntType = int> class geometric_distribution;
template<class IntType = int> class negative_binomial_distribution;
template<class IntType = int> class poisson_distribution;
template<class RealType = double> class exponential_distribution;
template<class RealType = double> class gamma_distribution;
template<class RealType = double> class weibull_distribution;
template<class RealType = double> class extreme_value_distribution;
template<class RealType = double> class normal_distribution;
template<class RealType = double> class lognormal_distribution;
template<class RealType = double> class chi_squared_distribution;
template<class RealType = double> class cauchy_distribution;
template<class RealType = double> class fisher_f_distribution;
template<class RealType = double> class student_t_distribution;
template<class IntType = int> class discrete_distribution;
template<class RealType = double> class piecewise_constant_distribution;
template<class RealType = double> class piecewise_linear_distribution;
}

View File

@ -97,7 +97,6 @@ reload_implicit_pages() {
}
_implicit_pages.clear();
#ifndef ANDROID
// If we are running inside a deployed application, see if it exposes
// information about how the PRC data should be initialized.
struct BlobInfo {
@ -129,11 +128,13 @@ reload_implicit_pages() {
// const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo");
#elif defined(__EMSCRIPTEN__)
const BlobInfo *blobinfo = nullptr;
#elif defined(ANDROID)
const BlobInfo *blobinfo = nullptr;
#else
const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(nullptr, RTLD_NOW), "blobinfo");
#endif
if (blobinfo == nullptr) {
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__)
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(ANDROID)
// Clear the error flag.
dlerror();
#endif
@ -482,7 +483,6 @@ reload_implicit_pages() {
}
}
}
#endif // ANDROID
if (!_loaded_implicit) {
config_initialized();
@ -492,6 +492,21 @@ reload_implicit_pages() {
_currently_loading = false;
invalidate_cache();
// These important variables are updated here to avoid recursion, since
// they may be accessed by the PRC system.
ConfigVariableBool notify_timestamp
("notify-timestamp", false,
PRC_DESC("Set true to output the date & time with each notify message."));
NotifyCategory::_notify_timestamp = notify_timestamp;
#ifndef NDEBUG
ConfigVariableBool check_debug_notify_protect
("check-debug-notify-protect", false,
PRC_DESC("Set true to issue a warning message if a debug or spam "
"notify output is not protected within an if statement."));
NotifyCategory::_check_debug_notify_protect = check_debug_notify_protect;
#endif
#ifdef USE_PANDAFILESTREAM
// Update this very low-level config variable here, for lack of any better
// place.

View File

@ -27,6 +27,7 @@
#endif
#ifdef ANDROID
#include <sys/stat.h>
#include <android/log.h>
#include "androidLogStream.h"
#endif
@ -35,6 +36,18 @@
#include "emscriptenLogStream.h"
#endif
#ifndef NDEBUG
#ifdef PHAVE_EXECINFO_H
#include <execinfo.h> // for backtrace()
#include <dlfcn.h>
#include <cxxabi.h>
#endif
#ifdef _WIN32
#include <dbghelp.h>
#endif
#endif // NDEBUG
using std::cerr;
using std::cout;
using std::ostream;
@ -348,15 +361,16 @@ assert_failure(const char *expression, int line,
<< expression << " at line " << line << " of " << source_file;
string message = message_str.str();
if (!_assert_failed) {
Notify *self = ptr();
if (!self->_assert_failed) {
// We only save the first assertion failure message, as this is usually
// the most meaningful when several occur in a row.
_assert_failed = true;
_assert_error_message = message;
self->_assert_failed = true;
self->_assert_error_message = message;
}
if (has_assert_handler()) {
return (*_assert_handler)(expression, line, source_file);
if (self->has_assert_handler()) {
return (*self->_assert_handler)(expression, line, source_file);
}
#ifdef ANDROID
@ -380,6 +394,33 @@ assert_failure(const char *expression, int line,
// Make sure the error message has been flushed to the output.
nout.flush();
// Capture and list a stack trace.
#ifdef NDEBUG
#elif defined(PHAVE_EXECINFO_H)
void *trace[64];
int size = backtrace(trace, 64);
if (size > 0) {
// Remove the frame(s) corresponding to the current function.
void **tracep = trace;
void *return_addr = __builtin_return_address(0);
for (int i = 0; i < size; ++i) {
if (trace[i] == return_addr) {
tracep = trace + (i + 1);
size -= i + 1;
break;
}
}
write_backtrace(tracep, size);
}
#elif defined(_WIN32)
const ULONG max_size = 62;
void *trace[max_size];
int size = CaptureStackBackTrace(1, max_size, trace, nullptr);
if (size > 0) {
write_backtrace(trace, size);
}
#endif
#ifdef _MSC_VER
// How to trigger an exception in VC++ that offers to take us into the
// debugger? abort() doesn't do it. We used to be able to assert(false),
@ -411,6 +452,141 @@ assert_failure(const char *expression, int line,
return true;
}
/**
*
*/
void Notify::
write_backtrace(void **trace, int size) {
std::ostream &out = nout;
#ifdef NDEBUG
#elif defined(PHAVE_EXECINFO_H)
char namebuf[128];
for (int i = 0; i < size; ++i) {
void *addr = trace[i];
Dl_info info;
if (dladdr((char *)addr - 1, &info) != 0) {
const char *name = nullptr;
if (info.dli_sname != nullptr) {
int status = 0;
size_t size = 0;
char *demangled = nullptr;
if (info.dli_sname[0] == '_') {
size = sizeof(namebuf) - 1;
demangled = abi::__cxa_demangle(info.dli_sname, namebuf, &size, &status);
}
if (status == 0 && demangled != nullptr) {
namebuf[size] = 0;
name = demangled;
//if (strncmp(name, "Notify::assert_failure", 22) == 0) {
//continue;
//}
} else {
name = info.dli_sname;
}
}
out << "[" << addr << "] ";
if (info.dli_fname != nullptr) {
const char *slash = strrchr(info.dli_fname, '/');
out << std::left << std::setw(30) << (slash ? slash + 1 : info.dli_fname) << " ";
}
if (name != nullptr) {
out << name;
} else {
out << info.dli_saddr;
}
int offset = reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(info.dli_saddr);
if (offset > 0) {
out << " + " << offset;
}
out << "\n";
} else {
out << "[" << addr << "]\n";
}
}
#elif defined(_WIN32)
HMODULE handle = LoadLibraryA("dbghelp.dll");
if (!handle) {
return;
}
auto pSymInitialize = (BOOL (WINAPI *)(HANDLE, PCSTR, BOOL))GetProcAddress(handle, "SymInitialize");
auto pSymCleanup = (BOOL (WINAPI *)(HANDLE))GetProcAddress(handle, "SymCleanup");
auto pSymFromAddr = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD64 *, PSYMBOL_INFO))GetProcAddress(handle, "SymFromAddr");
auto pSymGetModuleInfo64 = (BOOL (WINAPI *)(HANDLE, DWORD64, PIMAGEHLP_MODULE64))GetProcAddress(handle, "SymGetModuleInfo64");
auto pSymGetLineFromAddr64 = (BOOL (WINAPI *)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64))GetProcAddress(handle, "SymGetLineFromAddr64");
if (!pSymInitialize || !pSymCleanup || !pSymFromAddr) {
FreeLibrary(handle);
return;
}
HANDLE process = GetCurrentProcess();
pSymInitialize(process, nullptr, TRUE);
for (int i = 0; i < size; ++i) {
DWORD64 addr = (DWORD64)trace[i];
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + 256] = {0};
SYMBOL_INFO *symbol = (SYMBOL_INFO *)buffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = 255;
out << "[" << (void *)addr << "]";
// Show the name of the library.
IMAGEHLP_MODULE64 module;
module.SizeOfStruct = sizeof(module);
const char *basename = "???";
if (pSymGetModuleInfo64 && pSymGetModuleInfo64(process, addr, &module)) {
const char *slash = strrchr(module.ImageName, '\\');
basename = (slash ? slash + 1 : module.ImageName);
}
// Look up the symbol name. If the reported name comes from the export
// table and the address is way off, ignore it, it's probably not right.
if (pSymFromAddr(process, addr, nullptr, symbol) &&
((symbol->Flags & SYMFLAG_EXPORT) == 0 || addr - (DWORD64)symbol->Address < 0x4000)) {
out << " " << std::left << std::setw(30) << basename << " " << symbol->Name;
DWORD64 offset = addr - (DWORD64)(symbol->Address);
if (offset > 0) {
out << " + " << offset;
}
// Look up filename + line number if available.
IMAGEHLP_LINE64 line;
DWORD displacement = 0;
line.SizeOfStruct = sizeof(line);
if (pSymGetLineFromAddr64(process, addr, &displacement, &line)) {
const char *slash = strrchr(line.FileName, '\\');
const char *basename = (slash ? slash + 1 : line.FileName);
out << " (" << basename << ":" << line.LineNumber;
//if (displacement > 0) {
// out << " + " << displacement;
//}
out << ")";
}
} else {
out << " " << basename;
}
out << "\n";
}
pSymCleanup(process);
FreeLibrary(handle);
#endif
out.flush();
}
/**
* Given a string, one of "debug", "info", "warning", etc., return the
* corresponding Severity level, or NS_unspecified if none of the strings
@ -460,22 +636,7 @@ config_initialized() {
// notify-output even after the initial import of Panda3D modules. However,
// it cannot be changed after the first time it is set.
#if defined(ANDROID)
// Android redirects stdio and stderr to /dev/null,
// but does provide its own logging system. We use a special
// type of stream that redirects it to Android's log system.
Notify *ptr = Notify::ptr();
for (int severity = 0; severity <= NS_fatal; ++severity) {
int priority = ANDROID_LOG_UNKNOWN;
if (severity != NS_unspecified) {
priority = severity + 1;
}
ptr->_log_streams[severity] = new AndroidLogStream(priority);
}
#elif defined(__EMSCRIPTEN__)
#if defined(__EMSCRIPTEN__)
// We have no writable filesystem in JavaScript. Instead, we set up a
// special stream that logs straight into the Javascript console.
@ -540,11 +701,38 @@ config_initialized() {
}
#endif // BUILD_IPHONE
}
#ifdef ANDROID
for (int severity = 0; severity <= NS_fatal; ++severity) {
ptr->_log_streams[severity] = ptr->_ostream_ptr;
}
} else {
// By default, we always redirect the notify stream to the Android log.
// By default, we always redirect the notify stream to the Android log,
// except if we are running from the adb shell. We decide this based
// on whether stderr is redirected to /dev/null.
Notify *ptr = Notify::ptr();
ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true);
struct stat a, b;
if (fstat(STDERR_FILENO, &a) == 0 && stat("/dev/null", &b) == 0 &&
a.st_dev == b.st_dev && a.st_ino == b.st_ino) {
// Android redirects stdio and stderr to /dev/null,
// but does provide its own logging system. We use a special
// type of stream that redirects it to Android's log system.
for (int severity = 0; severity <= NS_fatal; ++severity) {
int priority = ANDROID_LOG_UNKNOWN;
if (severity != NS_unspecified) {
priority = severity + 1;
}
ptr->_log_streams[severity] = new AndroidLogStream(priority);
}
ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true);
} else {
// Running from the terminal, set all the log streams to point to the
// same output.
for (int severity = 0; severity <= NS_fatal; ++severity) {
ptr->_log_streams[severity] = &cerr;
}
}
#endif
}
}

View File

@ -21,6 +21,9 @@
#include <time.h> // for strftime().
#include <assert.h>
bool NotifyCategory::_notify_timestamp = false;
bool NotifyCategory::_check_debug_notify_protect = false;
long NotifyCategory::_server_delta = 0;
/**
@ -55,7 +58,7 @@ std::ostream &NotifyCategory::
out(NotifySeverity severity, bool prefix) const {
if (is_on(severity)) {
if (prefix) {
if (get_notify_timestamp()) {
if (_notify_timestamp) {
// Format a timestamp to include as a prefix as well.
time_t now = time(nullptr) + _server_delta;
struct tm atm;
@ -79,7 +82,7 @@ out(NotifySeverity severity, bool prefix) const {
return Notify::out(severity);
}
} else if (severity <= NS_debug && get_check_debug_notify_protect()) {
} else if (severity <= NS_debug && _check_debug_notify_protect) {
// Someone issued a debug Notify output statement without protecting it
// within an if statement. This can cause a significant runtime
// performance hit, since it forces the iostream library to fully format
@ -172,36 +175,3 @@ update_severity_cache() {
mark_cache_valid(_local_modified);
}
/**
* Returns the value of the notify-timestamp ConfigVariable. This is defined
* using a method accessor rather than a static ConfigVariableBool, to protect
* against the variable needing to be accessed at static init time.
*/
bool NotifyCategory::
get_notify_timestamp() {
static ConfigVariableBool *notify_timestamp = nullptr;
if (notify_timestamp == nullptr) {
notify_timestamp = new ConfigVariableBool
("notify-timestamp", false,
"Set true to output the date & time with each notify message.");
}
return *notify_timestamp;
}
/**
* Returns the value of the check-debug-notify-protect ConfigVariable. This
* is defined using a method accessor rather than a static ConfigVariableBool,
* to protect against the variable needing to be accessed at static init time.
*/
bool NotifyCategory::
get_check_debug_notify_protect() {
static ConfigVariableBool *check_debug_notify_protect = nullptr;
if (check_debug_notify_protect == nullptr) {
check_debug_notify_protect = new ConfigVariableBool
("check-debug-notify-protect", false,
"Set true to issue a warning message if a debug or spam "
"notify output is not protected within an if statement.");
}
return *check_debug_notify_protect;
}

View File

@ -76,8 +76,6 @@ PUBLISHED:
private:
std::string get_config_name() const;
void update_severity_cache();
static bool get_notify_timestamp();
static bool get_check_debug_notify_protect();
std::string _fullname;
std::string _basename;
@ -86,12 +84,16 @@ private:
typedef std::vector<NotifyCategory *> Children;
Children _children;
static bool _notify_timestamp;
static bool _check_debug_notify_protect;
static long _server_delta; // not a time_t because server delta may be signed.
AtomicAdjust::Integer _local_modified;
NotifySeverity _severity_cache;
friend class Notify;
friend class ConfigPageManager;
};
INLINE std::ostream &operator << (std::ostream &out, const NotifyCategory &cat);

View File

@ -76,10 +76,12 @@ PUBLISHED:
public:
static ios_fmtflags get_literal_flag();
bool assert_failure(const std::string &expression, int line,
const char *source_file);
bool assert_failure(const char *expression, int line,
const char *source_file);
static bool assert_failure(const std::string &expression, int line,
const char *source_file);
static bool assert_failure(const char *expression, int line,
const char *source_file);
static void write_backtrace(void **trace, int size);
static NotifySeverity string_severity(const std::string &string);
@ -100,7 +102,7 @@ private:
Categories _categories;
#if defined(ANDROID)
AndroidLogStream *_log_streams[NS_fatal + 1];
std::ostream *_log_streams[NS_fatal + 1];
#elif defined(__EMSCRIPTEN__)
EmscriptenLogStream *_log_streams[NS_fatal + 1];
#endif
@ -204,7 +206,7 @@ private:
#define nassertr(condition, return_value) \
{ \
if (_nassert_check(condition)) { \
if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \
if (Notify::assert_failure(#condition, __LINE__, __FILE__)) { \
return return_value; \
} \
} \
@ -213,7 +215,7 @@ private:
#define nassertv(condition) \
{ \
if (_nassert_check(condition)) { \
if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \
if (Notify::assert_failure(#condition, __LINE__, __FILE__)) { \
return; \
} \
} \
@ -221,12 +223,12 @@ private:
#define nassertd(condition) \
if (_nassert_check(condition) && \
Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__))
Notify::assert_failure(#condition, __LINE__, __FILE__))
#define nassertr_always(condition, return_value) nassertr(condition, return_value)
#define nassertv_always(condition) nassertv(condition)
#define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__)
#define nassert_raise(message) Notify::assert_failure(message, __LINE__, __FILE__)
#endif // NDEBUG

View File

@ -114,6 +114,56 @@ output_c_string(std::ostream &out, const string &string_name,
*/
EVP_PKEY *
generate_key() {
#if OPENSSL_VERSION_MAJOR >= 3
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
if (ctx == nullptr) {
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
if (EVP_PKEY_keygen_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024) <= 0) {
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
BIGNUM *e = BN_new();
if (e == nullptr || !BN_set_word(e, 7)) {
BN_free(e);
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
unsigned char *e_buf = nullptr;
int e_len = BN_num_bytes(e);
e_buf = (unsigned char *)OPENSSL_malloc(e_len);
BN_bn2bin(e, e_buf);
size_t bits = 1024;
OSSL_PARAM params[] = {
OSSL_PARAM_construct_size_t("bits", &bits),
OSSL_PARAM_construct_BN("e", e_buf, e_len),
OSSL_PARAM_END
};
EVP_PKEY *pkey = nullptr;
if (EVP_PKEY_CTX_set_params(ctx, params) <= 0 ||
EVP_PKEY_generate(ctx, &pkey) <= 0) {
OPENSSL_free(e_buf);
BN_free(e);
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
OPENSSL_free(e_buf);
BN_free(e);
EVP_PKEY_CTX_free(ctx);
return pkey;
#else
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
if (rsa == nullptr || e == nullptr) {
@ -134,6 +184,7 @@ generate_key() {
EVP_PKEY *pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa);
return pkey;
#endif
}
/**

View File

@ -383,6 +383,7 @@ SectionGroup "Python modules" SecGroupPython
!insertmacro PyBindingSection 3.12-32 .cp312-win32.pyd
!insertmacro PyBindingSection 3.13-32 .cp313-win32.pyd
!insertmacro PyBindingSection 3.14-32 .cp314-win32.pyd
!insertmacro PyBindingSection 3.15-32 .cp315-win32.pyd
!else
!insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd
!insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd
@ -394,6 +395,7 @@ SectionGroup "Python modules" SecGroupPython
!insertmacro PyBindingSection 3.12 .cp312-win_amd64.pyd
!insertmacro PyBindingSection 3.13 .cp313-win_amd64.pyd
!insertmacro PyBindingSection 3.14 .cp314-win_amd64.pyd
!insertmacro PyBindingSection 3.15 .cp315-win_amd64.pyd
!endif
SectionGroupEnd
@ -506,6 +508,7 @@ Function .onInit
!insertmacro MaybeEnablePyBindingSection 3.12-32
!insertmacro MaybeEnablePyBindingSection 3.13-32
!insertmacro MaybeEnablePyBindingSection 3.14-32
!insertmacro MaybeEnablePyBindingSection 3.15-32
${EndIf}
!else
!insertmacro MaybeEnablePyBindingSection 3.5
@ -519,6 +522,7 @@ Function .onInit
!insertmacro MaybeEnablePyBindingSection 3.12
!insertmacro MaybeEnablePyBindingSection 3.13
!insertmacro MaybeEnablePyBindingSection 3.14
!insertmacro MaybeEnablePyBindingSection 3.15
${EndIf}
!endif
@ -548,6 +552,10 @@ Function .onInit
SectionSetFlags ${SecPyBindings3.14} ${SF_RO}
SectionSetInstTypes ${SecPyBindings3.14} 0
!endif
!ifdef SecPyBindings3.15
SectionSetFlags ${SecPyBindings3.15} ${SF_RO}
SectionSetInstTypes ${SecPyBindings3.15} 0
!endif
${EndUnless}
FunctionEnd
@ -853,6 +861,7 @@ Section Uninstall
!insertmacro RemovePythonPath 3.12-32
!insertmacro RemovePythonPath 3.13-32
!insertmacro RemovePythonPath 3.14-32
!insertmacro RemovePythonPath 3.15-32
!else
!insertmacro RemovePythonPath 3.5
!insertmacro RemovePythonPath 3.6
@ -864,6 +873,7 @@ Section Uninstall
!insertmacro RemovePythonPath 3.12
!insertmacro RemovePythonPath 3.13
!insertmacro RemovePythonPath 3.14
!insertmacro RemovePythonPath 3.15
!endif
SetDetailsPrint both
@ -936,6 +946,7 @@ SectionEnd
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12-32} $(DESC_SecPyBindings3.12-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13-32} $(DESC_SecPyBindings3.13-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14-32} $(DESC_SecPyBindings3.14-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.15-32} $(DESC_SecPyBindings3.15-32)
!else
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6)
@ -947,6 +958,7 @@ SectionEnd
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12} $(DESC_SecPyBindings3.12)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13} $(DESC_SecPyBindings3.13)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14} $(DESC_SecPyBindings3.14)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.15} $(DESC_SecPyBindings3.15)
!endif
!ifdef INCLUDE_PYVER
!insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)

Some files were not shown because too many files have changed in this diff Show More