After it has been called once, the Configuration class constructor effectively ignores the parameters passed to it on subsequent calls, and instead returns a copy of the configuration created the first time it was called.
v4.0.1 (this is the version I was using, and also where the issue first appears)
java -jar openapi-generator-cli-4.0.1.jar generate -i my-spec.json -g python -o .\my_package -DprojectName=my-project -DpackageName=my_package
Once you've generated the api client package as above, run:
import my_package
config1 = my_package.Configuration(host="https://my-first-site.com")
config2 = my_package.Configuration(host="https://a-different-site.com")
print(config1.host) # https://my-first-site.com
print(config2.host) # https://my-first-site.com
Or alternatively:
import my_package
config1 = my_package.Configuration() # this call uses the default host from the spec
config2 = my_package.Configuration(host="https://a-different-site.com")
print(config1.host) # https://default-host-from-spec.com
print(config2.host) # https://default-host-from-spec.com
The two hosts for the two different configurations should be different.
[Note: it may seem strange to want to set different hosts for one API, but the software I'm working with doesn't have a single central API, but rather allows users to set up their own instance(s) of the API. Each user's API has the same spec, except for being hosted wherever the user has set it up. I'm working to develop tools that any user could use, but obviously they have to provide the host to begin with.]
TypeWithDefault class which Configuration inherits from.TypeWithDefault parent class leads to the above unexpected and undesired behavior.It depends what the purpose of TypeWithDefault is supposed to be. I can see two potential purposes:
a) Enforce the configuration to always be the 'default', i.e. with the host URL from the API spec (except when manually changed by setting attributes directly):
❌ This has already been undermined by the changes in #3002, as it's now possible to set a different configuration on the first call. In any case, I would also argue this enforcement is an anti-pattern and not in the normal spirit of Python (being an "adult consenting" language).
b) To set a ._default class attribute, so that the user has this available should they ever need to retrieve the default configuration:
❌ It goes further than this, as it returns this default on every call.
Either way, TypeWithDefault is currently not correctly doing what it was supposed to. It's also worth noting that I don't think the TypeWithDefault.set_method() could ever work correctly - how would you create a different configuration to pass in as the new default, when the class will always return the 'old' default for any configuration you create?!
[There's a small chance I might have missed something here, as I don't have much experience with six and haven't done much metaprogramming, but that seems to be how it behaves.]
The solutions I see are:
1) The configuration should be fixed to the default given by the API spec -> revert #3002
2) There should be a default configuration available to retrieve as a class attribute -> change the return value of the TypeWithDefault.__call__() method from return copy.copy(cls._default) to return type.__call__(cls, **kwargs)
3) Have no default configuration -> remove TypeWithDefault entirely.
My choice:
TypeWithDefault does is create the ._default attribute. If users really need a copy of the default configuration, they can just call Configuration() with no arguments, i.e. the default parameters.Configuration() with their desired parameters and get back what they expect, no surprises.I've made a gist with the change suggested in (3): https://gist.github.com/TimoMorris/b3b0b64f966408a5475dce05bc4243dc
I haven't done a PR, partly because I wanted to get some feedback on what is partly a design decision, and partly because I'm not sure I know enough to make the change myself.
(I also changed the constructor slightly to avoid api_key and api_key_prefix having dictionaries as their default argument, as this can cause subtle bugs - this would be a good change to make in any case.)
👍 Thanks for opening this issue!
🏷 I have applied any labels matching special text in your issue.
The team will review the labels and make any necessary changes.
cc @saigiridhar21 (author of #3002)
cc @mbohlool would be great if you could give us some background info on the TypeWithDefault class 😀
@TimoMorris I like the option 3 as you have mentioned. Thanks for the detailed explanation.
@TimoMorris Please feel free to raise the pull request. I don't see any benefit of using TypeWithDefault.