Help with Python Errors!

The Capo

godless Heathen
Joined
Jan 29, 2001
Messages
9,302
Location
Washington, DC
I am working on a mod, and I thought I had it finished. Everything "works" (As far as I can tell), but I got the three following Python errors;

CvEventInterface, line 23, in onEvent
CvEventManager, line 194, in handleEvent
CvEventManager, line 435, in onBeginPlayerTurn

UnBoundLocalError: local variable 'iShrine' referenced before assigment

Here's the second one;

CvGameinterface, line 180, in AI_chooseProduction
CvGameUtils, line 269, in AI_chooseProduction

TypeError: 'NoneType' object is not callable

Then the last one;

CvScreensInterface, line 705, in forceScreenRedraw
CvMainInterface, line 721, in redraw
CvMainInterface, line 1491, in updateSelectionButtons

RuntimeError: unidentifiable C++ exception.

If anyone knows what any of this means that would be a big help to me, I am pretty new to this.
 
It would really help if you posted the lines on which the errors occur, possibly with some surrounding lines for context....

Anyhow, the first one means the variable iShrine was used before any useful value was assigned to it. Something like this could trigger it:
Code:
def myFunction:
	foo = iShrine * 3
	iShrine = 12
	return foo
I tried to multiply iShrine by 3 before I said what iShrine was. Fixing should be self-explanatory: make sure it has been assigned a useful value before it is used.

The second one means you are trying to operate on an object (Player, City, Unit, etc) which doesn't exist. Something like this could trigger it:
Code:
pPlayer = gc.getPlayer(19) # only 18 players in standard game
t = pPlayer.getTeam()
Since there is no player #19, pPlayer is assigned the value None but then it is accessed afterwards. That example would actually throw a slightly different error but the general idea is the same. To fix it, make sure that the object is only accessed if it's valid and/or figure out what is causing it to not be valid.

The third one is a crapshoot. "unidentifiable" means pretty much what it says. The Python function didn't notice any problem but something it passed to the SDK caused the function to bork. I usually see this if I try to access XML strings that haven't been loaded yet, but lots of problems can trigger it.
 
I am trying to open these files, but I can't find the CvEventInterface, CvGameInterface, and CvScreensInterface files. Then for the files I could find the lines that they mention don't have the values in them at all, for example the CvGameUtils.py file doesn't say AI_chooseProduction at all, in fact this is what that line says...

Code:
iInqVersions.get( pPlayer.getStateReligion( ), ) ()

I'm sorry to bug you guys with this stuff, but this is the first mod work I've ever done so I may be failing to see the most simple issue.

So here are a few examples from the first error report. Mind you I could not find a file called CvEventInterface, but I did find the CvEventManager.py file, and for the two issues there I will provide the surrounding/appropriate code. These are lines 192 through 195 in that file, which encompass line 194 which is in the error report;

Code:
if self.EventHandlerMap.has_key(tag):
			fxn = self.EventHandlerMap[tag]
			ret = fxn(argsList[1:idx])
		return ret

And from that same file lines 434 through 440, which encomopasses line 435 described in the error report;

Code:
while(loopCity):
					if (loopCity.isHasBuilding(iShrine)):
						# City has the official State Religion Shrine - Prerequisite 3
						#CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'A Shrine was Found!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/TerrainFeatures/Forest.dds',ColorTypes(8),0,0,False,False)
						bHasShrine = True
						break
					(loopCity, iter) = pPlayer.nextCity(iter, false)

As you can see handleEvent doesn't show in the first one, and onBeginPlayerTurn doesn't show in the second one. But again, I basically have little to no idea what I am doing here, so any help would be great. If this helps I have combined Just Another Religion Mod and the Inquisitor Mod together. The Inquisitor mod uses the GodsofOld.py file from the Gods of Old mod, if that helps.

Thanks again.
 
The file references are what we call a stack trace. The last line shows where the actual problem is and the other lines give context on how we got there to give additional help in tracking down the problem and to show how that error propagates backwards through the code. Generally, you only care about the last line but sometimes it's useful to know what else was happening.

Let's look at your first error:
Code:
[COLOR="Blue"]CvEventInterface, line 23, in onEvent[/COLOR]
[COLOR="Green"]CvEventManager, line 194, in handleEvent[/COLOR]
[COLOR="Red"]CvEventManager, line 435, in onBeginPlayerTurn[/COLOR]

Here is the function onEvent() from the unmodified CvEventInterface file. The reason you can't find it is because your mod doesn't change it and so the game is running the original one in the Assets folder.
Code:
21 def onEvent(argsList):
22 	'Called when a game event happens - return 1 if the event was consumed'
[COLOR="Blue"]23 	return getEventManager().handleEvent(argsList)[/COLOR]

