github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/asp/objects.go (about) 1 package asp 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strconv" 8 "strings" 9 10 "github.com/thought-machine/please/src/core" 11 ) 12 13 // A pyObject is the base type for all interpreter objects. 14 // Strictly the "py" prefix is a misnomer but it's short and easy to follow... 15 type pyObject interface { 16 // All pyObjects are stringable. 17 fmt.Stringer 18 // Returns the name of this object's type. 19 Type() string 20 // Returns true if this object evaluates to something truthy. 21 IsTruthy() bool 22 // Returns a property of this object with the given name. 23 Property(name string) pyObject 24 // Invokes the given operator on this object and returns the result. 25 Operator(operator Operator, operand pyObject) pyObject 26 // Used for index-assignment statements 27 IndexAssign(index, value pyObject) 28 // Returns the length of this object 29 Len() int 30 } 31 32 // A freezable represents an object that can be frozen into a readonly state. 33 // Not all pyObjects implement this. 34 type freezable interface { 35 Freeze() pyObject 36 } 37 38 type pyBool int // Don't ask. 39 40 // True, False and None are the singletons representing those values in Python. 41 // None isn't really a bool of course, but it's easier to use an instance of bool than another 42 // custom type. 43 var ( 44 True pyBool = 1 45 False pyBool 46 None pyBool = -1 47 FileNotFound pyBool = -2 48 // continueIteration is used as a sentinel value to implement the "continue" statement. 49 continueIteration pyBool = -3 50 ) 51 52 // newPyBool creates a new bool. It's a minor optimisation to treat them as singletons 53 // although also means one can write "is True" and have it work (not that you should, really). 54 func newPyBool(b bool) pyObject { 55 if b { 56 return True 57 } 58 return False 59 } 60 61 func (b pyBool) Type() string { 62 return "bool" 63 } 64 65 func (b pyBool) IsTruthy() bool { 66 return b == True 67 } 68 69 func (b pyBool) Property(name string) pyObject { 70 panic("bool object has no property " + name) 71 } 72 73 func (b pyBool) Operator(operator Operator, operand pyObject) pyObject { 74 panic(fmt.Sprintf("operator %s not implemented on type bool", operator)) 75 } 76 77 func (b pyBool) IndexAssign(index, value pyObject) { 78 panic("bool type is not indexable") 79 } 80 81 func (b pyBool) Len() int { 82 panic("bool has no len()") 83 } 84 85 func (b pyBool) String() string { 86 if b == None { 87 return "None" 88 } else if b == True { 89 return "True" 90 } 91 return "False" 92 } 93 94 type pyInt int 95 96 // pyIndex converts an object that's being used as an index to an int. 97 func pyIndex(obj, index pyObject, slice bool) pyInt { 98 i, ok := index.(pyInt) 99 if !ok { 100 panic(obj.Type() + " indices must be integers, not " + index.Type()) 101 } else if i < 0 { 102 i = pyInt(obj.Len()) + i // Go doesn't support negative indices 103 } else if int(i) > obj.Len() { 104 if slice { 105 return pyInt(obj.Len()) 106 } 107 panic(obj.Type() + " index out of range") 108 } 109 return i 110 } 111 112 func (i pyInt) Type() string { 113 return "int" 114 } 115 116 func (i pyInt) IsTruthy() bool { 117 return i != 0 118 } 119 120 func (i pyInt) Property(name string) pyObject { 121 panic("int object has no property " + name) 122 } 123 124 func (i pyInt) Operator(operator Operator, operand pyObject) pyObject { 125 i2, ok := operand.(pyInt) 126 if !ok { 127 panic("Cannot operate on int and " + operand.Type()) 128 } 129 switch operator { 130 case Add: 131 return i + i2 132 case Subtract: 133 return i - i2 134 case LessThan: 135 return newPyBool(i < i2) 136 case GreaterThan: 137 return newPyBool(i > i2) 138 case LessThanOrEqual: 139 return newPyBool(i <= i2) 140 case GreaterThanOrEqual: 141 return newPyBool(i >= i2) 142 case Modulo: 143 return i % i2 144 case In: 145 panic("bad operator: 'in' int") 146 } 147 panic("unknown operator") 148 } 149 func (i pyInt) IndexAssign(index, value pyObject) { 150 panic("int type is not indexable") 151 } 152 153 func (i pyInt) Len() int { 154 panic("int has no len()") 155 } 156 157 func (i pyInt) String() string { 158 return strconv.Itoa(int(i)) 159 } 160 161 type pyString string 162 163 func (s pyString) Type() string { 164 return "str" 165 } 166 167 func (s pyString) IsTruthy() bool { 168 return s != "" 169 } 170 171 func (s pyString) Property(name string) pyObject { 172 if prop, present := stringMethods[name]; present { 173 return prop.Member(s) 174 } 175 panic("str object has no property " + name) 176 } 177 178 func (s pyString) Operator(operator Operator, operand pyObject) pyObject { 179 s2, ok := operand.(pyString) 180 if !ok && operator != Modulo && operator != Index { 181 panic("Cannot operate on str and " + operand.Type()) 182 } 183 switch operator { 184 case Add: 185 return s + s2 186 case LessThan: 187 return newPyBool(s < s2) 188 case GreaterThan: 189 return newPyBool(s > s2) 190 case LessThanOrEqual: 191 return newPyBool(s <= s2) 192 case GreaterThanOrEqual: 193 return newPyBool(s >= s2) 194 case Modulo: 195 if ok { 196 // Special case: "%s" % "x" 197 return pyString(fmt.Sprintf(string(s), s2)) 198 } else if i, ok := operand.(pyInt); ok { 199 // Another one: "%d" % 4 200 return pyString(fmt.Sprintf(string(s), i)) 201 } 202 l, ok := operand.(pyList) 203 if !ok { 204 panic("Argument to string interpolation must be a string or list; was " + operand.Type()) 205 } 206 // Classic issue: can't use a []pyObject as a []interface{} :( 207 l2 := make([]interface{}, len(l)) 208 for i, v := range l { 209 l2[i] = v 210 } 211 return pyString(fmt.Sprintf(string(s), l2...)) 212 case In: 213 return newPyBool(strings.Contains(string(s), string(s2))) 214 case NotIn: 215 return newPyBool(!strings.Contains(string(s), string(s2))) 216 case Index: 217 return pyString(s[pyIndex(s, operand, false)]) 218 } 219 panic("unknown operator") 220 } 221 222 func (s pyString) IndexAssign(index, value pyObject) { 223 panic("str type cannot be partially assigned to") 224 } 225 226 func (s pyString) Len() int { 227 return len(s) 228 } 229 230 func (s pyString) String() string { 231 return string(s) 232 } 233 234 type pyList []pyObject 235 236 func (l pyList) Type() string { 237 return "list" 238 } 239 240 func (l pyList) IsTruthy() bool { 241 return len(l) > 0 242 } 243 244 func (l pyList) Property(name string) pyObject { 245 panic("list object has no property " + name) 246 } 247 248 func (l pyList) Operator(operator Operator, operand pyObject) pyObject { 249 switch operator { 250 case Add: 251 l2, ok := operand.(pyList) 252 if !ok { 253 if l2, ok := operand.(pyFrozenList); ok { 254 return append(l, l2.pyList...) 255 } 256 panic("Cannot add list and " + operand.Type()) 257 } 258 return append(l, l2...) 259 case In, NotIn: 260 for _, item := range l { 261 if item == operand { 262 return newPyBool(operator == In) 263 } 264 } 265 return newPyBool(operator == NotIn) 266 case Index: 267 return l[pyIndex(l, operand, false)] 268 case LessThan: 269 // Needed for sorting. 270 l2, ok := operand.(pyList) 271 if !ok { 272 panic("Cannot compare list and " + operand.Type()) 273 } 274 for i, li := range l { 275 if i >= len(l2) || l2[i].Operator(LessThan, li).IsTruthy() { 276 return False 277 } else if li.Operator(LessThan, l2[i]).IsTruthy() { 278 return True 279 } 280 } 281 if len(l) < len(l2) { 282 return True 283 } 284 return False 285 } 286 panic("Unsupported operator on list: " + operator.String()) 287 } 288 289 func (l pyList) IndexAssign(index, value pyObject) { 290 i, ok := index.(pyInt) 291 if !ok { 292 panic("List indices must be integers, not " + index.Type()) 293 } 294 l[i] = value 295 } 296 297 func (l pyList) Len() int { 298 return len(l) 299 } 300 301 func (l pyList) String() string { 302 return fmt.Sprintf("%s", []pyObject(l)) 303 } 304 305 // Freeze freezes this list for further updates. 306 // Note that this is a "soft" freeze; callers holding the original unfrozen 307 // reference can still modify it. 308 func (l pyList) Freeze() pyObject { 309 return pyFrozenList{pyList: l} 310 } 311 312 // A pyFrozenList implements an immutable list. 313 type pyFrozenList struct{ pyList } 314 315 func (l pyFrozenList) IndexAssign(index, value pyObject) { 316 panic("list is immutable") 317 } 318 319 type pyDict map[string]pyObject // Dicts can only be keyed by strings 320 321 func (d pyDict) Type() string { 322 return "dict" 323 } 324 325 func (d pyDict) IsTruthy() bool { 326 return len(d) > 0 327 } 328 329 func (d pyDict) Property(name string) pyObject { 330 // We allow looking up dict members by . as well as by indexing in order to facilitate the config map. 331 if obj, present := d[name]; present { 332 return obj 333 } else if prop, present := dictMethods[name]; present { 334 return prop.Member(d) 335 } 336 panic("dict object has no property " + name) 337 } 338 339 func (d pyDict) Operator(operator Operator, operand pyObject) pyObject { 340 if operator == In || operator == NotIn { 341 if s, ok := operand.(pyString); ok { 342 _, present := d[string(s)] 343 return newPyBool(present == (operator == In)) 344 } 345 return newPyBool(operator == NotIn) 346 } else if operator == Index { 347 s, ok := operand.(pyString) 348 if !ok { 349 panic("Dict keys must be strings, not " + operand.Type()) 350 } else if v, present := d[string(s)]; present { 351 return v 352 } 353 panic("unknown dict key: " + s.String()) 354 } 355 panic("Unsupported operator on dict") 356 } 357 358 func (d pyDict) IndexAssign(index, value pyObject) { 359 key, ok := index.(pyString) 360 if !ok { 361 panic("Dict keys must be strings, not " + index.Type()) 362 } 363 d[string(key)] = value 364 } 365 366 func (d pyDict) Len() int { 367 return len(d) 368 } 369 370 func (d pyDict) String() string { 371 var b strings.Builder 372 b.WriteByte('{') 373 started := false 374 for _, k := range d.Keys() { 375 if started { 376 b.WriteString(", ") 377 } 378 started = true 379 b.WriteByte('"') 380 b.WriteString(k) 381 b.WriteString(`": `) 382 b.WriteString(d[k].String()) 383 } 384 b.WriteByte('}') 385 return b.String() 386 } 387 388 // Copy creates a shallow duplicate of this dictionary. 389 func (d pyDict) Copy() pyDict { 390 m := make(pyDict, len(d)) 391 for k, v := range d { 392 m[k] = v 393 } 394 return m 395 } 396 397 // Freeze freezes this dict for further updates. 398 // Note that this is a "soft" freeze; callers holding the original unfrozen 399 // reference can still modify it. 400 func (d pyDict) Freeze() pyObject { 401 return pyFrozenDict{pyDict: d} 402 } 403 404 // Keys returns the keys of this dict, in order. 405 func (d pyDict) Keys() []string { 406 ret := make([]string, 0, len(d)) 407 for k := range d { 408 ret = append(ret, k) 409 } 410 sort.Strings(ret) 411 return ret 412 } 413 414 // A pyFrozenDict implements an immutable python dict. 415 type pyFrozenDict struct{ pyDict } 416 417 func (d pyFrozenDict) Property(name string) pyObject { 418 if name == "setdefault" { 419 panic("dict is immutable") 420 } 421 return d.pyDict.Property(name) 422 } 423 424 func (d pyFrozenDict) IndexAssign(index, value pyObject) { 425 panic("dict is immutable") 426 } 427 428 type pyFunc struct { 429 name string 430 docstring string 431 scope *scope 432 args []string 433 argIndices map[string]int 434 defaults []*Expression 435 constants []pyObject 436 types [][]string 437 code []*Statement 438 // If the function is implemented natively, this is the pointer to its real code. 439 nativeCode func(*scope, []pyObject) pyObject 440 // If the function has been bound as a member function, this is the implicit self argument. 441 self pyObject 442 // True if this function accepts non-keyword varargs (like the log functions, or zip()). 443 varargs bool 444 // True if this function accepts arbitrary keyword arguments (e.g. package(), str.format()). 445 kwargs bool 446 // True if this function may only be called using keyword arguments. 447 // This is the case for all builtin build rules, although for now it cannot be specified 448 // on any user-defined ones. 449 kwargsonly bool 450 // return type of the function 451 returnType string 452 } 453 454 func newPyFunc(parentScope *scope, def *FuncDef) pyObject { 455 f := &pyFunc{ 456 name: def.Name, 457 scope: parentScope, 458 args: make([]string, len(def.Arguments)), 459 argIndices: make(map[string]int, len(def.Arguments)), 460 constants: make([]pyObject, len(def.Arguments)), 461 types: make([][]string, len(def.Arguments)), 462 code: def.Statements, 463 kwargsonly: def.KeywordsOnly, 464 returnType: def.Return, 465 } 466 if def.Docstring != "" { 467 f.docstring = stringLiteral(def.Docstring) 468 } 469 for i, arg := range def.Arguments { 470 f.args[i] = arg.Name 471 f.argIndices[arg.Name] = i 472 f.types[i] = arg.Type 473 if arg.Value != nil { 474 if constant := parentScope.Constant(arg.Value); constant != nil { 475 f.constants[i] = constant 476 } else { 477 if f.defaults == nil { 478 // Minor optimisation: defaults is lazily allocated 479 f.defaults = make([]*Expression, len(def.Arguments)) 480 } 481 f.defaults[i] = arg.Value 482 } 483 } 484 for _, alias := range arg.Aliases { 485 f.argIndices[alias] = i 486 } 487 } 488 return f 489 } 490 491 func (f *pyFunc) Type() string { 492 return "function" 493 } 494 495 func (f *pyFunc) IsTruthy() bool { 496 return true 497 } 498 499 func (f *pyFunc) Property(name string) pyObject { 500 panic("function object has no property " + name) 501 } 502 503 func (f *pyFunc) Operator(operator Operator, operand pyObject) pyObject { 504 panic("cannot use operators on a function") 505 } 506 507 func (f *pyFunc) IndexAssign(index, value pyObject) { 508 panic("function type is not indexable") 509 } 510 511 func (f *pyFunc) Len() int { 512 panic("function has no len()") 513 } 514 515 func (f *pyFunc) String() string { 516 return fmt.Sprintf("<function %s>", f.name) 517 } 518 519 func (f *pyFunc) Call(s *scope, c *Call) pyObject { 520 if f.nativeCode != nil { 521 if f.kwargs { 522 return f.callNative(s.NewScope(), c) 523 } 524 return f.callNative(s, c) 525 } 526 s2 := f.scope.NewPackagedScope(s.pkg) 527 s2.config = s.config 528 s2.Set("CONFIG", s.config) // This needs to be copied across too :( 529 s2.Callback = s.Callback 530 // Handle implicit 'self' parameter for bound functions. 531 args := c.Arguments 532 if f.self != nil { 533 args = append([]CallArgument{{ 534 Value: Expression{Optimised: &OptimisedExpression{Constant: f.self}}, 535 }}, args...) 536 } 537 for i, a := range args { 538 if a.Name != "" { // Named argument 539 name := a.Name 540 idx, present := f.argIndices[name] 541 s.Assert(present || f.kwargs, "Unknown argument to %s: %s", f.name, name) 542 if present { 543 name = f.args[idx] 544 } 545 s2.Set(name, f.validateType(s, idx, &a.Value)) 546 } else { 547 s.NAssert(i >= len(f.args), "Too many arguments to %s", f.name) 548 s.NAssert(f.kwargsonly, "Function %s can only be called with keyword arguments", f.name) 549 s2.Set(f.args[i], f.validateType(s, i, &a.Value)) 550 } 551 } 552 // Now make sure any arguments with defaults are set, and check any others have been passed. 553 for i, a := range f.args { 554 if s2.LocalLookup(a) == nil { 555 s2.Set(a, f.defaultArg(s, i, a)) 556 } 557 } 558 ret := s2.interpretStatements(f.code) 559 if ret == nil { 560 return None // Implicit 'return None' in any function that didn't do that itself. 561 } 562 if f.returnType != "" && ret.Type() != f.returnType { 563 return s.Error("Invalid return type %s from function %s, expecting %s", ret.Type(), f.name, f.returnType) 564 } 565 566 return ret 567 } 568 569 // callNative implements the "calling convention" for functions implemented with native code. 570 // For performance reasons these are done differently - rather then receiving a pointer to a scope 571 // they receive their arguments as a slice, in which unpassed arguments are nil. 572 func (f *pyFunc) callNative(s *scope, c *Call) pyObject { 573 args := make([]pyObject, len(f.args)) 574 offset := 0 575 if f.self != nil { 576 args[0] = f.self 577 offset = 1 578 } 579 for i, a := range c.Arguments { 580 if a.Name != "" { // Named argument 581 if idx, present := f.argIndices[a.Name]; present { 582 args[idx] = f.validateType(s, idx, &a.Value) 583 } else if f.kwargs { 584 s.Set(a.Name, s.interpretExpression(&a.Value)) 585 } else { 586 s.Error("Unknown argument to %s: %s", f.name, a.Name) 587 } 588 } else if i >= len(args) { 589 s.Assert(f.varargs, "Too many arguments to %s", f.name) 590 args = append(args, s.interpretExpression(&a.Value)) 591 } else { 592 s.NAssert(f.kwargsonly, "Function %s can only be called with keyword arguments", f.name) 593 args[i+offset] = f.validateType(s, i+offset, &a.Value) 594 } 595 } 596 597 // Now make sure any arguments with defaults are set, and check any others have been passed. 598 for i, a := range f.args { 599 if args[i] == nil { 600 args[i] = f.defaultArg(s, i, a) 601 } 602 } 603 return f.nativeCode(s, args) 604 } 605 606 // defaultArg returns the default value for an argument, whether it's constant or not. 607 func (f *pyFunc) defaultArg(s *scope, i int, arg string) pyObject { 608 if f.constants[i] != nil { 609 return f.constants[i] 610 } 611 s.Assert(f.defaults != nil && f.defaults[i] != nil, "Missing required argument to %s: %s", f.name, arg) 612 return s.interpretExpression(f.defaults[i]) 613 } 614 615 // Member duplicates this function as a member function of the given object. 616 func (f *pyFunc) Member(obj pyObject) pyObject { 617 return &pyFunc{ 618 name: f.name, 619 scope: f.scope, 620 args: f.args, 621 argIndices: f.argIndices, 622 defaults: f.defaults, 623 constants: f.constants, 624 types: f.types, 625 code: f.code, 626 nativeCode: f.nativeCode, 627 varargs: f.varargs, 628 kwargs: f.kwargs, 629 self: obj, 630 } 631 } 632 633 // validateType validates that this argument matches the given type 634 func (f *pyFunc) validateType(s *scope, i int, expr *Expression) pyObject { 635 val := s.interpretExpression(expr) 636 if f.types[i] == nil { 637 return val 638 } else if val == None { 639 if f.constants[i] == nil && f.defaults[i] == nil { 640 return val 641 } 642 return f.defaultArg(s, i, f.args[i]) 643 } 644 actual := val.Type() 645 for _, t := range f.types[i] { 646 if t == actual { 647 return val 648 } 649 } 650 // Using integers in place of booleans seems common in Bazel BUILD files :( 651 if s.state.Config.Bazel.Compatibility && f.types[i][0] == "bool" && actual == "int" { 652 return val 653 } 654 defer func() { 655 panic(AddStackFrame(expr.Pos, recover())) 656 }() 657 return s.Error("Invalid type for argument %s to %s; expected %s, was %s", f.args[i], f.name, strings.Join(f.types[i], " or "), actual) 658 } 659 660 // A pyConfig is a wrapper object around Please's global config. 661 // Initially it was implemented as just a dict but that requires us to spend a lot of time 662 // copying & duplicating it - this structure instead requires very little to be copied 663 // on each update. 664 type pyConfig struct { 665 base pyDict 666 overlay pyDict 667 } 668 669 func (c *pyConfig) String() string { 670 return "<global config object>" 671 } 672 673 func (c *pyConfig) Type() string { 674 return "config" 675 } 676 677 func (c *pyConfig) IsTruthy() bool { 678 return true // sure, why not 679 } 680 681 func (c *pyConfig) Property(name string) pyObject { 682 if obj := c.Get(name, nil); obj != nil { 683 return obj 684 } else if f, present := configMethods[name]; present { 685 return f.Member(c) 686 } 687 panic("Config has no such property " + name) 688 } 689 690 func (c *pyConfig) Operator(operator Operator, operand pyObject) pyObject { 691 s, ok := operand.(pyString) 692 if !ok { 693 panic("config keys must be strings") 694 } 695 if operator == In || operator == NotIn { 696 v := c.Get(string(s), nil) 697 if (v != nil) == (operator == In) { 698 return True 699 } 700 return False 701 } else if operator == Index { 702 return c.MustGet(string(s)) 703 } 704 panic("Cannot operate on config object") 705 } 706 707 func (c *pyConfig) IndexAssign(index, value pyObject) { 708 key := string(index.(pyString)) 709 if c.overlay == nil { 710 c.overlay = pyDict{key: value} 711 } else { 712 c.overlay[key] = value 713 } 714 } 715 716 func (c *pyConfig) Len() int { 717 panic("Config object has no len()") 718 } 719 720 // Copy creates a copy of this config object. It does not copy the overlay config, so be careful 721 // where it is used. 722 func (c *pyConfig) Copy() *pyConfig { 723 return &pyConfig{base: c.base} 724 } 725 726 // Get implements the get() method, similarly to a dict but looks up in both internal maps. 727 func (c *pyConfig) Get(key string, fallback pyObject) pyObject { 728 if c.overlay != nil { 729 if obj, present := c.overlay[key]; present { 730 return obj 731 } 732 } 733 if obj, present := c.base[key]; present { 734 return obj 735 } 736 return fallback 737 } 738 739 // MustGet implements getting items from the config. If the requested item is not present, it panics. 740 func (c *pyConfig) MustGet(key string) pyObject { 741 v := c.Get(key, nil) 742 if v == nil { 743 panic("unknown config key " + key) 744 } 745 return v 746 } 747 748 // Freeze returns a copy of this config that is frozen for further updates. 749 func (c *pyConfig) Freeze() pyObject { 750 return &pyFrozenConfig{pyConfig: *c} 751 } 752 753 // Merge merges the contents of the given config object into this one. 754 func (c *pyConfig) Merge(other *pyFrozenConfig) { 755 if c.overlay == nil { 756 // N.B. We cannot directly copy since this might get mutated again later on. 757 c.overlay = make(pyDict, len(other.overlay)) 758 } 759 for k, v := range other.overlay { 760 c.overlay[k] = v 761 } 762 } 763 764 // newConfig creates a new pyConfig object from the configuration. 765 // This is typically only created once at global scope, other scopes copy it with .Copy() 766 func newConfig(config *core.Configuration) *pyConfig { 767 c := make(pyDict, 100) 768 v := reflect.ValueOf(config).Elem() 769 for i := 0; i < v.NumField(); i++ { 770 if field := v.Field(i); field.Kind() == reflect.Struct { 771 for j := 0; j < field.NumField(); j++ { 772 if tag := field.Type().Field(j).Tag.Get("var"); tag != "" { 773 subfield := field.Field(j) 774 switch subfield.Kind() { 775 case reflect.String: 776 c[tag] = pyString(subfield.String()) 777 case reflect.Bool: 778 c[tag] = newPyBool(subfield.Bool()) 779 case reflect.Slice: 780 l := make(pyList, subfield.Len()) 781 for i := 0; i < subfield.Len(); i++ { 782 l[i] = pyString(subfield.Index(i).String()) 783 } 784 c[tag] = l 785 case reflect.Struct: 786 c[tag] = pyString(subfield.Interface().(fmt.Stringer).String()) 787 default: 788 log.Fatalf("Unknown config field type for %s", tag) 789 } 790 } 791 } 792 } 793 } 794 // Arbitrary build config stuff 795 for k, v := range config.BuildConfig { 796 c[strings.Replace(strings.ToUpper(k), "-", "_", -1)] = pyString(v) 797 } 798 // Settings specific to package() which aren't in the config, but it's easier to 799 // just put them in now. 800 c["DEFAULT_VISIBILITY"] = None 801 c["DEFAULT_TESTONLY"] = False 802 c["DEFAULT_LICENCES"] = None 803 // Bazel supports a 'features' flag to toggle things on and off. 804 // We don't but at least let them call package() without blowing up. 805 if config.Bazel.Compatibility { 806 c["FEATURES"] = pyList{} 807 } 808 c["OS"] = pyString(config.Build.Arch.OS) 809 c["ARCH"] = pyString(config.Build.Arch.Arch) 810 return &pyConfig{base: c} 811 } 812 813 // A pyFrozenConfig is a config object that disallows further updates. 814 type pyFrozenConfig struct{ pyConfig } 815 816 // IndexAssign always fails, assignments to a pyFrozenConfig aren't allowed. 817 func (c *pyFrozenConfig) IndexAssign(index, value pyObject) { 818 panic("Config object is not assignable in this scope") 819 } 820 821 // Property disallows setdefault() since it's immutable. 822 func (c *pyFrozenConfig) Property(name string) pyObject { 823 if name == "setdefault" { 824 panic("Config object is not assignable in this scope") 825 } 826 return c.pyConfig.Property(name) 827 }