docs: def node_assign(self, node, val):
          """Assign a value (not the node.value object) to a node.
  
          This is used by on_assign, but also by for, list comprehension,
          etc.
  
          """
          if node.__class__ == ast.Name:
              if not valid_symbol_name(node.id) or node.id in self.readonly_symbols:
                  errmsg = "invalid symbol name (reserved word?) %s" % node.id
                  self.raise_exception(node, exc=NameError, msg=errmsg)
              self.symtable[node.id] = val
              if node.id in self.no_deepcopy:
                  self.no_deepcopy.remove(node.id)
  
          elif node.__class__ == ast.Attribute:
              if node.ctx.__class__ == ast.Load:
                  msg = "cannot assign to attribute %s" % node.attr
                  self.raise_exception(node, exc=AttributeError, msg=msg)
  
              setattr(self.run(node.value), node.attr, val)
  
          elif node.__class__ == ast.Subscript:
              sym = self.run(node.value)
              xslice = self.run(node.slice)
              if isinstance(node.slice, ast.Index):
                  sym[xslice] = val
              elif isinstance(node.slice, ast.Slice):
                  sym[slice(xslice.start, xslice.stop)] = val
              elif isinstance(node.slice, ast.ExtSlice):
                  sym[xslice] = val
          elif node.__class__ in (ast.Tuple, ast.List):
              if len(val) == len(node.elts):
                  for telem, tval in zip(node.elts, val):
                      self.node_assign(telem, tval)
              else:
                  raise ValueError('too many values to unpack')