Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Open Toontown 2023-10-09 00:06:40 -04:00
commit c7ebf71634
403 changed files with 19852 additions and 9258 deletions

View File

@ -40,7 +40,7 @@ jobs:
eigen: NO
- profile: macos-eigen-coverage-unity-xcode
os: macOS-10.15
os: macOS-11
config: Coverage
unity: YES
generator: Xcode
@ -50,7 +50,7 @@ jobs:
eigen: YES
- profile: macos-nometa-standard-makefile
os: macOS-10.15
os: macOS-11
config: Standard
unity: NO
generator: Unix Makefiles
@ -87,7 +87,7 @@ jobs:
fetch-depth: 10
- name: Self-destruct makepanda
run: python makepanda/selfdestruct.py --yes
run: python3 makepanda/selfdestruct.py --yes
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
@ -113,7 +113,7 @@ jobs:
libeigen3-dev 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
python3-setuptools
python3-setuptools python3-tk
- name: Cache dependencies (Windows)
if: runner.os == 'Windows'
@ -180,38 +180,6 @@ jobs:
run: cmake --build . --config ${{ matrix.config }} --parallel 4
# END A
- name: Setup Python (Python 3.7)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v4
with:
python-version: '3.7'
- name: Configure (Python 3.7)
if: contains(matrix.python, 'YES')
working-directory: build
shell: bash
run: >
cmake -DWANT_PYTHON_VERSION=3.7 -DHAVE_PYTHON=YES
-DPython_FIND_REGISTRY=NEVER -DPython_ROOT="$pythonLocation" .
- name: Build (Python 3.7)
if: contains(matrix.python, 'YES')
# BEGIN A
working-directory: build
run: cmake --build . --config ${{ matrix.config }} --parallel 4
# END A
- name: Test (Python 3.7)
# BEGIN B
if: contains(matrix.python, 'YES')
working-directory: build
shell: bash
env:
PYTHONPATH: ${{ matrix.config }}
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install pytest pytest-cov
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=.
# END B
- name: Setup Python (Python 3.8)
if: contains(matrix.python, 'YES')
uses: actions/setup-python@v4
@ -239,7 +207,7 @@ jobs:
PYTHONPATH: ${{ matrix.config }}
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install pytest pytest-cov
$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=.
# END B
@ -271,7 +239,7 @@ jobs:
PYTHONPATH: ${{ matrix.config }}
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install pytest pytest-cov
$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=.
# END B
@ -303,7 +271,7 @@ jobs:
PYTHONPATH: ${{ matrix.config }}
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install pytest pytest-cov
$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=.
# END B
@ -335,7 +303,7 @@ jobs:
PYTHONPATH: ${{ matrix.config }}
run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install pytest pytest-cov
$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=.
# END B
@ -398,7 +366,7 @@ jobs:
- name: Test Python 3.11
shell: bash
run: |
python -m pip install pytest
python -m pip install -r requirements-test.txt
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.10
@ -412,7 +380,7 @@ jobs:
- name: Test Python 3.10
shell: bash
run: |
python -m pip install pytest
python -m pip install -r requirements-test.txt
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.9
@ -426,7 +394,7 @@ jobs:
- name: Test Python 3.9
shell: bash
run: |
python -m pip install pytest
python -m pip install -r requirements-test.txt
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.8
@ -440,21 +408,7 @@ jobs:
- name: Test Python 3.8
shell: bash
run: |
python -m pip install pytest
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.7
uses: actions/setup-python@v4
with:
python-version: '3.7'
- name: Build Python 3.7
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
- name: Test Python 3.7
shell: bash
run: |
python -m pip install pytest
python -m pip install -r requirements-test.txt
PYTHONPATH=built LD_LIBRARY_PATH=built/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Make installer

23
.github/workflows/mypy.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Run Mypy
on: [push, pull_request]
jobs:
mypy:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.8', '3.11']
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy==1.4.0
- name: Run mypy on direct
run: python tests/run_mypy.py

4
.gitignore vendored
View File

@ -17,6 +17,7 @@ vgcore.*
*.save.1
*.sublime-workspace
.vscode/
.idea/
# Temporary build files
/_vfsimporter.*
@ -51,9 +52,10 @@ cmake_install.cmake
install_manifest.txt
CTestTestfile.cmake
# Windows
# Operating system
Thumbs.db
ehthumbs.db
.DS_Store
# macOS
.DS_Store

View File

@ -17,6 +17,8 @@ This is a list of all the people who are contributing financially to Panda3D. I
* Max Voss
* Hawkheart
* Veronica
* Cody Sevier
* Marek Alexa
## Enthusiasts
@ -28,6 +30,8 @@ This is a list of all the people who are contributing financially to Panda3D. I
* SureBet
* Gyedo Jeon
* GameDev JONI
* Max Rodriguez
* Jethro Schoppenhorst
## Backers

View File

@ -24,6 +24,9 @@ endif()
# Set defaults for macOS, must be before project().
if(APPLE)
# Needed for enable_language(OBJCXX)
cmake_minimum_required(VERSION 3.16)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum macOS version to target")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
@ -46,6 +49,11 @@ project(Panda3D VERSION ${_version})
unset(_version)
unset(_s)
if(APPLE)
# Allows separating out C++ flags from ObjC++ flags
enable_language(OBJCXX)
endif()
# Determine the possible build types. Must be *after* calling project().
set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")

View File

@ -1905,7 +1905,7 @@ class main(wx.Frame):
self.txaExtraLines.append(line)
txafile.close()
except:
print "Error opening .txa file!"
print("Error opening .txa file!")
self.palettize_saveTxaTxt.SetValue(os.path.join(dirname + os.sep , filename))
dlg.Destroy()
@ -2705,7 +2705,7 @@ class main(wx.Frame):
selectedItemIndex = int(self.batchTree.GetItemText(selectedItemId).split()[0])-1
batchItem = self.batchList[selectedItemIndex]
print '\n'+self.BuildCommand(batchItem)
print('\n'+self.BuildCommand(batchItem))
if (batchItem['cmd'].count('maya2egg')):
# Display Maya2Egg Tool Panel
@ -2840,7 +2840,7 @@ class main(wx.Frame):
self.txaExtraLines.append(line)
txafile.close()
except:
print "Error opening .txa file!"
print("Error opening .txa file!")
self.batchItemNameTxt.SetValue(batchItem['label'])

View File

