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

[BUG] [Python] Default configuration gives unexpected behaviour #3577

Closed
TimoMorris opened this issue Aug 7, 2019 · 6 comments
Closed

[BUG] [Python] Default configuration gives unexpected behaviour #3577

TimoMorris opened this issue Aug 7, 2019 · 6 comments

Comments

@TimoMorris
Copy link

TimoMorris commented Aug 7, 2019

Description

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.

openapi-generator version

v4.0.1 (this is the version I was using, and also where the issue first appears)

OpenAPI declaration file content or url

https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/python/configuration.mustache

Command line used for generation
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
Steps to reproduce

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.]

Related issues/PRs
Suggest a fix/enhancement

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 [python] Adding constructor parameters to Configuration and improving documentation #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:

  • As previously mentioned, I think the "enforcement" behavior is an anti-pattern, and unhelpful. Furthermore, the changes made in [python] Adding constructor parameters to Configuration and improving documentation #3002 are also really useful, so I am against (1).
  • I think (2) is a bad compromise, as it would mean all 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.
  • As mentioned in the previous point, getting a 'default' configuration is still possible, and this would mean that users can call 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.)

@auto-labeler
Copy link

auto-labeler bot commented Aug 7, 2019

👍 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.

@TimoMorris TimoMorris changed the title [Python] Default configuration gives unexpected behaviour [BUG] [Python] Default configuration gives unexpected behaviour Aug 7, 2019
@wing328
Copy link
Member

wing328 commented Aug 14, 2019

cc @saigiridhar21 (author of #3002)

@TimoMorris
Copy link
Author

cc @mbohlool would be great if you could give us some background info on the TypeWithDefault class 😀

@saigiridhar21
Copy link
Contributor

@TimoMorris I like the option 3 as you have mentioned. Thanks for the detailed explanation.

@saigiridhar21
Copy link
Contributor

saigiridhar21 commented Sep 3, 2019

@TimoMorris Please feel free to raise the pull request. I don't see any benefit of using TypeWithDefault.

@wing328
Copy link
Member

wing328 commented Nov 17, 2019

Closed via #4485

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants