Skip to content

Commit 7f55cd3

Browse files
drew-512Drew O'Mearasbinet
authored
examples/{embedding,multi-context}: first import
Co-authored-by: Drew O'Meara <[email protected]> Co-authored-by: Sebastien Binet <[email protected]>
1 parent c9f2d8b commit 7f55cd3

File tree

11 files changed

+821
-0
lines changed

11 files changed

+821
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ cover.out
1111

1212
# tests
1313
builtin/testfile
14+
examples/embedding/embedding

examples/embedding/README.md

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
## Embedding gpython
2+
3+
This is an example demonstrating how to embed gpython into a Go application.
4+
5+
6+
### Why embed gpython?
7+
8+
Embedding a highly capable and familiar "interpreted" language allows your users
9+
to easily augment app behavior, configuration, and customization -- all post-deployment.
10+
11+
Have you ever discovered an exciting software project but lost interest when you had to also
12+
learn its esoteric language schema? In an era of limited attention span,
13+
most people are generally turned off if they have to learn a new language in addition to learning
14+
to use your app.
15+
16+
If you consider [why use Python](https://www.stxnext.com/what-is-python-used-for/), then perhaps also
17+
consider that your users will be interested to hear that your software offers
18+
even more value that it can be driven from a scripting language they already know.
19+
20+
Python is widespread in finance, sciences, hobbyist programming and is often
21+
endearingly regarded as most popular programming language for non-developers.
22+
If your application can be driven by embedded Python, then chances are others will
23+
feel excited and empowered that your project can be used out of the box
24+
and feel like familiar territory.
25+
26+
### But what about the lack of python modules?
27+
28+
There are only be a small number of native modules available, but don't forget you have the entire
29+
Go standard library and *any* Go package you can name at your fingertips to expose!
30+
This plus multi-context capability gives gpython enormous potential on how it can
31+
serve you.
32+
33+
So basically, gpython is only off the table if you need to run python that makes heavy use of
34+
modules that are only available in CPython.
35+
36+
### Packing List
37+
38+
| | |
39+
|---------------------- | ------------------------------------------------------------------|
40+
| `main.go` | if no args, runs in REPL mode, otherwise runs the given file |
41+
| `lib/mylib.py` | models a library that your application would expose for users |
42+
| `lib/REPL-startup.py` | invoked by `main.go` when starting REPL mode |
43+
| `mylib-demo.py` | models a user-authored script that consumes `mylib` |
44+
| `mylib.module.go` | Go implementation of `mylib_go` consumed by `mylib` |
45+
46+
47+
### Invoking a Python Script
48+
49+
```bash
50+
$ cd examples/embedding/
51+
$ go build .
52+
$ ./embedding mylib-demo.py
53+
```
54+
```
55+
Welcome to a gpython embedded example,
56+
where your wildest Go-based python dreams come true!
57+
58+
==========================================================
59+
Python 3.4 (github.com/go-python/gpython)
60+
go1.17.6 on darwin amd64
61+
==========================================================
62+
63+
Spring Break itinerary:
64+
Stop 1: Miami, Florida | 7 nights
65+
Stop 2: Mallorca, Spain | 3 nights
66+
Stop 3: Ibiza, Spain | 14 nights
67+
Stop 4: Monaco | 12 nights
68+
### Made with Vacaton 1.0 by Fletch F. Fletcher
69+
70+
I bet Monaco will be the best!
71+
```
72+
73+
### REPL Mode
74+
75+
```bash
76+
$ ./embedding
77+
```
78+
```
79+
======= Entering REPL mode, press Ctrl+D to exit =======
80+
81+
==========================================================
82+
Python 3.4 (github.com/go-python/gpython)
83+
go1.17.6 on darwin amd64
84+
==========================================================
85+
86+
>>> v = Vacation("Spring Break", Stop("Florida", 3), Stop("Nice", 7))
87+
>>> print(str(v))
88+
Spring Break, 2 stop(s)
89+
>>> v.PrintItinerary()
90+
Spring Break itinerary:
91+
Stop 1: Florida | 3 nights
92+
Stop 2: Nice | 7 nights
93+
### Made with Vacaton 1.0 by Fletch F. Fletcher
94+
```
95+
96+
## Takeways
97+
98+
- `main.go` demonstrates high-level convenience functions such as `py.RunFile()`.
99+
- Embedding a Go `struct` as a Python object only requires that it implement `py.Object`, which is a single function:
100+
`Type() *py.Type`
101+
- See [py/run.go](https://github.com/go-python/gpython/tree/master/py/run.go) for more about interpreter instances and `py.Context`
102+
- Helper functions are available in [py/util.go](https://github.com/go-python/gpython/tree/master/py/util.go) and your contributions are welcome!
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
3+
# This file is called from main.go when in REPL mode
4+
5+
# This is here to demonstrate making life easier for your users in REPL mode
6+
# by doing pre-setup here so they don't have to import every time they start.
7+
from mylib import *
8+

examples/embedding/lib/mylib.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import mylib_go as _go
2+
3+
PY_VERSION = _go.PY_VERSION
4+
5+
6+
print('''
7+
==========================================================
8+
%s
9+
==========================================================
10+
''' % (PY_VERSION, ))
11+
12+
13+
def Stop(location, num_nights = 2):
14+
return _go.VacationStop_new(location, num_nights)
15+
16+
17+
class Vacation:
18+
19+
def __init__(self, tripName, *stops):
20+
self._v, self._libVers = _go.Vacation_new()
21+
self.tripName = tripName
22+
self.AddStops(*stops)
23+
24+
def __str__(self):
25+
return "%s, %d stop(s)" % (self.tripName, self.NumStops())
26+
27+
def NumStops(self):
28+
return self._v.num_stops()
29+
30+
def GetStop(self, stop_num):
31+
return self._v.get_stop(stop_num)
32+
33+
def AddStops(self, *stops):
34+
self._v.add_stops(stops)
35+
36+
def PrintItinerary(self):
37+
print(self.tripName, "itinerary:")
38+
i = 1
39+
while 1:
40+
41+
try:
42+
stop = self.GetStop(i)
43+
except IndexError:
44+
break
45+
46+
print(" Stop %d: %s" % (i, str(stop)))
47+
i += 1
48+
49+
print("### Made with %s " % self._libVers)
50+
51+

examples/embedding/main.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2022 The go-python Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"flag"
9+
"fmt"
10+
11+
// This initializes gpython for runtime execution and is essential.
12+
// It defines forward-declared symbols and registers native built-in modules, such as sys and time.
13+
_ "github.com/go-python/gpython/modules"
14+
15+
// Commonly consumed gpython
16+
"github.com/go-python/gpython/py"
17+
"github.com/go-python/gpython/repl"
18+
"github.com/go-python/gpython/repl/cli"
19+
)
20+
21+
func main() {
22+
flag.Parse()
23+
runWithFile(flag.Arg(0))
24+
}
25+
26+
func runWithFile(pyFile string) error {
27+
28+
// See type Context interface and related docs
29+
ctx := py.NewContext(py.DefaultContextOpts())
30+
31+
var err error
32+
if len(pyFile) == 0 {
33+
replCtx := repl.New(ctx)
34+
35+
fmt.Print("\n======= Entering REPL mode, press Ctrl+D to exit =======\n")
36+
37+
_, err = py.RunFile(ctx, "lib/REPL-startup.py", py.CompileOpts{}, replCtx.Module)
38+
if err == nil {
39+
cli.RunREPL(replCtx)
40+
}
41+
42+
} else {
43+
_, err = py.RunFile(ctx, pyFile, py.CompileOpts{}, nil)
44+
}
45+
46+
if err != nil {
47+
py.TracebackDump(err)
48+
}
49+
50+
return err
51+
}

examples/embedding/main_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"testing"
10+
)
11+
12+
const embeddingTestOutput = "testdata/embedding_out_golden.txt"
13+
14+
var regen = flag.Bool("regen", false, "regenerate golden files")
15+
16+
func TestEmbeddedExample(t *testing.T) {
17+
18+
tmp, err := os.MkdirTemp("", "go-python-embedding-")
19+
if err != nil {
20+
t.Fatal(err)
21+
}
22+
defer os.RemoveAll(tmp)
23+
24+
exe := filepath.Join(tmp, "out.exe")
25+
cmd := exec.Command("go", "build", "-o", exe, ".")
26+
err = cmd.Run()
27+
if err != nil {
28+
t.Fatalf("failed to compile embedding example: %v", err)
29+
}
30+
31+
out := new(bytes.Buffer)
32+
cmd = exec.Command(exe, "mylib-demo.py")
33+
cmd.Stdout = out
34+
35+
err = cmd.Run()
36+
if err != nil {
37+
t.Fatalf("failed to run embedding binary: %v", err)
38+
}
39+
40+
testOutput := out.Bytes()
41+
42+
flag.Parse()
43+
if *regen {
44+
err = os.WriteFile(embeddingTestOutput, testOutput, 0644)
45+
if err != nil {
46+
t.Fatalf("failed to write test output: %v", err)
47+
}
48+
}
49+
50+
mustMatch, err := os.ReadFile(embeddingTestOutput)
51+
if err != nil {
52+
t.Fatalf("failed read %q", embeddingTestOutput)
53+
}
54+
if !bytes.Equal(testOutput, mustMatch) {
55+
t.Fatalf("embedded test output did not match accepted output from %q", embeddingTestOutput)
56+
}
57+
}

examples/embedding/mylib-demo.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
print('''
2+
Welcome to a gpython embedded example,
3+
where your wildest Go-based python dreams come true!''')
4+
5+
# This is a model for a public/user-side script that you or users would maintain,
6+
# offering an open canvas to drive app behavior, customization, or anything you can dream up.
7+
#
8+
# Modules you offer for consumption can also serve to document such things.
9+
from mylib import *
10+
11+
springBreak = Vacation("Spring Break", Stop("Miami, Florida", 7), Stop("Mallorca, Spain", 3))
12+
springBreak.AddStops(Stop("Ibiza, Spain", 14), Stop("Monaco", 12))
13+
springBreak.PrintItinerary()
14+
15+
print("\nI bet %s will be the best!\n" % springBreak.GetStop(4).Get()[0])

0 commit comments

Comments
 (0)