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