@@ -23,7 +23,7 @@ Arguments
2323Return value: the created class (`::PyTypeObject`)
2424"""
2525function def_py_class (type_name:: AbstractString , methods:: Vector ;
26- base_classes= [], getsets:: Vector = [])
26+ base_classes= [], getsets:: Vector = [], class_vars = [] )
2727 # Only create new-style classes
2828 base_classes = union (base_classes, [pybuiltin (" object" )])
2929 methods = Dict (py_name => jlfun2pyfun (jl_fun:: Function )
@@ -32,7 +32,7 @@ function def_py_class(type_name::AbstractString, methods::Vector;
3232 jlfun2pyfun (setter))
3333 for (py_name:: Symbol , getter, setter) in getsets)
3434 return pybuiltin (" type" )(type_name, tuple (base_classes... ),
35- merge (methods, getter_setters))
35+ merge (methods, getter_setters, Dict (class_vars) ))
3636end
3737
3838# #####################################################################
@@ -58,6 +58,11 @@ function parse_pydef_toplevel(expr)
5858 return class_name:: Symbol , base_classes, lines
5959end
6060
61+ # From MacroTools
62+ function isfunction (expr)
63+ @capture (MacroTools. longdef1 (expr), function (fcall_ | fcall_) body_ end )
64+ end
65+
6166function parse_pydef (expr)
6267 class_name, base_classes, lines = parse_pydef_toplevel (expr)
6368 # Now we parse every method definition / getter / setter
@@ -66,8 +71,12 @@ function parse_pydef(expr)
6671 getter_dict = Dict {Any, Symbol} () # python_var => jl_getter_name
6772 setter_dict = Dict {Any, Symbol} ()
6873 method_syms = Dict {Any, Symbol} () # see below
74+ class_vars = Dict {Symbol, Any} ()
6975 for line in lines
70- if ! isa (line, LineNumberNode) && line. head != :line # need to skip those
76+ line isa LineNumberNode && continue
77+ line isa Expr || error (" Malformed line: $line " )
78+ line. head == :line && continue
79+ if isfunction (line)
7180 def_dict = splitdef (line)
7281 py_f = def_dict[:name ]
7382 # The dictionary of the new Julia-side definition.
@@ -101,11 +110,23 @@ function parse_pydef(expr)
101110 error (" Malformed line: $line " )
102111 end
103112 push! (function_defs, combinedef (jl_def_dict))
113+ elseif line. head == :(= ) # Non function assignment
114+ class_vars[line. args[1 ]] = line. args[2 ]
115+ else
116+ error (" Malformed line: $line " )
104117 end
105118 end
106119 @assert (isempty (setdiff (keys (setter_dict), keys (getter_dict))),
107120 " All .set attributes must have a .get" )
108- class_name, base_classes, methods, getter_dict, setter_dict, function_defs
121+ return (
122+ class_name,
123+ base_classes,
124+ methods,
125+ getter_dict,
126+ setter_dict,
127+ function_defs,
128+ class_vars
129+ )
109130end
110131
111132"""
@@ -159,20 +180,30 @@ metaclass as a `PyObject` instead of binding it to the class name.
159180It's side-effect-free, except for the definition of the class methods.
160181"""
161182macro pydef_object (class_expr)
162- class_name, base_classes, methods_, getter_dict, setter_dict, function_defs=
183+ class_name,
184+ base_classes,
185+ methods_,
186+ getter_dict,
187+ setter_dict,
188+ function_defs,
189+ class_vars =
163190 parse_pydef (class_expr)
164191 methods = [:($ (Expr (:quote , py_name:: Symbol )), $ (esc (jl_fun:: Symbol )))
165192 for (py_name, jl_fun) in methods_]
166193 getsets = [:($ (Expr (:quote , attribute)),
167194 $ (esc (getter)),
168195 $ (esc (get (setter_dict, attribute, nothing ))))
169196 for (attribute, getter) in getter_dict]
197+ class_var_pairs = [
198+ :($ (Expr (:quote , py_name)), $ (esc (val_expr)))
199+ for (py_name, val_expr) in class_vars
200+ ]
170201 :(begin
171202 $ (map (esc, function_defs)... )
172203 # This line doesn't have any side-effect, it just returns the Python
173204 # (meta-)class, as a PyObject
174205 def_py_class ($ (string (class_name)), [$ (methods... )];
175206 base_classes= [$ (map (esc, base_classes)... )],
176- getsets= [$ (getsets... )])
207+ getsets= [$ (getsets... )], class_vars = [ $ (class_var_pairs ... )] )
177208 end )
178209end
0 commit comments