Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client instances can share config because default_config contains mutable values #138

Open
jonprindiville opened this issue Oct 20, 2021 · 0 comments

Comments

@jonprindiville
Copy link
Member

jonprindiville commented Oct 20, 2021

I have never (knowingly) been tripped up by it, but I noticed this while reviewing #137...

gapipy.client.default_config is basically used as a default argument to client initialization and some of its content are (mutable) dicts. Because of that it's possible to accidentally "share" data there -- you might have some surprises if you instantiate multiple clients in the same process with different configs.


To demonstrate...

>>> from gapipy import Client

>>> # Let's make a client and enable connection pooling
>>> c1 = Client(connection_pool_options={"enable": True})
>>> c1.connection_pool_options["enable"]
True

>>> # Ok, let's make another WITHOUT connection pooling
>>> c2 = Client(connection_pool_options={"enable": False})
>>> c2.connection_pool_options["enable"]
False

>>> # Cool, now let's see what that first client is up to, it was True to begin with...
>>> c1.connection_pool_options["enable"]
False

>>> c1.connection_pool_options is c2.connection_pool_options
True

I suspect the crux of the issue is probably twofold:

  • new client instances get direct references to stuff in default_config and some of those things are mutable
  • Client.__init__ explicitly mutates the default connection_pool_options dict when some extra connection pool configs have been passed

I bet you could have a similar issue with the global http headers config, except that Client.__init__ doesn't mutate that one you'd have to have a series of events like:

  • instantiate a client
  • instantiate another client
  • mutate one of their client.global_http_headers and the other client is affected because they both shared the same default value

(Pretty sure I had a hand in both of those connection-pool-options and global-http-headers things 🤦 oops)


I haven't taken a run at writing tests for it yet, but I wonder if the fix is simply:

  • update get_config to copy.deepcopy (or similar) the value it gets from default_config before returning it, and
  • use get_config (instead of direct dict access) when getting "connection_pool_options" out of default_config

We could also take other approaches like:

  • Client.__init__ should deepcopy the default dict before yanking stuff out, or
  • the default dict should be returned from a function instead of existing at at module level, or
  • something else entirely 🤷
@jonprindiville jonprindiville changed the title Client instances can share config because default_config is essentially a mutable default arg to __init__ Client instances can share config because default_config contains mutable values Oct 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant