COMMUNITY

[Python] Using decorators

haxe-python

(Geoffroy) #1

Hello,
I’m Geoffroy and I’m new to this forum. I’m experimenting with python target this days especially with external libraries.

Some external libraries are working with decorators (ie: Flask, Bottle, Click…)
I used the HelloWorld of Click for learning purposes.

import click

@click.command()
@click.option('--name', prompt='Your name', help='The person to greet.')
def hello(name):
     print('Hello' + name)

How would you output such code? I experimented with externs without success. I omitted the name parameter to focus on the @command decorator and then focus on the @option decorator.

import haxe.Constraints.Function;

@:pythonImport("click")
extern class Click{
  function new();
  function command<T:Function>():T;
}

class Main {
	static function main():Void {
    var click = new Click();
    click.command()(hello);
	}

	static function hello() {
		trace("Hello");
	}
}

Outputs

import click as Click


class Main:
    __slots__ = ()

    @staticmethod
    def main():
        click = Click()
        click.command()(Main.hello)

    @staticmethod
    def hello():
        print("Hello")

And return:

Traceback (most recent call last):
  File ".\TestHX.py", line 34, in <module>
    Main.main()
  File ".\TestHX.py", line 12, in main
    click = Click()

TypeError: 'module' object is not callable

Any idea on how to handle such libraries?

I’m still a beginner, so please excuse me if my understanding of all of this is wrong.

Best regards to you all.


(azrafe7) #2

Hi @supergeoff, I think you’re on the right track.

I don’t know if haxe has special support for python decorators built-in, but you can define them as function generators in the externs - more or less like you did -, and then use them with standard haxe syntax.

It gets a bit verbose, but should work.

See this gist as a starting step (which is also where my knowledge stops :stuck_out_tongue_winking_eye:): https://gist.github.com/azrafe7/8b65733f7b22fad3e35bcb4bbd2fc2d1

EDIT: I see there’s also this: https://github.com/andyli/pyextern. Haven’t used it though.


(Geoffroy) #3

Thanks Azrafe7, I will look a it in detail. As for the “verbosity” i was thinking macro to clean things up.
I’ll check that.

Very much appreciated!
Have a nice day.

Edit: Here’s the result of using Click

Click.hx (externs)

package click;

import haxe.Constraints.Function;
import python.VarArgs;
import python.KwArgs;

@:pythonImport("click")
extern class Click {
  static function command():Function;
  static function option(v:Dynamic, ?kvargs:KwArgs<Dynamic>):Function;
}

Usage:

import click.*;
import haxe.Constraints.Function;

class Click_HelloWorld {
	public static function main():Void {
		Click.command()(Click.option("--name", {"default": "Geoff", "help": "Name of the greeting."})(hello))();
	}

	public static function hello(name:String):Void {
		trace("Hello " + name);
	}
}

I’ll edit with a less verbose/clearer option using macros. It will be a good exercise for me.

Again, thx for the help. Regards;

Edit: Here’s a way to make it less verbose (well in my opinion at least) using macros.

package macros;

import haxe.macro.Expr;

class Click {
  macro public static function command(f:Expr, v:Dynamic, ?opt:Dynamic):Expr {
    return macro Click.command()(Click.option(${v}, ${opt})(${f}))();
  }
}

Usage:

import click.*;
import macros.Click as C;

class Click_HelloWorld {
	public static function main():Void {
		C.command(hello, "--name", {"prompt": true, "help": "Specify the name for the greeting."});
	}

	public static function hello(name:String):Void {
		trace("Hello " + name);
	}
}

I’m going to try what’s happening when you need to chain different options. I’ll keep posting here in case it can be of any help for someone with the same questions as me.

Regards;


(azrafe7) #4

Glad to have been of help!
And yes, please post your findings if you look more into it. :smiley: