Python lazy proxy

Oleksii Petrenko
2 min readJan 26, 2021

Sometime it’s happens, that we need some object to be imported before real object construction.

Suppose we have client for third party service like this

## third_party_service.py ##class ThirdPartyService:
def __init__(self, connection_args):
self.login = connection_args["login"]
self.password = connection_args["password"]
def get_login(self):
return f"login: {self.login}, password: {self.password}"

and we want to use it in our application.

We don’t want to create multiple instances of this service, so we initialize this service in core.py

## core.py ##from third_party_service import ThirdPartyServiceservice = Nonedef init_all():
config = load_config()
global service
service = ThirdPartyService(config["connection_args"])
def load_config():
return {"connection_args": {"login": "foo", "password": "bar"}}

Everything looks good for now, but let’s now write our service user.

## calculations.py ##from core import service
def calculate():
return service.get_login()

And at the end we setup an entry point script

## main.py ##from core import init_all
from calculations import calculate
init_all()
print(calculate())

Let’s now run main.py

$ python main.py 
Traceback (most recent call last):
File "main.py", line 5, in <module>
calculate()
File "calculations.py", line 4, in calculate
return service.get_login()
AttributeError: 'NoneType' object has no attribute 'get_login'

We can see that service is None. But why? We initiated it during init_all()!

The answer is that if we imported something that is not set (equals None), no matter how this object is changed later, it’s still None in module, that imported this object.

This can be solved by importing service inside the function. But this is not the Python way.

Another possibility is to use proxied python library.

proxied exposes Proxy class. This class can be initialized instead of real object, and later we can simply set value in proxy and work with proxy as with real object.

## core.py ##from proxy import Proxy
from third_party_service import ThirdPartyService
service = Proxy()def init_all():
config = load_config()
Proxy.set_inner(
service, ThirdPartyService(config["connection_args"])
)
def load_config():
return {"connection_args": {"login": "foo", "password": "bar"}}

Now we can run main.py. We can see that it works correctly.

$ python main.py
login: foo, password: bar

To simplify IDE auto completion, it’s possible to specify type of our service

from typing import Union
service: Union[Proxy, ThirdPartyService] = Proxy()

MyPy won’t raise error for this line of code, and the code autocomplete will work.

--

--