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

Making hug a no-op when running in python2? #435

Closed
nealmcb opened this issue Dec 22, 2016 · 8 comments
Closed

Making hug a no-op when running in python2? #435

nealmcb opened this issue Dec 22, 2016 · 8 comments
Labels

Comments

@nealmcb
Copy link

nealmcb commented Dec 22, 2016

Hug is great. I'd love to take advantage of it in my code when I'm using python3, e.g. to provide a CLI interface.

But I'd also like my code to run under python2.

Can I special-case the import of hug itself, then somehow mock out the decorators, so they just don't get in the way if the code is run via python2?

@nealmcb nealmcb changed the title Mocking out hug when running in python2? Making hug a no-op when running in python2? Dec 22, 2016
@cag
Copy link
Contributor

cag commented Jan 2, 2017

Sure, why not? Just wrap the decorators.

def my_get(func, *args, **kwargs):
    if 'hug' in globals():
        return hug.get(func, *args, **kwargs)
    return func

@nealmcb
Copy link
Author

nealmcb commented Jan 8, 2017

Thanks - that helps.

Now I'm stuck on how to add function annotations, as required for proper error handling around types, since code like this naturally leads to syntax errors under python2:

def nmin(alpha: hug.types.float_number=0.1, margin: hug.types.float_number=0.05):
    return 96

The suggestion at Function annotations — Python-Future documentation to provide them by adding an __annotations__ attribute would seem to be helpful, but I end up with type errors. E.g. I define:

import hug

@hug.get(examples='alpha=0.1&margin=0.05')
def nminhug2(alpha=0.1, margin=0.05):
    if margin > 1.0:
        return -1

    return 96

nminhug2.__annotations__ = {'alpha': hug.types.float_number, 'margin': hug.types.float_number}

And then visit http://localhost:8080/nminhug2?margin=0.1 and get:
TypeError: unorderable types: str() > float()

I guess that's because the hug decorators are processing the function before the annotation is added, and I don't know if there's a way to finesse that without things getting too ugly.

And I don't know if I'm going to keep running into things like this, as I explore other hug features that I want to take advantage of.

Also note that if I add the @hug.local() decorator to the function, I get this puzzling error:

nminhug2.__annotations__ = {'alpha': hug.types.float_number, 'margin': hug.types.float_number}
AttributeError: 'Local' object has no attribute '__annotations__'

@cag
Copy link
Contributor

cag commented Jan 8, 2017

Try this:

import hug

def nminhug2(alpha=0.1, margin=0.05):
    if margin > 1.0:
        return -1

    return 96

nminhug2.__annotations__ = {'alpha': hug.types.float_number, 'margin': hug.types.float_number}
nminhug2 = hug.get(examples='alpha=0.1&margin=0.05')(nminhug2)
nminhug2 = hug.local()(nminhug2)

@nealmcb
Copy link
Author

nealmcb commented Jan 12, 2017

Very helpful!

I also figured out how to make a substitute hug_noop library that defines a few hug functions which just do nothing. Now I can just use this include approach:

try:
    import hug
except:
    import hug_noop as hug

and then use your tips on annotating and manually decorating the functions.

The hug_noop.py module is very sparse now, only including the functions I needed:

def decorator_generator(*args, **kwargs):
    def identity_decorator(f):
        return f
    return identity_decorator

get = decorator_generator
local = decorator_generator
cli = decorator_generator

class types:
    number = None
    float_number = None

So now I can define a library that provides a lovely hug UI when used in python3 code, but also works with python2 code that doesn't use the hug UI.

If this approach is useful for others, we might want to flesh this out and include it in hug.

@nealmcb
Copy link
Author

nealmcb commented Jan 12, 2017

I'm guessing that there is actually also a way to add the annotations via a decorator, which would also allow the code to use decorations and look much cleaner and more similar to normal hug code.

@nealmcb
Copy link
Author

nealmcb commented Jan 14, 2017

....and just as I hoped, this seems to work fine for adding annotations in a way that works for python3 and doesn't cause errors in python2:

def annotate(annotations):
    """
    Add function annotations (PEP 3107) in a way that works for python2 also.
    """

    def decorator(f):
        f.__annotations__ = annotations
        return f
    return decorator

@hug.get(examples='alpha=0.1&margin=0.05')
@hug.local()
@annotate(dict(alpha=hug.types.float_number, margin=hug.types.float_number))
def demo(alpha=0.1, margin=0.05):
    if margin > 1.0:
        return -1

    return 96

So combined with the hug_noop.py solution above, this makes things very clean.
I guess it would help to add annotate() to both the regular hug library and hug_noop.

@cag
Copy link
Contributor

cag commented Jan 15, 2017

I'm glad I could help ^_^

Maybe annotate would be welcome in the python decorator library as a backwards-compatible type annotation decorator. However, this exists...

@timothycrosley
Copy link
Collaborator

Going to go ahead and close this as I think @cag did a great job answering it.

Glad you found a working solution,

Thanks!

~Timothy

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

No branches or pull requests

3 participants