github.com/pygolin/runtime@v0.0.0-20201208210830-a62e3cd39798/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 runtime 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 msg := fmt.Sprintf("No module named %s", name) 127 raised = f.RaiseType(ImportErrorType, msg) 128 } else { 129 o = newModule(name, c.filename).ToObject() 130 raised = SysModules.SetItemString(f, name, o) 131 } 132 } 133 importMutex.Unlock() 134 if raised != nil { 135 return nil, raised 136 } 137 if o.isInstance(ModuleType) { 138 var raised *BaseException 139 m := toModuleUnsafe(o) 140 m.mutex.Lock(f) 141 if m.state == moduleStateNew { 142 m.state = moduleStateInitializing 143 if _, raised = c.Eval(f, m.Dict(), nil, nil); raised == nil { 144 m.state = moduleStateReady 145 } else { 146 // If the module failed to initialize 147 // then before we relinquish the module 148 // lock, remove it from sys.modules. 149 // Threads waiting on this module will 150 // fail when they don't find it in 151 // sys.modules below. 152 e, tb := f.ExcInfo() 153 if _, raised := SysModules.DelItemString(f, name); raised != nil { 154 f.RestoreExc(e, tb) 155 } 156 } 157 } 158 m.mutex.Unlock(f) 159 if raised != nil { 160 return nil, raised 161 } 162 // The result should be what's in sys.modules, not 163 // necessarily the originally created module since this 164 // is CPython's behavior. 165 o, raised = SysModules.GetItemString(f, name) 166 if raised != nil { 167 return nil, raised 168 } 169 if o == nil { 170 // This can happen in the pathological case 171 // where the module clears itself from 172 // sys.modules during execution and is handled 173 // by CPython in PyImport_ExecCodeModuleEx in 174 // import.c. 175 format := "Loaded module %s not found in sys.modules" 176 return nil, f.RaiseType(ImportErrorType, fmt.Sprintf(format, name)) 177 } 178 } 179 return o, nil 180 } 181 182 // LoadMembers scans over all the members in module 183 // and populates globals with them, taking __all__ into 184 // account. 185 func LoadMembers(f *Frame, module *Object) *BaseException { 186 allAttr, raised := GetAttr(f, module, NewStr("__all__"), nil) 187 if raised != nil && !raised.isInstance(AttributeErrorType) { 188 return raised 189 } 190 f.RestoreExc(nil, nil) 191 192 if raised == nil { 193 raised = loadMembersFromIterable(f, module, allAttr, nil) 194 if raised != nil { 195 return raised 196 } 197 return nil 198 } 199 200 // Fall back on __dict__ 201 dictAttr := module.dict.ToObject() 202 raised = loadMembersFromIterable(f, module, dictAttr, func(key *Object) bool { 203 return strings.HasPrefix(toStrUnsafe(key).value, "_") 204 }) 205 if raised != nil { 206 return raised 207 } 208 return nil 209 } 210 211 func loadMembersFromIterable(f *Frame, module, iterable *Object, filterF func(*Object) bool) *BaseException { 212 globals := f.Globals() 213 raised := seqForEach(f, iterable, func(memberName *Object) *BaseException { 214 if !memberName.isInstance(StrType) { 215 errorMessage := fmt.Sprintf("attribute name must be string, not '%v'", memberName.typ.Name()) 216 return f.RaiseType(AttributeErrorType, errorMessage) 217 } 218 member, raised := GetAttr(f, module, toStrUnsafe(memberName), nil) 219 if raised != nil { 220 return raised 221 } 222 if filterF != nil && filterF(memberName) { 223 return nil 224 } 225 raised = globals.SetItem(f, memberName, member) 226 if raised != nil { 227 return raised 228 } 229 return nil 230 }) 231 return raised 232 } 233 234 // newModule creates a new Module object with the given fully qualified name 235 // (e.g a.b.c) and its corresponding Python filename and package. 236 func newModule(name, filename string) *Module { 237 pkgName := "" 238 if strings.Contains(name, ".") { 239 pkgParts := strings.Split(name, ".") 240 pkgName = strings.Join(pkgParts[:len(pkgParts)-1], ".") 241 } 242 243 d := newStringDict(map[string]*Object{ 244 "__file__": NewStr(filename).ToObject(), 245 "__name__": NewStr(name).ToObject(), 246 "__package__": NewStr(pkgName).ToObject(), 247 "__doc__": None, 248 }) 249 return &Module{Object: Object{typ: ModuleType, dict: d}} 250 } 251 252 func toModuleUnsafe(o *Object) *Module { 253 return (*Module)(o.toPointer()) 254 } 255 256 // GetFilename returns the __file__ attribute of m, raising SystemError if it 257 // does not exist. 258 func (m *Module) GetFilename(f *Frame) (*Str, *BaseException) { 259 fileAttr, raised := GetAttr(f, m.ToObject(), NewStr("__file__"), None) 260 if raised != nil { 261 return nil, raised 262 } 263 if !fileAttr.isInstance(StrType) { 264 return nil, f.RaiseType(SystemErrorType, "module filename missing") 265 } 266 return toStrUnsafe(fileAttr), nil 267 } 268 269 // GetName returns the __name__ attribute of m, raising SystemError if it does 270 // not exist. 271 func (m *Module) GetName(f *Frame) (*Str, *BaseException) { 272 nameAttr, raised := GetAttr(f, m.ToObject(), internedName, None) 273 if raised != nil { 274 return nil, raised 275 } 276 if !nameAttr.isInstance(StrType) { 277 return nil, f.RaiseType(SystemErrorType, "nameless module") 278 } 279 return toStrUnsafe(nameAttr), nil 280 } 281 282 // ToObject upcasts m to an Object. 283 func (m *Module) ToObject() *Object { 284 return &m.Object 285 } 286 287 func moduleInit(f *Frame, o *Object, args Args, _ KWArgs) (*Object, *BaseException) { 288 expectedTypes := []*Type{StrType, ObjectType} 289 argc := len(args) 290 if argc == 1 { 291 expectedTypes = expectedTypes[:1] 292 } 293 if raised := checkFunctionArgs(f, "__init__", args, expectedTypes...); raised != nil { 294 return nil, raised 295 } 296 if raised := SetAttr(f, o, internedName, args[0]); raised != nil { 297 return nil, raised 298 } 299 if argc > 1 { 300 if raised := SetAttr(f, o, NewStr("__doc__"), args[1]); raised != nil { 301 return nil, raised 302 } 303 } 304 return None, nil 305 } 306 307 func moduleRepr(f *Frame, o *Object) (*Object, *BaseException) { 308 m := toModuleUnsafe(o) 309 name := "?" 310 nameAttr, raised := m.GetName(f) 311 if raised == nil { 312 name = nameAttr.Value() 313 } else { 314 f.RestoreExc(nil, nil) 315 } 316 file := "(built-in)" 317 fileAttr, raised := m.GetFilename(f) 318 if raised == nil { 319 file = fmt.Sprintf("from '%s'", fileAttr.Value()) 320 } else { 321 f.RestoreExc(nil, nil) 322 } 323 return NewStr(fmt.Sprintf("<module '%s' %s>", name, file)).ToObject(), nil 324 } 325 326 func initModuleType(map[string]*Object) { 327 ModuleType.slots.Init = &initSlot{moduleInit} 328 ModuleType.slots.Repr = &unaryOpSlot{moduleRepr} 329 } 330 331 // RunMain execs the given code object as a module under the name "__main__". 332 // It handles any exceptions raised during module execution. If no exceptions 333 // were raised then the return value is zero. If a SystemExit was raised then 334 // the return value depends on its code attribute: None -> zero, integer values 335 // are returned as-is. Other code values and exception types produce a return 336 // value of 1. 337 func RunMain(code *Code) int { 338 if file := os.Getenv("GRUMPY_PROFILE"); file != "" { 339 f, err := os.Create(file) 340 if err != nil { 341 logFatal(err.Error()) 342 } 343 if err := pprof.StartCPUProfile(f); err != nil { 344 logFatal(err.Error()) 345 } 346 defer pprof.StopCPUProfile() 347 } 348 m := newModule("__main__", code.filename) 349 m.state = moduleStateInitializing 350 f := NewRootFrame() 351 f.code = code 352 f.globals = m.Dict() 353 if raised := SysModules.SetItemString(f, "__main__", m.ToObject()); raised != nil { 354 Stderr.writeString(raised.String()) 355 } 356 _, e := code.fn(f, nil) 357 if e == nil { 358 return 0 359 } 360 if !e.isInstance(SystemExitType) { 361 Stderr.writeString(FormatExc(f)) 362 return 1 363 } 364 f.RestoreExc(nil, nil) 365 o, raised := GetAttr(f, e.ToObject(), NewStr("code"), nil) 366 if raised != nil { 367 return 1 368 } 369 if o.isInstance(IntType) { 370 return toIntUnsafe(o).Value() 371 } 372 if o == None { 373 return 0 374 } 375 if s, raised := ToStr(f, o); raised == nil { 376 Stderr.writeString(s.Value() + "\n") 377 } 378 return 1 379 }