Line 23 is calling your event manager and telling it to handle some event. So now this code is executed in your CvEventManager file. Even though it doesn't show in the part you posted, this code must be within the function handleEvent():
Code:
192 		if self.EventHandlerMap.has_key(tag):
193 			fxn = self.EventHandlerMap[tag]
[COLOR="Green"]194 			ret = fxn(argsList[1:idx])[/COLOR]
195 		return ret
Now line 194 calls another function (in this case onBeginPlayerTurn()) which is elsewhere in that file, so we jump down to it. Once again, this code has to be inside the function onBeginPlayerTurn(). If for some reason you don't think it is, there may be an indentation issue.
Code:
434 				while(loopCity):
[COLOR="Red"]435 					if (loopCity.isHasBuilding(iShrine)):
[/COLOR]436 						# City has the official State Religion Shrine - Prerequisite 3
437 						#CyInterface().addMessage(CyGame().getActivePlayer(),True,25,'A Shrine was Found!','AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/TerrainFeatures/Forest.dds',ColorTypes(8),0,0,False,False)
438 						bHasShrine = True
439 						break
440 					(loopCity, iter) = pPlayer.nextCity(iter, false)
And now we finally get to your problem. Line 435 is checking if a city has a building with ID number "iShrine" but iShrine has not yet been defined. To fix it, figure out what iShrine is supposed to represent and make sure it gets initialized somewhere in that function, probably before the loop.

Note that only reason I worked through the first 2 references was to illustrate how this all works; to identify the problem we really only needed to look at the final line reference. So for the other errors, you want to start with the last line reference to track down the problem and only work backwards if you need to know why the error-causing function was being called.

For your second error, assuming that the line you quoted from CvGameUtils is line 269, then your problem is probably that iInqVersions.get(...) is supposed to return some function reference which is then called but it is instead returning None. Your first step would be to try and determine what that line is *supposed* to do and then see why None is being returned.
 
Thank you, that was extremely helpful. I fixed the first two problems with one area of code. I forgot to add the Shrines for the new religions to the iShrine code in the Python, so simply doing that took care of the first two problems. Thankfully.

The next problem, I think, occurs due to the SuperSpy mod that I added to my mod. Two missions "assassinate" and "bribe" are supposed to show up, and I believe as buttons in the interface. The code probably isn't set correctly and they aren't showing up. I am going to try and figure this out, but as I've said I am not really good at this stuff. So if you or anybody else could help me with this part or at least lead me in the right direction that would be awesome. But that said, I am going to try and look to see if I can figure it out myself.
 
Oh yeah, this error occurs when a spy goes into a tile with a foreign unit. The "assassinate" and "bribe" buttons that are supposed to come up don't, so it probably has something to do with that. If this helps.
 
Alright, I guess nobody recognizes the issue based on the error report, so I am going to post the lines in question here. To reiderate the problem shows up like so in the python error pop-up;

CvScreensInterface, line 705, in forceScreenRedraw
CvMainInterface, line 721, in redraw
CvMainInterface, line 1491, updateSelectionButtons

RuntimeError: unidentifiable C++ exception

I'll start with the first issue, line 705 in CvScreensInterface.py. I've included lines 703 through 711 just so there is some sort of context here:

Code:
# Main Interface Screen
	if ( argsList[0] == MAIN_INTERFACE ):
		mainInterface.redraw()
	elif ( argsList[0] == WORLDBUILDER_SCREEN ):
		worldBuilderScreen.redraw()
	elif ( argsList[0] == WORLDBUILDER_DIPLOMACY_SCREEN ):
		worldBuilderDiplomacyScreen.redraw()
	elif ( argsList[0] == TECH_CHOOSER ):
		techChooser.updateTechRecords(true)

Line 705 is the "mainInterface.redraw()" line.

The next problem in the error pop up says "CvMainInterface, line 721, in redraw," so I have included 720 through 723:

Code:
			# Selection Buttons Dirty
			self.updateSelectionButtons()
			CyInterface().setDirty(InterfaceDirtyBits.SelectionButtons_DIRTY_BIT, False)
		if ( CyInterface().isDirty(InterfaceDirtyBits.ResearchButtons_DIRTY_BIT) == True ):

The line that is reporting a problem is the "self.updateSelectionButtons()" line.

The final one says "CvMainInterface, line 1491, in updateSelectionButtons," I have included 1489 through 1492 here for this one:

