Python Console Error, stating the obvious?

Basically the code is telling a Track to actuator to track to an object, but only when it is in the scene. (So when the object is destroyed there won’t be script errors that drop performance and cause lag)

if scene.objects[(own['Object2'])] in scene.objects:
cont.activate(cont.actuators['Edit Object'])
cont.actuators['Edit Object'].object = scene.objects[(own['Object2'])]

The code runs and works fine, but whenever the object that is being tracked to is not in the scene, I get these errors in the console.

KeyError: “CList[key]: ‘‘Object’’ key not in list”

But why is it an error? Isn’t it just stating that there is not an object called Object in the scene? I’m confused.

is the edit object actuator in add object mode?

if it is, I think the object would have to be in own.scene.objectsInactive


if scene.objects[(own['Object2'])] in scene.objectsInactive:
    cont.activate(cont.actuators['Edit Object'])
    cont.actuators['Edit Object'].object = scene.objects[(own['Object2'])]

if not scene.objects[obj_name].invalid:
    #do stuff

Check if an object is invalid beforehand

@BPR: this would be a solution on AddObject mode. I do not thing tracking an inactive object is really that useful ;).

@LordoftheChings: Lets analyse your code and how the error message fits in.

The problem is there is too much code in one line. So we better focus on this line:


cont.actuators['Edit Object'].object = scene.objects[(own['Object2'])]

lets split it into single operations (the order of processing):


string_1 = 'Object2'                                                   # "Object2"
propertyValue_2 = own['Object2']                                       # "Object" 
propertyValue_3 = (own['Object2'])                                     # "Object"
listOfGameObjects_4 =  scene.objects                                   # list of objects from scene
listEntry_5 =  scene.objects[(own['Object2'])]                         # listOfGameObjects_4["Object"]

# to complete the line
listOfActuators_6 = cont.actuators                                     # a list of connected actuators
string_7 = 'Edit Object'                                               # "Edit Object"
actuator_8 = cont.actuators['Edit Object']                             # listOfActuators_6[string_7]
objectParameter_9 = cont.actuators['Edit Object'].object               # actuator_8.object

finally the assignment
cont.actuators['Edit Object'].object = scene.objects[(own['Object2'])] # actuator_8.object = listEntry_5  

There are two possibilities to cause a KeyError:

  • listEntry_5
  • actuator_8

Actuator_8 is accessed by key ‘Edit Object’. It can’t be the cause.
Lets look at creation of listEntry_5 -> the key is propertyValue_3 which is not explicitly shown by your post. But I’m sure it contains “Object” (in case you are confused … it is the value of property “Object2”).

Looking closer at your close I’m sure you get this error much earlier at this line:


if scene.objects[(own['Object2'])] in scene.objects:

here you have a replication of the above code:


#before 'in'
string_1 = 'Object2'                                                   # "Object2"
propertyValue_2 = own['Object2']                                       # "Object" 
propertyValue_3 = (own['Object2'])                                     # "Object"
listOfGameObjects_4 =  scene.objects                                   # list of objects from scene
listEntry_5 =  scene.objects[(own['Object2'])]                         # listOfGameObjects_4["Object"]

# after 'in'
listOfGameObjects_10 = scene.objects                                   # list of objects from scene

# 'in'
evaluationResult_11 = scene.objects[(own['Object2'])] in scene.objects # listEntry_5 in listOfGameObjects_10 

#'if'
if scene.objects[(own['Object2'])] in scene.objects                    # if evaluationResult_11 

The same as above you access by key “Object” but there is not such a key.

I guess this “if” should check if the key exist. Unfortunately you should not use this key while checking if it exists. It will not work. What you need is:


targetName = own['Object2']
if targetName in scene.objects:
    trackToActuator = cont.actuators['Edit Object']
    cont.activate(trackToActuator)
    target = scene.objects[targetName]
    trackToActuator.object = target

Be aware scene.objects[<object name>] is not sufficient when there are multiple objects with the same name. You will get any object without information there are more. (You can be sure it has this name).

I hope it helps

Also (own[‘Object2’]) seems like a tuple containing a single item. You can drop the ().

With one item () is interpreted as expression. You need (,) to create a tuple. () without item creates an empty tuple.

… I found this in the Python docs 6.2.3. Parenthesized forms.

Btw. I did not know about it before.
+1 to Python skills.

Hmmm
Experssions.

So it would be a bit like;
a = (1 > 2)
b = (“purple” != “white”)
c = (own)

Which should return;
a=False
b=True
c=True

Own is a game object and not None so it should return True???
Don’t know about that. I’ll have to try it out at home…

