docstr: Add a file to the current component of the directory, starting a new one
  if there is no current component. By default, the file name in the source
  and the file table will be identical. If the src file is specified, it is
  interpreted relative to the current directory. Optionally, a version and a
  language can be specified for the entry in the File table.
code: def add_file(self, file, src=None, version=None, language=None):
      """Add a file to the current component of the directory, starting a new one
      if there is no current component. By default, the file name in the source
      and the file table will be identical. If the src file is specified, it is
      interpreted relative to the current directory. Optionally, a version and a
      language can be specified for the entry in the File table."""
      if not self.component:
          self.start_component(self.logical, current_feature, 0)
      if not src:
          # Allow relative paths for file if src is not specified
          src = file
          file = os.path.basename(file)
      absolute = os.path.join(self.absolute, src)
      assert not re.search(
          r'[\?|><:/*]"', file
      )  # restrictions on long names
      if file in self.keyfiles:
          logical = self.keyfiles[file]
      else:
          logical = None
      sequence, logical = self.cab.append(absolute, file, logical)
      assert logical not in self.ids
      self.ids.add(logical)
      short = self.make_short(file)
      full = "%s|%s" % (short, file)
      filesize = os.stat(absolute).st_size
      # constants.msidbFileAttributesVital
      # Compressed omitted, since it is the database default
      # could add r/o, system, hidden
      attributes = 512
      add_data(
          self.db,
          "File",
          [
              (
                  logical,
                  self.component,
                  full,
                  filesize,
                  version,
                  language,
                  attributes,
                  sequence,
              )
          ],
      )
      # if not version:
      #    # Add hash if the file is not versioned
      #    filehash = FileHash(absolute, 0)
      #    add_data(self.db, "MsiFileHash",
      #             [(logical, 0, filehash.IntegerData(1),
      #               filehash.IntegerData(2), filehash.IntegerData(3),
      #               filehash.IntegerData(4))])
      # Automatically remove .pyc files on uninstall (2)
      # XXX: adding so many RemoveFile entries makes installer unbelievably
      # slow. So instead, we have to use wildcard remove entries
      if file.endswith(".py"):
          add_data(
              self.db,
              "RemoveFile",
              [
                  (
                      logical + "c",
                      self.component,
                      "%sC|%sc" % (short, file),
                      self.logical,
                      2,
                  ),
                  (
                      logical + "o",
                      self.component,
                      "%sO|%so" % (short, file),
                      self.logical,
                      2,
                  ),
              ],
          )
      return logical
  
imports: from _msi import *
  import fnmatch
  import os
  import re
  import string
  import sys
  import warnings
label_desc: Use image node_id 4 for calling the Directory obj's underlying member method code with example usage: obj.add_file(file, src, version, language) and returns: logical