Skip to content

Commit f4ded48

Browse files
committed
[fix] generate single-threaded-wrappers auto-manually
1 parent f55c026 commit f4ded48

File tree

4 files changed

+512
-129
lines changed

4 files changed

+512
-129
lines changed

py4cl2-cffi.asd

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ as asdf was unable to find \"py4cl2-cffi-tests\".")))))
7070
:serial t
7171
:components ((:file "package")
7272
(:file "main-thread")
73+
(:file "wrapper-core")
7374
(:file "single-threaded-wrappers")
7475
(:file "pystart")
7576
(:file "import-export")))

single-threaded/gen.lisp

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
;;; sbcl --load ~/quicklisp/setup.lisp --script gen.lisp
2+
3+
;;; This file is expected to be run as a script. Some systems like ECL do not
4+
;;; provide lambda lists functions and macros in all cases. Especially for them,
5+
;;; we generate all the functions and macros beforehand.
6+
7+
(ql:quickload '(:py4cl2-cffi))
8+
9+
(load (asdf:component-pathname
10+
(asdf:find-component "py4cl2-cffi/single-threaded" "package")))
11+
12+
(defpackage :py4cl2-cffi/single-threaded-generator
13+
(:use :cl :alexandria))
14+
15+
(in-package :py4cl2-cffi/single-threaded-generator)
16+
17+
(defparameter *single-threaded-wrappers-file*
18+
(asdf:component-pathname
19+
(asdf:find-component "py4cl2-cffi/single-threaded" "single-threaded-wrappers")))
20+
21+
(defparameter *single-threaded-blacklist*
22+
'(pystart mkfifo raw-py define-lispifier with-lispifiers with-pythonizers
23+
defpyfun defpymodule)
24+
"List of function and macro names which will not be translated to their single-threaded.")
25+
26+
(defun call-form-from-fn-and-ll (fn lambda-list)
27+
(multiple-value-bind
28+
(required optional rest keywords)
29+
(parse-ordinary-lambda-list lambda-list)
30+
(cond (rest
31+
`(apply/single-threaded
32+
,fn
33+
,@required
34+
,@(mapcar #'first optional)
35+
,rest))
36+
(keywords
37+
`(funcall/single-threaded
38+
,fn
39+
,@required
40+
,@(mapcar #'first optional)
41+
,@(loop :for ((key name) init suppliedp) :in keywords
42+
:nconcing (list (make-keyword key) name))))
43+
(t
44+
`(funcall/single-threaded
45+
,fn
46+
,@required
47+
,@(mapcar #'first optional))))))
48+
49+
(defun sanitize-lambda-list (lambda-list)
50+
(let ((sanitized nil)
51+
(state :required))
52+
(dolist (item lambda-list)
53+
(cond ((member item lambda-list-keywords)
54+
(setq state item)
55+
(push item sanitized))
56+
(t
57+
(push
58+
(ecase state
59+
(:required item)
60+
(&optional item)
61+
(&key
62+
(if (first item)
63+
item
64+
(if (keywordp item)
65+
(intern (string item)
66+
:py4cl2-cffi/single-threaded)
67+
item)))
68+
(&rest item))
69+
sanitized))))
70+
(nreverse sanitized)))
71+
72+
(defun expand-single-threaded-sym (single-threaded-sym)
73+
"Given a SYMBOL, returns an appropriate function or macro for single-threaded calling."
74+
(declare (optimize debug))
75+
(let* ((multi-threaded-sym
76+
(find-symbol (symbol-name single-threaded-sym)
77+
:py4cl2-cffi))
78+
(args (gentemp "ARGS"))
79+
(macrop (macro-function multi-threaded-sym)))
80+
(when (and (not (member single-threaded-sym
81+
*single-threaded-blacklist*))
82+
(fboundp multi-threaded-sym)
83+
(not (eq single-threaded-sym multi-threaded-sym)))
84+
;; FIXME: Handle keyword args
85+
`(,(let ((lambda-list
86+
(sanitize-lambda-list
87+
(swank/backend:arglist multi-threaded-sym))))
88+
`(,(if macrop 'defmacro 'defun)
89+
,single-threaded-sym ,lambda-list
90+
,(call-form-from-fn-and-ll
91+
`(lambda (&rest ,args)
92+
(float-features:with-float-traps-masked t
93+
,(if macrop
94+
`(funcall (macro-function ',multi-threaded-sym)
95+
(list* ',multi-threaded-sym ,args) nil)
96+
`(apply #',multi-threaded-sym ,args))))
97+
lambda-list)))
98+
,(when (fboundp `(setf ,multi-threaded-sym))
99+
(let ((lambda-list
100+
(sanitize-lambda-list
101+
(swank/backend:arglist `(setf ,multi-threaded-sym)))))
102+
`(,(if macrop 'defmacro 'defun)
103+
(setf ,single-threaded-sym) ,lambda-list
104+
,(call-form-from-fn-and-ll
105+
`(lambda (&rest ,args)
106+
(float-features:with-float-traps-masked t
107+
,(if macrop
108+
`(apply (macro-function '(setf ,multi-threaded-sym))
109+
,args)
110+
`(apply #',multi-threaded-sym ,args))
111+
(apply #'(setf ,multi-threaded-sym) ,args)))
112+
lambda-list))))))))
113+
114+
(defun generate ()
115+
(let ((all nil))
116+
(do-external-symbols (s :py4cl2-cffi/single-threaded)
117+
(nconcf all (expand-single-threaded-sym s)))
118+
;; Additional whitelisted names.
119+
(dolist (s `(py-module-pointer
120+
load-python-and-libraries
121+
mkfifo
122+
pycall*
123+
pyeval-save-thread
124+
pygil-held-p
125+
pyvalue*))
126+
(nconcf all (expand-single-threaded-sym s)))
127+
(with-open-file (*standard-output* *single-threaded-wrappers-file*
128+
:direction :output
129+
:if-exists :supersede
130+
:if-does-not-exist :create)
131+
(write-line ";;; This file was automatically generated by gen.lisp in the same directory.")
132+
(write-line ";;; Please do not edit this file manually.")
133+
(terpri)
134+
(let ((*print-case* :downcase))
135+
(write `(in-package :py4cl2-cffi/single-threaded))
136+
(dolist (form all)
137+
(terpri)
138+
(terpri)
139+
(write form))))))
140+
141+
(generate)

0 commit comments

Comments
 (0)