github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/module.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grumpy 16 17 import ( 18 "fmt" 19 "os" 20 "reflect" 21 "runtime/pprof" 22 "strings" 23 "sync" 24 ) 25 26 type moduleState int 27 28 const ( 29 moduleStateNew moduleState = iota 30 moduleStateInitializing 31 moduleStateReady 32 ) 33 34 var ( 35 importMutex sync.Mutex 36 moduleRegistry = map[string]*Code{} 37 // ModuleType is the object representing the Python 'module' type. 38 ModuleType = newBasisType("module", reflect.TypeOf(Module{}), toModuleUnsafe, ObjectType) 39 // SysModules is the global dict of imported modules, aka sys.modules. 40 SysModules = NewDict() 41 ) 42 43 // Module represents Python 'module' objects. 44 type Module struct { 45 Object 46 mutex recursiveMutex 47 state moduleState 48 code *Code 49 } 50 51 // ModuleInit functions are called when importing Grumpy modules to execute the 52 // top level code for that module. 53 type ModuleInit func(f *Frame, m *Module) *BaseException 54 55 // RegisterModule adds the named module to the registry so that it can be 56 // subsequently imported. 57 func RegisterModule(name string, c *Code) { 58 err := "" 59 importMutex.Lock() 60 if moduleRegistry[name] == nil { 61 moduleRegistry[name] = c 62 } else { 63 err = "module already registered: " + name 64 } 65 importMutex.Unlock() 66 if err != "" { 67 logFatal(err) 68 } 69 } 70 71 // ImportModule takes a fully qualified module name (e.g. a.b.c) and a slice of 72 // code objects where the name of the i'th module is the prefix of name 73 // ending in the i'th dot. The number of dot delimited parts of name must be the 74 // same as the number of code objects. For each successive prefix, ImportModule 75 // looks in sys.modules for an existing module with that name and if not 76 // present creates a new module object, adds it to sys.modules and initializes 77 // it with the corresponding code object. If the module was already present in 78 // sys.modules, it is not re-initialized. The returned slice contains each 79 // package and module initialized in this way in order. 80 // 81 // For example, ImportModule(f, "a.b", []*Code{a.Code, b.Code}) 82 // causes the initialization and entry into sys.modules of Grumpy module a and 83 // then Grumpy module b. The two initialized modules are returned. 84 // 85 // If ImportModule is called in two threads concurrently to import the same 86 // module, both invocations will produce the same module object and the module 87 // is guaranteed to only be initialized once. The second invocation will not 88 // return the module until it is fully initialized. 89 func ImportModule(f *Frame, name string) ([]*Object, *BaseException) { 90 if strings.Contains(name, "/") { 91 o, raised := importOne(f, name) 92 if raised != nil { 93 return nil, raised 94 } 95 return []*Object{o}, nil 96 } 97 parts := strings.Split(name, ".") 98 numParts := len(parts) 99 result := make([]*Object, numParts) 100 var prev *Object 101 for i := 0; i < numParts; i++ { 102 name := strings.Join(parts[:i+1], ".") 103 o, raised := importOne(f, name) 104 if raised != nil { 105 return nil, raised 106 } 107 if prev != nil { 108 if raised := SetAttr(f, prev, NewStr(parts[i]), o); raised != nil { 109 return nil, raised 110 } 111 } 112 result[i] = o 113 prev = o 114 } 115 return result, nil 116 } 117 118 func importOne(f *Frame, name string) (*Object, *BaseException) { 119 var c *Code 120 // We do very limited locking here resulting in some 121 // sys.modules consistency gotchas. 122 importMutex.Lock() 123 o, raised := SysModules.GetItemString(f, name) 124 if raised == nil && o == nil { 125 if c = moduleRegistry[name]; c == nil { 126 raised = f.RaiseType(ImportErrorType, name) 127 } else { 128 o = newModule(name, c.filename).ToObject() 129 raised = SysModules.SetItemString(f, name, o) 130 } 131 } 132 importMutex.Unlock() 133 if raised != nil { 134 return nil, raised 135 } 136 if o.isInstance(ModuleType) { 137 var raised *BaseException 138 m := toModuleUnsafe(o) 139 m.mutex.Lock(f) 140 if m.state == moduleStateNew { 141 m.state = moduleStateInitializing 142 if _, raised = c.Eval(f, m.Dict(), nil, nil); raised == nil { 143 m.state = moduleStateReady 144 } else { 145 // If the module failed to initialize 146 // then before we relinquish the module 147 // lock, remove it from sys.modules. 148 // Threads waiting on this module will 149 // fail when they don't find it in 150 // sys.modules below. 151 e, tb := f.ExcInfo() 152 if _, raised := SysModules.DelItemString(f, name); raised != nil { 153 f.RestoreExc(e, tb) 154 } 155 } 156 } 157 m.mutex.Unlock(f) 158 if raised != nil { 159 return nil, raised 160 } 161 // The result should be what's in sys.modules, not 162 // necessarily the originally created module since this 163 // is CPython's behavior. 164 o, raised = SysModules.GetItemString(f, name) 165 if raised != nil { 166 return nil, raised 167 } 168 if o == nil { 169 // This can happen in the pathological case 170 // where the module clears itself from 171 // sys.modules during execution and is handled 172 // by CPython in PyImport_ExecCodeModuleEx in 173 // import.c. 174 format := "Loaded module %s not found in sys.modules" 175 return nil, f.RaiseType(ImportErrorType, fmt.Sprintf(format, name)) 176 } 177 } 178 return o, nil 179 } 180 181 // newModule creates a new Module object with the given fully qualified name 182 // (e.g a.b.c) and its corresponding Python filename. 183 func newModule(name, filename string) *Module { 184 d := newStringDict(map[string]*Object{ 185 "__file__": NewStr(filename).ToObject(), 186 "__name__": NewStr(name).ToObject(), 187 }) 188 return &Module{Object: Object{typ: ModuleType, dict: d}} 189 } 190 191 func toModuleUnsafe(o *Object) *Module { 192 return (*Module)(o.toPointer()) 193 } 194 195 // GetFilename returns the __file__ attribute of m, raising SystemError if it 196 // does not exist. 197 func (m *Module) GetFilename(f *Frame) (*Str, *BaseException) { 198 fileAttr, raised := GetAttr(f, m.ToObject(), NewStr("__file__"), None) 199 if raised != nil { 200 return nil, raised 201 } 202 if !fileAttr.isInstance(StrType) { 203 return nil, f.RaiseType(SystemErrorType, "module filename missing") 204 } 205 return toStrUnsafe(fileAttr), nil 206 } 207 208 // GetName returns the __name__ attribute of m, raising SystemError if it does 209 // not exist. 210 func (m *Module) GetName(f *Frame) (*Str, *BaseException) { 211 nameAttr, raised := GetAttr(f, m.ToObject(), internedName, None) 212 if raised != nil { 213 return nil, raised 214 } 215 if !nameAttr.isInstance(StrType) { 216 return nil, f.RaiseType(SystemErrorType, "nameless module") 217 } 218 return toStrUnsafe(nameAttr), nil 219 } 220 221 // ToObject upcasts m to an Object. 222 func (m *Module) ToObject() *Object { 223 return &m.Object 224 } 225 226 func moduleInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 227 expectedTypes := []*Type{StrType, ObjectType} 228 argc := len(args) 229 if argc == 1 { 230 expectedTypes = expectedTypes[:1] 231 } 232 if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil { 233 return nil, raised 234 } 235 if raised := SetAttr(f, o, internedName, args[0]); raised != nil { 236 return nil, raised 237 } 238 if argc > 1 { 239 if raised := SetAttr(f, o, NewStr("__doc__"), args[1]); raised != nil { 240 return nil, raised 241 } 242 } 243 return None, nil 244 } 245 246 func moduleRepr(f *Frame, o *Object) (*Object, *BaseException) { 247 m := toModuleUnsafe(o) 248 name := "?" 249 nameAttr, raised := m.GetName(f) 250 if raised == nil { 251 name = nameAttr.Value() 252 } else { 253 f.RestoreExc(nil, nil) 254 } 255 file := "(built-in)" 256 fileAttr, raised := m.GetFilename(f) 257 if raised == nil { 258 file = fmt.Sprintf("from '%s'", fileAttr.Value()) 259 } else { 260 f.RestoreExc(nil, nil) 261 } 262 return NewStr(fmt.Sprintf("<module '%s' %s>", name, file)).ToObject(), nil 263 } 264 265 func initModuleType(map[string]*Object) { 266 ModuleType.slots.Init = &initSlot{moduleInit} 267 ModuleType.slots.Repr = &unaryOpSlot{moduleRepr} 268 } 269 270 // RunMain execs the given code object as a module under the name "__main__". 271 // It handles any exceptions raised during module execution. If no exceptions 272 // were raised then the return value is zero. If a SystemExit was raised then 273 // the return value depends on its code attribute: None -> zero, integer values 274 // are returned as-is. Other code values and exception types produce a return 275 // value of 1. 276 func RunMain(code *Code) int { 277 if file := os.Getenv("GRUMPY_PROFILE"); file != "" { 278 f, err := os.Create(file) 279 if err != nil { 280 logFatal(err.Error()) 281 } 282 if err := pprof.StartCPUProfile(f); err != nil { 283 logFatal(err.Error()) 284 } 285 defer pprof.StopCPUProfile() 286 } 287 m := newModule("__main__", code.filename) 288 m.state = moduleStateInitializing 289 f := NewRootFrame() 290 f.code = code 291 f.globals = m.Dict() 292 if raised := SysModules.SetItemString(f, "__main__", m.ToObject()); raised != nil { 293 Stderr.writeString(raised.String()) 294 } 295 _, e := code.fn(f, nil) 296 if e == nil { 297 return 0 298 } 299 if !e.isInstance(SystemExitType) { 300 Stderr.writeString(FormatExc(f)) 301 return 1 302 } 303 f.RestoreExc(nil, nil) 304 o, raised := GetAttr(f, e.ToObject(), NewStr("code"), nil) 305 if raised != nil { 306 return 1 307 } 308 if o.isInstance(IntType) { 309 return toIntUnsafe(o).Value() 310 } 311 if o == None { 312 return 0 313 } 314 if s, raised := ToStr(f, o); raised == nil { 315 Stderr.writeString(s.Value() + "\n") 316 } 317 return 1 318 }