Search code examples
pythonpython-3.xscopeevalformat-string

self only in scope for eval when self is referred to beforehand in same scope/function


In the following code below the eval call is successful:

from zeep import Client
from zeep import xsd
from zeep.plugins import HistoryPlugin

class TrainAPI:
    def __init__(self,LDB_TOKEN):
        if LDB_TOKEN == '':
            raise Exception("Please configure your OpenLDBWS token")
        WSDL = 'http://lite.realtime.nationalrail.co.uk/OpenLDBWS/wsdl.aspx?ver=2017-10-01'
        history = HistoryPlugin()
        self.client = Client(wsdl=WSDL, plugins=[history])

        header = xsd.Element(
            '{http://thalesgroup.com/RTTI/2013-11-28/Token/types}AccessToken',
            xsd.ComplexType([
                xsd.Element(
                    '{http://thalesgroup.com/RTTI/2013-11-28/Token/types}TokenValue',
                    xsd.String()),
            ])
        )
        self.header_value = header(TokenValue=LDB_TOKEN)
        self.token = LDB_TOKEN
        return

    def __getattr__(self, action):
        def method(*args,**kwargs):
            print(action,args,kwargs)
            print(self)
            return eval(f"self.client.service.{action}(*args,**kwargs, _soapheaders=[self.header_value])")
        return method

However, if the print(self) line is removed, then the following error is thrown:

File "C:/Users/-/Documents/-/main.py", line 32, in method
    return eval(f"self.client.service.{action}(*args,**kwargs, _soapheaders=[self.header_value])")
  File "<string>", line 1, in <module>
NameError: name 'self' is not defined

Sorry if I am missing something obvious, but my question is: why is self not defined unless I call something that refers to it (such as print(self)) beforehand within the method function? Seems complex since the traceback finally refers to line 1 of <string>...

Edit: trying this also returns an error:

def __getattr__(self, action):
        def method(*args,**kwargs):
            print(action,args,kwargs)
            print(f"self.client.service.{action}(*args,**kwargs, _soapheaders=[self.header_value])")
            return eval(f"self.client.service.{action}(*args,**kwargs, _soapheaders=[self.header_value])")
        return method

Maybe I don't understand how scopes or format strings work.


Solution

  • The reason goes a bit deep into how python works, and I can tell you if you really want, but to solve your actual problem the answer is to avoid eval whenever possible as there is usually a better way. In this case:

    method = getattr(self.client.service, action)
    return method(*args,**kwargs, _soapheaders=[self.header_value])