X Tutup
// 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 stdlib provides the bootstrap code to wire in all the stdlib // (python) modules into a gpython context and VM. package stdlib import ( "bytes" "os" "path" "path/filepath" "strings" "sync" "github.com/go-python/gpython/py" "github.com/go-python/gpython/stdlib/marshal" "github.com/go-python/gpython/vm" _ "github.com/go-python/gpython/stdlib/array" _ "github.com/go-python/gpython/stdlib/binascii" _ "github.com/go-python/gpython/stdlib/builtin" _ "github.com/go-python/gpython/stdlib/glob" _ "github.com/go-python/gpython/stdlib/math" _ "github.com/go-python/gpython/stdlib/os" _ "github.com/go-python/gpython/stdlib/string" _ "github.com/go-python/gpython/stdlib/sys" _ "github.com/go-python/gpython/stdlib/tempfile" _ "github.com/go-python/gpython/stdlib/time" ) func init() { // Assign the base-level py.Context creation function while also preventing an import cycle. py.NewContext = NewContext } // context implements interface py.Context type context struct { store *py.ModuleStore opts py.ContextOpts closeOnce sync.Once closing bool closed bool running sync.WaitGroup done chan struct{} } // NewContext creates a new gpython interpreter instance context. // // See interface py.Context defined in py/run.go func NewContext(opts py.ContextOpts) py.Context { ctx := &context{ opts: opts, done: make(chan struct{}), closing: false, closed: false, } ctx.store = py.NewModuleStore() py.Import(ctx, "builtins", "sys") sys_mod := ctx.Store().MustGetModule("sys") sys_mod.Globals["argv"] = py.NewListFromStrings(opts.SysArgs) sys_mod.Globals["path"] = py.NewListFromStrings(opts.SysPaths) return ctx } // ModuleInit digests a ModuleImpl, compiling and marshalling as needed, creating a new Module instance in this Context. func (ctx *context) ModuleInit(impl *py.ModuleImpl) (*py.Module, error) { err := ctx.pushBusy() defer ctx.popBusy() if err != nil { return nil, err } if impl.Code == nil && len(impl.CodeSrc) > 0 { impl.Code, err = py.Compile(string(impl.CodeSrc), impl.Info.FileDesc, py.ExecMode, 0, true) if err != nil { return nil, err } } if impl.Code == nil && len(impl.CodeBuf) > 0 { codeBuf := bytes.NewBuffer(impl.CodeBuf) obj, err := marshal.ReadObject(codeBuf) if err != nil { return nil, err } impl.Code, _ = obj.(*py.Code) if impl.Code == nil { return nil, py.ExceptionNewf(py.AssertionError, "Embedded code did not produce a py.Code object") } } module, err := ctx.Store().NewModule(ctx, impl) if err != nil { return nil, err } if impl.Code != nil { _, err = ctx.RunCode(impl.Code, module.Globals, module.Globals, nil) if err != nil { return nil, err } } return module, nil } // See interface py.Context defined in py/run.go func (ctx *context) ResolveAndCompile(pathname string, opts py.CompileOpts) (py.CompileOut, error) { err := ctx.pushBusy() defer ctx.popBusy() if err != nil { return py.CompileOut{}, err } tryPaths := defaultPaths if opts.UseSysPaths { tryPaths = ctx.Store().MustGetModule("sys").Globals["path"].(*py.List).Items } out := py.CompileOut{} err = resolveRunPath(pathname, opts, tryPaths, func(fpath string) (bool, error) { stat, err := os.Stat(fpath) if err == nil && stat.IsDir() { // FIXME this is a massive simplification! fpath = path.Join(fpath, "__init__.py") _, err = os.Stat(fpath) } ext := strings.ToLower(filepath.Ext(fpath)) if ext == "" && os.IsNotExist(err) { fpath += ".py" ext = ".py" _, err = os.Stat(fpath) } // Keep searching while we get FNFs, stop on an error if err != nil { if os.IsNotExist(err) { return true, nil } err = py.ExceptionNewf(py.OSError, "Error accessing %q: %v", fpath, err) return false, err } switch ext { case ".py": var pySrc []byte pySrc, err = os.ReadFile(fpath) if err != nil { return false, py.ExceptionNewf(py.OSError, "Error reading %q: %v", fpath, err) } out.Code, err = py.Compile(string(pySrc), fpath, py.ExecMode, 0, true) if err != nil { return false, err } out.SrcPathname = fpath case ".pyc": file, err := os.Open(fpath) if err != nil { return false, py.ExceptionNewf(py.OSError, "Error opening %q: %v", fpath, err) } defer file.Close() codeObj, err := marshal.ReadPyc(file) if err != nil { return false, py.ExceptionNewf(py.ImportError, "Failed to marshal %q: %v", fpath, err) } out.Code, _ = codeObj.(*py.Code) out.PycPathname = fpath } out.FileDesc = fpath return false, nil }) if out.Code == nil && err == nil { err = py.ExceptionNewf(py.AssertionError, "Missing code object") } if err != nil { return py.CompileOut{}, err } return out, nil } func (ctx *context) pushBusy() error { if ctx.closed { return py.ExceptionNewf(py.RuntimeError, "Context closed") } ctx.running.Add(1) return nil } func (ctx *context) popBusy() { ctx.running.Done() } // See interface py.Context defined in py/run.go func (ctx *context) Close() error { ctx.closeOnce.Do(func() { ctx.closing = true ctx.running.Wait() ctx.closed = true // Give each module a chance to release resources ctx.store.OnContextClosed() close(ctx.done) }) return nil } // See interface py.Context defined in py/run.go func (ctx *context) Done() <-chan struct{} { return ctx.done } var defaultPaths = []py.Object{ py.String("."), } func resolveRunPath(runPath string, opts py.CompileOpts, pathObjs []py.Object, tryPath func(pyPath string) (bool, error)) error { runPath = strings.TrimSuffix(runPath, "/") var ( err error cwd string cont = true ) for _, pathObj := range pathObjs { pathStr, ok := pathObj.(py.String) if !ok { continue } // If an absolute path, just try that. // Otherwise, check from the passed current dir then check from the current working dir. fpath := path.Join(string(pathStr), runPath) if filepath.IsAbs(fpath) { cont, err = tryPath(fpath) } else { if len(opts.CurDir) > 0 { subPath := path.Join(opts.CurDir, fpath) cont, err = tryPath(subPath) } if cont && err == nil { if cwd == "" { cwd, _ = os.Getwd() } subPath := path.Join(cwd, fpath) cont, err = tryPath(subPath) } } if !cont { break } } if err != nil { return err } if cont { return py.ExceptionNewf(py.FileNotFoundError, "Failed to resolve %q", runPath) } return err } // See interface py.Context defined in py/run.go func (ctx *context) RunCode(code *py.Code, globals, locals py.StringDict, closure py.Tuple) (py.Object, error) { err := ctx.pushBusy() defer ctx.popBusy() if err != nil { return nil, err } return vm.EvalCode(ctx, code, globals, locals, nil, nil, nil, nil, closure) } // See interface py.Context defined in py/run.go func (ctx *context) GetModule(moduleName string) (*py.Module, error) { return ctx.store.GetModule(moduleName) } // See interface py.Context defined in py/run.go func (ctx *context) Store() *py.ModuleStore { return ctx.store }
X Tutup