wouldnt just:


if own["Object2"] in scene.objects: # i look if there is an object named own["Object2"] (should be string) in scene.objects (i look if the string is in the CList)
    #do stuff

work instead of what he did:


if scene.objects[own["Object2"]] in scene.objects:
    #do stuff

he tries to get the KX_GameObject through scene.objects[own[“Object2”]] (already searches the CList with a string), but it isnt in the scene, so of course it throws an error

@Smoking_mirror

Sauce: https://en.wikipedia.org/wiki/Expression_(computer_science)

Nope, that would be if it where only boolean expressions or expressions that return a boolean value (like if), to do that you would have to cast the final value of the expression to bool:

a = bool(1>2) is equivelent to a = (1>2) and a = 1>2, becouse the “>” operator only return boolean values.
b is like a, the “!=” operator is only returns boolean values.
c is diferent, “own” may be anything, so c = bool(own) is a boolean value but c = (own) and c = own makes c to take the same type than own.

In short an expression may return values of any type, aka using somithing like x = (expr) is equivelent to x = expr and therefore useless and missleading, however the pharantesis can still be used to indicate precedence on any expression (not only mathematical), for example:

a = 1>2 == False is equivelet to doing a = 1 > 2 and 2 == False, and returns False.
a = (1>2) == False is equivelent to doing a = 1 > 2; a = a == False, and returns True. Notice that doing a = a == False is equivalent than a = not a

@Leralasss
You’re right. A try except block would also work and it’s the coding style the Python community usually uses (EAFP), but at the end both are fine.

Oh yeah, I get you.

The code I have works fine, whenever the object is in the scene everything works accordingly, but whenever it’s not, I look in the system console afterwords and get errors.

For example, it’s like me writing,

if cont.sensors['Keyboard'].positive:
    #do something

But then I get an error whenever the keyboard sensor is not positive saying, “Error, the keyboard sensor is not positive”

Well, duh, I know it’s not positive, that’s why I say if it IS positive then do this.

Python reads lines as they come in. So if you go:


if "property" in obj and obj['property'] == 3:
    #do_something

YOu don’t get errors, but if you do:


if obj['property'] == 3:
    #

Then you do.

Rather nicely, you can also do:


if obj.get('property') == 3:
    #do_something

Which won’t error either. You can do some cool things with the get method actually:


obj['count'] = obj.get('count', 0) + 1

Will increment count by one, creating it if it doesn’t exist.

The thing is that computers are dumb. They do exactly what you tell them. If it’s looking for something and can’t find it, it will throw an error because it doesn’t know the programmer expected it.

sensor.positive return True or False dependent on the current sensor evaluation status (which gets refreshed at each single frame before the controllers get executed).

It does not raise Errors.

Your code does more. First it searches for a sensor named “Keyboard” which can cause a KeyError. Then it evaluates the sensor status of the found sensor which does not cause an Error of any kind.

Then the processing flow forks dependent on the result of the last evaluation. I can only guess that your #do something results in an Error.

@sdfgeoff

wow that works? i always used another indent for something like that, thinking it would throw an error if the property dooesnt exist

This depends on if that is an error or an expected behavior.

Unexpected Errors
When you expect the property has to exist, you directly access it.


container[propertyName]

If the situation does not match your assumption Python will raise an error. The default behavior is to exit all functions until someone catches the error. When the error is not caught Python performs the default error handling (printing the stack trace + error message to console). At that stage the execution stack is emptied and no further processing will happen within Python. In a Python application the application would end, in the BGE the execution of your Python controller ends for this individual frame).

Expected Errors
When you know that errors can happen, but they are no expected behavior, you can implement your own custom error handler, that brings your process flow back to track.

For example: You expect a property “targetName” to contains the name of a game object.
Your code

  1. reads the property value,
  2. searches for an object of that name,
  3. assigns the result to a trackTo actuator,
  4. activates the actuator
    When the property does not exist your code can skip the further steps but the actuator should be activated regardless of the result.

It could be like this:


try: 
    targetName = container["targetName"]
    target = scene.objects[targetName]
    actuator.object = target
except KeyError:
    pass # do nothing

controller.activate(actuator)

Be aware this deals with an expected KeyError. It does not matter what statement causes the error. It can be anywhere between try and except. It does not deal with unexpected errors such as AttributeError unless you explicitly mention that in the code.

Alternative Processing
If you expect the property may exist or maybe not you need to deal with the situation that it is present as well as that it is not present. The non existing property is a valid situation - not a error situation.

When both situations need the same processing you simply use the same code:


count = container.get("count", 0)
sendMessage("count value", count)
[/count]

This is not always possible especially when the used statements cause Errors by themselves (such as dictionary access). In this case you can perform a pre-check:

if "count in container:
count = container[“count”]
else:
count = 0


When each situation needs a different processing you need a pre-check to separate the process flow:

if “count” in container:
count = 0
container[“count”] = count
sendMessage(“new count”, count)
else:
count = container[“count”]
sendMessage(“current count”, count)

print(“Count:”, count)




Remarks:
Be aware checking each possible situation results in a lot of validation code. This is inefficient, hard to read and hard to maintain. It can quickly happen, that the same check will be performed several times (code replications, process replication). 

Therefore it is necessary to think about what your code expects, and how what situations it can manage. It is really no problem to use the default error handling when something unexpected happens during your tests. You might want to review your design and/or turn the unexpected error into an expected error.

I hope it helps a bit.

IMHO .get () is almost always a better choice than try/except.
Get() can return None as a default if it doesn’t find anything, or it can be set up to return a different default, such as a default object or vector.
Try/except will fail without reporting the problem unless you specify. This allows game breaking bugs to happen without any trace of where they came from.

This a matter of the semantics.

Try/except belongs to expected errors (error = not the normal behavior). You do expect the key is always present.

get(key, defaultValue) belongs to expected (normal) behavior. You do expect the key might not be present.

It depends on the purpose of the design around that code.

Additionally you should consider following:

get(key) equals get(key, None) -> The default of the default value is None.

  • It does not show you if there is an entry or if the value is just None. If you need this information try/except and pre-check is better.
  • You need to create the default value object regardless if needed or not. This can have impact on larger objects. If you need an “heavy” default value try/except and pre-check is better.
  • reading the default value does not add an entry with that key to the container (none of the read access options does that).
  • dictionary access (container[key]) is slightly faster than get()

Within the BGE I use dictionary access when the property is mandatory and get() when it is optional. On internal properties (properties created via Python) I often use try/except to add a new entry in case it does not exist.e.g.


def getOrCreateProperty(container, propertyName, None=defaultValue):
    try:
        return container[propertyName]
    except KeyError:
        container[propertyName] = defaultValue
        return defaultValue

That is how I do it. All three options can be interchanged in most of the cases.

That should never happen. This happens when you do not define a proper error handling or you naively do not specify what errors you expect.

The “unless you specify” is the normal case rather than a special behavior.

It’s not working for me, either I’m typing it wrong or it’s all going over my head.

The blend file below is what I’m trying to do with the script.

Example.blend (671 KB)

I guess you need to read the documentation regarding the “in” statement (of dictionaries).

You can use it to look for the existence of an entry of a specific key (in your case an object name). Unfortunately you try to get the entry first, which will fail as the entry does not exist. Better try:


if Object in scene.objects:

Remarks:
There are a few things to increase readability in your snippet

  • You do not need to define a function. It forces any reader to jump up and down in your code. Imagine you read a book and it tells you at the first page the story continues somewhere later without telling where. What a surprise you need to browse through the whole book, to see at the last page, it continues on the first page.
  • The name “main” means nothing in the context of the BGE and Python. It is just a bad transition from other programming languages such as C or GLSL. It would be much better to spend some minutes finding some words that describe what the code should do. So you tell the semantic of the code. In your case I suggest to skip the function as there is just one and spend the time on finding a name on the script.
  • The name TrackTo.py is misleading. This code does not track anything. After analyzing the code I gues it is supposed to setup a trackTo actuator. The actuator tracks, the code does not. I suggest a better name would be: set_target.py or similar.
  • I strongly suggest to chose a naming convention. Mixing lower case and upper case variable names create a lot of confusion.
  • Logic bricks would benefit from a semantic description too. The actuator name “Edit Object” does not tell really much. Unfortunately it is hard-coded in your snippet. I think “Track to” would be a slightly better name. As it expresses that the object will track something.

Your code could look like this:
set_target.py


import bge


controller = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()
owner = controller.owner

targetName = owner['Object']

if targetName in scene.objects:
    target = scene.objects[targetName]
    trackToActuator = controller.actuators['Track to']
    trackToActuator.object = target
    controller.activate(trackToActuator)
else:
    owner.worldOrientation = (0, 0, 0)

Thanks Monster! :slight_smile:
And thanks to the rest for the feedback, I’m sure to use what has been suggested in the future!

Yea, I guess I should make the code more readable for others if I’m uploading it for help on a Forum, :stuck_out_tongue: I usually understand what I mean myself, but I’ll try to get in the habit of making it say what actually does!