forked from DataDog/go-python3
-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathmain.go
More file actions
115 lines (91 loc) · 3.29 KB
/
main.go
File metadata and controls
115 lines (91 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package main
import (
"errors"
"log"
"os"
"sync"
python3 "github.com/go-python/cpy3"
)
func main() {
var err error
// At the end of all, if there was an error
// prints the error and exit with the non-zero code
defer func() {
if err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
}()
// Undo all initializations made by Py_Initialize() and subsequent
defer python3.Py_Finalize()
// Prints any python error if it was here
// no needs to call it after each single check of PyErr_Occurred()
defer python3.PyErr_Print()
// Initialize the Python interpreter and
// since version 3.7 it also create the GIL explicitly by calling PyEval_InitThreads()
// so you don’t have to call PyEval_InitThreads() yourself anymore
python3.Py_Initialize() // create the GIL, the GIL is locked by the main thread
if !python3.Py_IsInitialized() {
err = errors.New("error initializing the python interpreter")
return
}
var wg sync.WaitGroup
wg.Add(2)
fooModule := python3.PyImport_ImportModule("foo") // new reference, a call DecRef() is needed
if fooModule == nil && python3.PyErr_Occurred() != nil {
err = errors.New("error importing the python module")
return
}
defer fooModule.DecRef()
odds := fooModule.GetAttrString("print_odds") // new reference, a call DecRef() is needed
if odds == nil && python3.PyErr_Occurred() != nil {
err = errors.New("error getting the attribute print_odds")
return
}
defer odds.DecRef()
even := fooModule.GetAttrString("print_even") // new reference, a call DecRef() is needed
if even == nil && python3.PyErr_Occurred() != nil {
err = errors.New("error getting the attribute print_even")
return
}
defer even.DecRef()
limit := python3.PyLong_FromGoInt(50) // new reference, will stolen later, a call DecRef() is NOT needed
if limit == nil && python3.PyErr_Occurred() != nil {
err = errors.New("error creating python long object")
return
}
args := python3.PyTuple_New(1) // new reference, a call DecRef() is needed
if args == nil && python3.PyErr_Occurred() != nil {
err = errors.New("error creating python tuple object")
return
}
defer args.DecRef()
ret := python3.PyTuple_SetItem(args, 0, limit) // steals reference to limit
// Cleans the Go variable, because now a new owner is caring about related PyObject
// no action, such as a call DecRef(), is needed here
limit = nil
if ret != 0 {
err = errors.New("error setting a tuple item")
limit.DecRef()
limit = nil
return
}
// Save the current state and release the GIL
// so that goroutines can acquire it
state := python3.PyEval_SaveThread() // release the GIL, the GIL is unlocked for using by goroutines
go func() {
_gstate := python3.PyGILState_Ensure() // acquire the GIL, the GIL is locked by the 1st goroutine
odds.Call(args, python3.PyDict_New())
python3.PyGILState_Release(_gstate) // release the GIL, the GIL is unlocked for using by others
wg.Done()
}()
go func() {
_gstate := python3.PyGILState_Ensure() // acquire the GIL, the GIL is locked by the 2nd goroutine
even.Call(args, python3.PyDict_New())
python3.PyGILState_Release(_gstate) // release the GIL, the GIL is unlocked for using by others
wg.Done()
}()
wg.Wait()
// Restore the state and lock the GIL
python3.PyEval_RestoreThread(state) // acquire the GIL, the GIL is locked by the main thread
}