@@ -23,7 +23,7 @@ Arguments
23
23
Return value: the created class (`::PyTypeObject`)
24
24
"""
25
25
function def_py_class (type_name:: AbstractString , methods:: Vector ;
26
- base_classes= [], getsets:: Vector = [])
26
+ base_classes= [], getsets:: Vector = [], class_vars = [] )
27
27
# Only create new-style classes
28
28
base_classes = union (base_classes, [pybuiltin (" object" )])
29
29
methods = Dict (py_name => jlfun2pyfun (jl_fun:: Function )
@@ -32,7 +32,7 @@ function def_py_class(type_name::AbstractString, methods::Vector;
32
32
jlfun2pyfun (setter))
33
33
for (py_name:: Symbol , getter, setter) in getsets)
34
34
return pybuiltin (" type" )(type_name, tuple (base_classes... ),
35
- merge (methods, getter_setters))
35
+ merge (methods, getter_setters, Dict (class_vars) ))
36
36
end
37
37
38
38
# #####################################################################
@@ -58,6 +58,11 @@ function parse_pydef_toplevel(expr)
58
58
return class_name:: Symbol , base_classes, lines
59
59
end
60
60
61
+ # From MacroTools
62
+ function isfunction (expr)
63
+ @capture (MacroTools. longdef1 (expr), function (fcall_ | fcall_) body_ end )
64
+ end
65
+
61
66
function parse_pydef (expr)
62
67
class_name, base_classes, lines = parse_pydef_toplevel (expr)
63
68
# Now we parse every method definition / getter / setter
@@ -66,8 +71,12 @@ function parse_pydef(expr)
66
71
getter_dict = Dict {Any, Symbol} () # python_var => jl_getter_name
67
72
setter_dict = Dict {Any, Symbol} ()
68
73
method_syms = Dict {Any, Symbol} () # see below
74
+ class_vars = Dict {Symbol, Any} ()
69
75
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)
71
80
def_dict = splitdef (line)
72
81
py_f = def_dict[:name ]
73
82
# The dictionary of the new Julia-side definition.
@@ -101,11 +110,23 @@ function parse_pydef(expr)
101
110
error (" Malformed line: $line " )
102
111
end
103
112
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 " )
104
117
end
105
118
end
106
119
@assert (isempty (setdiff (keys (setter_dict), keys (getter_dict))),
107
120
" 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
+ )
109
130
end
110
131
111
132
"""
@@ -159,20 +180,30 @@ metaclass as a `PyObject` instead of binding it to the class name.
159
180
It's side-effect-free, except for the definition of the class methods.
160
181
"""
161
182
macro 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 =
163
190
parse_pydef (class_expr)
164
191
methods = [:($ (Expr (:quote , py_name:: Symbol )), $ (esc (jl_fun:: Symbol )))
165
192
for (py_name, jl_fun) in methods_]
166
193
getsets = [:($ (Expr (:quote , attribute)),
167
194
$ (esc (getter)),
168
195
$ (esc (get (setter_dict, attribute, nothing ))))
169
196
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
+ ]
170
201
:(begin
171
202
$ (map (esc, function_defs)... )
172
203
# This line doesn't have any side-effect, it just returns the Python
173
204
# (meta-)class, as a PyObject
174
205
def_py_class ($ (string (class_name)), [$ (methods... )];
175
206
base_classes= [$ (map (esc, base_classes)... )],
176
- getsets= [$ (getsets... )])
207
+ getsets= [$ (getsets... )], class_vars = [ $ (class_var_pairs ... )] )
177
208
end )
178
209
end
0 commit comments