Code:
				actions = CyInterface().getActionsToShow()
					for i in actions:
						screen.appendMultiListButton( "BottomButtonContainer", gc.getActionInfo(i).getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )
						screen.show( "BottomButtonContainer" )

The line that is problematic here, according to the pop-up at least, is the "screen.appendMultiListButton( "BottomButtonContainer", gc.getActionInfo(i).getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )" line.

I hope this helps, and hopefully somebody is seeing something here that I am missing. Thanks for taking your time to read this.
 
Most likely you have incompletely setup your new actions. The C++ exception probably happens when trying to get the new CvActionInfo in this part of the last line:

Code:
gc.getActionInfo(i)

You can test to make sure by splitting that part out from the rest of the line:

Code:
[B]actionInfo = gc.getActionInfo(i)[/B]
screen.appendMultiListButton( "BottomButtonContainer", [B]actionInfo[/B].getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )

Did you add the definitions to the DLL? Do they match the new actions in the XML?
 
Basically what I did was merge the SuperSpy mod into a mod I was working on, which didn't have a DLL so I used the superspy mod DLL. I didnt' think until today to check the original (superspy) mod itself, and when I did that I realized that this mod already had this error. So it isn't anything I have done myself, it was already in the mod before and I just failed to check for it. So I'm not sure what was done really.

And I am not sure exactly what you are asking me to do when you say to split it from the rest of the line. I'm sorry if this is a stupid comment, but I don't have much knowlege when it comes to this stuff, so even if its a simple thing I probably need more explanation, thank you for helping me though I really appreciate it.

So what do I do?
 
Well the problem isn't with the Python code, so I wouldn't worry about that yet. I would check out this thread about adding action buttons. I haven't done it before, but that thread gives the basics.

It sounds like you're adding new actions (assassinate and bribe), so you have to define them in XML and C++. Given that you are using someone else's DLL that doesn't have those actions, this is probably what's missing.
 
I'm not adding the action buttons myself, they were supposed to be in the SuperSpies mod, and apparently that mod already had the problem in it. So I don't know how to move on from here really, I checked the SDK files of the SS mod and they are all coded into that, but I guess not in the DLL. I don't know how to change this though, that must be the problem.
 
The SDK is the collection of C++ files that get compiled into the DLL. Then there are XML files and Python files that can also be modified, but they don't need to be compiled using an application -- they can be dropped into the Civ4 directories and get used as they are.

How familiar are you with C++ and XML? I don't really know where to help without knowing your level of experience. I would start by comparing the ActionInfos.xml file in the mod to the one installed with Civ4 to make sure it has the necessary additions. Then make sure the C++ files have the correct changes for them.
 
I practically have no experience with SDK, Python, C++ or XML. Although I learned a lot of XML and some Python by virtue of working on this mod. So I have hardly any experience with any of this.
 
I didn't see an ActionInfos.xml, but I did see an EspionageMissionInfos.xml, and this is what it says for the "Destroy Building Espionage Mission;"

Code:
		<EspionageMissionInfo>
			<Type>ESPIONAGEMISSION_DESTROY_BUILDING</Type>
			<Description>TXT_KEY_ESPIONAGE_DESTROY_BUILDING</Description>
			<iCost>0</iCost>
			<bIsPassive>0</bIsPassive>
			<bIsTwoPhases>1</bIsTwoPhases>
			<bTargetsCity>1</bTargetsCity>
			<bSelectPlot>0</bSelectPlot>
			<TechPrereq>NONE</TechPrereq>
			<iVisibilityLevel>0</iVisibilityLevel>
			<bInvestigateCity>0</bInvestigateCity>
			<bSeeDemographics>0</bSeeDemographics>
			<bNoActiveMissions>0</bNoActiveMissions>
			<bSeeResearch>0</bSeeResearch>
			<bDestroyImprovement>0</bDestroyImprovement>
			<iDifficultyMod>10</iDifficultyMod>
			<iDestroyBuildingCostFactor>500</iDestroyBuildingCostFactor>
			<iDestroyUnitCostFactor>0</iDestroyUnitCostFactor>
			<iDestroyProjectCostFactor>0</iDestroyProjectCostFactor>
			<iDestroyProductionCostFactor>0</iDestroyProductionCostFactor>
			<iBuyUnitCostFactor>0</iBuyUnitCostFactor>
			<iBuyCityCostFactor>0</iBuyCityCostFactor>
			<iStealTreasuryTypes>0</iStealTreasuryTypes>
			<iCityInsertCultureAmountFactor>0</iCityInsertCultureAmountFactor>
			<iCityInsertCultureCostFactor>0</iCityInsertCultureCostFactor>
			<iCityPoisonWaterCounter>0</iCityPoisonWaterCounter>
			<iCityUnhappinessCounter>0</iCityUnhappinessCounter>
			<iCityRevoltCounter>0</iCityRevoltCounter>
			<iBuyTechCostFactor>0</iBuyTechCostFactor>
			<iSwitchCivicCostFactor>0</iSwitchCivicCostFactor>
			<iSwitchReligionCostFactor>0</iSwitchReligionCostFactor>
			<iPlayerAnarchyCounter>0</iPlayerAnarchyCounter>
			<iCounterespionageNumTurns>0</iCounterespionageNumTurns>
			<iCounterespionageMod>0</iCounterespionageMod>
		</EspionageMissionInfo>

Now, I can't find where it says ESPIONAGEMISSION_DESTROY_BUILDING anywhere else in any of the code. But in the SuperSpy mod, which I am using there is a file called CIV4MissionInfos.xml (but no ActionInfos.xml that I can see anywhere, I can't find that in the BtS files either BTW), and remember this is in MY MOD the prior code is in the regular BtS xmls. Anyway here is what I have in my mod for the Assassination Mission:

Code:
		<MissionInfo>
			<Type>MISSION_ASSASSIN</Type>
			<Description>TXT_KEY_MISSION_ASSASSIN</Description>
			<Help>TXT_KEY_MISSION_ASSASSIN_HELP</Help>
			<Waypoint>NONE</Waypoint>
			<EntityEventType>ENTITY_EVENT_GREAT_EVENT</EntityEventType>
			<iTime>14</iTime>
			<bTarget>0</bTarget>
			<bBuild>0</bBuild>
			<bSound>0</bSound>
			<HotKey/>
			<bAltDown>0</bAltDown>
			<bShiftDown>0</bShiftDown>
			<bCtrlDown>0</bCtrlDown>
			<iHotKeyPriority>0</iHotKeyPriority>
			<HotKeyAlt/>
			<bAltDownAlt>0</bAltDownAlt>
			<bShiftDownAlt>0</bShiftDownAlt>
			<bCtrlDownAlt>0</bCtrlDownAlt>
			<iHotKeyPriorityAlt>0</iHotKeyPriorityAlt>
			<bVisible>1</bVisible>
			<Button>Art/Interface/Buttons/Actions/Assassinate.dds</Button>
		</MissionInfo>

Now one tag says <EspionageMissionInfo> and the other just says <MissionInfo> so I guess the two are unrelated, but it looks like I'm on the right track somewhere. I think I want the Asassin and Bribe to be on the screen like how "build road" and "sentry" would show up, at least that is what I can gather based on the buttons for bribe and assassinate that come with the mod. I don't know if this helps in any way, shape, or form, but hopefully it does. Thanks again.
 
So basically if there is a mission added, in this case "MISSION_ASSASSIN," which SDK files will/need to be changed?

This will help me immensely, this is the last python error I have in my mod and if I can get this figured out it will be perfectly fine. I have looked in the CvDLLWidgetData.cpp file in the SDK and it says this:

Code:
			//TSHEEP Assassin Mission
			else if (GC.getActionInfo(widgetDataStruct.m_iData1).getMissionType() == MISSION_ASSASSIN)
			{
				if(pMissionPlot->plotCount(PUF_isOtherTeam, pHeadSelectedUnit->getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, pHeadSelectedUnit->getOwnerINLINE()) == 1)
				{
					CvUnit* pTargetUnit;
					int iEspionageCost = 0;
					int iMissionChance = 0;

					pTargetUnit = pMissionPlot->plotCheck(PUF_isOtherTeam, pHeadSelectedUnit->getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, pHeadSelectedUnit->getOwnerINLINE());
					iEspionageCost = pHeadSelectedUnit->assassinCost(pTargetUnit);
					iMissionChance = pHeadSelectedUnit->assassinProb(pTargetUnit);
					szBuffer.append(NEWLINE);
					szBuffer.append(gDLL->getText("TXT_KEY_ACTION_ASSASSIN_MISSION",pTargetUnit->getNameKey(),iEspionageCost,iMissionChance));
				}
			}
			//TSHEEP End

I'm thinking this is probably where my problems are arising. But there are other instances, here is another one from CvSelectionGroup.cpp:

Code:
		//TSHEEP Assassin Mission
		case MISSION_ASSASSIN:
			if (pLoopUnit->canAssassin(pPlot, bTestVisible))
			{
				return true;
			}
			break;
		//TSHEEP End

Again from CvSelectionGroup.cpp:

