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

Allow using the decorators on non-module-level functions #82

Closed
kostasdizas opened this issue Oct 11, 2015 · 8 comments
Closed

Allow using the decorators on non-module-level functions #82

kostasdizas opened this issue Oct 11, 2015 · 8 comments

Comments

@kostasdizas
Copy link
Contributor

I am trying to decorate the methods of a class as API calls but I can't make it ignore the 'self' parameter. Is it possibly to manually set the accepted parameters, or set it to ignore one?

@timothycrosley
Copy link
Collaborator

Hi @kostasdizas,

Thanks for reporting this! There are some round-about ways to work around it, but I think it should be fixed within the project itself so that you can attach hug routes to methods without modification. Ill work on getting a fix up soon.

Thanks!

~Timothy

@timothycrosley
Copy link
Collaborator

Actually, thinking about it some more - I think I need better background on what you're trying to accomplish.
You can easily make hug "ignore" the self parameter by providing a default value:

class API(object):

    @hug.call()
    def hello_world(self=None):
        return "Hello World!"

api_instance = API()
assert api_instance.hello_world.interface
assert api_instance.hello_world()
assert hug.test.get(api, '/hello_world').data == "Hello World!"

Or you could wrap the method in the staticmethod decorator and it wouldn't require self at all. But, I'm assuming that since the API call is a method on an object that you want the API call to have access to that object and it's methods / properties. The problem being that hug cant know what instance of the object you want, if you're attaching to a class definition. So another way to accomplish the above, if you want it to actually have self defined:

class API(object):

    def hello_world(self):
        return "Hello World!"

api_instance = API()

@hug.call()
def hello_world():
    return api_instance.hello_world()

I've going ahead and written tests to verify both of these methods work here:
https://github.com/timothycrosley/hug/pull/83/files#diff-8107272ea49a7ea4505985b05542ce00R45

And I've insured that the auto-documenter wont document that an exposed API call takes a 'self' argument.

Let me know if this solves your issue, or if there is a more robust framework level solution that would help.

Thanks!

~Timothy

@kostasdizas
Copy link
Contributor Author

Hello Timothy,
I was actually trying two things at the same time. In the beginning I was only trying to group together similar endpoints, more specifically CRUD operations for a specific model. It worked like a charm:

class BlogEndpoints():

    base = "blog"
    model = Blog

    @hug.get(["/{0}".format(base), "/{0}/{{id}}".format(base)])
    def get(id:int=None):
        result = __class__.model.get(id) if id else __class__.model.get()
        return result

    @hug.post("/{0}/new".format(base))
    def add(title:str, content:str):
        __class__.model.create(title=title, content=content)
        return "ok"

    @hug.put(["/{0}".format(base), "/{0}/{{id}}".format(base)])
    def edit(id:int, title:str, content:str):
        x = __class__.model.update(id).values(title, content)
        return "ok"

    @hug.delete(["/{0}".format(base), "/{0}/{{id}}".format(base)])
    def delete(id:int):
        __class__.model.delete(id)
        return "ok"

Here's my sample code including pseudo access to the model.
Secondly, I wanted to create something more abstract that can be inherited, and essentially automate the process of creating those basic endpoints. I've experimented with different things and decided to just use a method that registers all the endpoints.

...
def __init__(self, base, model):
    self.base = base
    self.model = model

def register_endpoints(self):
    hug.get(["/{}".format(self.base), "/{}/{{id}}".format(self.base)] )(self.get)
    hug.post("/{}/new".format(self.base))(self.add)
    hug.put(["/{}".format(self.base), "/{}/{{id}}".format(self.base)] )(self.edit)
    hug.delete(["/{}".format(self.base), "/{}/{{id}}".format(self.base)] )(self.delete)
...

This sort of works but the hug.call decorator won't accept class methods. I get an error saying:

AttributeError: 'method' object has no attribute 'interface'

All in all I'm pretty pleased with the first method, but it involves a lot of code repetition. Using self=None can work but limits the flexibility of the method since it cannot access any class variables. I'll keep working on it and see how far I can take it. Ideally I want to get to the point where you only need this to create the CRUD endpoints:

class BlogEndpoints(hugCRUD):
    base = "blog"
    model = Blog

@timothycrosley
Copy link
Collaborator

@kostasdizas,

Thanks for adding these useful usage scenarios, I certainly see merit in your initial register_endpoints approach. I'll go ahead and implement support for that approach now and if in the future you think of more clean ways to implement the re-use you want let me know, and I'll try to implement them as well (or certainly accept a pull-request if you have time). I'll try to get a new release out ASAP that implements this feature.

Thanks!

~Timothy

@timothycrosley
Copy link
Collaborator

I just released version 1.6.0 which has built-in support for attaching hug routes to methods

Thanks!

~Timothy

@kostasdizas
Copy link
Contributor Author

Hey @timothycrosley,

I've created a small module called hugpee that automatically does what I described above. It's in its early stages and only supports peewee models for the time being. I'd greatly appreciate your comments and suggestions on this.

Kostas

@timothycrosley
Copy link
Collaborator

@kostasdizas,

Haven't had a great deal of time to look at it yet, but on the surface it looks great! Once I release a new version of hug that includes all the features hugpee is dependent on, and you feel okay with hugpee itself - I'll go ahead and link it on the main repo. Hopefully I'll have some more time to look at hugpee tonight.

Thanks!

~Timothy

@tgalery
Copy link

tgalery commented Apr 5, 2017

@timothycrosley Any updates on this ? I quite like hug and have been using for a project but it lacks features related to exposing CRUD operations on models (at the moment we have to expose certain routs for each operation we want and add the code manually). I'm also using pewee, so hugpee sounds like a good call. However, if one doesn't use this module, what would be a good way to best expose a models CRUD operations ?

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