@ -280,9 +280,7 @@ class Actor(DirectObject, NodePath):
self.setLODNode(node = lodNode)
# preserve numerical order for lod's
# this will make it easier to set ranges
sortedKeys = list(models.keys())
sortedKeys.sort()
for lodName in sortedKeys:
for lodName in sorted(models):
# make a node under the LOD switch
# for each lod (just because!)
self.addLOD(str(lodName))
@ -302,9 +300,7 @@ class Actor(DirectObject, NodePath):
# it is a single part actor w/LOD
self.setLODNode(node = lodNode)
# preserve order of LOD's
sortedKeys = list(models.keys())
sortedKeys.sort()
for lodName in sortedKeys:
for lodName in sorted(models):
self.addLOD(str(lodName))
# pass in dictionary of parts
self.loadModel(models[lodName], lodName=lodName,
@ -323,9 +319,7 @@ class Actor(DirectObject, NodePath):
if isinstance(models, dict):
if isinstance(models[next(iter(models))], dict):
# then we have a multi-part w/ LOD
sortedKeys = list(models.keys())
sortedKeys.sort()
for lodName in sortedKeys:
for lodName in sorted(models):
# iterate over both dicts
for partName in anims:
self.loadAnims(
@ -336,9 +330,7 @@ class Actor(DirectObject, NodePath):
self.loadAnims(anims[partName], partName)
elif isinstance(models, dict):
# then we have single-part w/ LOD
sortedKeys = list(models.keys())
sortedKeys.sort()
for lodName in sortedKeys:
for lodName in sorted(models):
self.loadAnims(anims, lodName=lodName)
else:
# else it is single-part w/o LOD
@ -603,9 +595,6 @@ class Actor(DirectObject, NodePath):
return bundles
def __updateSortedLODNames(self):
# Cache the sorted LOD names so we don't have to grab them
# and sort them every time somebody asks for the list
self.__sortedLODNames = list(self.__partBundleDict.keys())
# Reverse sort the doing a string->int
def sortKey(x):
if not str(x).isdigit():
@ -622,7 +611,9 @@ class Actor(DirectObject, NodePath):
else:
return int(x)
self.__sortedLODNames.sort(key=sortKey, reverse=True)
# Cache the sorted LOD names so we don't have to grab them
# and sort them every time somebody asks for the list
self.__sortedLODNames = sorted(self.__partBundleDict, key=sortKey, reverse=True)
def getLODNames(self):
"""

View File

@ -169,12 +169,9 @@ class ClusterClient(DirectObject.DirectObject):
self.serverList[server].sendNamedMovementDone()
def redoSortedPriorities(self):
self.sortedControlMappings = []
for key in self.controlMappings:
self.sortedControlMappings.append([self.controlPriorities[key],
key])
self.sortedControlMappings.sort()
self.sortedControlMappings = sorted(
[self.controlPriorities[key], key] for key in self.controlMappings
)
def moveObject(self, nodePath, object, serverList, offset, hasColor = True):
self.notify.debug('moving object '+object)

View File

@ -137,13 +137,9 @@ class ClusterServer(DirectObject.DirectObject):
self.objectMappings.pop(name)
def redoSortedPriorities(self):
self.sortedControlMappings = []
for key in self.objectMappings:
self.sortedControlMappings.append([self.controlPriorities[key],
key])
self.sortedControlMappings.sort()
self.sortedControlMappings = sorted(
[self.controlPriorities[key], key] for key in self.objectMappings
)
def addControlMapping(self, objectName, controlledName, offset = None,
priority = 0):

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
/* A Bison parser, made by GNU Bison 3.1. */
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc.
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -15,7 +16,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
@ -30,6 +31,10 @@
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED
# define YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED
/* Debug traces. */
@ -40,54 +45,63 @@
extern int dcyydebug;
#endif
/* Token type. */
/* Token kinds. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
UNSIGNED_INTEGER = 258,
SIGNED_INTEGER = 259,
REAL = 260,
STRING = 261,
IDENTIFIER = 262,
HEX_STRING = 263,
KEYWORD = 264,
KW_DCLASS = 265,
KW_STRUCT = 266,
KW_FROM = 267,
KW_IMPORT = 268,
KW_TYPEDEF = 269,
KW_KEYWORD = 270,
KW_SWITCH = 271,
KW_CASE = 272,
KW_DEFAULT = 273,
KW_BREAK = 274,
KW_INT8 = 275,
KW_INT16 = 276,
KW_INT32 = 277,
KW_INT64 = 278,
KW_UINT8 = 279,
KW_UINT16 = 280,
KW_UINT32 = 281,
KW_UINT64 = 282,
KW_FLOAT64 = 283,
KW_STRING = 284,
KW_BLOB = 285,
KW_BLOB32 = 286,
KW_INT8ARRAY = 287,
KW_INT16ARRAY = 288,
KW_INT32ARRAY = 289,
KW_UINT8ARRAY = 290,
KW_UINT16ARRAY = 291,
KW_UINT32ARRAY = 292,
KW_UINT32UINT8ARRAY = 293,
KW_CHAR = 294,
START_DC = 295,
START_PARAMETER_VALUE = 296,
START_PARAMETER_DESCRIPTION = 297
YYEMPTY = -2,
YYEOF = 0, /* "end of file" */
YYerror = 256, /* error */
YYUNDEF = 257, /* "invalid token" */
UNSIGNED_INTEGER = 258, /* UNSIGNED_INTEGER */
SIGNED_INTEGER = 259, /* SIGNED_INTEGER */
REAL = 260, /* REAL */
STRING = 261, /* STRING */
IDENTIFIER = 262, /* IDENTIFIER */
HEX_STRING = 263, /* HEX_STRING */
KEYWORD = 264, /* KEYWORD */
KW_DCLASS = 265, /* KW_DCLASS */
KW_STRUCT = 266, /* KW_STRUCT */
KW_FROM = 267, /* KW_FROM */
KW_IMPORT = 268, /* KW_IMPORT */
KW_TYPEDEF = 269, /* KW_TYPEDEF */
KW_KEYWORD = 270, /* KW_KEYWORD */
KW_SWITCH = 271, /* KW_SWITCH */
KW_CASE = 272, /* KW_CASE */
KW_DEFAULT = 273, /* KW_DEFAULT */
KW_BREAK = 274, /* KW_BREAK */
KW_INT8 = 275, /* KW_INT8 */
KW_INT16 = 276, /* KW_INT16 */
KW_INT32 = 277, /* KW_INT32 */
KW_INT64 = 278, /* KW_INT64 */
KW_UINT8 = 279, /* KW_UINT8 */
KW_UINT16 = 280, /* KW_UINT16 */
KW_UINT32 = 281, /* KW_UINT32 */
KW_UINT64 = 282, /* KW_UINT64 */
KW_FLOAT64 = 283, /* KW_FLOAT64 */
KW_STRING = 284, /* KW_STRING */
KW_BLOB = 285, /* KW_BLOB */
KW_BLOB32 = 286, /* KW_BLOB32 */
KW_INT8ARRAY = 287, /* KW_INT8ARRAY */
KW_INT16ARRAY = 288, /* KW_INT16ARRAY */
KW_INT32ARRAY = 289, /* KW_INT32ARRAY */
KW_UINT8ARRAY = 290, /* KW_UINT8ARRAY */
KW_UINT16ARRAY = 291, /* KW_UINT16ARRAY */
KW_UINT32ARRAY = 292, /* KW_UINT32ARRAY */
KW_UINT32UINT8ARRAY = 293, /* KW_UINT32UINT8ARRAY */
KW_CHAR = 294, /* KW_CHAR */
START_DC = 295, /* START_DC */
START_PARAMETER_VALUE = 296, /* START_PARAMETER_VALUE */
START_PARAMETER_DESCRIPTION = 297 /* START_PARAMETER_DESCRIPTION */
};
typedef enum yytokentype yytoken_kind_t;
#endif
/* Tokens. */
/* Token kinds. */
#define YYEMPTY -2
#define YYEOF 0
#define YYerror 256
#define YYUNDEF 257
#define UNSIGNED_INTEGER 258
#define SIGNED_INTEGER 259
#define REAL 260
@ -134,6 +148,8 @@ extern int dcyydebug;
extern YYSTYPE dcyylval;
int dcyyparse (void);
#endif /* !YY_DCYY_BUILT_TMP_DCPARSER_YXX_H_INCLUDED */

View File

@ -297,6 +297,7 @@ dclass_or_struct:
dclass:
KW_DCLASS optional_name
{
$<u.dclass>$ = current_class;
current_class = new DCClass(dc_file, $2, false, false);
}
dclass_derivation '{' dclass_fields '}'
@ -401,6 +402,7 @@ dclass_field:
struct:
KW_STRUCT optional_name
{
$<u.dclass>$ = current_class;
current_class = new DCClass(dc_file, $2, true, false);
}
struct_derivation '{' struct_fields '}'
@ -488,6 +490,7 @@ struct_field:
atomic_field:
optional_name '('
{
$<u.atomic>$ = current_atomic;
if (current_class == nullptr) {
yyerror("Cannot define a method outside of a struct or class.");
DCClass *temp_class = new DCClass(dc_file, "temp", false, false); // memory leak.
@ -1243,12 +1246,13 @@ optional_name:
switch:
KW_SWITCH optional_name '(' parameter_or_atomic ')' '{'
{
$<u.dswitch>$ = current_switch;
current_switch = new DCSwitch($2, $4);
}
switch_fields '}'
{
$$ = current_switch;
current_switch = (DCSwitch *)$<u.parameter>7;
current_switch = $<u.dswitch>7;
}
;

View File

@ -15,7 +15,7 @@ to and may be replaced by the following code:
base = ShowBase()
"""
__all__ = []
__all__ = ()
if __debug__:
print('Using deprecated DirectStart interface.')

View File

@ -6,7 +6,7 @@ from direct.showbase import ShowBase
base = ShowBase.ShowBase()
# Put an axis in the world:
base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
base.camLens.setFov(52.0)

View File

@ -8,7 +8,7 @@ from direct.showbase import ThreeUpShow
base = ThreeUpShow.ThreeUpShow()
# Put an axis in the world:
base.loader.loadModel("models/misc/xyzAxis").reparentTo(render)
base.loader.loadModel("models/misc/xyzAxis").reparentTo(base.render)
base.camera.setPosHpr(0, -10.0, 0, 0, 0, 0)
base.camLens.setFov(52.0)

View File

@ -41,8 +41,12 @@ class DirectJoybox(DirectObject):
xyzMultiplier = 1.0
hprMultiplier = 1.0
def __init__(self, device = 'CerealBox', nodePath = base.direct.camera,
headingNP = base.direct.camera):
def __init__(self, device = 'CerealBox', nodePath = None, headingNP = None):
from direct.showbase.ShowBaseGlobal import base
if nodePath is None:
nodePath = base.direct.camera
if headingNP is None:
headingNP = base.direct.camera
# See if device manager has been initialized
if base.direct.deviceManager is None:
base.direct.deviceManager = DirectDeviceManager()

View File

@ -55,10 +55,11 @@ class RotatingLog:
return 0
def filePath(self):
dateString=time.strftime("%Y_%m_%d_%H", time.localtime())
dateString = time.strftime("%Y_%m_%d_%H", time.localtime())
for i in range(26):
path="%s_%s_%s.log"%(self.path, dateString, chr(i+97))
if not os.path.exists(path) or os.stat(path)[6] < self.sizeLimit:
limit = self.sizeLimit
path = "%s_%s_%s.log" % (self.path, dateString, chr(i+97))
if limit is None or not os.path.exists(path) or os.stat(path)[6] < limit:
return path
# Hmm, 26 files are full? throw the rest in z:
# Maybe we should clear the self.sizeLimit here... maybe.
@ -83,7 +84,6 @@ class RotatingLog:
self.closed = self.file.closed
self.mode = self.file.mode
self.name = self.file.name
self.softspace = self.file.softspace
#self.encoding = self.file.encoding # Python 2.3
#self.newlines = self.file.newlines # Python 2.3, maybe

View File

@ -66,9 +66,7 @@ class DirectLights(NodePath):
def getNameList(self):
# Return a sorted list of all lights in the light dict
nameList = [x.getName() for x in self.lightDict.values()]
nameList.sort()
return nameList
return sorted(x.getName() for x in self.lightDict.values())
def create(self, ltype):
ltype = ltype.lower()

View File

@ -90,6 +90,7 @@ defaultHiddenImports = {
'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
'scipy.stats._stats': ['scipy.special.cython_special'],
'setuptools.monkey': ['setuptools.msvc'],
'shapely._geometry_helpers': ['shapely._geos'],
}
@ -1145,15 +1146,12 @@ class Freezer:
# Walk through the list in sorted order, so we reach parents
# before children.
names = list(self.modules.items())
names.sort()
excludeDict = {}
implicitParentDict = {}
includes = []
autoIncludes = []
origToNewName = {}
for newName, mdef in names:
for newName, mdef in sorted(self.modules.items()):
moduleName = mdef.moduleName
origToNewName[moduleName] = newName
if mdef.implicit and '.' in newName:
@ -2583,6 +2581,21 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
else:
code = fp.read()
# Strip out delvewheel patch (see GitHub issue #1492)
if isinstance(code, bytes):
# Don't look for \n at the end, it may also be \r\n
start_marker = b'# start delvewheel patch'
end_marker = b'# end delvewheel patch'
else:
start_marker = '# start delvewheel patch'
end_marker = '# end delvewheel patch'
start = code.find(start_marker)
while start >= 0:
end = code.find(end_marker, start) + len(end_marker)
code = code[:start] + code[end:]
start = code.find(start_marker)
code += b'\n' if isinstance(code, bytes) else '\n'
co = compile(code, pathname, 'exec', optimize=self.optimize)
elif type == imp.PY_COMPILED:

View File

@ -634,7 +634,20 @@ class build_apps(setuptools.Command):
for index in self.pypi_extra_indexes:
pip_args += ['--extra-index-url', index]
subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
try:
subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
except:
# Display a more helpful message for these common issues.
if platform.startswith('manylinux2010_') and sys.version_info >= (3, 11):
new_platform = platform.replace('manylinux2010_', 'manylinux2014_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.11.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('manylinux1_') and sys.version_info >= (3, 10):
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):
new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.8.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
raise
# Return a list of paths to the downloaded whls
return [
@ -846,16 +859,12 @@ class build_apps(setuptools.Command):
libdir = os.path.dirname(dtool_fn.to_os_specific())
etcdir = os.path.join(libdir, '..', 'etc')
etcfiles = os.listdir(etcdir)
etcfiles.sort(reverse=True)
for fn in etcfiles:
for fn in sorted(os.listdir(etcdir), reverse=True):
if fn.lower().endswith('.prc'):
with open(os.path.join(etcdir, fn)) as f:
prcstring += f.read()
else:
etcfiles = [i for i in p3dwhl.namelist() if i.endswith('.prc')]
etcfiles.sort(reverse=True)
for fn in etcfiles:
for fn in sorted((i for i in p3dwhl.namelist() if i.endswith('.prc')), reverse=True):
with p3dwhl.open(fn) as f:
prcstring += f.read().decode('utf8')
@ -1689,6 +1698,8 @@ class bdist_apps(setuptools.Command):
setattr(self, opt, None)
def finalize_options(self):
import pkg_resources
# We need to massage the inputs a bit in case they came from a
# setup.cfg file.
self.installers = {

View File

@ -1,3 +1,5 @@
from __future__ import annotations
#from otp.ai.AIBaseGlobal import *
from direct.directnotify import DirectNotifyGlobal
from direct.showbase.DirectObject import DirectObject
@ -39,7 +41,7 @@ class AsyncRequest(DirectObject):
will be called again when the new self.neededObjects is complete. You
may repeat this as necessary.
"""
_asyncRequests = {}
_asyncRequests: dict[int, AsyncRequest] = {}
notify = DirectNotifyGlobal.directNotify.newCategory('AsyncRequest')

View File

@ -76,41 +76,3 @@ if __debug__:
def flush(self):
CachedDOData.flush(self)
self._flushed = True
dc = CRDataCache()
dc._startMemLeakCheck()
cd = CachedDOData()
cd.foo = 34
dc.setCachedData(1, 'testCachedData', cd)
del cd
cd = CachedDOData()
cd.bar = 45
dc.setCachedData(1, 'testCachedData2', cd)
del cd
assert dc.hasCachedData(1)
assert dc.hasCachedData(1)
assert not dc.hasCachedData(2)
# data is dict of dataName->data
data = dc.popCachedData(1)
assert len(data) == 2
assert 'testCachedData' in data
assert 'testCachedData2' in data
assert data['testCachedData'].foo == 34
assert data['testCachedData2'].bar == 45
for cd in data.values():
cd.flush()
del data
dc._checkMemLeaks()
cd = CachedDOData()
cd.bar = 1234
dc.setCachedData(43, 'testCachedData2', cd)
del cd
assert dc.hasCachedData(43)
dc.flush()
dc._checkMemLeaks()
dc._stopMemLeakCheck()
dc.destroy()
del dc

View File

@ -20,3 +20,10 @@ class CachedDOData:
# override and destroy the cached data
# cached data is typically created by the DistributedObject and destroyed here
pass
# These next two methods tell mypy to allow arbitrary attributes.
def __getattribute__(self, name: str):
return object.__getattribute__(self, name)
def __setattr__(self, name: str, value) -> None:
object.__setattr__(self, name, value)

View File

@ -478,6 +478,9 @@ class ClientRepositoryBase(ConnectionRepository):
f"Asked to update non-existent DistObj {doId} and failed to find it")
def __doUpdateOwner(self, doId, di):
if not self.hasOwnerView():
return False
ovObj = self.doId2ownerView.get(doId)
if ovObj:
odg = Datagram(di.getDatagram())

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
from otp.ai.AIZoneData import AIZoneData # type: ignore[import]
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
from otp.ai import Barrier # type: ignore[import]
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
from otp.ai import Barrier # type: ignore[import]
context = self.__nextBarrierContext
# We assume the context number is passed as a uint16.
self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

View File

@ -149,10 +149,7 @@ class DoCollectionManager:
class2count.setdefault(className, 0)
class2count[className] += 1
count2classes = invertDictLossless(class2count)
counts = list(count2classes.keys())
counts.sort()
counts.reverse()
for count in counts:
for count in sorted(count2classes, reverse=True):
count2classes[count].sort()
for name in count2classes[count]:
print('%s %s' % (count, name))
@ -166,10 +163,7 @@ class DoCollectionManager:
class2count.setdefault(className, 0)
class2count[className] += 1
count2classes = invertDictLossless(class2count)
counts = list(count2classes.keys())
counts.sort()
counts.reverse()
for count in counts:
for count in sorted(count2classes, reverse=True):
count2classes[count].sort()
for name in count2classes[count]:
# print '%s %s' % (count, name)

View File

@ -7,6 +7,8 @@ zone, remove interest in that zone.
p.s. A great deal of this code is just code moved from ClientRepository.py.
"""
from __future__ import annotations
from panda3d.core import ConfigVariableBool
from .MsgTypes import CLIENT_ADD_INTEREST, CLIENT_REMOVE_INTEREST
if ConfigVariableBool('astron-support', True):
@ -101,9 +103,9 @@ class DoInterestManager(DirectObject.DirectObject):
_ContextIdSerialNum = 100
_ContextIdMask = 0x3FFFFFFF # avoid making Python create a long
_interests = {}
_interests: dict[int, InterestState] = {}
if __debug__:
_debug_interestHistory = []
_debug_interestHistory: list[tuple] = []
_debug_maxDescriptionLen = 40
_SerialGen = SerialNumGen()
@ -516,8 +518,7 @@ class DoInterestManager(DirectObject.DirectObject):
# Add message type
if ConfigVariableBool('astron-support', True):
if isinstance(zoneIdList, list):
vzl = list(zoneIdList)
vzl.sort()
vzl = sorted(zoneIdList)
uniqueElements(vzl)
datagram.addUint16(CLIENT_ADD_INTEREST_MULTIPLE)
datagram.addUint32(contextId)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from panda3d.core import NodePath
#
@ -15,8 +17,8 @@ from panda3d.core import NodePath
class GridParent:
# this lets GridParents share CellOrigins
GridZone2CellOrigin = {}
GridZone2count = {}
GridZone2CellOrigin: dict[tuple, NodePath] = {}
GridZone2count: dict[tuple, int] = {}
@staticmethod
def getCellOrigin(grid, zoneId):
tup = (grid, zoneId)

View File

@ -1,247 +1,247 @@
"""MsgTypes module: contains distributed object message types"""
from __future__ import annotations
from panda3d.core import ConfigVariableBool
from direct.showbase.PythonUtil import invertDictLossless
if ConfigVariableBool('astron-support', True):
MsgName2Id = {
'CLIENT_HELLO': 1,
'CLIENT_HELLO_RESP': 2,
CLIENT_HELLO = 1
CLIENT_HELLO_RESP = 2
# Sent by the client when it's leaving.
'CLIENT_DISCONNECT': 3,
# Sent by the client when it's leaving.
CLIENT_DISCONNECT = 3
# Sent by the server when it is dropping the connection deliberately.
'CLIENT_EJECT': 4,
# Sent by the server when it is dropping the connection deliberately.
CLIENT_EJECT = 4
'CLIENT_HEARTBEAT': 5,
CLIENT_HEARTBEAT = 5
'CLIENT_OBJECT_SET_FIELD': 120,
'CLIENT_OBJECT_SET_FIELDS': 121,
'CLIENT_OBJECT_LEAVING': 132,
'CLIENT_OBJECT_LEAVING_OWNER': 161,
'CLIENT_ENTER_OBJECT_REQUIRED': 142,
'CLIENT_ENTER_OBJECT_REQUIRED_OTHER': 143,
'CLIENT_ENTER_OBJECT_REQUIRED_OWNER': 172,
'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER': 173,
CLIENT_OBJECT_SET_FIELD = 120
CLIENT_OBJECT_SET_FIELDS = 121
CLIENT_OBJECT_LEAVING = 132
CLIENT_OBJECT_LEAVING_OWNER = 161
CLIENT_ENTER_OBJECT_REQUIRED = 142
CLIENT_ENTER_OBJECT_REQUIRED_OTHER = 143
CLIENT_ENTER_OBJECT_REQUIRED_OWNER = 172
CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER = 173
'CLIENT_DONE_INTEREST_RESP': 204,
CLIENT_DONE_INTEREST_RESP = 204
'CLIENT_ADD_INTEREST': 200,
'CLIENT_ADD_INTEREST_MULTIPLE': 201,
'CLIENT_REMOVE_INTEREST': 203,
'CLIENT_OBJECT_LOCATION': 140,
CLIENT_ADD_INTEREST = 200
CLIENT_ADD_INTEREST_MULTIPLE = 201
CLIENT_REMOVE_INTEREST = 203
CLIENT_OBJECT_LOCATION = 140
# These are sent internally inside the Astron cluster.
# These are sent internally inside the Astron cluster.
# Message Director control messages:
'CONTROL_CHANNEL': 1,
'CONTROL_ADD_CHANNEL': 9000,
'CONTROL_REMOVE_CHANNEL': 9001,
'CONTROL_ADD_RANGE': 9002,
'CONTROL_REMOVE_RANGE': 9003,
'CONTROL_ADD_POST_REMOVE': 9010,
'CONTROL_CLEAR_POST_REMOVES': 9011,
# Message Director control messages:
CONTROL_CHANNEL = 1
CONTROL_ADD_CHANNEL = 9000
CONTROL_REMOVE_CHANNEL = 9001
CONTROL_ADD_RANGE = 9002
CONTROL_REMOVE_RANGE = 9003
CONTROL_ADD_POST_REMOVE = 9010
CONTROL_CLEAR_POST_REMOVES = 9011
# State Server control messages:
'STATESERVER_CREATE_OBJECT_WITH_REQUIRED': 2000,
'STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER': 2001,
'STATESERVER_DELETE_AI_OBJECTS': 2009,
'STATESERVER_OBJECT_GET_FIELD': 2010,
'STATESERVER_OBJECT_GET_FIELD_RESP': 2011,
'STATESERVER_OBJECT_GET_FIELDS': 2012,
'STATESERVER_OBJECT_GET_FIELDS_RESP': 2013,
'STATESERVER_OBJECT_GET_ALL': 2014,
'STATESERVER_OBJECT_GET_ALL_RESP': 2015,
'STATESERVER_OBJECT_SET_FIELD': 2020,
'STATESERVER_OBJECT_SET_FIELDS': 2021,
'STATESERVER_OBJECT_DELETE_FIELD_RAM': 2030,
'STATESERVER_OBJECT_DELETE_FIELDS_RAM': 2031,
'STATESERVER_OBJECT_DELETE_RAM': 2032,
'STATESERVER_OBJECT_SET_LOCATION': 2040,
'STATESERVER_OBJECT_CHANGING_LOCATION': 2041,
'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED': 2042,
'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER': 2043,
'STATESERVER_OBJECT_GET_LOCATION': 2044,
'STATESERVER_OBJECT_GET_LOCATION_RESP': 2045,
'STATESERVER_OBJECT_SET_AI': 2050,
'STATESERVER_OBJECT_CHANGING_AI': 2051,
'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED': 2052,
'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER': 2053,
'STATESERVER_OBJECT_GET_AI': 2054,
'STATESERVER_OBJECT_GET_AI_RESP': 2055,
'STATESERVER_OBJECT_SET_OWNER': 2060,
'STATESERVER_OBJECT_CHANGING_OWNER': 2061,
'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED': 2062,
'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER': 2063,
'STATESERVER_OBJECT_GET_OWNER': 2064,
'STATESERVER_OBJECT_GET_OWNER_RESP': 2065,
'STATESERVER_OBJECT_GET_ZONE_OBJECTS': 2100,
'STATESERVER_OBJECT_GET_ZONES_OBJECTS': 2102,
'STATESERVER_OBJECT_GET_CHILDREN': 2104,
'STATESERVER_OBJECT_GET_ZONE_COUNT': 2110,
'STATESERVER_OBJECT_GET_ZONE_COUNT_RESP': 2111,
'STATESERVER_OBJECT_GET_ZONES_COUNT': 2112,
'STATESERVER_OBJECT_GET_ZONES_COUNT_RESP': 2113,
'STATESERVER_OBJECT_GET_CHILD_COUNT': 2114,
'STATESERVER_OBJECT_GET_CHILD_COUNT_RESP': 2115,
'STATESERVER_OBJECT_DELETE_ZONE': 2120,
'STATESERVER_OBJECT_DELETE_ZONES': 2122,
'STATESERVER_OBJECT_DELETE_CHILDREN': 2124,
# DBSS-backed-object messages:
'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS': 2200,
'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER': 2201,
'DBSS_OBJECT_GET_ACTIVATED': 2207,
'DBSS_OBJECT_GET_ACTIVATED_RESP': 2208,
'DBSS_OBJECT_DELETE_FIELD_DISK': 2230,
'DBSS_OBJECT_DELETE_FIELDS_DISK': 2231,
'DBSS_OBJECT_DELETE_DISK': 2232,
# State Server control messages:
STATESERVER_CREATE_OBJECT_WITH_REQUIRED = 2000
STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER = 2001
STATESERVER_DELETE_AI_OBJECTS = 2009
STATESERVER_OBJECT_GET_FIELD = 2010
STATESERVER_OBJECT_GET_FIELD_RESP = 2011
STATESERVER_OBJECT_GET_FIELDS = 2012
STATESERVER_OBJECT_GET_FIELDS_RESP = 2013
STATESERVER_OBJECT_GET_ALL = 2014
STATESERVER_OBJECT_GET_ALL_RESP = 2015
STATESERVER_OBJECT_SET_FIELD = 2020
STATESERVER_OBJECT_SET_FIELDS = 2021
STATESERVER_OBJECT_DELETE_FIELD_RAM = 2030
STATESERVER_OBJECT_DELETE_FIELDS_RAM = 2031
STATESERVER_OBJECT_DELETE_RAM = 2032
STATESERVER_OBJECT_SET_LOCATION = 2040
STATESERVER_OBJECT_CHANGING_LOCATION = 2041
STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED = 2042
STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER = 2043
STATESERVER_OBJECT_GET_LOCATION = 2044
STATESERVER_OBJECT_GET_LOCATION_RESP = 2045
STATESERVER_OBJECT_SET_AI = 2050
STATESERVER_OBJECT_CHANGING_AI = 2051
STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED = 2052
STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER = 2053
STATESERVER_OBJECT_GET_AI = 2054
STATESERVER_OBJECT_GET_AI_RESP = 2055
STATESERVER_OBJECT_SET_OWNER = 2060
STATESERVER_OBJECT_CHANGING_OWNER = 2061
STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED = 2062
STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER = 2063
STATESERVER_OBJECT_GET_OWNER = 2064
STATESERVER_OBJECT_GET_OWNER_RESP = 2065
STATESERVER_OBJECT_GET_ZONE_OBJECTS = 2100
STATESERVER_OBJECT_GET_ZONES_OBJECTS = 2102
STATESERVER_OBJECT_GET_CHILDREN = 2104
STATESERVER_OBJECT_GET_ZONE_COUNT = 2110
STATESERVER_OBJECT_GET_ZONE_COUNT_RESP = 2111
STATESERVER_OBJECT_GET_ZONES_COUNT = 2112
STATESERVER_OBJECT_GET_ZONES_COUNT_RESP = 2113
STATESERVER_OBJECT_GET_CHILD_COUNT = 2114
STATESERVER_OBJECT_GET_CHILD_COUNT_RESP = 2115
STATESERVER_OBJECT_DELETE_ZONE = 2120
STATESERVER_OBJECT_DELETE_ZONES = 2122
STATESERVER_OBJECT_DELETE_CHILDREN = 2124
# DBSS-backed-object messages:
DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS = 2200
DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER = 2201
DBSS_OBJECT_GET_ACTIVATED = 2207
DBSS_OBJECT_GET_ACTIVATED_RESP = 2208
DBSS_OBJECT_DELETE_FIELD_DISK = 2230
DBSS_OBJECT_DELETE_FIELDS_DISK = 2231
DBSS_OBJECT_DELETE_DISK = 2232
# Database Server control messages:
'DBSERVER_CREATE_OBJECT': 3000,
'DBSERVER_CREATE_OBJECT_RESP': 3001,
'DBSERVER_OBJECT_GET_FIELD': 3010,
'DBSERVER_OBJECT_GET_FIELD_RESP': 3011,
'DBSERVER_OBJECT_GET_FIELDS': 3012,
'DBSERVER_OBJECT_GET_FIELDS_RESP': 3013,
'DBSERVER_OBJECT_GET_ALL': 3014,
'DBSERVER_OBJECT_GET_ALL_RESP': 3015,
'DBSERVER_OBJECT_SET_FIELD': 3020,
'DBSERVER_OBJECT_SET_FIELDS': 3021,
'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS': 3022,
'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP': 3023,
'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS': 3024,
'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP': 3025,
'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY': 3026,
'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP': 3027,
'DBSERVER_OBJECT_DELETE_FIELD': 3030,
'DBSERVER_OBJECT_DELETE_FIELDS': 3031,
'DBSERVER_OBJECT_DELETE': 3032,
# Database Server control messages:
DBSERVER_CREATE_OBJECT = 3000
DBSERVER_CREATE_OBJECT_RESP = 3001
DBSERVER_OBJECT_GET_FIELD = 3010
DBSERVER_OBJECT_GET_FIELD_RESP = 3011
DBSERVER_OBJECT_GET_FIELDS = 3012
DBSERVER_OBJECT_GET_FIELDS_RESP = 3013
DBSERVER_OBJECT_GET_ALL = 3014
DBSERVER_OBJECT_GET_ALL_RESP = 3015
DBSERVER_OBJECT_SET_FIELD = 3020
DBSERVER_OBJECT_SET_FIELDS = 3021
DBSERVER_OBJECT_SET_FIELD_IF_EQUALS = 3022
DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP = 3023
DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS = 3024
DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP = 3025
DBSERVER_OBJECT_SET_FIELD_IF_EMPTY = 3026
DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP = 3027
DBSERVER_OBJECT_DELETE_FIELD = 3030
DBSERVER_OBJECT_DELETE_FIELDS = 3031
DBSERVER_OBJECT_DELETE = 3032
# Client Agent control messages:
'CLIENTAGENT_SET_STATE': 1000,
'CLIENTAGENT_SET_CLIENT_ID': 1001,
'CLIENTAGENT_SEND_DATAGRAM': 1002,
'CLIENTAGENT_EJECT': 1004,
'CLIENTAGENT_DROP': 1005,
'CLIENTAGENT_GET_NETWORK_ADDRESS': 1006,
'CLIENTAGENT_GET_NETWORK_ADDRESS_RESP': 1007,
'CLIENTAGENT_DECLARE_OBJECT': 1010,
'CLIENTAGENT_UNDECLARE_OBJECT': 1011,
'CLIENTAGENT_ADD_SESSION_OBJECT': 1012,
'CLIENTAGENT_REMOVE_SESSION_OBJECT': 1013,
'CLIENTAGENT_SET_FIELDS_SENDABLE': 1014,
'CLIENTAGENT_OPEN_CHANNEL': 1100,
'CLIENTAGENT_CLOSE_CHANNEL': 1101,
'CLIENTAGENT_ADD_POST_REMOVE': 1110,
'CLIENTAGENT_CLEAR_POST_REMOVES': 1111,
'CLIENTAGENT_ADD_INTEREST': 1200,
'CLIENTAGENT_ADD_INTEREST_MULTIPLE': 1201,
'CLIENTAGENT_REMOVE_INTEREST': 1203,
}
# Client Agent control messages:
CLIENTAGENT_SET_STATE = 1000
CLIENTAGENT_SET_CLIENT_ID = 1001
CLIENTAGENT_SEND_DATAGRAM = 1002
CLIENTAGENT_EJECT = 1004
CLIENTAGENT_DROP = 1005
CLIENTAGENT_GET_NETWORK_ADDRESS = 1006
CLIENTAGENT_GET_NETWORK_ADDRESS_RESP = 1007
CLIENTAGENT_DECLARE_OBJECT = 1010
CLIENTAGENT_UNDECLARE_OBJECT = 1011
CLIENTAGENT_ADD_SESSION_OBJECT = 1012
CLIENTAGENT_REMOVE_SESSION_OBJECT = 1013
CLIENTAGENT_SET_FIELDS_SENDABLE = 1014
CLIENTAGENT_OPEN_CHANNEL = 1100
CLIENTAGENT_CLOSE_CHANNEL = 1101
CLIENTAGENT_ADD_POST_REMOVE = 1110
CLIENTAGENT_CLEAR_POST_REMOVES = 1111
CLIENTAGENT_ADD_INTEREST = 1200
CLIENTAGENT_ADD_INTEREST_MULTIPLE = 1201
CLIENTAGENT_REMOVE_INTEREST = 1203
else:
MsgName2Id = {
# 2 new params: passwd, char bool 0/1 1 = new account
# 2 new return values: 129 = not found, 12 = bad passwd,
'CLIENT_LOGIN': 1,
'CLIENT_LOGIN_RESP': 2,
'CLIENT_GET_AVATARS': 3,
# Sent by the server when it is dropping the connection deliberately.
'CLIENT_GO_GET_LOST': 4,
'CLIENT_GET_AVATARS_RESP': 5,
'CLIENT_CREATE_AVATAR': 6,
'CLIENT_CREATE_AVATAR_RESP': 7,
'CLIENT_GET_FRIEND_LIST': 10,
'CLIENT_GET_FRIEND_LIST_RESP': 11,
'CLIENT_GET_AVATAR_DETAILS': 14,
'CLIENT_GET_AVATAR_DETAILS_RESP': 15,
'CLIENT_LOGIN_2': 16,
'CLIENT_LOGIN_2_RESP': 17,
# 2 new params: passwd, char bool 0/1 1 = new account
# 2 new return values: 129 = not found, 12 = bad passwd,
CLIENT_LOGIN = 1
CLIENT_LOGIN_RESP = 2
CLIENT_GET_AVATARS = 3
# Sent by the server when it is dropping the connection deliberately.
CLIENT_GO_GET_LOST = 4
CLIENT_GET_AVATARS_RESP = 5
CLIENT_CREATE_AVATAR = 6
CLIENT_CREATE_AVATAR_RESP = 7
CLIENT_GET_FRIEND_LIST = 10
CLIENT_GET_FRIEND_LIST_RESP = 11
CLIENT_GET_AVATAR_DETAILS = 14
CLIENT_GET_AVATAR_DETAILS_RESP = 15
CLIENT_LOGIN_2 = 16
CLIENT_LOGIN_2_RESP = 17
'CLIENT_OBJECT_UPDATE_FIELD': 24,
'CLIENT_OBJECT_UPDATE_FIELD_RESP': 24,
'CLIENT_OBJECT_DISABLE': 25,
'CLIENT_OBJECT_DISABLE_RESP': 25,
'CLIENT_OBJECT_DISABLE_OWNER': 26,
'CLIENT_OBJECT_DISABLE_OWNER_RESP': 26,
'CLIENT_OBJECT_DELETE': 27,
'CLIENT_OBJECT_DELETE_RESP': 27,
'CLIENT_SET_ZONE_CMU': 29,
'CLIENT_REMOVE_ZONE': 30,
'CLIENT_SET_AVATAR': 32,
'CLIENT_CREATE_OBJECT_REQUIRED': 34,
'CLIENT_CREATE_OBJECT_REQUIRED_RESP': 34,
'CLIENT_CREATE_OBJECT_REQUIRED_OTHER': 35,
'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP': 35,
'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER': 36,
'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER_RESP':36,
CLIENT_OBJECT_UPDATE_FIELD = 24
CLIENT_OBJECT_UPDATE_FIELD_RESP = 24
CLIENT_OBJECT_DISABLE = 25
CLIENT_OBJECT_DISABLE_RESP = 25
CLIENT_OBJECT_DISABLE_OWNER = 26
CLIENT_OBJECT_DISABLE_OWNER_RESP = 26
CLIENT_OBJECT_DELETE = 27
CLIENT_OBJECT_DELETE_RESP = 27
CLIENT_SET_ZONE_CMU = 29
CLIENT_REMOVE_ZONE = 30
CLIENT_SET_AVATAR = 32
CLIENT_CREATE_OBJECT_REQUIRED = 34
CLIENT_CREATE_OBJECT_REQUIRED_RESP = 34
CLIENT_CREATE_OBJECT_REQUIRED_OTHER = 35
CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP = 35
CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER = 36
CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER_RESP = 36
'CLIENT_REQUEST_GENERATES': 36,
CLIENT_REQUEST_GENERATES = 36
'CLIENT_DISCONNECT': 37,
CLIENT_DISCONNECT = 37
'CLIENT_GET_STATE_RESP': 47,
'CLIENT_DONE_INTEREST_RESP': 48,
CLIENT_GET_STATE_RESP = 47
CLIENT_DONE_INTEREST_RESP = 48
'CLIENT_DELETE_AVATAR': 49,
CLIENT_DELETE_AVATAR = 49
'CLIENT_DELETE_AVATAR_RESP': 5,
CLIENT_DELETE_AVATAR_RESP = 5
'CLIENT_HEARTBEAT': 52,
'CLIENT_FRIEND_ONLINE': 53,
'CLIENT_FRIEND_OFFLINE': 54,
'CLIENT_REMOVE_FRIEND': 56,
CLIENT_HEARTBEAT = 52
CLIENT_FRIEND_ONLINE = 53
CLIENT_FRIEND_OFFLINE = 54
CLIENT_REMOVE_FRIEND = 56
'CLIENT_CHANGE_PASSWORD': 65,
CLIENT_CHANGE_PASSWORD = 65
'CLIENT_SET_NAME_PATTERN': 67,
'CLIENT_SET_NAME_PATTERN_ANSWER': 68,
CLIENT_SET_NAME_PATTERN = 67
CLIENT_SET_NAME_PATTERN_ANSWER = 68
'CLIENT_SET_WISHNAME': 70,
'CLIENT_SET_WISHNAME_RESP': 71,
'CLIENT_SET_WISHNAME_CLEAR': 72,
'CLIENT_SET_SECURITY': 73,
CLIENT_SET_WISHNAME = 70
CLIENT_SET_WISHNAME_RESP = 71
CLIENT_SET_WISHNAME_CLEAR = 72
CLIENT_SET_SECURITY = 73
'CLIENT_SET_DOID_RANGE': 74,
CLIENT_SET_DOID_RANGE = 74
'CLIENT_GET_AVATARS_RESP2': 75,
'CLIENT_CREATE_AVATAR2': 76,
'CLIENT_SYSTEM_MESSAGE': 78,
'CLIENT_SET_AVTYPE': 80,
CLIENT_GET_AVATARS_RESP2 = 75
CLIENT_CREATE_AVATAR2 = 76
CLIENT_SYSTEM_MESSAGE = 78
CLIENT_SET_AVTYPE = 80
'CLIENT_GET_PET_DETAILS': 81,
'CLIENT_GET_PET_DETAILS_RESP': 82,
CLIENT_GET_PET_DETAILS = 81
CLIENT_GET_PET_DETAILS_RESP = 82
'CLIENT_ADD_INTEREST': 97,
'CLIENT_REMOVE_INTEREST': 99,
'CLIENT_OBJECT_LOCATION': 102,
CLIENT_ADD_INTEREST = 97
CLIENT_REMOVE_INTEREST = 99
CLIENT_OBJECT_LOCATION = 102
'CLIENT_LOGIN_3': 111,
'CLIENT_LOGIN_3_RESP': 110,
CLIENT_LOGIN_3 = 111
CLIENT_LOGIN_3_RESP = 110
'CLIENT_GET_FRIEND_LIST_EXTENDED': 115,
'CLIENT_GET_FRIEND_LIST_EXTENDED_RESP': 116,
CLIENT_GET_FRIEND_LIST_EXTENDED = 115
CLIENT_GET_FRIEND_LIST_EXTENDED_RESP = 116
'CLIENT_SET_FIELD_SENDABLE': 120,
CLIENT_SET_FIELD_SENDABLE = 120
'CLIENT_SYSTEMMESSAGE_AKNOWLEDGE': 123,
'CLIENT_CHANGE_GENERATE_ORDER': 124,
CLIENT_SYSTEMMESSAGE_AKNOWLEDGE = 123
CLIENT_CHANGE_GENERATE_ORDER = 124
# new toontown specific login message, adds last logged in, and if child account has parent acount
'CLIENT_LOGIN_TOONTOWN': 125,
'CLIENT_LOGIN_TOONTOWN_RESP': 126,
# new toontown specific login message, adds last logged in, and if child account has parent acount
CLIENT_LOGIN_TOONTOWN = 125
CLIENT_LOGIN_TOONTOWN_RESP = 126
'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED': 2001,
'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER': 2003,
'STATESERVER_OBJECT_UPDATE_FIELD': 2004,
'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT': 2050,
'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT': 2051,
'STATESERVER_BOUNCE_MESSAGE': 2086,
}
STATESERVER_OBJECT_GENERATE_WITH_REQUIRED = 2001
STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER = 2003
STATESERVER_OBJECT_UPDATE_FIELD = 2004
STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT = 2050
STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT = 2051
STATESERVER_BOUNCE_MESSAGE = 2086
MsgName2Id = {name: value for name, value in globals().items() if isinstance(value, int)}
# create id->name table for debugging
MsgId2Names = invertDictLossless(MsgName2Id)
@ -250,7 +250,7 @@ MsgId2Names = invertDictLossless(MsgName2Id)
globals().update(MsgName2Id)
# These messages are ignored when the client is headed to the quiet zone
QUIET_ZONE_IGNORED_LIST = [
QUIET_ZONE_IGNORED_LIST: list[int] = [
# We mustn't ignore updates, because some updates for localToon
# are always important.

View File

@ -7,28 +7,25 @@ from panda3d.core import ConfigVariableBool
from direct.showbase.PythonUtil import invertDictLossless
MsgName2Id = {
'SET_DOID_RANGE_CMU' : 9001,
'CLIENT_OBJECT_GENERATE_CMU' : 9002,
'OBJECT_GENERATE_CMU' : 9003,
'OBJECT_UPDATE_FIELD_CMU' : 9004,
'OBJECT_DISABLE_CMU' : 9005,
'OBJECT_DELETE_CMU' : 9006,
'REQUEST_GENERATES_CMU' : 9007,
'CLIENT_DISCONNECT_CMU' : 9008,
'CLIENT_SET_INTEREST_CMU' : 9009,
'OBJECT_SET_ZONE_CMU' : 9010,
'CLIENT_HEARTBEAT_CMU' : 9011,
'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU' : 9011,
}
SET_DOID_RANGE_CMU = 9001
CLIENT_OBJECT_GENERATE_CMU = 9002
OBJECT_GENERATE_CMU = 9003
OBJECT_UPDATE_FIELD_CMU = 9004
OBJECT_DISABLE_CMU = 9005
OBJECT_DELETE_CMU = 9006
REQUEST_GENERATES_CMU = 9007
CLIENT_DISCONNECT_CMU = 9008
CLIENT_SET_INTEREST_CMU = 9009
OBJECT_SET_ZONE_CMU = 9010
CLIENT_HEARTBEAT_CMU = 9011
CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU = 9011
if ConfigVariableBool('astron-support', True):
MsgName2Id['CLIENT_OBJECT_UPDATE_FIELD'] = 120 # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
CLIENT_OBJECT_UPDATE_FIELD = 120 # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
else:
MsgName2Id['CLIENT_OBJECT_UPDATE_FIELD'] = 24 # Matches MsgTypes.CLIENT_OBJECT_UPDATE_FIELD
CLIENT_OBJECT_UPDATE_FIELD = 24 # Matches MsgTypes.CLIENT_OBJECT_UPDATE_FIELD
MsgName2Id = {name: value for name, value in globals().items() if isinstance(value, int)}
# create id->name table for debugging
MsgId2Names = invertDictLossless(MsgName2Id)
# put msg names in module scope, assigned to msg value
globals().update(MsgName2Id)

View File

@ -5,6 +5,8 @@ Note:
existing code. New code should use the :mod:`.FSM` module instead.
"""
from __future__ import annotations
__all__ = ['ClassicFSM']
from direct.directnotify.DirectNotifyGlobal import directNotify
@ -13,7 +15,7 @@ from direct.showbase.MessengerGlobal import messenger
import weakref
if __debug__:
_debugFsms = {}
_debugFsms: dict[str, weakref.ref] = {}
def printDebugFsmList():
for k in sorted(_debugFsms.keys()):

View File

@ -1,5 +1,7 @@
"""State module: contains State class"""
from __future__ import annotations
__all__ = ['State']
from direct.directnotify.DirectNotifyGlobal import directNotify
@ -18,7 +20,7 @@ class State(DirectObject):
# should not cause any leaks.
if __debug__:
import weakref
States = weakref.WeakKeyDictionary()
States: weakref.WeakKeyDictionary[State, int] = weakref.WeakKeyDictionary()
@classmethod
def replaceMethod(cls, oldFunction, newFunction):

View File

@ -1,6 +1,8 @@
# classes for event-driven programming
# http://en.wikipedia.org/wiki/Event-driven_programming
from __future__ import annotations
__all__ = ['StateVar', 'FunctionCall', 'EnterExit', 'Pulse', 'EventPulse',
'EventArgument', ]
@ -46,11 +48,6 @@ class PushesStateChanges:
for subscriber in self._subscribers:
subscriber._recvStatePush(self)
if __debug__:
psc = PushesStateChanges(0)
assert psc.getState() == 0
psc.destroy()
del psc
class ReceivesStateChanges:
# base class for objects that subscribe to state changes from PushesStateChanges objects
@ -83,10 +80,6 @@ class ReceivesStateChanges:
def _recvStatePush(self, source):
pass
if __debug__:
rsc = ReceivesStateChanges(None)
rsc.destroy()
del rsc
class StateVar(PushesStateChanges):
# coder-friendly object that allows values to be set on it and pushes those values
@ -97,13 +90,6 @@ class StateVar(PushesStateChanges):
def get(self):
return PushesStateChanges.getState(self)
if __debug__:
sv = StateVar(0)
assert sv.get() == 0
sv.set(1)
assert sv.get() == 1
sv.destroy()
del sv
class StateChangeNode(PushesStateChanges, ReceivesStateChanges):
# base class that can be used to create a state-change notification chain
@ -120,31 +106,6 @@ class StateChangeNode(PushesStateChanges, ReceivesStateChanges):
# got a state push, apply new state to self
self._handlePotentialStateChange(source._value)
if __debug__:
sv = StateVar(0)
assert sv.get() == 0
scn = StateChangeNode(sv)
assert scn.getState() == 0
sv.set(1)
assert sv.get() == 1
assert scn.getState() == 1
scn2 = StateChangeNode(scn)
assert scn2.getState() == 1
sv.set(2)
assert scn2.getState() == 2
scn3 = StateChangeNode(scn)
assert scn3.getState() == 2
sv.set(3)
assert scn2.getState() == 3
assert scn3.getState() == 3
scn3.destroy()
scn2.destroy()
scn.destroy()
sv.destroy()
del scn3
del scn2
del scn
del sv
class ReceivesMultipleStateChanges:
# base class for objects that subscribe to state changes from multiple PushesStateChanges
@ -179,15 +140,6 @@ class ReceivesMultipleStateChanges:
def _recvMultiStatePush(self, key, source):
pass
if __debug__:
rsc = ReceivesMultipleStateChanges()
sv = StateVar(0)
sv2 = StateVar('b')
rsc._subscribeTo(sv, 'a')
rsc._subscribeTo(sv2, 2)
rsc._unsubscribe('a')
rsc.destroy()
del rsc
class FunctionCall(ReceivesMultipleStateChanges, PushesStateChanges):
# calls func with provided args whenever arguments' state changes
@ -249,47 +201,6 @@ class FunctionCall(ReceivesMultipleStateChanges, PushesStateChanges):
self._func(*self._bakedArgs, **self._bakedKargs)
PushesStateChanges._handleStateChange(self)
if __debug__:
l = []
def handler1(value, l=l):
l.append(value)
assert not l
sv = StateVar(0)
fc = FunctionCall(handler1, sv)
assert not l
fc.pushCurrentState()
assert l == [0,]
sv.set(1)
assert l == [0,1,]
sv.set(2)
assert l == [0,1,2,]
fc.destroy()
sv.destroy()
del fc
del sv
del handler1
del l
l = []
def handler2(value, kDummy=None, kValue=None, l=l):
l.append((value, kValue))
assert not l
sv = StateVar(0)
ksv = StateVar('a')
fc = FunctionCall(handler2, sv, kValue=ksv)
assert not l
fc.pushCurrentState()
assert l == [(0,'a',),]
sv.set(1)
assert l == [(0,'a'),(1,'a'),]
ksv.set('b')
assert l == [(0,'a'),(1,'a'),(1,'b'),]
fc.destroy()
sv.destroy()
del fc
del sv
del handler2
del l
class EnterExit(StateChangeNode):
# call enterFunc when our state becomes true, exitFunc when it becomes false
@ -314,33 +225,6 @@ class EnterExit(StateChangeNode):
self._exitFunc()
StateChangeNode._handleStateChange(self)
if __debug__:
l = []
def enter(l=l):
l.append(1)
def exit(l=l):
l.append(0)
sv = StateVar(0)
ee = EnterExit(sv, enter, exit)
sv.set(0)
assert not l
sv.set(1)
assert l == [1,]
sv.set(2)
assert l == [1,]
sv.set(0)
assert l == [1,0,]
sv.set(True)
assert l == [1,0,1,]
sv.set(False)
assert l == [1,0,1,0,]
ee.destroy()
sv.destroy()
del ee
del sv
del enter
del exit
del l
class Pulse(PushesStateChanges):
# changes state to True then immediately to False whenever sendPulse is called
@ -351,25 +235,6 @@ class Pulse(PushesStateChanges):
self._handlePotentialStateChange(True)
self._handlePotentialStateChange(False)
if __debug__:
l = []
def handler(value, l=l):
l.append(value)
p = Pulse()
fc = FunctionCall(handler, p)
assert not l
fc.pushCurrentState()
assert l == [False, ]
p.sendPulse()
assert l == [False, True, False, ]
p.sendPulse()
assert l == [False, True, False, True, False, ]
fc.destroy()
p.destroy()
del fc
del p
del l
del handler
class EventPulse(Pulse, DirectObject):
# sends a True-False "pulse" whenever a specific messenger message is sent
@ -381,6 +246,7 @@ class EventPulse(Pulse, DirectObject):
self.ignoreAll()
Pulse.destroy(self)
class EventArgument(PushesStateChanges, DirectObject):
# tracks a particular argument to a particular messenger event
def __init__(self, event, index=0):
@ -396,6 +262,7 @@ class EventArgument(PushesStateChanges, DirectObject):
def _handleEvent(self, *args):
self._handlePotentialStateChange(args[self._index])
class AttrSetter(StateChangeNode):
def __init__(self, source, object, attrName):
self._object = object
@ -406,19 +273,3 @@ class AttrSetter(StateChangeNode):
def _handleStateChange(self):
setattr(self._object, self._attrName, self._value)
StateChangeNode._handleStateChange(self)
if __debug__:
from direct.showbase.PythonUtil import ScratchPad
o = ScratchPad()
svar = StateVar(0)
aset = AttrSetter(svar, o, 'testAttr')
assert hasattr(o, 'testAttr')
assert o.testAttr == 0
svar.set('red')
assert o.testAttr == 'red'
aset.destroy()
svar.destroy()
o.destroy()
del aset
del svar
del o

View File

@ -4,6 +4,8 @@ See the :ref:`directdialog` page in the programming manual for a more
in-depth explanation and an example of how to use this class.
"""
from __future__ import annotations
__all__ = [
'findDialog', 'cleanupDialog', 'DirectDialog', 'OkDialog',
'OkCancelDialog', 'YesNoDialog', 'YesNoCancelDialog', 'RetryCancelDialog',
@ -45,7 +47,7 @@ def cleanupDialog(uniqueName):
class DirectDialog(DirectFrame):
AllDialogs = {}
AllDialogs: dict[str, DirectDialog] = {}
PanelIndex = 0
def __init__(self, parent=None, **kw):

View File

@ -84,6 +84,8 @@ Code overview:
see if any keywords are left unused. If so, an error is raised.
"""
from __future__ import annotations
__all__ = ['DirectGuiBase', 'DirectGuiWidget']
@ -616,9 +618,7 @@ class DirectGuiBase(DirectObject.DirectObject):
def components(self):
# Return a list of all components.
names = list(self.__componentInfo.keys())
names.sort()
return names
return sorted(self.__componentInfo)
def hascomponent(self, component):
return component in self.__componentInfo
@ -682,7 +682,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
else:
inactiveInitState = DGG.DISABLED
guiDict = {}
guiDict: dict[str, DirectGuiWidget] = {}
def __init__(self, parent = None, **kw):
# Direct gui widgets are node paths

View File

@ -3,7 +3,7 @@ Global definitions used by Direct Gui Classes and handy constants
that can be used during widget construction
"""
__all__ = []
__all__ = ()
from panda3d.core import (
KeyboardButton,

View File

@ -1,5 +1,7 @@
"""FunctionInterval module: contains the FunctionInterval class"""
from __future__ import annotations
__all__ = ['FunctionInterval', 'EventInterval', 'AcceptInterval', 'IgnoreInterval', 'ParentInterval', 'WrtParentInterval', 'PosInterval', 'HprInterval', 'ScaleInterval', 'PosHprInterval', 'HprScaleInterval', 'PosHprScaleInterval', 'Func', 'Wait']
from panda3d.direct import WaitInterval
@ -23,7 +25,7 @@ class FunctionInterval(Interval.Interval):
# should not cause any leaks.
if __debug__:
import weakref
FunctionIntervals = weakref.WeakKeyDictionary()
FunctionIntervals: weakref.WeakKeyDictionary[FunctionInterval, int] = weakref.WeakKeyDictionary()
@classmethod
def replaceMethod(cls, oldFunction, newFunction):

View File

@ -1,6 +1,6 @@
"""Undocumented Module"""
__all__ = []
__all__ = ()
if __name__ == "__main__":
@ -26,7 +26,7 @@ if __name__ == "__main__":
base = ShowBase()
boat = base.loader.loadModel('models/misc/smiley')
boat.reparentTo(render)
boat.reparentTo(base.render)
donald = Actor()
donald.loadModel("phase_6/models/char/donald-wheel-1000")
@ -34,7 +34,7 @@ if __name__ == "__main__":
donald.reparentTo(boat)
dock = base.loader.loadModel('models/misc/smiley')
dock.reparentTo(render)
dock.reparentTo(base.render)
sound = base.loader.loadSfx('phase_6/audio/sfx/SZ_DD_waterlap.mp3')
foghorn = base.loader.loadSfx('phase_6/audio/sfx/SZ_DD_foghorn.mp3')
@ -93,7 +93,7 @@ if __name__ == "__main__":
foghornSound = SoundInterval(foghorn, name='foghorn')
soundTrack2 = Track([(foghornStartTime, foghornSound)], 'soundtrack2')
mtrack = MultiTrack([boatTrack, dockTrack, soundTrack, soundTrack2, waterEventTrack,
mtrack = MultiTrack([boatTrack, dockTrack, soundTrack, soundTrack2, waterEventTrack, # type: ignore[name-defined]
donaldSteerTrack])
# Print out MultiTrack parameters
print(mtrack)
@ -175,11 +175,11 @@ if __name__ == "__main__":
# Just to take time
i2 = LerpPosInterval(base.camera, 2.0, Point3(0, 10, 5))
# This will be relative to end of camera move
i3 = FunctionInterval(printPreviousEnd)
i3 = FunctionInterval(printPreviousEnd) # type: ignore[assignment]
# Just to take time
i4 = LerpPosInterval(base.camera, 2.0, Point3(0, 0, 5))
# This will be relative to the start of the camera move
i5 = FunctionInterval(printPreviousStart)
i5 = FunctionInterval(printPreviousStart) # type: ignore[assignment]
# This will be relative to track start
i6 = FunctionInterval(printTrackStart)
# This will print some arguments

View File

@ -116,9 +116,7 @@ class HotKeyPanel(ScrolledPanel):
def updateUI(self):
vbox = wx.BoxSizer(wx.VERTICAL)
keys = list(base.direct.hotKeyMap.keys())
keys.sort()
for key in keys:
for key in sorted(base.direct.hotKeyMap):
keyDesc = base.direct.hotKeyMap[key]
itemPanel = wx.Panel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)

View File

@ -4,7 +4,7 @@ if __name__ == '__main__':
from direct.showbase.ShowBase import ShowBase
base = ShowBase()
base.le = LevelEditor.LevelEditor()
base.le = LevelEditor.LevelEditor() # type: ignore[attr-defined]
# You should define LevelEditor instance as
# base.le so it can be reached in global scope

View File

@ -1,4 +1,5 @@
from panda3d.core import Point3, VBase3
from direct.showbase.ShowBaseGlobal import base
if hasattr(base, 'le'):
objectMgr = base.le.objectMgr
@ -6,7 +7,7 @@ if hasattr(base, 'le'):
ui.sceneGraphUI.reset()
else:
objectMgr = base.objectMgr
objectMgr = base.objectMgr # type: ignore[attr-defined]
# temporary place holder for nodepath
objects = {}

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from panda3d.core import (
BoundingVolume,
ColorBlendAttrib,
@ -92,7 +94,7 @@ class MotionTrail(NodePath, DirectObject):
notify = directNotify.newCategory("MotionTrail")
task_added = False
motion_trail_list = []
motion_trail_list: list[MotionTrail] = []
motion_trail_task_name = "motion_trail_task"
global_enable = True

View File

@ -55,8 +55,6 @@ class BulletinBoard:
def __repr__(self):
str = 'Bulletin Board Contents\n'
str += '======================='
keys = list(self._dict.keys())
keys.sort()
for postName in keys:
for postName in sorted(self._dict):
str += '\n%s: %s' % (postName, self._dict[postName])
return str

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from direct.directnotify.DirectNotifyGlobal import directNotify
import direct.showbase.DConfig as config
from direct.showbase.PythonUtil import makeFlywheelGen, flywheel
@ -11,7 +13,6 @@ import types
import weakref
import random
import builtins
import sys
deadEndTypes = frozenset((
@ -566,7 +567,7 @@ class FindContainers(Job):
curObjRef = None
# types.CellType was added in Python 3.8
if sys.version_info >= (3, 8) and type(curObj) is types.CellType:
if type(curObj) is types.CellType:
child = curObj.cell_contents
hasLength = self._hasLength(child)
notDeadEnd = not self._isDeadEnd(child)
@ -983,7 +984,7 @@ class ContainerLeakDetector(Job):
"""
notify = directNotify.newCategory("ContainerLeakDetector")
# set of containers that should not be examined
PrivateIds = set()
PrivateIds: set[int] = set()
def __init__(self, name, firstCheckDelay = None):
Job.__init__(self, name)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import Queue, invertDictLossless
from direct.showbase.PythonUtil import safeRepr
@ -5,14 +7,13 @@ from direct.showbase.Job import Job
from direct.showbase.JobManagerGlobal import jobMgr
from direct.showbase.ContainerLeakDetector import deadEndTypes
import types
import sys
import io
class ContainerReport(Job):
notify = directNotify.newCategory("ContainerReport")
# set of containers that should not be included in the report
PrivateIds = set()
PrivateIds: set[int] = set()
def __init__(self, name, log=False, limit=None, threaded=False):
Job.__init__(self, name)
@ -122,7 +123,7 @@ class ContainerReport(Job):
continue
# types.CellType was added in Python 3.8
if sys.version_info >= (3, 8) and type(parentObj) is types.CellType:
if type(parentObj) is types.CellType:
child = parentObj.cell_contents
if self._examine(child):
assert (self._queue.back() is child)
@ -228,14 +229,11 @@ class ContainerReport(Job):
if type not in self._type2id2len:
return
len2ids = invertDictLossless(self._type2id2len[type])
lengths = list(len2ids.keys())
lengths.sort()
lengths.reverse()
print('=====')
print('===== %s' % type)
count = 0
stop = False
for l in lengths:
for l in sorted(len2ids, reverse=True):
#len2ids[l].sort()
pathStrList = list()
for id in len2ids[l]:
@ -257,9 +255,8 @@ class ContainerReport(Job):
for type in initialTypes:
for i in self._outputType(type, **kArgs):
yield None
otherTypes = list(set(self._type2id2len.keys()).difference(set(initialTypes)))
otherTypes.sort(key=lambda obj: obj.__name__)
for type in otherTypes:
otherTypes = set(self._type2id2len).difference(initialTypes)
for type in sorted(otherTypes, key=lambda obj: obj.__name__):
for i in self._outputType(type, **kArgs):
yield None

View File

@ -81,147 +81,3 @@ class CountedResource(object):
def __del__(self):
self.decrementCounter()
if __debug__ and __name__ == '__main__':
class MouseResource(CountedResource):
"""
A simple class to demonstrate the acquisition of a resource.
"""
@classmethod
def acquire(cls):
# The call to the super-class's acquire() is
# not necessary at the moment, but may be in
# the future, so do it now for good measure.
super(MouseResource, cls).acquire()
# Now acquire the resource this class is
# managing.
print('-- Acquire Mouse')
@classmethod
def release(cls):
# First, release the resource this class is
# managing.
print('-- Release Mouse')
# The call to the super-class's release() is
# not necessary at the moment, but may be in
# the future, so do it now for good measure.
super(MouseResource, cls).release()
def __init__(self):
super(MouseResource, self).__init__()
def __del__(self):
super(MouseResource, self).__del__()
class CursorResource(CountedResource):
"""
A class to demonstrate how to implement a dependent
resource. Notice how this class also inherits from
CountedResource. Instead of subclassing MouseCounter,
we will just acquire it in our __init__() and release
it in our __del__().
"""
@classmethod
def acquire(cls):
super(CursorResource, cls).acquire()
print('-- Acquire Cursor')
@classmethod
def release(cls):
print('-- Release Cursor')
super(CursorResource, cls).release()
def __init__(self):
# The required resource references should
# be stored on 'self' since we want to
# release it when the object is deleted.
self.__mouseResource = MouseResource()
# Call the super-classes __init__()
# after all required resources are
# referenced.
super(CursorResource, self).__init__()
def __del__(self):
# Free up the most dependent resource
# first, the one this class is managing.
super(CursorResource, self).__del__()
# Now unlink any required resources.
del self.__mouseResource
class InvalidResource(MouseResource):
@classmethod
def acquire(cls):
super(InvalidResource, cls).acquire()
print('-- Acquire Invalid')
@classmethod
def release(cls):
print('-- Release Invalid')
super(InvalidResource, cls).release()
print('\nAllocate Mouse')
m = MouseResource()
print('Free up Mouse')
del m
print('\nAllocate Cursor')
c = CursorResource()
print('Free up Cursor')
del c
print('\nAllocate Mouse then Cursor')
m = MouseResource()
c = CursorResource()
print('Free up Cursor')
del c
print('Free up Mouse')
del m
print('\nAllocate Mouse then Cursor')
m = MouseResource()
c = CursorResource()
print('Free up Mouse')
del m
print('Free up Cursor')
del c
print('\nAllocate Cursor then Mouse')
c = CursorResource()
m = MouseResource()
print('Free up Mouse')
del m
print('Free up Cursor')
del c
print('\nAllocate Cursor then Mouse')
c = CursorResource()
m = MouseResource()
print('Free up Cursor')
del c
# example of an invalid subclass
try:
print('\nAllocate Invalid')
i = InvalidResource()
print('Free up Invalid')
except AssertionError as e:
print(e)
print('')
print('Free up Mouse')
del m
def demoFunc():
print('\nAllocate Cursor within function')
c = CursorResource()
print('Cursor will be freed on function exit')
demoFunc()

View File

@ -1,6 +1,6 @@
"This module contains a deprecated shim emulating the old DConfig API."
__all__ = []
__all__ = ()
from panda3d.core import (ConfigFlags, ConfigVariableBool, ConfigVariableInt,
ConfigVariableDouble, ConfigVariableString)

View File

@ -1,3 +1,5 @@
from __future__ import annotations
from direct.showbase.DirectObject import DirectObject
from direct.directnotify.DirectNotifyGlobal import directNotify
from panda3d.core import (
@ -62,7 +64,7 @@ class DistancePhasedNode(PhasedObject, DirectObject, NodePath):
notify = directNotify.newCategory("DistancePhasedObject")
__InstanceSequence = 0
__InstanceDeque = []
__InstanceDeque: list[int] = []
@staticmethod
def __allocateId():
@ -327,31 +329,3 @@ class BufferedDistancePhasedNode(DistancePhasedNode):
for x,sphere in enumerate(self._colSpheres[phase+1:]):
sphere.node().modifySolid(0).setRadius(self.bufferParamList[x+phase+1][1][0])
sphere.node().markInternalBoundsStale()
if __debug__ and 0:
cSphere = CollisionSphere(0, 0, 0, 0.1)
cNode = CollisionNode('camCol')
cNode.addSolid(cSphere)
cNodePath = NodePath(cNode)
cNodePath.reparentTo(base.cam)
# cNodePath.show()
# cNodePath.setPos(25,0,0)
base.cTrav = CollisionTraverser()
eventHandler = CollisionHandlerEvent()
eventHandler.addInPattern('enter%in')
eventHandler.addOutPattern('exit%in')
# messenger.toggleVerbose()
base.cTrav.addCollider(cNodePath, eventHandler)
p = BufferedDistancePhasedNode('p', {'At': (10, 20), 'Near': (100, 200), 'Far': (1000, 1020)},
autoCleanup=False,
fromCollideNode=cNodePath,
)
p.reparentTo(render)
p._DistancePhasedNode__oneTimeCollide()
base.eventMgr.doEvents()

View File

@ -69,7 +69,7 @@ oldExcepthook = None
# from its main exception handler
wantStackDumpLog = False
wantStackDumpUpload = False
variableDumpReasons = []
variableDumpReasons: list = []
dumpOnExceptionInit = False
@ -116,15 +116,10 @@ def _excepthookDumpVars(eType, eValue, tb):
for name, obj in frame.f_locals.items():
if name in codeNames:
name2obj[name] = obj
# show them in alphabetical order
names = list(name2obj.keys())
names.sort()
# push them in reverse order so they'll be popped in the correct order
names.reverse()
traversedIds = set()
for name in names:
# push them in reverse alphabetical order so they'll be popped in the correct order
for name in sorted(name2obj, reverse=True):
stateStack.push([name, name2obj[name], traversedIds])
while len(stateStack) > 0:
@ -150,14 +145,10 @@ def _excepthookDumpVars(eType, eValue, tb):
continue
attrName2obj[attrName] = attr
if len(attrName2obj) > 0:
# show them in alphabetical order
attrNames = list(attrName2obj.keys())
attrNames.sort()
# push them in reverse order so they'll be popped in the correct order
attrNames.reverse()
ids = set(traversedIds)
ids.add(id(obj))
for attrName in attrNames:
# push them in reverse alphabetical order so they'll be popped in the correct order
for attrName in sorted(attrName2obj, reverse=True):
obj = attrName2obj[attrName]
stateStack.push(['%s.%s' % (name, attrName), obj, ids])

View File

@ -294,8 +294,7 @@ class GarbageReport(Job):
if self._args.fullReport:
garbageIndices = range(self.numGarbage)
else:
garbageIndices = list(self.cycleIds)
garbageIndices.sort()
garbageIndices = sorted(self.cycleIds)
numGarbage = len(garbageIndices)
# log each individual item with a number in front of it

View File

@ -141,9 +141,7 @@ class JobManager:
def _getSortedPriorities(self):
# returns all job priorities in ascending order
priorities = list(self._pri2jobId2job.keys())
priorities.sort()
return priorities
return sorted(self._pri2jobId2job)
def _process(self, task=None):
if self._useOverflowTime is None:

View File

@ -540,9 +540,7 @@ class Messenger:
return a matching event (needle) if found (in haystack).
This is primarily a debugging tool.
"""
keys = list(self.__callbacks.keys())
keys.sort()
for event in keys:
for event in sorted(self.__callbacks):
if repr(event).find(needle) >= 0:
return {event: self.__callbacks[event]}
@ -553,9 +551,7 @@ class Messenger:
This is primarily a debugging tool.
"""
matches = {}
keys = list(self.__callbacks.keys())
keys.sort()
for event in keys:
for event in sorted(self.__callbacks):
if repr(event).find(needle) >= 0:
matches[event] = self.__callbacks[event]
# if the limit is not None, decrement and
@ -596,9 +592,7 @@ class Messenger:
Compact version of event, acceptor pairs
"""
str = "The messenger is currently handling:\n" + "="*64 + "\n"
keys = list(self.__callbacks.keys())
keys.sort()
for event in keys:
for event in sorted(self.__callbacks):
str += self.__eventRepr(event)
# Print out the object: event dictionary too
str += "="*64 + "\n"
@ -617,9 +611,7 @@ class Messenger:
"""
str = 'Messenger\n'
str = str + '='*50 + '\n'
keys = list(self.__callbacks.keys())
keys.sort()
for event in keys:
for event in sorted(self.__callbacks):
acceptorDict = self.__callbacks[event]
str = str + 'Event: ' + event + '\n'
for key in list(acceptorDict.keys()):

View File

@ -90,10 +90,7 @@ class ObjectPool:
def typeFreqStr(self):
s = 'Object Pool: Type Frequencies'
s += '\n============================='
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
for count in sorted(self._count2types, reverse=True):
types = makeList(self._count2types[count])
for typ in types:
s += '\n%s\t%s' % (count, typ)
@ -102,12 +99,10 @@ class ObjectPool:
def printObjsByType(self):
print('Object Pool: Objects By Type')
print('\n============================')
counts = list(set(self._count2types.keys()))
counts.sort()
# print types with the smallest number of instances first, in case
# there's a large group that waits a long time before printing
#counts.reverse()
for count in counts:
for count in sorted(self._count2types):
types = makeList(self._count2types[count])
for typ in types:
print('TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])))
@ -115,10 +110,7 @@ class ObjectPool:
def printReferrers(self, numEach=3):
"""referrers of the first few of each type of object"""
counts = list(set(self._count2types.keys()))
counts.sort()
counts.reverse()
for count in counts:
for count in sorted(self._count2types, reverse=True):
types = makeList(self._count2types[count])
for typ in types:
print('\n\nTYPE: %s' % repr(typ))

View File

@ -11,6 +11,8 @@
>>> o.diff(o2)
"""
from __future__ import annotations
__all__ = ['ExclusiveObjectPool', 'ObjectReport']
from direct.directnotify.DirectNotifyGlobal import directNotify
@ -26,8 +28,8 @@ import builtins
class ExclusiveObjectPool(DirectObject):
# ObjectPool specialization that excludes particular objects
# IDs of objects to globally exclude from reporting
_ExclObjs = []
_ExclObjIds = {}
_ExclObjs: list[object] = []
_ExclObjIds: dict[int, int] = {}
_SyncMaster = Sync('ExclusiveObjectPool.ExcludedObjectList')
_SerialNumGen = SerialNumGen()

View File

@ -57,9 +57,7 @@ class OnScreenDebug:
if not self.onScreenText:
self.load()
self.onScreenText.clearText()
entries = list(self.data.items())
entries.sort()
for k, v in entries:
for k, v in sorted(self.data.items()):
if v[0] == self.frame:
# It was updated this frame (key equals value):
#isNew = " is"

View File

@ -1,5 +1,7 @@
"""Contains miscellaneous utility functions and classes."""
from __future__ import annotations
__all__ = [
'indent', 'doc', 'adjust', 'difference', 'intersection', 'union',
@ -11,7 +13,7 @@ __all__ = [
'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
'findPythonModule', 'mostDerivedLast', 'clampScalar', 'weightedChoice',
'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32',
'SerialNumGen', 'serialNum', 'uniqueName', 'Singleton',
'SerialNumGen', 'SerialMaskedGen', 'serialNum', 'uniqueName', 'Singleton',
'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr',
'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString',
'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall',
@ -39,6 +41,7 @@ import time
import builtins
import importlib
import functools
from typing import Callable
__report_indent = 3
@ -699,9 +702,9 @@ if __debug__:
return profileDecorator
# intercept profile-related file operations to avoid disk access
movedOpenFuncs = []
movedDumpFuncs = []
movedLoadFuncs = []
movedOpenFuncs: list[Callable] = []
movedDumpFuncs: list[Callable] = []
movedLoadFuncs: list[Callable] = []
profileFilenames = set()
profileFilenameList = Stack()
profileFilename2file = {}
@ -1899,7 +1902,7 @@ class SubframeCall:
class PStatScope:
collectors = {}
collectors: dict = {}
def __init__(self, level = None):
self.levels = []
@ -2458,18 +2461,6 @@ def formatTimeCompact(seconds):
return result
if __debug__ and __name__ == '__main__':
ftc = formatTimeCompact
assert ftc(0) == '0s'
assert ftc(1) == '1s'
assert ftc(60) == '1m0s'
assert ftc(64) == '1m4s'
assert ftc(60*60) == '1h0m0s'
assert ftc(24*60*60) == '1d0h0m0s'
assert ftc(24*60*60 + 2*60*60 + 34*60 + 12) == '1d2h34m12s'
del ftc
def formatTimeExact(seconds):
# like formatTimeCompact but leaves off '0 seconds', '0 minutes' etc. for
# times that are e.g. 1 hour, 3 days etc.
@ -2496,19 +2487,6 @@ def formatTimeExact(seconds):
return result
if __debug__ and __name__ == '__main__':
fte = formatTimeExact
assert fte(0) == '0s'
assert fte(1) == '1s'
assert fte(2) == '2s'
assert fte(61) == '1m1s'
assert fte(60) == '1m'
assert fte(60*60) == '1h'
assert fte(24*60*60) == '1d'
assert fte((24*60*60) + (2 * 60)) == '1d0h2m'
del fte
class AlphabetCounter:
# object that produces 'A', 'B', 'C', ... 'AA', 'AB', etc.
def __init__(self):
@ -2540,28 +2518,6 @@ class AlphabetCounter:
__next__ = next
if __debug__ and __name__ == '__main__':
def testAlphabetCounter():
tempList = []
ac = AlphabetCounter()
for i in range(26*3):
tempList.append(ac.next())
assert tempList == [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM','AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ',
'BA','BB','BC','BD','BE','BF','BG','BH','BI','BJ','BK','BL','BM','BN','BO','BP','BQ','BR','BS','BT','BU','BV','BW','BX','BY','BZ',]
ac = AlphabetCounter()
num = 26 # A-Z
num += (26*26) # AA-ZZ
num += 26 # AAZ
num += 1 # ABA
num += 2 # ABC
for i in range(num):
x = ac.next()
assert x == 'ABC'
testAlphabetCounter()
del testAlphabetCounter
class Default:
# represents 'use the default value'
# useful for keyword arguments to virtual methods
@ -2665,52 +2621,52 @@ class PriorityCallbacks:
callback()
builtins.Functor = Functor
builtins.Stack = Stack
builtins.Queue = Queue
builtins.SerialNumGen = SerialNumGen
builtins.SerialMaskedGen = SerialMaskedGen
builtins.ScratchPad = ScratchPad
builtins.uniqueName = uniqueName
builtins.serialNum = serialNum
builtins.Functor = Functor # type: ignore[attr-defined]
builtins.Stack = Stack # type: ignore[attr-defined]
builtins.Queue = Queue # type: ignore[attr-defined]
builtins.SerialNumGen = SerialNumGen # type: ignore[attr-defined]
builtins.SerialMaskedGen = SerialMaskedGen # type: ignore[attr-defined]
builtins.ScratchPad = ScratchPad # type: ignore[attr-defined]
builtins.uniqueName = uniqueName # type: ignore[attr-defined]
builtins.serialNum = serialNum # type: ignore[attr-defined]
if __debug__:
builtins.profiled = profiled
builtins.exceptionLogged = exceptionLogged
builtins.itype = itype
builtins.appendStr = appendStr
builtins.bound = bound
builtins.clamp = clamp
builtins.lerp = lerp
builtins.makeList = makeList
builtins.makeTuple = makeTuple
builtins.profiled = profiled # type: ignore[attr-defined]
builtins.exceptionLogged = exceptionLogged # type: ignore[attr-defined]
builtins.itype = itype # type: ignore[attr-defined]
builtins.appendStr = appendStr # type: ignore[attr-defined]
builtins.bound = bound # type: ignore[attr-defined]
builtins.clamp = clamp # type: ignore[attr-defined]
builtins.lerp = lerp # type: ignore[attr-defined]
builtins.makeList = makeList # type: ignore[attr-defined]
builtins.makeTuple = makeTuple # type: ignore[attr-defined]
if __debug__:
builtins.printStack = printStack
builtins.printReverseStack = printReverseStack
builtins.printVerboseStack = printVerboseStack
builtins.DelayedCall = DelayedCall
builtins.DelayedFunctor = DelayedFunctor
builtins.FrameDelayedCall = FrameDelayedCall
builtins.SubframeCall = SubframeCall
builtins.invertDict = invertDict
builtins.invertDictLossless = invertDictLossless
builtins.getBase = getBase
builtins.getRepository = getRepository
builtins.safeRepr = safeRepr
builtins.fastRepr = fastRepr
builtins.nullGen = nullGen
builtins.flywheel = flywheel
builtins.loopGen = loopGen
builtins.printStack = printStack # type: ignore[attr-defined]
builtins.printReverseStack = printReverseStack # type: ignore[attr-defined]
builtins.printVerboseStack = printVerboseStack # type: ignore[attr-defined]
builtins.DelayedCall = DelayedCall # type: ignore[attr-defined]
builtins.DelayedFunctor = DelayedFunctor # type: ignore[attr-defined]
builtins.FrameDelayedCall = FrameDelayedCall # type: ignore[attr-defined]
builtins.SubframeCall = SubframeCall # type: ignore[attr-defined]
builtins.invertDict = invertDict # type: ignore[attr-defined]
builtins.invertDictLossless = invertDictLossless # type: ignore[attr-defined]
builtins.getBase = getBase # type: ignore[attr-defined]
builtins.getRepository = getRepository # type: ignore[attr-defined]
builtins.safeRepr = safeRepr # type: ignore[attr-defined]
builtins.fastRepr = fastRepr # type: ignore[attr-defined]
builtins.nullGen = nullGen # type: ignore[attr-defined]
builtins.flywheel = flywheel # type: ignore[attr-defined]
builtins.loopGen = loopGen # type: ignore[attr-defined]
if __debug__:
builtins.StackTrace = StackTrace
builtins.report = report
builtins.pstatcollect = pstatcollect
builtins.MiniLog = MiniLog
builtins.MiniLogSentry = MiniLogSentry
builtins.logBlock = logBlock
builtins.HierarchyException = HierarchyException
builtins.deeptype = deeptype
builtins.Default = Default
builtins.configIsToday = configIsToday
builtins.typeName = typeName
builtins.safeTypeName = safeTypeName
builtins.histogramDict = histogramDict
builtins.StackTrace = StackTrace # type: ignore[attr-defined]
builtins.report = report # type: ignore[attr-defined]
builtins.pstatcollect = pstatcollect # type: ignore[attr-defined]
builtins.MiniLog = MiniLog # type: ignore[attr-defined]
builtins.MiniLogSentry = MiniLogSentry # type: ignore[attr-defined]
builtins.logBlock = logBlock # type: ignore[attr-defined]
builtins.HierarchyException = HierarchyException # type: ignore[attr-defined]
builtins.deeptype = deeptype # type: ignore[attr-defined]
builtins.Default = Default # type: ignore[attr-defined]
builtins.configIsToday = configIsToday # type: ignore[attr-defined]
builtins.typeName = typeName # type: ignore[attr-defined]
builtins.safeTypeName = safeTypeName # type: ignore[attr-defined]
builtins.histogramDict = histogramDict # type: ignore[attr-defined]

View File

@ -120,7 +120,7 @@ from direct.extensions_native import NodePath_extensions # pylint: disable=unuse
# This needs to be available early for DirectGUI imports
import sys
import builtins
builtins.config = DConfig
builtins.config = DConfig # type: ignore[attr-defined]
from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify
from .MessengerGlobal import messenger
@ -3315,8 +3315,9 @@ class ShowBase(DirectObject.DirectObject):
init_app_for_gui()
# Disable the Windows message loop, since Tcl wants to handle this all
# on its own.
ConfigVariableBool('disable-message-loop', False).value = True
# on its own, except if the Panda window is on a separate thread.
if self.graphicsEngine.getThreadingModel().getDrawStage() == 0:
ConfigVariableBool('disable-message-loop', False).value = True
if ConfigVariableBool('tk-main-loop', True):
# Put Tkinter in charge of the main loop. It really

View File

@ -11,7 +11,7 @@ Note that you cannot directly import :data:`~builtins.base` from this module
since ShowBase may not have been created yet; instead, ShowBase dynamically
adds itself to this module's scope when instantiated."""
__all__ = []
__all__ = ()
from .ShowBase import ShowBase, WindowControls # pylint: disable=unused-import
from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify # pylint: disable=unused-import
@ -23,6 +23,8 @@ import warnings
__dev__ = ConfigVariableBool('want-dev', __debug__).value
base: ShowBase
#: The global instance of the :ref:`virtual-file-system`, as obtained using
#: :meth:`panda3d.core.VirtualFileSystem.getGlobalPtr()`.
vfs = VirtualFileSystem.getGlobalPtr()
@ -81,7 +83,7 @@ def inspect(anObject):
import builtins
builtins.inspect = inspect
builtins.inspect = inspect # type: ignore[attr-defined]
# this also appears in AIBaseGlobal
if (not __debug__) and __dev__:

View File

@ -3,7 +3,7 @@ This module hooks into Python's import mechanism to print out all imports to
the standard output as they happen.
"""
__all__ = []
__all__ = ()
import sys

View File

@ -756,8 +756,8 @@ class TexMemWatcher(DirectObject):
# Sort the regions from largest to smallest to maximize
# packing effectiveness.
texRecords = list(self.texRecordsByTex.values())
texRecords.sort(key = lambda tr: (tr.tw, tr.th), reverse = True)
texRecords = sorted(self.texRecordsByTex.values(),
key=lambda tr: (tr.tw, tr.th), reverse=True)
for tr in texRecords:
self.placeTexture(tr)

View File

@ -45,7 +45,7 @@ BasePickler = pickle._Pickler
BaseUnpickler = pickle._Unpickler
class Pickler(BasePickler):
class Pickler(BasePickler): # type: ignore[misc, valid-type]
def __init__(self, *args, **kw):
self.bamWriter = BamWriter()
@ -148,7 +148,7 @@ class Pickler(BasePickler):
self.save_reduce(obj=obj, *rv)
class Unpickler(BaseUnpickler):
class Unpickler(BaseUnpickler): # type: ignore[misc, valid-type]
def __init__(self, *args, **kw):
self.bamReader = BamReader()

View File

@ -83,8 +83,8 @@ class ThreadBase:
# Copy these static methods from Panda's Thread object. These are
# useful if you may be running in Panda's SIMPLE_THREADS compilation
# mode.
ThreadBase.forceYield = core.Thread.forceYield
ThreadBase.considerYield = core.Thread.considerYield
ThreadBase.forceYield = core.Thread.forceYield # type: ignore[attr-defined]
ThreadBase.considerYield = core.Thread.considerYield # type: ignore[attr-defined]
class Thread(ThreadBase):
@ -123,10 +123,7 @@ class Thread(ThreadBase):
self.__dict__['ident'] = threadId
def __del__(self):
# On interpreter shutdown, the _thread module might have
# already been cleaned up.
if _thread and _thread._remove_thread_id:
_thread._remove_thread_id(self.ident)
_thread._remove_thread_id(self.ident)
def is_alive(self):
thread = self.__thread
@ -435,108 +432,3 @@ def setprofile(func):
def stack_size(size = None):
raise ThreadError
if __debug__:
def _test():
from collections import deque
_sleep = core.Thread.sleep
_VERBOSE = False
class _Verbose(object):
def __init__(self, verbose=None):
if verbose is None:
verbose = _VERBOSE
self.__verbose = verbose
def _note(self, format, *args):
if self.__verbose:
format = format % args
format = "%s: %s\n" % (
currentThread().getName(), format)
_sys.stderr.write(format)
class BoundedQueue(_Verbose):
def __init__(self, limit):
_Verbose.__init__(self)
self.mon = Lock(name = "BoundedQueue.mon")
self.rc = Condition(self.mon)
self.wc = Condition(self.mon)
self.limit = limit
self.queue = deque()
def put(self, item):
self.mon.acquire()
while len(self.queue) >= self.limit:
self._note("put(%s): queue full", item)
self.wc.wait()
self.queue.append(item)
self._note("put(%s): appended, length now %d",
item, len(self.queue))
self.rc.notify()
self.mon.release()
def get(self):
self.mon.acquire()
while not self.queue:
self._note("get(): queue empty")
self.rc.wait()
item = self.queue.popleft()
self._note("get(): got %s, %d left", item, len(self.queue))
self.wc.notify()
self.mon.release()
return item
class ProducerThread(Thread):
def __init__(self, queue, quota):
Thread.__init__(self, name="Producer")
self.queue = queue
self.quota = quota
def run(self):
from random import random
counter = 0
while counter < self.quota:
counter = counter + 1
self.queue.put("%s.%d" % (self.getName(), counter))
_sleep(random() * 0.00001)
class ConsumerThread(Thread):
def __init__(self, queue, count):
Thread.__init__(self, name="Consumer")
self.queue = queue
self.count = count
def run(self):
while self.count > 0:
item = self.queue.get()
print(item)
self.count = self.count - 1
NP = 3
QL = 4
NI = 5
Q = BoundedQueue(QL)
P = []
for i in range(NP):
t = ProducerThread(Q, NI)
t.setName("Producer-%d" % (i+1))
P.append(t)
C = ConsumerThread(Q, NI*NP)
for t in P:
t.start()
_sleep(0.000001)
C.start()
for t in P:
t.join()
C.join()
if __name__ == '__main__':
_test()

View File

@ -65,7 +65,7 @@ if __debug__:
else:
# Disable this when using "python -O"
class _Verbose(object):
class _Verbose(object): # type: ignore[no-redef]
def __init__(self, verbose=None):
pass
def _note(self, *args):
@ -762,92 +762,3 @@ def main_thread():
## from thread import _local as local
## except ImportError:
## from _threading_local import local
# Self-test code
if __debug__:
def _test():
from collections import deque
class BoundedQueue(_Verbose):
def __init__(self, limit):
_Verbose.__init__(self)
self.mon = RLock()
self.rc = Condition(self.mon)
self.wc = Condition(self.mon)
self.limit = limit
self.queue = deque()
def put(self, item):
self.mon.acquire()
while len(self.queue) >= self.limit:
self._note("put(%s): queue full", item)
self.wc.wait()
self.queue.append(item)
self._note("put(%s): appended, length now %d",
item, len(self.queue))
self.rc.notify()
self.mon.release()
def get(self):
self.mon.acquire()
while not self.queue:
self._note("get(): queue empty")
self.rc.wait()
item = self.queue.popleft()
self._note("get(): got %s, %d left", item, len(self.queue))
self.wc.notify()
self.mon.release()
return item
class ProducerThread(Thread):
def __init__(self, queue, quota):
Thread.__init__(self, name="Producer")
self.queue = queue
self.quota = quota
def run(self):
from random import random
counter = 0
while counter < self.quota:
counter = counter + 1
self.queue.put("%s.%d" % (self.getName(), counter))
_sleep(random() * 0.00001)
class ConsumerThread(Thread):
def __init__(self, queue, count):
Thread.__init__(self, name="Consumer")
self.queue = queue
self.count = count
def run(self):
while self.count > 0:
item = self.queue.get()
print(item)
self.count = self.count - 1
NP = 3
QL = 4
NI = 5
Q = BoundedQueue(QL)
P = []
for i in range(NP):
t = ProducerThread(Q, NI)
t.setName("Producer-%d" % (i+1))
P.append(t)
C = ConsumerThread(Q, NI*NP)
for t in P:
t.start()
_sleep(0.000001)
C.start()
for t in P:
t.join()
C.join()
if __name__ == '__main__':
_test()

View File

@ -24,7 +24,7 @@ if hasattr(sys, 'getandroidapilevel'):
signal = None
else:
try:
import _signal as signal
import _signal as signal # type: ignore[import, no-redef]
except ImportError:
signal = None
@ -801,532 +801,6 @@ class TaskManager:
# time.sleep(delta)
# delta = minFinTime - self.globalClock.getRealTime()
if __debug__:
# to catch memory leaks during the tests at the bottom of the file
def _startTrackingMemLeaks(self):
pass
def _stopTrackingMemLeaks(self):
pass
def _checkMemLeaks(self):
pass
def _runTests(self):
if __debug__:
tm = TaskManager()
tm.setClock(ClockObject())
tm.setupTaskChain("default", tickClock = True)
# check for memory leaks after every test
tm._startTrackingMemLeaks()
tm._checkMemLeaks()
# run-once task
l = []
def _testDone(task, l=l):
l.append(None)
return task.done
tm.add(_testDone, 'testDone')
tm.step()
assert len(l) == 1
tm.step()
assert len(l) == 1
_testDone = None
tm._checkMemLeaks()
# remove by name
def _testRemoveByName(task):
return task.done
tm.add(_testRemoveByName, 'testRemoveByName')
assert tm.remove('testRemoveByName') == 1
assert tm.remove('testRemoveByName') == 0
_testRemoveByName = None
tm._checkMemLeaks()
# duplicate named tasks
def _testDupNamedTasks(task):
return task.done
tm.add(_testDupNamedTasks, 'testDupNamedTasks')
tm.add(_testDupNamedTasks, 'testDupNamedTasks')
assert tm.remove('testRemoveByName') == 0
_testDupNamedTasks = None
tm._checkMemLeaks()
# continued task
l = []
def _testCont(task, l = l):
l.append(None)
return task.cont
tm.add(_testCont, 'testCont')
tm.step()
assert len(l) == 1
tm.step()
assert len(l) == 2
tm.remove('testCont')
_testCont = None
tm._checkMemLeaks()
# continue until done task
l = []
def _testContDone(task, l = l):
l.append(None)
if len(l) >= 2:
return task.done
else:
return task.cont
tm.add(_testContDone, 'testContDone')
tm.step()
assert len(l) == 1
tm.step()
assert len(l) == 2
tm.step()
assert len(l) == 2
assert not tm.hasTaskNamed('testContDone')
_testContDone = None
tm._checkMemLeaks()
# hasTaskNamed
def _testHasTaskNamed(task):
return task.done
tm.add(_testHasTaskNamed, 'testHasTaskNamed')
assert tm.hasTaskNamed('testHasTaskNamed')
tm.step()
assert not tm.hasTaskNamed('testHasTaskNamed')
_testHasTaskNamed = None
tm._checkMemLeaks()
# task sort
l = []
def _testPri1(task, l = l):
l.append(1)
return task.cont
def _testPri2(task, l = l):
l.append(2)
return task.cont
tm.add(_testPri1, 'testPri1', sort = 1)
tm.add(_testPri2, 'testPri2', sort = 2)
tm.step()
assert len(l) == 2
assert l == [1, 2,]
tm.step()
assert len(l) == 4
assert l == [1, 2, 1, 2,]
tm.remove('testPri1')
tm.remove('testPri2')
_testPri1 = None
_testPri2 = None
tm._checkMemLeaks()
# task extraArgs
l = []
def _testExtraArgs(arg1, arg2, l=l):
l.extend([arg1, arg2,])
return done
tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
tm.step()
assert len(l) == 2
assert l == [4, 5,]
_testExtraArgs = None
tm._checkMemLeaks()
# task appendTask
l = []
def _testAppendTask(arg1, arg2, task, l=l):
l.extend([arg1, arg2,])
return task.done
tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
tm.step()
assert len(l) == 2
assert l == [4, 5,]
_testAppendTask = None
tm._checkMemLeaks()
# task uponDeath
l = []
def _uponDeathFunc(task, l=l):
l.append(task.name)
def _testUponDeath(task):
return done
tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
tm.step()
assert len(l) == 1
assert l == ['testUponDeath']
_testUponDeath = None
_uponDeathFunc = None
tm._checkMemLeaks()
# task owner
class _TaskOwner:
def _addTask(self, task):
self.addedTaskName = task.name
def _clearTask(self, task):
self.clearedTaskName = task.name
to = _TaskOwner()
l = []
def _testOwner(task):
return done
tm.add(_testOwner, 'testOwner', owner=to)
tm.step()
assert getattr(to, 'addedTaskName', None) == 'testOwner'
assert getattr(to, 'clearedTaskName', None) == 'testOwner'
_testOwner = None
del to
_TaskOwner = None
tm._checkMemLeaks()
doLaterTests = [0,]
# doLater
l = []
def _testDoLater1(task, l=l):
l.append(1)
def _testDoLater2(task, l=l):
l.append(2)
def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
if task.time > .03:
assert l == [1, 2,]
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
_testDoLater1 = None
_testDoLater2 = None
_monitorDoLater = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater sort
l = []
def _testDoLaterPri1(task, l=l):
l.append(1)
def _testDoLaterPri2(task, l=l):
l.append(2)
def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
if task.time > .02:
assert l == [1, 2,]
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
_testDoLaterPri1 = None
_testDoLaterPri2 = None
_monitorDoLaterPri = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater extraArgs
l = []
def _testDoLaterExtraArgs(arg1, l=l):
l.append(arg1)
def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
if task.time > .02:
assert l == [3,]
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
_testDoLaterExtraArgs = None
_monitorDoLaterExtraArgs = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater appendTask
l = []
def _testDoLaterAppendTask(arg1, task, l=l):
assert task.name == 'testDoLaterAppendTask'
l.append(arg1)
def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
if task.time > .02:
assert l == [4,]
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
extraArgs=[4,], appendTask=True)
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
_testDoLaterAppendTask = None
_monitorDoLaterAppendTask = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater uponDeath
l = []
def _testUponDeathFunc(task, l=l):
assert task.name == 'testDoLaterUponDeath'
l.append(10)
def _testDoLaterUponDeath(arg1, l=l):
return done
def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
if task.time > .02:
assert l == [10,]
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
uponDeath=_testUponDeathFunc)
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
_testUponDeathFunc = None
_testDoLaterUponDeath = None
_monitorDoLaterUponDeath = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater owner
class _DoLaterOwner:
def _addTask(self, task):
self.addedTaskName = task.name
def _clearTask(self, task):
self.clearedTaskName = task.name
doLaterOwner = _DoLaterOwner()
l = []
def _testDoLaterOwner(l=l):
pass
def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
doLaterTests=doLaterTests):
if task.time > .02:
assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
doLaterTests[0] -= 1
return task.done
return task.cont
tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
owner=doLaterOwner)
doLaterTests[0] += 1
# make sure we run this task after the doLaters if they all occur on the same frame
tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
_testDoLaterOwner = None
_monitorDoLaterOwner = None
del doLaterOwner
_DoLaterOwner = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# run the doLater tests
while doLaterTests[0] > 0:
tm.step()
del doLaterTests
tm._checkMemLeaks()
# getTasks
def _testGetTasks(task):
return task.cont
# No doLaterProcessor in the new world.
assert len(tm.getTasks()) == 0
tm.add(_testGetTasks, 'testGetTasks1')
assert len(tm.getTasks()) == 1
assert (tm.getTasks()[0].name == 'testGetTasks1' or
tm.getTasks()[1].name == 'testGetTasks1')
tm.add(_testGetTasks, 'testGetTasks2')
tm.add(_testGetTasks, 'testGetTasks3')
assert len(tm.getTasks()) == 3
tm.remove('testGetTasks2')
assert len(tm.getTasks()) == 2
tm.remove('testGetTasks1')
tm.remove('testGetTasks3')
assert len(tm.getTasks()) == 0
_testGetTasks = None
tm._checkMemLeaks()
# getDoLaters
def _testGetDoLaters():
pass
assert len(tm.getDoLaters()) == 0
tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
assert len(tm.getDoLaters()) == 1
assert tm.getDoLaters()[0].name == 'testDoLater1'
tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
assert len(tm.getDoLaters()) == 3
tm.remove('testDoLater2')
assert len(tm.getDoLaters()) == 2
tm.remove('testDoLater1')
tm.remove('testDoLater3')
assert len(tm.getDoLaters()) == 0
_testGetDoLaters = None
tm._checkMemLeaks()
# duplicate named doLaters removed via taskMgr.remove
def _testDupNameDoLaters():
pass
# the doLaterProcessor is always running
tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
assert len(tm.getDoLaters()) == 2
tm.remove('testDupNameDoLater')
assert len(tm.getDoLaters()) == 0
_testDupNameDoLaters = None
tm._checkMemLeaks()
# duplicate named doLaters removed via remove()
def _testDupNameDoLatersRemove():
pass
# the doLaterProcessor is always running
dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
assert len(tm.getDoLaters()) == 2
dl2.remove()
assert len(tm.getDoLaters()) == 1
dl1.remove()
assert len(tm.getDoLaters()) == 0
_testDupNameDoLatersRemove = None
# nameDict etc. isn't cleared out right away with task.remove()
tm._checkMemLeaks()
# getTasksNamed
def _testGetTasksNamed(task):
return task.cont
assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
tm.add(_testGetTasksNamed, 'testGetTasksNamed')
assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
tm.add(_testGetTasksNamed, 'testGetTasksNamed')
tm.add(_testGetTasksNamed, 'testGetTasksNamed')
assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
tm.remove('testGetTasksNamed')
assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
_testGetTasksNamed = None
tm._checkMemLeaks()
# removeTasksMatching
def _testRemoveTasksMatching(task):
return task.cont
tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
tm.removeTasksMatching('testRemoveTasksMatching')
assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
tm.removeTasksMatching('testRemoveTasksMatching*')
assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
tm.removeTasksMatching('testRemoveTasksMatching?a')
assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
_testRemoveTasksMatching = None
tm._checkMemLeaks()
# create Task object and add to mgr
l = []
def _testTaskObj(task, l=l):
l.append(None)
return task.cont
t = Task(_testTaskObj)
tm.add(t, 'testTaskObj')
tm.step()
assert len(l) == 1
tm.step()
assert len(l) == 2
tm.remove('testTaskObj')
tm.step()
assert len(l) == 2
_testTaskObj = None
tm._checkMemLeaks()
# remove Task via task.remove()
l = []
def _testTaskObjRemove(task, l=l):
l.append(None)
return task.cont
t = Task(_testTaskObjRemove)
tm.add(t, 'testTaskObjRemove')
tm.step()
assert len(l) == 1
tm.step()
assert len(l) == 2
t.remove()
tm.step()
assert len(l) == 2
del t
_testTaskObjRemove = None
tm._checkMemLeaks()
# this test fails, and it's not clear what the correct behavior should be.
# sort passed to Task.__init__ is always overridden by taskMgr.add()
# even if no sort is specified, and calling Task.setSort() has no
# effect on the taskMgr's behavior.
# set/get Task sort
#l = []
#def _testTaskObjSort(arg, task, l=l):
# l.append(arg)
# return task.cont
#t1 = Task(_testTaskObjSort, sort=1)
#t2 = Task(_testTaskObjSort, sort=2)
#tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
#tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
#tm.step()
#assert len(l) == 2
#assert l == ['a', 'b']
#assert t1.getSort() == 1
#assert t2.getSort() == 2
#t1.setSort(3)
#assert t1.getSort() == 3
#tm.step()
#assert len(l) == 4
#assert l == ['a', 'b', 'b', 'a',]
#t1.remove()
#t2.remove()
#tm.step()
#assert len(l) == 4
#del t1
#del t2
#_testTaskObjSort = None
#tm._checkMemLeaks()
del l
tm.destroy()
del tm
if __debug__:
def checkLeak():

View File

@ -358,8 +358,7 @@ class FSMInspector(AppShell):
def printLayout(self):
dict = self.stateInspectorDict
keys = list(dict.keys())
keys.sort()
keys = sorted(dict)
print("ClassicFSM.ClassicFSM('%s', [" % self.name)
for key in keys[:-1]:
si = dict[key]

View File

@ -95,9 +95,7 @@ class Inspector:
def initializePartsList(self):
self._partsList = []
keys = self.namedParts()
keys.sort()
for each in keys:
for each in sorted(self.namedParts()):
self._partsList.append(each)
#if not callable(getattr(self.object, each)):
# self._partsList.append(each)
@ -202,9 +200,7 @@ class DictionaryInspector(Inspector):
def initializePartsList(self):
Inspector.initializePartsList(self)
keys = list(self.object.keys())
keys.sort()
for each in keys:
for each in sorted(self.object):
self._partsList.append(each)
def partNumber(self, partNumber):

View File

@ -1174,9 +1174,7 @@ class ParticlePanel(AppShell):
self.particlesLabelMenu.add_separator()
# Add in a checkbutton for each effect (to toggle on/off)
particles = self.particleEffect.getParticlesList()
names = [x.getName() for x in particles]
names.sort()
for name in names:
for name in sorted(x.getName() for x in particles):
particle = self.particleEffect.getParticlesNamed(name)
self.particlesLabelMenu.add_command(
label = name,
@ -1199,9 +1197,7 @@ class ParticlePanel(AppShell):
self.forceGroupLabelMenu.add_separator()
# Add in a checkbutton for each effect (to toggle on/off)
forceGroupList = self.particleEffect.getForceGroupList()
names = [x.getName() for x in forceGroupList]
names.sort()
for name in names:
for name in sorted(x.getName() for x in forceGroupList):
force = self.particleEffect.getForceGroupNamed(name)
self.forceGroupLabelMenu.add_command(
label = name,
@ -2913,21 +2909,3 @@ class ParticlePanel(AppShell):
min = 0.01,
value = force.getRadius())
self.createForceActiveWidget(frame, pageName, forceName, force)
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
try:
base
except NameError:
from direct.showbase.ShowBase import ShowBase
base = ShowBase()
root = Pmw.initialise()
pp = ParticlePanel()
base.pp=pp
#ve = VectorEntry(Toplevel(), relief = GROOVE)
#ve.pack()
base.run()

View File

@ -786,11 +786,3 @@ class Placer(AppShell):
def place(nodePath):
return Placer(nodePath = nodePath)
######################################################################
# Create demo in root window for testing.
if __name__ == '__main__':
root = Pmw.initialise()
widget = Placer()

View File

@ -151,10 +151,8 @@ class TaskManagerWidget(DirectObject):
# Get a list of task names
taskNames = []
self.__taskDict = {}
tasks = self.taskMgr.getTasks()
tasks.sort(key = lambda t: t.getName())
count = 0
for task in tasks:
for task in sorted(self.taskMgr.getTasks(), key=lambda t: t.getName()):
taskNames.append(task.getName())
self.__taskDict[count] = task
count += 1

View File

@ -21,11 +21,11 @@ import builtins
# Create toplevel widget dictionary
if not hasattr(builtins, "widgetDict"):
builtins.widgetDict = {}
builtins.widgetDict = {} # type: ignore[attr-defined]
# Create toplevel variable dictionary
if not hasattr(builtins, "variableDict"):
builtins.variableDict = {}
builtins.variableDict = {} # type: ignore[attr-defined]
def resetWidgetDict():
@ -550,7 +550,3 @@ class TestAppShell(AppShell):
def createInterface(self):
self.createButtons()
self.createMain()
if __name__ == '__main__':
test = TestAppShell(balloon_state='none')

View File

@ -422,18 +422,3 @@ class DialWidget(Pmw.MegaWidget):
""" User redefinable callback executed on button release """
if self['postCallback']:
self['postCallback'](*self['callbackData'])
if __name__ == '__main__':
tl = tk.Toplevel()
d = Dial(tl)
d2 = Dial(tl, dial_numSegments = 12, max = 360,
dial_fRollover = 0, value = 180)
d3 = Dial(tl, dial_numSegments = 12, max = 90, min = -90,
dial_fRollover = 0)
d4 = Dial(tl, dial_numSegments = 16, max = 256,
dial_fRollover = 0)
d.pack(expand = 1, fill = tk.X)
d2.pack(expand = 1, fill = tk.X)
d3.pack(expand = 1, fill = tk.X)
d4.pack(expand = 1, fill = tk.X)

View File

@ -517,45 +517,3 @@ def rgbPanel(nodePath, callback = None):
messenger.send('RGBPanel_setColor', [nodePath, r, g, b, a])
esg['postCallback'] = onRelease
return esg
## SAMPLE CODE
if __name__ == '__main__':
# Initialise Tkinter and Pmw.
root = tk.Toplevel()
root.title('Pmw EntryScale demonstration')
# Dummy command
def printVal(val):
print(val)
# Create and pack a EntryScale megawidget.
mega1 = EntryScale(root, command = printVal)
mega1.pack(side = 'left', expand = 1, fill = 'x')
# These are things you can set/configure
# Starting value for entryScale
#mega1['value'] = 123.456
#mega1['text'] = 'Drive delta X'
#mega1['min'] = 0.0
#mega1['max'] = 1000.0
#mega1['resolution'] = 1.0
# To change the color of the label:
#mega1.label['foreground'] = 'Red'
# Max change/update, default is 100
# To have really fine control, for example
# mega1['maxVelocity'] = 0.1
# Number of digits to the right of the decimal point, default = 2
# mega1['numDigits'] = 5
# To create a entryScale group to set an RGBA value:
group1 = EntryScaleGroup(root, dim = 4,
title = 'Simple RGBA Panel',
labels = ('R', 'G', 'B', 'A'),
Valuator_min = 0.0,
Valuator_max = 255.0,
Valuator_resolution = 1.0,
command = printVal)
# Uncomment this if you aren't running in IDLE
#root.mainloop()

View File

@ -329,42 +329,3 @@ class FloaterGroup(Pmw.MegaToplevel):
def reset(self):
self.set(self['value'])
## SAMPLE CODE
if __name__ == '__main__':
# Initialise Tkinter and Pmw.
root = tk.Toplevel()
root.title('Pmw Floater demonstration')
# Dummy command
def printVal(val):
print(val)
# Create and pack a Floater megawidget.
mega1 = Floater(root, command = printVal)
mega1.pack(side = 'left', expand = 1, fill = 'x')
# These are things you can set/configure
# Starting value for floater
#mega1['value'] = 123.456
#mega1['text'] = 'Drive delta X'
# To change the color of the label:
#mega1.label['foreground'] = 'Red'
# Max change/update, default is 100
# To have really fine control, for example
#mega1['maxVelocity'] = 0.1
# Number of digits to the right of the decimal point, default = 2
#mega1['numDigits'] = 5
# To create a floater group to set an RGBA value:
group1 = FloaterGroup(root, dim = 4,
title = 'Simple RGBA Panel',
labels = ('R', 'G', 'B', 'A'),
Valuator_min = 0.0,
Valuator_max = 255.0,
Valuator_resolution = 1.0,
command = printVal)
# Uncomment this if you aren't running in IDLE
#root.mainloop()

View File

@ -329,17 +329,3 @@ class ColorEntry(VectorEntry):
initialcolor = tuple(self.get()[:3]))[0]
if color:
self.set((color[0], color[1], color[2], self.getAt(3)))
if __name__ == '__main__':
root = tk.Toplevel()
root.title('Vector Widget demo')
ve = VectorEntry(root)
ve.pack()
v3e = Vector3Entry(root)
v3e.pack()
v4e = Vector4Entry(root)
v4e.pack()
ce = ColorEntry(root)
ce.pack()

View File

@ -21,8 +21,7 @@ class WidgetPropertiesDialog(tk.Toplevel):
self.propertyDict = propertyDict
self.propertyList = propertyList
if self.propertyList is None:
self.propertyList = list(self.propertyDict.keys())
self.propertyList.sort()
self.propertyList = sorted(self.propertyDict)
# Use default parent if none specified
if not parent:
parent = tk._default_root

View File

@ -6,6 +6,8 @@ Modified by gjeon.
Modified by Summer 2010 Carnegie Mellon University ETC PandaLE team: fixed a bug in Viewport.Close
"""
from __future__ import annotations
__all__ = ["Viewport", "ViewportManager"]
from panda3d.core import (
@ -36,7 +38,7 @@ VPPERSPECTIVE = 13
class ViewportManager:
"""Manages the global viewport stuff."""
viewports = []
viewports: list[Viewport] = []
gsg = None
@staticmethod
@ -58,7 +60,7 @@ class ViewportManager:
v.Layout(*args, **kwargs)
class Viewport(WxPandaWindow, DirectObject):
class Viewport(WxPandaWindow, DirectObject): # type: ignore[misc, valid-type]
"""Class representing a 3D Viewport."""
CREATENEW = CREATENEW
VPLEFT = VPLEFT

View File

@ -1,2 +1,3 @@
from direct.showbase.ShowBaseGlobal import base
from .WxPandaShell import WxPandaShell
base.app = WxPandaShell()
base.app = WxPandaShell() # type: ignore[attr-defined]

View File

@ -81,7 +81,7 @@ class EmbeddedPandaWindow(wx.Window):
if not hasattr(wxgl, 'GLCanvas'):
OpenGLPandaWindow = None
else:
class OpenGLPandaWindow(wxgl.GLCanvas):
class OpenGLPandaWindow(wxgl.GLCanvas): # type: ignore[no-redef]
""" This class implements a Panda3D "window" that actually draws
within the wx GLCanvas object. It is supported whenever OpenGL is
Panda's rendering engine, and GLCanvas is available in wx. """

200
doc/CODING_STYLE.md Normal file
View File

@ -0,0 +1,200 @@
Panda3D C++ Style Guide
=======================
Rather than try to justify every decision made, this document attempts to be as
at-a-glance as possible. Examples provided where necessary to illustrate best
practices.
Note that this style guide is, for the most part, not very strict, and there
are exceptions to every rule. However, this guide does serve to illustrate the
preferred style for the entire codebase.
Naming conventions
------------------
- **Classes**: `CamelCaseFirstUpper`
- **Filenames**: `lowerCamelCase.h`, `lowerCamelCase.cxx`
- Note, every header file must have a unique filename. They are all
installed into the same path.
- **Functions**: `lower_snake_case_without_prefix`
- **Local variables**: `lower_snake_case_without_prefix`
- **Member variables**: `_lower_snake_case_with_initial_score`
- **Enum constants**: `SE_enum_value` (for an enum type named SomeEnum)
- **Enum class constants**: `ENUM_VALUE`
Indentation
-----------
Always use two spaces to indent. Tabs should never appear in a C++ file.
Access modifiers (`public`/`private`/`protected`/`PUBLISHED`) are never
indented relative to the class they are in, and do not affect the indentation
of the surrounding lines.
Spaces
------
Always put one space after a control keyword, before the condition's
parentheses. Don't pad the inside of parentheses with spaces unless there are
several nested parenthetical expressions and the extra spaces contribute
substantially to readability.
if (x == y) {
Don't place a space after a function name (declarations, definitions, or calls).
void function_name();
...
function_name();
Always place a space after each comma in an arguments list.
function_call(x, y, z);
Always pad binary operators with a space on either side.
a || b
Never end a line with trailing whitespace.
Function definitions
--------------------
The recipe for a function definition is:
1. JavaDoc comment describing the purpose of the function
2. Function return value and class
3. **At the beginning of its own line,** the function's name, followed by the
parameter list and anything else necessary for the function's signature.
4. Opening brace on the same line, not its own line.
5. Single empty line before next definition.
For example:
/**
* String documenting the function, in JavaDoc style.
*/
void ClassName::
function_name(int x, int y) const {
...
}
/**
* ...
*/
...
If the function is a constructor with an initializer list, the `:` goes on the
same line as the constructor name, and the initializers are listed *one per
line*, indented by two spaces.
ClassName::
ClassName() :
_a("a"),
_b("b"),
_c("c")
{
}
Empty function bodies in a .cxx/.I file still contain a single line break (i.e.
to put `}` on its own line). In a .h file, an empty function can be given as
`{}`
Braces
------
Always put an opening `{` on the same line as the statement that calls for the
brace. There is exactly one exception to this: constructors with initializer
lists.
SomeClass::
SomeClass() :
_one(1),
_two(2)
{
if (_one < _two) {
...
}
}
Comments
--------
Comments should be properly-formatted English, with proper capitalization and
punctuation rules. There are two spaces between sentences.
Note that `//` is preferred over `/*` except in some special circumstances.
Except for commented-out lines of code, there should be a space between the
first word and the `//`.
// The following line is commented out.
//if (value == 0) {
Line limits and continuations
-----------------------------
Lines should be limited to 80 columns. This is not a hard limit, and lines
should not be wrapped if doing so hurts readability, except comments, which
should always be wrapped.
Continuation lines shall be aligned to the same column as appropriate, or
indented two spaces otherwise:
if ((a &&
b) ||
c) {
...
}
When choosing where to break a line, these are the preferred places, from best
to worst:
1. If inside a comment, break the comment to as many lines as appropriate, but
only after moving the comment to its own line.
2. After a comma in an argument/parameter list.
3. After a binary operator (leaving the binary operator at the end of the line)
4. In a function declaration, after the function's return type but before the
function's name. (Indented by two spaces.)
5. In a log statement with `<<`, before a `<<`. (Avoid ending a line with `<<`)
6. Around a string literal, at a natural word boundary, with a space left on
the previous line (e.g. `"This is a "` / `"continued string literal."`)
7. After the `=` in an assignment operator.
8. As a last resort, after the opening `(` in a function call.
If none of these rules can apply cleanly, it is okay to have lines extend past
the 80-column boundary.
Alignment
---------
Avoid the temptation to align multiple assignments in consecutive lines of code.
While it may look neater, it decreases the maintainability of the code.
Asterisks and ampersands indicating a pointer or reference type should always
be aligned against the name, and not the type:
Type *x, &y;
Grouping
--------
Try to group logically-similar lines, separating them with a single blank line.
Modern language features
------------------------
Panda3D is a C++11 project. The use of the following modern language features
is greatly encouraged:
1. `nullptr` over `NULL`
2. `std::move` and move semantics
3. Range-based for loops
Using `auto` can be okay, such as when storing an iterator, but consider
creating a typedef for the container type instead.
Avoid using `std::function` in cases where a lambda can be accepted directly
(using a template function), since it has extra overhead over lambdas.
C++14 and C++17 features should be avoided for now.

View File

@ -16,6 +16,7 @@ if(MSVC)
else()
set(CMAKE_C_FLAGS_STANDARD "-O3")
set(CMAKE_CXX_FLAGS_STANDARD "-O3")
set(CMAKE_OBJCXX_FLAGS_STANDARD "-O3")
endif()
set(CMAKE_SHARED_LINKER_FLAGS_STANDARD "")
set(CMAKE_MODULE_LINKER_FLAGS_STANDARD "")
@ -27,6 +28,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang)")
"${CMAKE_C_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
set(CMAKE_CXX_FLAGS_COVERAGE
"${CMAKE_CXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
set(CMAKE_OBJCXX_FLAGS_COVERAGE
"${CMAKE_OBJCXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fprofile-instr-generate")
@ -101,11 +104,14 @@ endif()
# Set warning levels
if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
set(CMAKE_CCXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
if(APPLE)
set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -Wall")
endif()
endif()
@ -133,6 +139,14 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${release_flags}")
set(CMAKE_CXX_FLAGS_STANDARD "${CMAKE_CXX_FLAGS_STANDARD} ${standard_flags}")
if(APPLE)
set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} ${global_flags}")
set(CMAKE_OBJCXX_FLAGS_RELEASE "${CMAKE_OBJCXX_FLAGS_RELEASE} ${global_flags}")
set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "${CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO} ${global_flags}")
set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "${CMAKE_OBJCXX_FLAGS_MINSIZEREL} ${global_flags}")
set(CMAKE_OBJCXX_FLAGS_STANDARD "${CMAKE_OBJCXX_FLAGS_STANDARD} ${global_flags}")
endif()
if(MSVC)
# Clang behaving as MSVC
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-command-line-argument")
@ -151,6 +165,7 @@ endif()
# and stops us from identifying cases where ENABLE_EXPORTS is needed.
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS "")
# As long as we're figuring out compiler flags, figure out the flags for
# turning C++ exception support on and off

View File

@ -518,7 +518,9 @@ package_status(OPUS "Opus")
#
# FMOD Ex
find_package(FMODEx QUIET)
if(NOT APPLE)
find_package(FMODEx QUIET)
endif()
package_option(FMODEx
"This enables support for the FMOD Ex sound library,

File diff suppressed because it is too large Load Diff

View File

@ -2971,8 +2971,17 @@ base_specification:
;
enum:
enum_decl '{' enum_body '}'
enum_decl
{
if (current_enum->_scope != nullptr) {
push_scope(current_enum->_scope);
}
}
'{' enum_body '}'
{
if (current_enum->_scope != nullptr) {
pop_scope();
}
$$ = current_enum;
current_enum = nullptr;
}

View File

@ -130,6 +130,7 @@
// flag to link in Python, we'll just excerpt the forward declaration of
// PyObject.
typedef struct _object PyObject;
typedef struct _typeobject PyTypeObject;
#ifndef HAVE_EIGEN
// If we don't have the Eigen library, don't define LINMATH_ALIGN.

View File

@ -155,7 +155,7 @@ deallocate_array(void *ptr) {
/**
* Returns the internal void pointer that is stored for interrogate's benefit.
*/
PyObject *TypeHandle::
PyTypeObject *TypeHandle::
get_python_type() const {
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
if (rnode != nullptr) {
@ -164,6 +164,22 @@ get_python_type() const {
return nullptr;
}
}
/**
* Returns a Python wrapper object corresponding to the given C++ pointer.
*/
PyObject *TypeHandle::
wrap_python(void *ptr, PyTypeObject *cast_from) const {
if (ptr == nullptr) {
return nullptr;
}
TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, nullptr);
if (rnode != nullptr) {
return rnode->wrap_python(ptr, cast_from);
} else {
return nullptr;
}
}
#endif
std::ostream &

View File

@ -140,7 +140,8 @@ PUBLISHED:
public:
#ifdef HAVE_PYTHON
PyObject *get_python_type() const;
PyTypeObject *get_python_type() const;
PyObject *wrap_python(void *ptr, PyTypeObject *cast_from = nullptr) const;
#endif // HAVE_PYTHON
void *allocate_array(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT);

View File

@ -46,7 +46,26 @@ __reduce__() const {
// If we have a Python binding registered for it, that's the preferred method,
// since it ensures that the appropriate module gets loaded by pickle.
PyObject *py_type = _this->get_python_type();
PyTypeObject *py_type = _this->get_python_type();
if (py_type != nullptr && py_type->tp_dict != nullptr) {
// Look for a get_class_type method, if it returns this handle.
PyObject *func = PyDict_GetItemString(py_type->tp_dict, "get_class_type");
if (func != nullptr && PyCallable_Check(func)) {
PyObject *result = PyObject_CallNoArgs(func);
TypeHandle *result_handle = nullptr;
if (result == nullptr) {
// Never mind.
PyErr_Clear();
}
else if (DtoolInstance_GetPointer(result, result_handle, Dtool_TypeHandle) &&
*result_handle == *_this) {
// It returned the correct result, so we can use this.
return Py_BuildValue("O()", func);
}
}
}
// Fall back to TypeHandle::make(), if would produce the correct result.
if (py_type != nullptr && *_this == ((Dtool_PyTypedObject *)py_type)->_type) {
PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_TypeHandle, "make");
return Py_BuildValue("N(O)", func, py_type);

View File

@ -213,12 +213,13 @@ record_alternate_name(TypeHandle type, const string &name) {
* of interrogate, which expects this to contain a Dtool_PyTypedObject.
*/
void TypeRegistry::
record_python_type(TypeHandle type, PyObject *python_type) {
record_python_type(TypeHandle type, PyTypeObject *cls, PythonWrapFunc *wrap_func) {
_lock.lock();
TypeRegistryNode *rnode = look_up(type, nullptr);
if (rnode != nullptr) {
rnode->_python_type = python_type;
rnode->_python_type = cls;
rnode->_python_wrap_func = wrap_func;
}
_lock.unlock();

View File

@ -40,13 +40,18 @@ public:
// convenience function, defined in register_type.h.
bool register_type(TypeHandle &type_handle, const std::string &name);
#ifdef HAVE_PYTHON
typedef PyObject *PythonWrapFunc(void *ptr, PyTypeObject *cast_from);
#endif
PUBLISHED:
TypeHandle register_dynamic_type(const std::string &name);
void record_derivation(TypeHandle child, TypeHandle parent);
void record_alternate_name(TypeHandle type, const std::string &name);
#ifdef HAVE_PYTHON
void record_python_type(TypeHandle type, PyObject *python_type);
void record_python_type(TypeHandle type, PyTypeObject *cls,
PythonWrapFunc *wrap_func);
#endif
TypeHandle find_type(const std::string &name) const;

View File

@ -14,7 +14,7 @@
/**
* Returns the Python type object associated with this node.
*/
INLINE PyObject *TypeRegistryNode::
INLINE PyTypeObject *TypeRegistryNode::
get_python_type() const {
if (_python_type != nullptr || _parent_classes.empty()) {
return _python_type;
@ -24,6 +24,23 @@ get_python_type() const {
}
}
/**
* Returns a Python wrapper object corresponding to the given C++ pointer.
*/
INLINE PyObject *TypeRegistryNode::
wrap_python(void *ptr, PyTypeObject *cast_from) const {
if (_python_wrap_func != nullptr) {
return _python_wrap_func(ptr, cast_from);
}
else if (_parent_classes.empty()) {
return nullptr;
}
else {
// Recurse through parent classes.
return r_wrap_python(ptr, cast_from);
}
}
/**
*
*/

View File

@ -311,16 +311,14 @@ r_build_subtrees(TypeRegistryNode *top, int bit_count,
* Recurses through the parent nodes to find the best Python type object to
* represent objects of this type.
*/
PyObject *TypeRegistryNode::
PyTypeObject *TypeRegistryNode::
r_get_python_type() const {
Classes::const_iterator ni;
for (ni = _parent_classes.begin(); ni != _parent_classes.end(); ++ni) {
const TypeRegistryNode *parent = *ni;
for (const TypeRegistryNode *parent : _parent_classes) {
if (parent->_python_type != nullptr) {
return parent->_python_type;
} else if (!parent->_parent_classes.empty()) {
PyObject *py_type = parent->r_get_python_type();
}
else if (!parent->_parent_classes.empty()) {
PyTypeObject *py_type = parent->r_get_python_type();
if (py_type != nullptr) {
return py_type;
}
@ -330,6 +328,28 @@ r_get_python_type() const {
return nullptr;
}
/**
* Creates a Python wrapper object to represent the given C++ pointer, which
* must be exactly of the correct type, unless cast_from is set to one of its
* base classes, in which case it will be cast appropriately.
*/
PyObject *TypeRegistryNode::
r_wrap_python(void *ptr, PyTypeObject *cast_from) const {
for (const TypeRegistryNode *parent : _parent_classes) {
if (parent->_python_wrap_func != nullptr) {
return parent->_python_wrap_func(ptr, cast_from);
}
else if (!parent->_parent_classes.empty()) {
PyObject *wrapper = parent->r_wrap_python(ptr, cast_from);
if (wrapper != nullptr) {
return wrapper;
}
}
}
return nullptr;
}
/**
* A recursive function to double-check the result of is_derived_from(). This
* is the slow, examine-the-whole-graph approach, as opposed to the clever and

View File

@ -30,6 +30,8 @@
*/
class EXPCL_DTOOL_DTOOLBASE TypeRegistryNode {
public:
typedef PyObject *PythonWrapFunc(void *ptr, PyTypeObject *cast_from);
TypeRegistryNode(TypeHandle handle, const std::string &name, TypeHandle &ref);
static bool is_derived_from(const TypeRegistryNode *child,
@ -38,7 +40,8 @@ public:
static TypeHandle get_parent_towards(const TypeRegistryNode *child,
const TypeRegistryNode *base);
INLINE PyObject *get_python_type() const;
INLINE PyTypeObject *get_python_type() const;
INLINE PyObject *wrap_python(void *ptr, PyTypeObject *cast_from) const;
void clear_subtree();
void define_subtree();
@ -49,7 +52,8 @@ public:
typedef std::vector<TypeRegistryNode *> Classes;
Classes _parent_classes;
Classes _child_classes;
PyObject *_python_type = nullptr;
PyTypeObject *_python_type = nullptr;
PythonWrapFunc *_python_wrap_func = nullptr;
patomic<size_t> _memory_usage[TypeHandle::MC_limit];
@ -81,7 +85,8 @@ private:
void r_build_subtrees(TypeRegistryNode *top,
int bit_count, SubtreeMaskType bits);
PyObject *r_get_python_type() const;
PyTypeObject *r_get_python_type() const;
PyObject *r_wrap_python(void *ptr, PyTypeObject *cast_from) const;
static bool check_derived_from(const TypeRegistryNode *child,
const TypeRegistryNode *base);

View File

@ -1289,9 +1289,12 @@ to_os_long_name() const {
}
/**
* Returns true if the filename exists on the disk, false otherwise. If the
* type is indicated to be executable, this also tests that the file has
* Returns true if the filename exists on the physical disk, false otherwise.
* If the type is indicated to be executable, this also tests that the file has
* execute permission.
*
* @see VirtualFileSystem::exists() for checking whether the filename exists in
* the virtual file system.
*/
bool Filename::
exists() const {
@ -1320,8 +1323,11 @@ exists() const {
}
/**
* Returns true if the filename exists and is the name of a regular file (i.e.
* not a directory or device), false otherwise.
* Returns true if the filename exists on the physical disk and is the name of
* a regular file (i.e. not a directory or device), false otherwise.
*
* @see VirtualFileSystem::is_regular_file() for checking whether the filename
* exists and is a regular file in the virtual file system.
*/
bool Filename::
is_regular_file() const {
@ -1350,8 +1356,8 @@ is_regular_file() const {
}
/**
* Returns true if the filename exists and is either a directory or a regular
* file that can be written to, or false otherwise.
* Returns true if the filename exists on the physical disk and is either a
* directory or a regular file that can be written to, or false otherwise.
*/
bool Filename::
is_writable() const {
@ -1382,8 +1388,11 @@ is_writable() const {
}
/**
* Returns true if the filename exists and is a directory name, false
* otherwise.
* Returns true if the filename exists on the physical disk and is a directory
* name, false otherwise.
*
* @see VirtualFileSystem::is_directory() for checking whether the filename
* exists as a directory in the virtual file system.
*/
bool Filename::
is_directory() const {

View File

@ -36,6 +36,11 @@ class DSearchPath;
* convention, and it knows how to perform basic OS-specific I/O, like testing
* for file existence and searching a searchpath, as well as the best way to
* open an fstream for reading or writing.
*
* Note that the methods of Filename that interact with the filesystem (such
* as exists(), open_read(), etc.) directly interface with the operating system
* and are not aware of Panda's virtual file system. To interact with the VFS,
* use the methods on VirtualFileSystem instead.
*/
class EXPCL_DTOOL_DTOOLUTIL Filename {
PUBLISHED:

View File

@ -494,6 +494,10 @@ get_call_str(const string &container, const vector_string &pexprs) const {
call << separator << "self";
separator = ", ";
}
if (_flags & F_explicit_cls) {
call << separator << "cls";
separator = ", ";
}
size_t pn;
size_t num_parameters = pexprs.size();
@ -781,14 +785,20 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
first_param = 1;
}
if (_parameters.size() > first_param && _parameters[first_param]._name == "self" &&
if (_parameters.size() > first_param &&
TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
// Here's a special case. If the first parameter of a nonstatic method
// is a PyObject * called "self", then we will automatically fill it in
// from the this pointer, and remove it from the generated parameter
// list.
_parameters.erase(_parameters.begin() + first_param);
_flags |= F_explicit_self;
// from the this pointer, and remove it from the generated parameter list.
// For static methods, we offer "cls" instead, containing the type object.
if (_parameters[first_param]._name == "self") {
_parameters.erase(_parameters.begin() + first_param);
_flags |= F_explicit_self;
}
else if (!_has_this && _parameters[first_param]._name == "cls") {
_parameters.erase(_parameters.begin() + first_param);
_flags |= F_explicit_cls;
}
}
if (_parameters.size() == first_param) {

View File

@ -101,6 +101,7 @@ public:
F_divide_integer = 0x2000,
F_hash = 0x4000,
F_explicit_args = 0x8000,
F_explicit_cls =0x10000,
};
typedef std::vector<Parameter> Parameters;

View File

@ -115,6 +115,7 @@ RenameSet methodRenameDictionary[] = {
{ "__deepcopy__" , "__deepcopy__", 0 },
{ "__getstate__" , "__getstate__", 0 },
{ "__setstate__" , "__setstate__", 0 },
{ "__new__" , "__new__", 0 },
{ "print" , "Cprint", 0 },
{ "CInterval.set_t", "_priv__cSetT", 0 },
{ nullptr, nullptr, -1 }
@ -664,6 +665,12 @@ get_slotted_function_def(Object *obj, Function *func, FunctionRemap *remap,
return true;
}
if (method_name == "__new__") {
def._answer_location = "tp_new";
def._wrapper_type = WT_new;
return true;
}
if (remap->_type == FunctionRemap::T_typecast_method) {
// A typecast operator. Check for a supported low-level typecast type.
if (TypeManager::is_bool(remap->_return_type->get_orig_type())) {
@ -1106,10 +1113,14 @@ write_class_details(ostream &out, Object *obj) {
out << " */\n";
// First write out all the wrapper functions for the methods.
bool have_new = false;
for (Function *func : obj->_methods) {
if (func) {
// Write the definition of the generic wrapper function for this
// function.
if (func->_ifunc.get_name() == "__new__") {
have_new = true;
}
write_function_for_top(out, obj, func);
}
}
@ -1128,10 +1139,16 @@ write_class_details(ostream &out, Object *obj) {
if (obj->_constructors.size() == 0) {
// We still need to write a dummy constructor to prevent inheriting the
// constructor from a base class.
out << fname << " {\n"
" Dtool_Raise_TypeError(\"cannot init abstract class\");\n"
" return -1;\n"
"}\n\n";
if (have_new) {
out << fname << " {\n"
" return 0;\n"
"}\n\n";
} else {
out << fname << " {\n"
" Dtool_Raise_TypeError(\"cannot init abstract class\");\n"
" return -1;\n"
"}\n\n";
}
}
CPPType *cpptype = TypeManager::resolve_type(obj->_itype._cpptype);
@ -1221,18 +1238,17 @@ write_class_details(ostream &out, Object *obj) {
out << " return nullptr;\n";
out << "}\n\n";
out << "static Dtool_PyInstDef *Dtool_Wrap_" << ClassName << "(void *from_this, Dtool_PyTypedObject *from_type) {\n";
out << " if (from_this == nullptr || from_type == nullptr) {\n";
out << " return nullptr;\n";
out << " }\n";
//NB. This may be called with nullptr in either argument and should produce
// a valid wrapper object even with a null pointer.
out << "static PyObject *Dtool_Wrap_" << ClassName << "(void *from_this, PyTypeObject *from_type) {\n";
out << " " << cClassName << " *to_this;\n";
out << " if (from_type == &Dtool_" << ClassName << ") {\n";
out << " if (from_type == nullptr || from_type == &Dtool_" << ClassName << "._PyType) {\n";
out << " to_this = (" << cClassName << "*)from_this;\n";
out << " }\n";
for (di = details.begin(); di != details.end(); di++) {
if (di->second._can_downcast && di->second._is_legal_py_class) {
out << " else if (from_type == Dtool_Ptr_" << make_safe_name(di->second._to_class_name) << ") {\n";
out << " " << di->second._to_class_name << "* other_this = (" << di->second._to_class_name << "*)from_this;\n" ;
out << " else if (from_type == (PyTypeObject *)Dtool_Ptr_" << make_safe_name(di->second._to_class_name) << ") {\n";
out << " " << di->second._to_class_name << " *other_this = (" << di->second._to_class_name << " *)from_this;\n" ;
out << " to_this = (" << cClassName << "*)other_this;\n";
out << " }\n";
}
@ -1247,7 +1263,7 @@ write_class_details(ostream &out, Object *obj) {
out << " self->_ptr_to_object = to_this;\n";
out << " self->_memory_rules = false;\n";
out << " self->_is_const = false;\n";
out << " return self;\n";
out << " return (PyObject *)self;\n";
out << "}\n\n";
}
}
@ -1405,8 +1421,9 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
out << " TypeHandle handle = " << type->get_local_name(&parser)
<< "::get_class_type();\n";
out << " Dtool_" << safe_name << "._type = handle;\n";
out << " registry->record_python_type(handle, "
"(PyObject *)&Dtool_" << safe_name << ");\n";
out << " registry->record_python_type(handle,"
" &Dtool_" << safe_name << "._PyType,"
" Dtool_Wrap_" << safe_name << ");\n";
out << " }\n";
} else {
if (IsPandaTypedObject(type->as_struct_type())) {
@ -1706,7 +1723,11 @@ write_module_class(ostream &out, Object *obj) {
}
if (!func->_has_this) {
flags += " | METH_STATIC";
if (func->_flags & FunctionRemap::F_explicit_cls) {
flags += " | METH_CLASS";
} else {
flags += " | METH_STATIC";
}
// Skip adding this entry if we also have a property with the same name.
// In that case, we will use a Dtool_StaticProperty to disambiguate
@ -2620,6 +2641,17 @@ write_module_class(ostream &out, Object *obj) {
}
break;
case WT_new:
{
string fname = "static PyObject *" + def._wrapper_name + "(PyTypeObject *cls, PyObject *args, PyObject *kwds)\n";
std::vector<FunctionRemap *> remaps;
remaps.insert(remaps.end(), def._remaps.begin(), def._remaps.end());
string expected_params;
write_function_for_name(out, obj, remaps, fname, expected_params, true, AT_keyword_args, RF_pyobject | RF_err_null);
}
break;
case WT_none:
// Nothing special about the wrapper function: just write it normally.
string fname = "static PyObject *" + def._wrapper_name + "(PyObject *self, PyObject *args, PyObject *kwds)\n";
@ -3225,7 +3257,7 @@ write_module_class(ostream &out, Object *obj) {
// allocfunc tp_alloc;
out << " PyType_GenericAlloc,\n";
// newfunc tp_new;
out << " Dtool_new_" << ClassName << ",\n";
write_function_slot(out, 4, slots, "tp_new", "Dtool_new_" + ClassName);
// freefunc tp_free;
if (obj->_protocol_types & Object::PT_python_gc) {
out << " PyObject_GC_Del,\n";
@ -3652,6 +3684,9 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
if (func->_has_this) {
prototype += "self";
}
else if (func->_flags & FunctionRemap::F_explicit_cls) {
prototype += "cls";
}
switch (func->_args_type) {
case AT_keyword_args:

View File

@ -82,6 +82,7 @@ private:
WT_traverse,
WT_compare,
WT_hash,
WT_new,
};
// This enum is passed to the wrapper generation functions to indicate what

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