Code:
				//TSHEEP Assassin Mission
				case MISSION_ASSASSIN:
					if (pLoopUnit->assassin())
					{
						bAction = true;
					}
					pUnitNode = NULL; // allow one unit at a time to do espionage
					break;
				//TSHEEP End

Then this is from CvUnit.cpp:

Code:
//TSHEEP Assassin Mission
bool CvUnit::canAssassin(const CvPlot* pPlot, bool bTestVisible) const
{
	if (isDelayedDeath())
	{
		return false;
	}

	if (!isSpy())
	{
		return false;
	}

	if(pPlot->plotCount(PUF_isOtherTeam, getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getOwnerINLINE()) != 1)
	{
		return false;
	}

	CvUnit* pTargetUnit;

	pTargetUnit = pPlot->plotCheck(PUF_isOtherTeam, getOwnerINLINE(), -1, NO_PLAYER, NO_TEAM, PUF_isVisible, getOwnerINLINE());

	CvPlayer& kTarget = GET_PLAYER(pTargetUnit->getOwnerINLINE());

	if (kTarget.getTeam() == getTeam())
	{
		return false;
	}

	if (kTarget.isBarbarian())
	{
		return false;
	}

	if (GET_TEAM(getTeam()).isVassal(kTarget.getTeam()))
	{
		return false;
	}

	if (!bTestVisible)
	{
		if (isMadeAttack())
		{
			return false;
		}

		if (hasMoved())
		{
			return false;
		}

		if (kTarget.getTeam() != getTeam() && !isInvisible(kTarget.getTeam(), false))
		{
			return false;
		}
	}

For the above code Trojan Sheep didn't mark where he ended it, so I'm not sure if that's everything for it.

Anyway here is more from CvUnit.h:

Code:
	//TSHEEP Assassin Mission
	bool canAssassin(const CvPlot* pPlot, bool bTestVisible) const;																										// Exposed to Python
	int assassinCost(const CvUnit* pTargetUnit) const;
	int assassinProb(const CvUnit* pTargetUnit) const;
	bool assassin();
	//TSHEEP Other functions
	bool awardSpyExperience(TeamTypes eTargetTeam, int iModifier);
	//TSHEEP End

And this is from CyUnit.cpp:

Code:
//TSHEEP Assassin Mission
bool CyUnit::canAssassin(CyPlot* pPlot, bool bTestVisible)
{
	return m_pUnit ? m_pUnit->canAssassin(pPlot->getPlot(), bTestVisible) : false;
}
int CyUnit::assassinCost(CyUnit* pUnit)
{
	return m_pUnit ? m_pUnit->assassinCost(pUnit->getUnit()) : -1;
}
int CyUnit::assassinProb(CyUnit* pUnit)
{
	return m_pUnit ? m_pUnit->assassinProb(pUnit->getUnit()) : -1;
}
//TSHEEP End

Alright and then from the original CvMainInterface.py:

Code:
				actions = CyInterface().getActionsToShow()
					for i in actions:
						screen.appendMultiListButton( "BottomButtonContainer", gc.getActionInfo(i).getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )
						screen.show( "BottomButtonContainer" )

I know this is a lot of stuff to look at, but the python error refers to this last python file and line 1491, this should be lines 1489 through 1492. I hope someone can help me out with this, and thank you for reading it.
 
My guess is that the problem is the C++ call

Code:
gc.getActionInfo(i)

and that you haven't defined all of the necessary actions in XML and/or the correct C++ header file. Unfortunately, I don't know which those are (that's why I posted the link to that other thread).

One way to test would be to split up line 1491 so you can isolate the problem. Replace line 1491 with these lines (indented to the same level as 1491):

Code:
pInfo = gc.getActionInfo(i)
screen.appendMultiListButton( "BottomButtonContainer", pInfo.getButton(), 0, WidgetTypes.WIDGET_ACTION, i, -1, False )

See if the error is still on line 1491 or if it moves to 1492.
 
The only thing I can think, then, is that the button for one of the new actions is not setup properly. I would verify that each of the buttons points to a valid DDS file.
 
But I've seen the button come up before, when I was screwing around with the SDK file code. And the button is there, it is properly tagged in the <button></button> slot. Strange.
 
The best way to diagnose SDK/DLL problems is to recompile the DLL with asserts turned on (debug mode), but you said you aren't building the DLL yourself. You could use VC++ 2003 to compile it.

The reason I say the problem must be the button is all of the other parameters you're passing to appendMultiListButton() are constants (except i which is just a number) taken directly from the original code. I don't see how they could be a problem.

I suspect an assert is being violated (probably i is out of range), except that the same value of i is fine when passed to getActionInfo, so that seems unlikely. :confused:
 
Back
Top Bottom