pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/go-python/gpython/pull/159.diff

ngTestOutput) + } +} diff --git a/examples/embedding/mylib-demo.py b/examples/embedding/mylib-demo.py new file mode 100644 index 00000000..4a461023 --- /dev/null +++ b/examples/embedding/mylib-demo.py @@ -0,0 +1,15 @@ +print(''' +Welcome to a gpython embedded example, + where your wildest Go-based python dreams come true!''') + +# This is a model for a public/user-side script that you or users would maintain, +# offering an open canvas to drive app behavior, customization, or anything you can dream up. +# +# Modules you offer for consumption can also serve to document such things. +from mylib import * + +springBreak = Vacation("Spring Break", Stop("Miami, Florida", 7), Stop("Mallorca, Spain", 3)) +springBreak.AddStops(Stop("Ibiza, Spain", 14), Stop("Monaco", 12)) +springBreak.PrintItinerary() + +print("\nI bet %s will be the best!\n" % springBreak.GetStop(4).Get()[0]) diff --git a/examples/embedding/mylib.module.go b/examples/embedding/mylib.module.go new file mode 100644 index 00000000..8a848411 --- /dev/null +++ b/examples/embedding/mylib.module.go @@ -0,0 +1,175 @@ +// Copyright 2022 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "runtime" + + "github.com/go-python/gpython/py" +) + +// These gpython py.Object type delcarations are the bridge between gpython and embedded Go types. +var ( + PyVacationStopType = py.NewType("Stop", "") + PyVacationType = py.NewType("Vacation", "") +) + +// init is where you register your embedded module and attach methods to your embedded class types. +func init() { + + // For each of your embedded python types, attach instance methods. + // When an instance method is invoked, the "self" py.Object is the instance. + PyVacationStopType.Dict["Set"] = py.MustNewMethod("Set", VacationStop_Set, 0, "") + PyVacationStopType.Dict["Get"] = py.MustNewMethod("Get", VacationStop_Get, 0, "") + PyVacationType.Dict["add_stops"] = py.MustNewMethod("Vacation.add_stops", Vacation_add_stops, 0, "") + PyVacationType.Dict["num_stops"] = py.MustNewMethod("Vacation.num_stops", Vacation_num_stops, 0, "") + PyVacationType.Dict["get_stop"] = py.MustNewMethod("Vacation.get_stop", Vacation_get_stop, 0, "") + + // Bind methods attached at the module (global) level. + // When these are invoked, the first py.Object param (typically "self") is the bound *Module instance. + methods := []*py.Method{ + py.MustNewMethod("VacationStop_new", VacationStop_new, 0, ""), + py.MustNewMethod("Vacation_new", Vacation_new, 0, ""), + } + + // Register a ModuleImpl instance used by the gpython runtime to instantiate new py.Module when first imported. + py.RegisterModule(&py.ModuleImpl{ + Info: py.ModuleInfo{ + Name: "mylib_go", + Doc: "Example embedded python module", + }, + Methods: methods, + Globals: py.StringDict{ + "PY_VERSION": py.String("Python 3.4 (github.com/go-python/gpython)"), + "GO_VERSION": py.String(fmt.Sprintf("%s on %s %s", runtime.Version(), runtime.GOOS, runtime.GOARCH)), + "MYLIB_VERS": py.String("Vacation 1.0 by Fletch F. Fletcher"), + }, + }) +} + +// VacationStop is an example Go struct to embed. +type VacationStop struct { + Desc py.String + NumNights py.Int +} + +// Type comprises the py.Object interface, allowing a Go struct to be cast as a py.Object. +// Instance methods of an type are then attached to this type object +func (stop *VacationStop) Type() *py.Type { + return PyVacationStopType +} + +func (stop *VacationStop) M__str__() (py.Object, error) { + line := fmt.Sprintf(" %-16v | %2v nights", stop.Desc, stop.NumNights) + return py.String(line), nil +} + +func (stop *VacationStop) M__repr__() (py.Object, error) { + return stop.M__str__() +} + +func VacationStop_new(module py.Object, args py.Tuple) (py.Object, error) { + stop := &VacationStop{} + VacationStop_Set(stop, args) + return stop, nil +} + +// VacationStop_Set is an embedded instance method of VacationStop +func VacationStop_Set(self py.Object, args py.Tuple) (py.Object, error) { + stop := self.(*VacationStop) + + // Check out other convenience functions in py/util.go + // Also available is py.ParseTuple(args, "si", ...) + err := py.LoadTuple(args, []interface{}{&stop.Desc, &stop.NumNights}) + if err != nil { + return nil, err + } + + /* Alternative util func is ParseTuple(): + var desc, nights py.Object + err := py.ParseTuple(args, "si", &desc, &nights) + if err != nil { + return nil, err + } + stop.Desc = desc.(py.String) + stop.NumNights = desc.(py.Int) + */ + + return py.None, nil +} + +// VacationStop_Get is an embedded instance method of VacationStop +func VacationStop_Get(self py.Object, args py.Tuple) (py.Object, error) { + stop := self.(*VacationStop) + + return py.Tuple{ + stop.Desc, + stop.NumNights, + }, nil +} + +type Vacation struct { + Stops []*VacationStop + MadeBy string +} + +func (v *Vacation) Type() *py.Type { + return PyVacationType +} + +func Vacation_new(module py.Object, args py.Tuple) (py.Object, error) { + v := &Vacation{} + + // For Module-bound methods, we have easy access to the parent Module + py.LoadAttr(module, "MYLIB_VERS", &v.MadeBy) + + ret := py.Tuple{ + v, + py.String(v.MadeBy), + } + return ret, nil +} + +func Vacation_num_stops(self py.Object, args py.Tuple) (py.Object, error) { + v := self.(*Vacation) + return py.Int(len(v.Stops)), nil +} + +func Vacation_get_stop(self py.Object, args py.Tuple) (py.Object, error) { + v := self.(*Vacation) + + // Check out other convenience functions in py/util.go + // If you would like to be a contributor for gpython, improving these or adding more is a great place to start! + stopNum, err := py.GetInt(args[0]) + if err != nil { + return nil, err + } + + if stopNum < 1 || int(stopNum) > len(v.Stops) { + return nil, py.ExceptionNewf(py.IndexError, "invalid stop index") + } + + return py.Object(v.Stops[stopNum-1]), nil +} + +func Vacation_add_stops(self py.Object, args py.Tuple) (py.Object, error) { + v := self.(*Vacation) + srcStops, ok := args[0].(py.Tuple) + if !ok { + return nil, py.ExceptionNewf(py.TypeError, "expected Tuple, got %T", args[0]) + } + + for _, arg := range srcStops { + stop, ok := arg.(*VacationStop) + if !ok { + return nil, py.ExceptionNewf(py.TypeError, "expected Stop, got %T", arg) + } + + v.Stops = append(v.Stops, stop) + } + + return py.None, nil +} diff --git a/examples/embedding/testdata/embedding_out_golden.txt b/examples/embedding/testdata/embedding_out_golden.txt new file mode 100644 index 00000000..c7443e07 --- /dev/null +++ b/examples/embedding/testdata/embedding_out_golden.txt @@ -0,0 +1,17 @@ + +Welcome to a gpython embedded example, + where your wildest Go-based python dreams come true! + +========================================================== + Python 3.4 (github.com/go-python/gpython) +========================================================== + +Spring Break itinerary: + Stop 1: Miami, Florida | 7 nights + Stop 2: Mallorca, Spain | 3 nights + Stop 3: Ibiza, Spain | 14 nights + Stop 4: Monaco | 12 nights +### Made with Vacation 1.0 by Fletch F. Fletcher + +I bet Monaco will be the best! + diff --git a/examples/multi-context/main.go b/examples/multi-context/main.go new file mode 100644 index 00000000..e9f72212 --- /dev/null +++ b/examples/multi-context/main.go @@ -0,0 +1,138 @@ +// Copyright 2022 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "log" + "runtime" + "strings" + "sync" + "time" + + // This initializes gpython for runtime execution and is critical. + // It defines forward-declared symbols and registers native built-in modules, such as sys and time. + _ "github.com/go-python/gpython/modules" + + // This is the primary import for gpython. + // It contains all symbols needed to fully compile and run python. + "github.com/go-python/gpython/py" +) + +func main() { + + // The total job count implies a fixed amount of work. + // The number of workers is how many py.Context (in concurrent goroutines) to pull jobs off the queue. + // One worker does all the work serially while N number of workers will (ideally) divides up. + totalJobs := 20 + + for i := 0; i < 10; i++ { + numWorkers := i + 1 + elapsed := RunMultiPi(numWorkers, totalJobs) + fmt.Printf("=====> %2d worker(s): %v\n\n", numWorkers, elapsed) + + // Give each trial a fresh start + runtime.GC() + } + +} + +var jobScript = ` +pi = chud.pi_chudnovsky_bs(numDigits) +last_5 = pi % 100000 +print("%s: last 5 digits of %d is %d (job #%0d)" % (WORKER_ID, numDigits, last_5, jobID)) +` + +var jobSrcTemplate = ` +import pi_chudnovsky_bs as chud + +WORKER_ID = "{{WORKER_ID}}" + +print("%s ready!" % (WORKER_ID)) +` + +type worker struct { + name string + ctx py.Context + main *py.Module + job *py.Code +} + +func (w *worker) compileTemplate(pySrc string) { + pySrc = strings.Replace(pySrc, "{{WORKER_ID}}", w.name, -1) + + mainImpl := py.ModuleImpl{ + CodeSrc: pySrc, + } + + var err error + w.main, err = w.ctx.ModuleInit(&mainImpl) + if err != nil { + log.Fatal(err) + } +} + +func RunMultiPi(numWorkers, numTimes int) time.Duration { + var workersRunning sync.WaitGroup + + fmt.Printf("Starting %d worker(s) to calculate %d jobs...\n", numWorkers, numTimes) + + jobPipe := make(chan int) + go func() { + for i := 0; i < numTimes; i++ { + jobPipe <- i + 1 + } + close(jobPipe) + }() + + // Note that py.Code can be shared (accessed concurrently) since it is an inherently read-only object + jobCode, err := py.Compile(jobScript, "", py.ExecMode, 0, true) + if err != nil { + log.Fatal("jobScript failed to comple") + } + + workers := make([]worker, numWorkers) + for i := 0; i < numWorkers; i++ { + + opts := py.DefaultContextOpts() + + // Make sure our import statement will find pi_chudnovsky_bs + opts.SysPaths = append(opts.SysPaths, "..") + + workers[i] = worker{ + name: fmt.Sprintf("Worker #%d", i+1), + ctx: py.NewContext(opts), + job: jobCode, + } + + workersRunning.Add(1) + } + + startTime := time.Now() + + for i := range workers { + w := workers[i] + go func() { + + // Compiling can be concurrent since there is no associated py.Context + w.compileTemplate(jobSrcTemplate) + + for jobID := range jobPipe { + numDigits := 100000 + if jobID%2 == 0 { + numDigits *= 10 + } + py.SetAttrString(w.main.Globals, "numDigits", py.Int(numDigits)) + py.SetAttrString(w.main.Globals, "jobID", py.Int(jobID)) + w.ctx.RunCode(jobCode, w.main.Globals, w.main.Globals, nil) + } + workersRunning.Done() + }() + } + + workersRunning.Wait() + + return time.Since(startTime) +} diff --git a/py/util.go b/py/util.go new file mode 100644 index 00000000..43028bce --- /dev/null +++ b/py/util.go @@ -0,0 +1,206 @@ +// Copyright 2022 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package py + +import ( + "errors" + "strconv" +) + +var ( + ErrUnsupportedObjType = errors.New("unsupported obj type") +) + +// GetLen is a high-level convenience function that returns the length of the given Object. +func GetLen(obj Object) (Int, error) { + getlen, ok := obj.(I__len__) + if !ok { + return 0, nil + } + + lenObj, err := getlen.M__len__() + if err != nil { + return 0, err + } + + return GetInt(lenObj) +} + +// GetInt is a high-level convenience function that converts the given value to an int. +func GetInt(obj Object) (Int, error) { + toIdx, ok := obj.(I__index__) + if !ok { + _, err := cantConvert(obj, "int") + return 0, err + } + + return toIdx.M__index__() +} + +// LoadTuple attempts to convert each element of the given list and store into each destination value (based on its type). +func LoadTuple(args Tuple, vars []interface{}) error { + + if len(args) > len(vars) { + return ExceptionNewf(RuntimeError, "%d args given, expected %d", len(args), len(vars)) + } + + if len(vars) > len(args) { + vars = vars[:len(args)] + } + + for i, rval := range vars { + err := loadValue(args[i], rval) + if err == ErrUnsupportedObjType { + return ExceptionNewf(TypeError, "arg %d has unsupported object type: %s", i, args[i].Type().Name) + } + } + + return nil +} + +// LoadAttr gets the named attribute and attempts to store it into the given destination value (based on its type). +func LoadAttr(obj Object, attrName string, dst interface{}) error { + attr, err := GetAttrString(obj, attrName) + if err != nil { + return err + } + err = loadValue(attr, dst) + if err == ErrUnsupportedObjType { + return ExceptionNewf(TypeError, "attribute \"%s\" has unsupported object type: %s", attrName, attr.Type().Name) + } + return nil +} + +// LoadIntsFromList extracts a list of ints contained given a py.List or py.Tuple +func LoadIntsFromList(list Object) ([]int64, error) { + N, err := GetLen(list) + if err != nil { + return nil, err + } + + getter, ok := list.(I__getitem__) + if !ok { + return nil, nil + } + + if N <= 0 { + return nil, nil + } + + intList := make([]int64, N) + for i := Int(0); i < N; i++ { + item, err := getter.M__getitem__(i) + if err != nil { + return nil, err + } + + var intVal Int + intVal, err = GetInt(item) + if err != nil { + return nil, err + } + + intList[i] = int64(intVal) + } + + return intList, nil +} + +func loadValue(src Object, data interface{}) error { + var ( + v_str string + v_float float64 + v_int int64 + ) + + haveStr := false + + switch v := src.(type) { + case Bool: + if v { + v_int = 1 + v_float = 1 + v_str = "True" + } else { + v_str = "False" + } + haveStr = true + case Int: + v_int = int64(v) + v_float = float64(v) + case Float: + v_int = int64(v) + v_float = float64(v) + case String: + v_str = string(v) + haveStr = true + case NoneType: + // No-op + default: + return ErrUnsupportedObjType + } + + switch dst := data.(type) { + case *Int: + *dst = Int(v_int) + case *bool: + *dst = v_int != 0 + case *int8: + *dst = int8(v_int) + case *uint8: + *dst = uint8(v_int) + case *int16: + *dst = int16(v_int) + case *uint16: + *dst = uint16(v_int) + case *int32: + *dst = int32(v_int) + case *uint32: + *dst = uint32(v_int) + case *int: + *dst = int(v_int) + case *uint: + *dst = uint(v_int) + case *int64: + *dst = v_int + case *uint64: + *dst = uint64(v_int) + case *float32: + if haveStr { + v_float, _ = strconv.ParseFloat(v_str, 32) + } + *dst = float32(v_float) + case *float64: + if haveStr { + v_float, _ = strconv.ParseFloat(v_str, 64) + } + *dst = v_float + case *Float: + if haveStr { + v_float, _ = strconv.ParseFloat(v_str, 64) + } + *dst = Float(v_float) + case *string: + *dst = v_str + case *String: + *dst = String(v_str) + // case []uint64: + // for i := range data { + // dst[i] = order.Uint64(bs[8*i:]) + // } + // case []float32: + // for i := range data { + // dst[i] = math.Float32frombits(order.Uint32(bs[4*i:])) + // } + // case []float64: + // for i := range data { + // dst[i] = math.Float64frombits(order.Uint64(bs[8*i:])) + // } + + default: + return ExceptionNewf(NotImplementedError, "%s", "unsupported Go data type") + } + return nil +} pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy