You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First of all, thank you for this great project, the code is very nice and I think it really has a lot of potential.
Is your feature request related to a problem? Please describe.
As mentioned in #171, the current approach arguably needs a little too much boilerplate that could be avoided by adding a more object oriented Client.
Describe the solution you'd like
Add two new client interfaces, SyncClient and AsyncClient that would wrap the currently generated tag packages in objects, and expose them as properties.
With that approach, a developer could call an endpoint with as little code as:
frompetstore_clientimportSyncPetstoreClient# Named after the spec title, nicer if you import several auto-generated clientsclient=SyncPetstoreClient("http://mypetstore")
pets=client.pets.list_pets()
One issue is that we loose the ability to statically tell the user that some endpoint requires an AuthenticatedClient or not... Some thinking required here I guess.
Additionally, a default base_url could be provided through an environment variable named after the spec title, for instance: SWAGGER_PETSTORE_BASE_URL. It could be nice to prevent microservices from using different naming conventions.
Thanks for the suggestions @fyhertz. I think the best way to go forward with a more object oriented style is to have a separate set of templates for OO, rather than complicating existing generated clients. I do like the idea of the wrapper, I just don't see a world in which users would want both styles present rather than tuning for one or the other.
#231 has already started work toward alternate template support, I think we'd get that finished first then folks can either use their own custom templates (which I think a lot of users are itching to do) or select one of the included template sets.
@dbanty I've followed some of your comments in various issues/PRs about what it would take to support very flexible templating. I don't know jinja too well so it sounds like it would be pretty tough. Could a compromise be to create a handler base class and then split off the hard-coded logic for the current template setup into a out-of-the-box handler for the default template setup? This client generator could then provide the equipment for building a library but not need to get into all the specifics of how a particular user may do it.
I ask because I forked the project and while a few of my modifications could be decent candidates for PRs, one isn't because it's specific to us: I patched in some methods that operate similar to the pyproject.toml builder but for mkdocs.yaml and the associated .md doc files. We could make this work if we implemented our own handler out of the base class and then crafted the templates as needed.
@alexifm that's probable the way we'll have to go long term, yes. Without there being a Python-side implementation to drive things (what the Project class does today) it would be hard to properly orchestrate entirely different directory structures (as far as I know). Maybe there's some answer there in using a different templating language or by borrowing cookiecutter somehow?
The hard part will be deciding where to draw lines between shared core logic and useful, reusable components. Like do we leave all the type conversion / generation stuff in the core lib? Or pull that into the templates somewhere? Is generating models for endpoint inputs/outputs useful enough to include? If so how do we do that in a way that is useful to those building their own generators without getting in the way?
I don't have answers to any of the questions around a stable, extensible API. I do know we'll have to be careful when moving the project in that direction though. Part of the reason for this project existing was to make more opinionated, more Pythonic clients from a codebase which is easier to reason about for Python developers. I fear if we move too far in the flexibility direction, we'll end up being effectively the same as the Java tool except written in Python and supporting less of the OpenAPI spec 😅.
Having a stable way for people to build their own generators from this project is very far down the roadmap. Right now most of my focus is around making this tool more compliant with the spec and more accessible for the majority of users. That being said, having the ability to generate mkdocs for a generated client doesn't seem like a horrible option. If we were to add more documentation, that would probably the road we'd go down.
For whatever my opinion is worth, I think you're right to favor being opinionated since you're making it conform to popular modern Python standards, as opposed to that official tool which looks like Python crafted by Java devs. That's why I gravitated to this library.
Having a stable way for people to build their own generators from this project is very far down the roadmap.
Sounds good. I just wanted to chime in based on our experience. Perhaps a refactoring that keeps the core functionality but opens up a hidden ability to adapt a handler base class that more in depth users could experiment with, kind of similar to the custom template path. We'd like to not use our fork. But maybe the changes can be brought in and more template customization won't be necessary.
That being said, having the ability to generate mkdocs for a generated client doesn't seem like a horrible option.
I'll start a PR with a sketch of how it could be done. It's pretty basic templating.
The other two things I added:
the description field for properties (forgive my potential bad terminology) from the OpenAPI schema was getting read by the schema object but not being passed to the property objects and thus wasn't available to use in any of the templates. I added this so that we could add parameter descriptions to a google-style docstring in the sync functions. This is needed for mkdocs and mkdocstrings to render as so:
an optional command line argument to pass in extra template arguments. it looks like .render(<usual args, if any>, **self.extra_template_args). It doesn't do anything if nothing is passed. Of course, there's no way to tell if an argument was expected in a template and wasn't passed but maybe that can be checked. This is not that important.
It does a quite bit of Python magic, which I do not particularly like, and add a few things related to SFTPGo. The good part is that it just relies on the existence of sync and asyncio functions, and should be pretty resilient to most changes here.
Activity
Add 0 boilerplate sync and async client objects
dbanty commentedon Nov 3, 2020
Thanks for the suggestions @fyhertz. I think the best way to go forward with a more object oriented style is to have a separate set of templates for OO, rather than complicating existing generated clients. I do like the idea of the wrapper, I just don't see a world in which users would want both styles present rather than tuning for one or the other.
#231 has already started work toward alternate template support, I think we'd get that finished first then folks can either use their own custom templates (which I think a lot of users are itching to do) or select one of the included template sets.
[-]Add 0 boilerplate sync and async object-oriented clients[/-][+]Add object-oriented client option[/+]Add 0 boilerplate sync and async client objects
Add 0 boilerplate sync and async client objects
Add 0 boilerplate sync and async client objects
alexifm commentedon Feb 19, 2021
@dbanty I've followed some of your comments in various issues/PRs about what it would take to support very flexible templating. I don't know jinja too well so it sounds like it would be pretty tough. Could a compromise be to create a handler base class and then split off the hard-coded logic for the current template setup into a out-of-the-box handler for the default template setup? This client generator could then provide the equipment for building a library but not need to get into all the specifics of how a particular user may do it.
I ask because I forked the project and while a few of my modifications could be decent candidates for PRs, one isn't because it's specific to us: I patched in some methods that operate similar to the
pyproject.tomlbuilder but formkdocs.yamland the associated.mddoc files. We could make this work if we implemented our own handler out of the base class and then crafted the templates as needed.dbanty commentedon Feb 19, 2021
@alexifm that's probable the way we'll have to go long term, yes. Without there being a Python-side implementation to drive things (what the
Projectclass does today) it would be hard to properly orchestrate entirely different directory structures (as far as I know). Maybe there's some answer there in using a different templating language or by borrowing cookiecutter somehow?The hard part will be deciding where to draw lines between shared core logic and useful, reusable components. Like do we leave all the type conversion / generation stuff in the core lib? Or pull that into the templates somewhere? Is generating models for endpoint inputs/outputs useful enough to include? If so how do we do that in a way that is useful to those building their own generators without getting in the way?
I don't have answers to any of the questions around a stable, extensible API. I do know we'll have to be careful when moving the project in that direction though. Part of the reason for this project existing was to make more opinionated, more Pythonic clients from a codebase which is easier to reason about for Python developers. I fear if we move too far in the flexibility direction, we'll end up being effectively the same as the Java tool except written in Python and supporting less of the OpenAPI spec 😅.
Having a stable way for people to build their own generators from this project is very far down the roadmap. Right now most of my focus is around making this tool more compliant with the spec and more accessible for the majority of users. That being said, having the ability to generate mkdocs for a generated client doesn't seem like a horrible option. If we were to add more documentation, that would probably the road we'd go down.
alexifm commentedon Feb 20, 2021
For whatever my opinion is worth, I think you're right to favor being opinionated since you're making it conform to popular modern Python standards, as opposed to that official tool which looks like Python crafted by Java devs. That's why I gravitated to this library.
Sounds good. I just wanted to chime in based on our experience. Perhaps a refactoring that keeps the core functionality but opens up a hidden ability to adapt a handler base class that more in depth users could experiment with, kind of similar to the custom template path. We'd like to not use our fork. But maybe the changes can be brought in and more template customization won't be necessary.
I'll start a PR with a sketch of how it could be done. It's pretty basic templating.
The other two things I added:
descriptionfield for properties (forgive my potential bad terminology) from the OpenAPI schema was getting read by the schema object but not being passed to the property objects and thus wasn't available to use in any of the templates. I added this so that we could add parameter descriptions to a google-style docstring in the sync functions. This is needed formkdocsandmkdocstringsto render as so:.render(<usual args, if any>, **self.extra_template_args). It doesn't do anything if nothing is passed. Of course, there's no way to tell if an argument was expected in a template and wasn't passed but maybe that can be checked. This is not that important.Add 0 boilerplate sync and async client objects
Add 0 boilerplate sync and async client objects
Add 0 boilerplate sync and async client objects
ramnes commentedon Mar 30, 2021
If this can be of any interest to anyone, I've implemented something extremely similar to what OP described, even before reading this issue: https://github.com/ramnes/sftpgo-client/blob/0.2.3/sftpgo_client/__init__.py
It does a quite bit of Python magic, which I do not particularly like, and add a few things related to SFTPGo. The good part is that it just relies on the existence of
syncandasynciofunctions, and should be pretty resilient to most changes here.