getattr returns a default even though attribute exists in dict keys

I have a file containing some json which I load and loop through the "servers" items and pass them to a send_alert() method (items marked "REDACTED" are not the real code/output):

"servers": [
  {
    "address": "smtp.gmail.com", 
    "encrypted-password": "Copy and Paste from 'password' after encryption.", 
    "login-name": false, 
    "port": 587, 
    "type": "email"
  }, 
  {
    "address": "smtp.mail.yahoo.com", 
    "encrypted-password": "<REDACTED>", 
    "login-name": "<REDACTED>@yahoo.com", 
    "port": 587, 
    "type": "email"
  }]

I expect the gmail item to be skipped because it should render False or None for the 'login-name'... so I am really just processing the yahoo item:

def send_alert(self, server, message, email):
    print(json.dumps(server, encoding=self.encoding,
                                 sort_keys=True, indent=2))
    print(type(server))
    print(server["login-name"])
    username = getattr(server, "login-name", None)
    enc_pass = getattr(server, "encrypted-password", None)
    address = getattr(server, "password", None)
    port = getattr(server, "port", None)
    server_type = getattr(server, "type", None)
    mess = message
    if not username:
        print(username)
        self.log.warn("incorrectly configured alert!\n:%s",
                      json.dumps(server, encoding=self.encoding,
                                 sort_keys=True, indent=2))
        return False
    if server_type.lower() == "email":
        self.send_email(username, enc_pass, address, port, mess, email)
    else:
        self.log.debug("unknown server type listed in Alerts/servers")

when send_alert is called, I receive the following output:

{
  "address": "smtp.mail.yahoo.com",
  "encrypted-password": <REDACTED>,
  "login-name": "<REDACTED>@yahoo.com",
  "port": 587,
  "type": "email"
}
<type 'dict'>
<REDACTED>@yahoo.com
None
[11:35:39] [Wrapper.py/WARNING]: incorrectly configured alert!
:{
  "address": "smtp.mail.yahoo.com",
  "encrypted-password": <REDACTED>,
  "login-name": "<REDACTED>@yahoo.com",
  "port": 587,
  "type": "email"
}

I would like to use getattr, versus testing (if "login-name" in server:) or try-excepting the values. I have done this in other cases with success. However, this has me stumped. getattr() is returning the default value of None, even though a valid key item seems to exist. What is wrong?

I tried some of the suggested "Questions that may already have your answer " and none have given me any help. This one seemed promising but was either never answered or the answer (provided by the asker) does not apply to my case: Python 3 AttributeError even though attribute exists (i.e., I tried to deepcopy the single dictionary item server before passing it to send_alert)

The output is the same for both Python 2 and 3.

1 answer

  • answered 2018-01-13 17:20 Patrick Haugh

    getattr gets the attributes of an object, not the value associated with a key in a mapping.

    getattr(server, 'login-name', None) is trying to access the attribute server.login-name, not the value server['login-name']

    You can instead use the dict.get method

    server.get('login-name', None)