converter.py :  » Language-Interface » VB-to-Python-Converter » vb2py-0.2 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Language Interface » VB to Python Converter 
VB to Python Converter » vb2py 0.2 » converter.py
# Created by Leo from: C:\Development\Python22\Lib\site-packages\vb2py\vb2py.leo

# << Documentation >>
"""VB2Py - VB to Python + PythonCard conversion

This application converts VB projects to Python and PythonCard projects.
The form layouts are converted and you can optionally convert the VB
to Python, including translation of the VB events.

The VB conversion is very preliminary!

So is the layout...

"""
# -- end -- << Documentation >>
# << Declarations >>
import re      # For text processing
import os     # For file processing
import pprint # For outputting dictionaries
import sys    # For getting Exec prefix
import getopt # For command line arguments
import imp    # For dynamic import of classes

from utils import rootPath
from config import VB2PYConfig
Config = VB2PYConfig()

import logging   # For logging output and debugging 
logging.basicConfig() # Console output for logging

log = logging.getLogger("vb2Py")
log.setLevel(int(Config["General", "LoggingLevel"]))


import vbparser

#from controls import *
twips_per_pixel = 15

__app_name__ = "VB2Py"
__version__ = "0.2a"
# -- end -- << Declarations >>
# << Error classes >>
class VB2PyError(Exception): """An error occured converting a project"""
# -- end -- << Error classes >>

# << VBConverter >> (1 of 14)
class VBConverter(object):
  """Class to convert VB projects to Python Card projects"""

  # << class VBConverter methods >> (1 of 3)
  def __init__(self, resource, parser=None):
    """Initialize with a target resource"""
    self._target_resource = resource
    if parser:
      self.parser = parser
    else:
      self.parser = ProjectParser
  # << class VBConverter methods >> (2 of 3)
  def doConversion(self, filename, callback=None):
    """Convert the named VB project to a python project"""
    project_root, project_file = os.path.split(filename)
    self.logText("Parsing '%s'" % filename)
    project = self.parser(filename)
    project.doParse()
    self.logText("Processing project '%s'" % project.name)
    #
    self.resources = []
    #
    # TODO: Refactor here
    self.project_structure = vbparser.VBProject()
    #
    total = len(project.forms) + len(project.modules) + len(project.classes) + 1
    done = 0
    #
    # << Handle modules >>
    self.modules = []
    for module in project.modules:
      module_name, module_filename = module.split(";")
      done +=1
      if callback:
        callback("Reading module '%s'" % module_name, 100.0*done/total)
      #
      self.logText("Reading module '%s'" % module_name)
      mod = ModuleParser(os.path.join(project_root, module_filename.strip()), module_name)
      mod.doParse(self.project_structure)
      self.modules.append(mod)
      self.resources.append(mod)
    # -- end -- << Handle modules >>
    # << Handle forms >>
    self.forms = []
    for form in project.forms:
      done +=1
      if callback:
        callback("Reading form '%s'" % form, 100.0*done/total)
      #
      self.logText("Reading form '%s'" % form)
      frm = FormParser(os.path.join(project_root, form))
      frm.doParse(self.project_structure)
      if frm.form:
        frm.resources = self._target_resource()
        frm.resources.updateFrom(frm.form)
        frm.resources.updateCode(frm)
        frm.resources.code_block = frm.code_block
        frm.resources.log = log
        self.forms.append(frm)
        self.resources.append(frm.resources)
      #
    # -- end -- << Handle forms >>    
    # << Handle classes >>
    self.classes = []
    for cls in project.classes:
      cls_name, cls_filename = cls.split(";")
      done +=1
      if callback:
        callback("Reading class '%s'" % cls_name, 100.0*done/total)
      #
      self.logText("Reading class module '%s'" % cls_name)
      class_mod = ClassParser(os.path.join(project_root, cls_filename.strip()), cls_name)
      class_mod.doParse(self.project_structure)
      self.classes.append(class_mod)
      self.resources.append(class_mod)
    # -- end -- << Handle classes >> 
    #
    if callback:
      callback("Done!", 100.0)
  # << class VBConverter methods >> (3 of 3)
  def logText(self, text):
    """Log text to show progress"""
    log.info("> %s" % text)
  # -- end -- << class VBConverter methods >>
# << VBConverter >> (2 of 14)
class BaseParser(object):
  """A base parser object"""

  # << class BaseParser methods >> (1 of 9)
  def __init__(self, filename, name=None):
    """Initialize the parser"""
    self.filename = filename
    self.text = open(filename.strip(), "r").read() # Use strip to remove \r 
    self.name = name or os.path.splitext(os.path.split(filename)[1])[0]
    self.basedir = os.path.split(filename)[0]
  # << class BaseParser methods >> (2 of 9)
  def doValidation(self):
    """Validate the data we parsed out of the file"""
    pass
  # << class BaseParser methods >> (3 of 9)
  def findMany(self, id):
    """Find a list of values in the file"""
    return self._getPattern(id).findall(self.text)
  # << class BaseParser methods >> (4 of 9)
  def findOne(self, id, default=None):
    """Find a value in the file"""
    match = self._getPattern(id).search(self.text)
    if match:
      return match.groups(1)[0]
    else:
      return default
  # << class BaseParser methods >> (5 of 9)
  def splitSectionByMarker(self, marker):
    """Split a block of text about a marker"""
    pattern = re.compile('^%s ' % marker, re.MULTILINE)
    match = pattern.search(self.text)
    if match:
      return (self.text[:match.start(0)], self.text[match.start(0):])
    else:
      return None
  # << class BaseParser methods >> (6 of 9)
  def _getPattern(self, id):
    """Create a search pattern"""
    return re.compile('^%s\s*=\s*"*(.*?)"*$' % id, re.MULTILINE)
  # << class BaseParser methods >> (7 of 9)
  def parseCode(self, project):
    """Parse the form code"""
    container = self.getContainer()
    container.parent = project
    try:
      self.code_structure = vbparser.parseVB(self.code_block, container=container)    
    except vbparser.VBParserError, err:
      log.error("Unable to parse '%s'(%s): %s" % (self.name, self.filename, err))
      self.code_structure = vbparser.VBMessage(
            messagetype="ParsingError",
            message="Failed to parse (%s)" % err)
  # << class BaseParser methods >> (8 of 9)
  def getContainer(self):
    """Return the container to use for code conversion"""
    return vbparser.VBModule()
  # << class BaseParser methods >> (9 of 9)
  def writeToFile(self, basedir, write_code=0):
    """Write this out to a file"""
    raise VB2PyError("Unable to write '%s' to a file" % self)
  # -- end -- << class BaseParser methods >>
# << VBConverter >> (3 of 14)
class ProjectParser(BaseParser):
  """A VB project parser object"""

  # << class ProjectParser methods >> (1 of 2)
  def doParse(self):
    """Parse the text"""
    self.forms = self.findMany("Form")
    self.startup = self.findOne("Startup")
    self.name = self.findOne("Name")
    self.modules = self.findMany("Module")
    self.classes = self.findMany("Class")
    #
    # Do sanity check
    self.doValidation()
  # << class ProjectParser methods >> (2 of 2)
  def doValidation(self):
    """Validate that the project was reasonable"""
    if not self.forms:
      raise VB2PyError("No forms in the project! Nothing to convert")
  # -- end -- << class ProjectParser methods >>
# << VBConverter >> (4 of 14)
class FileParser(ProjectParser):
  """A parser for VB files which are not part of a project"""

  # << class FileParser methods >> (1 of 2)
  def doParse(self):
    """Parse the text"""
    #
    log.info("Using single file parser")
    #
    self.forms = []
    self.modules = []
    self.classes = []
    self.startup = None
    #
    extn = os.path.splitext(self.filename)[1].lower()
    #
    if extn == ".frm":
      self.name = self.findOne("Attribute VB_Name")
      self.forms = [self.filename]
      self.startup = self.findOne("Startup")
    elif extn == ".bas":
      self.name = self.findOne("Attribute VB_Name")
      self.modules = ["%s; %s" % (self.name, self.filename)]
    elif extn == ".cls":
      self.name = self.findOne("Attribute VB_Name")
      self.classes = ["%s; %s" % (self.name, self.filename)]
    else:
      raise VB2PyError("Unknown file extension: '%s'" % extn)
    #
    # Do sanity check
    self.doValidation()
  # << class FileParser methods >> (2 of 2)
  def doValidation(self):
    """Validate that the project was reasonable"""
  # -- end -- << class FileParser methods >>
# << VBConverter >> (5 of 14)
class FormParser(BaseParser):
  """A VB form parser object"""

  # << class FormParser methods >> (1 of 4)
  def doParse(self, project):
    """Parse the text"""
    # << Split off code section >>
    #      We try to find the code section - this is delimeted by a series of 
#      "Attribute" definitions. This is pretty hokey and there should be a
#      better way to do this, but this method seems to work so far
    
    data = self.splitSectionByMarker("Attribute")
    if data:
      self.form_data, self.code_block = data
    else:
      self.form_data = self.code_block = None
    # -- end -- << Split off code section >>
    self.parseForm()
    if self.form:
      self.parseCode(project)
      # << Add controls to form namespace >>
      #        All the controls that this form owns are in the local namespace
#        so we need to add them to the VBFormModule so that they will be
#        converted to the proper (self.) style
      
      #
      # These events should be stored in the control and then accessed within the loop on
      # a control by control basis
      events = (("%s_Click", "on_%s_mouseClick"),
            ("%s_Change", "on_%s_textUpdate"),
           )

      distinct_names = {}
      for event_name, new_name in events:
        for control in self.form._getControlsOfType():
          name = control._realName()
          distinct_names[name] = 1
          for item in self.code_structure.locals:
            if event_name % name == item.identifier:
              self.code_structure.name_substitution[event_name % name] = "self." + new_name % name
              item.identifier = new_name % name
              event = vbparser.VBRenderDirect("event=None")
              item.parameters.append(event)
              item.scope = "Public"

      self.code_structure.local_names.extend(distinct_names.keys())

      #import pdb; pdb.set_trace()

      # Probably need to get self.form._getControlList()
      # then strip front of name (vbobj_txtName) and add to
      # code_structure.local_names
      # -- end -- << Add controls to form namespace >>
  # << class FormParser methods >> (2 of 4)
  #    To parse the form we take advantage of the fact that the data is almost
#    a readable python structure anyway. It looks like
#    VERSION 5.00
#    Begin VB.Form frmMain 
#       Caption         =   "Form1"
#       StartUpPosition =   3  'Windows Default
#       Begin VB.CommandButton btnSecond 
#          Caption         =   "Second Form"
#          Height          =   375
#       End
#    End
#    
#    Begin = class
#    name = super
  
  def parseForm(self):
    """Parse the form definition"""
    self.form_data = self.form_data.replace("\r\n", "\n") # For *nix
    # << Get name of form class >>
    #      Need to get the name of the form class - this is not always the 
#      same as the filename
    
    pattern = re.compile(r"^Begin\s+VB\.Form\s+(\w+)", re.MULTILINE)
    name_match = pattern.findall(self.form_data)

    if name_match:
      self.name = name_match[0]
    # -- end -- << Get name of form class >>
    # << Begin class conversion >>
    pattern = re.compile(r'^(\s*)Begin\s+(\w+)\.(.+?)\s+(.+?)\s*?$', re.MULTILINE)

    def sub_begin(match):
      if match.groups()[1] == "VB":
        return '%sclass vbobj_%s(resource.%s):' % (
            match.groups()[0],
            match.groups()[3],
            resource.possible_controls.get(match.groups()[2], "VBUnknownControl"))
      else:
        log.warn('Unknown control %s.%s' % (match.groups()[1], match.groups()[2]))
        return '%sclass vbobj_%s(resource.VBUnknownControl):' % (
            match.groups()[0], match.groups()[3])

    self.form_data = pattern.sub(sub_begin, self.form_data)
    # -- end -- << Begin class conversion >>
    # << Convert properties >>
    pattern = re.compile(r'^(\s*)BeginProperty\s+(\w+)(\(.*?\))?\s(.*?)$', re.MULTILINE)

    def sub_beginproperty(match):
      return '%sclass vbobj_%s(resource.%s): # %s %s' % (
          match.groups()[0],
          match.groups()[1],
          resource.possible_controls.get(match.groups()[1], "VBUnknownControl"),
          match.groups()[2],
          match.groups()[3],
    )

    self.form_data = pattern.sub(sub_beginproperty, self.form_data)
    # -- end -- << Convert properties >>
    # << Menu shortcuts >>
    pattern = re.compile(r'^(\s*)Shortcut\s*=\s*(\S+)\s*$', re.MULTILINE)

    def sub_shortcut(match):
      return '%sshortcut = "%s"' % (
          match.groups()[0],
          match.groups()[1])

    self.form_data = pattern.sub(sub_shortcut, self.form_data)
    # -- end -- << Menu shortcuts >>
    # << Remove meaningless bits >>
    #
    # End
    pattern = re.compile("^\s*End$", re.MULTILINE)
    self.form_data = pattern.sub("", self.form_data)

    #
    # End Property
    pattern = re.compile("^\s*EndProperty$", re.MULTILINE)
    self.form_data = pattern.sub("", self.form_data)

    #
    # Version
    pattern = re.compile("^VERSION\s+.*?$", re.MULTILINE)
    self.form_data = pattern.sub("", self.form_data)

    #
    # Comments
    self.form_data = self.form_data.replace("'", "#")
    # -- end -- << Remove meaningless bits >>
    # << Remove references to frx file >>
    def sub_frx(match):
      s = '"%s.frx@%s"' % (os.path.join(self.basedir, match.groups()[0]), match.groups()[1])
      return s.replace("\\", "/")

    pattern = re.compile('\$?"(.*)\.frx":(\S+)', re.MULTILINE)
    self.form_data = pattern.sub(sub_frx, self.form_data)
    # -- end -- << Remove references to frx file >>
    # << Hex numbers >>
    #
    # Convert hex numbers - which are colours 
    # We will have problems with system colours (&H80 ... ) so we 
    # ultimately need a lookup table here

    pattern = re.compile("\&H([A-F0-9]{8})\&", re.MULTILINE)

    def sub_hex(match):
      txt = match.groups()[0]
      return "(%d, %d, %d)" % (int(txt[2:4], 16),
                   int(txt[4:6], 16),
                   int(txt[6:8], 16))

    self.form_data = pattern.sub(sub_hex, self.form_data)
    # -- end -- << Hex numbers >>
    # << Convert object references >>
    pattern = re.compile(r'^(\s*)Object\s*=\s*"(\S+)"\s*;\s*(.*?)$', re.MULTILINE)

    def sub_beginobject(match):
      return '%s# %s, %s' % (
          match.groups()[0],
          match.groups()[1],
          match.groups()[2],
    )

    self.form_data = pattern.sub(sub_beginobject, self.form_data)
    # -- end -- << Convert object references >>
    if Config["General", "DumpFormData"] == "Yes":
      log.debug(self.form_data)
    self.namespace = {"resource" : resource, "Object" : NameSpace()}
    try:
      exec self.form_data.replace("\r", "") in self.namespace
    except Exception, err:
      log.error("Failed during conversion of '%s'" % self.name)
      self.form = None
    else:
      self.form = self.namespace["vbobj_%s" % self.name]
      self.form.name = self.name
      self.groupOptionButtons(self.form)
  # << class FormParser methods >> (3 of 4)
  #    Option buttons in VB are just properties of their container. The 
#    equivalent in PythonCard are option groups which are all in one place. 
#    We have to go into the VB form and pull all the options together so we 
#    start at the form level and then descend down each sub-container to 
#    make sure that we grab them all as a group.
  
  def groupOptionButtons(self, cls):
    """Pull all the option buttons together for this class"""
    #
    # Get all options
    options = cls._getControlsOfType("RadioGroup")
    if not options:
      return
    #
    # Get start properties
    grp = options[0]
    #
    # Make a list of the captions and of the currently selected one
    captions = []
    selected = None
    for option in options:
      caption = option._get('Caption', 'Option')
      captions.append(caption)
      if option._get('Value', 0) == -1:
        selected = caption
        #
        # TODO: map names of options to pycard names
      #
      # Delete the group
      if option is not grp and hasattr(cls, option.__name__):
        delattr(cls, option.__name__)
    #
    # Now add a new option group
    grp.items = captions
    grp.selected = selected
    #
    # Make sure we also look in other containers on this form
    for container in cls._getContainerControls():
      self.groupOptionButtons(container)
  # << class FormParser methods >> (4 of 4)
  def getContainer(self):
    """Return the container to use for code conversion"""
    return vbparser.VBFormModule(modulename=self.name)
  # -- end -- << class FormParser methods >>
# << VBConverter >> (6 of 14)
class ModuleParser(BaseParser):
  """A VB module parser object"""

  # << class ModuleParser methods >> (1 of 3)
  def doParse(self, project):
    """Parse the text"""
    self.code_block = self.text
    self.parseCode(project)
  # << class ModuleParser methods >> (2 of 3)
  def getContainer(self):
    """Return the container to use for code conversion"""
    return vbparser.VBCodeModule(modulename=self.name)
  # << class ModuleParser methods >> (3 of 3)
  def writeToFile(self, basedir, write_code=0):
    """Write this out to a file"""
    fname = os.path.join(basedir, self.name) + ".py"
    fle = open(fname, "w")
    log.info("Writing: %s" % fname)
    try:
      fle.write(vbparser.renderCodeStructure(self.code_structure))
    finally:
      fle.close()
  # -- end -- << class ModuleParser methods >>
# << VBConverter >> (7 of 14)
class ClassParser(ModuleParser):
  """A VB class module parser object"""

  # << class ClassParser methods >>
  def getContainer(self):
    """Return the container to use for code conversion"""
    return vbparser.VBClassModule(modulename=self.filename, classname=self.name)
  # -- end -- << class ClassParser methods >>
# << VBConverter >> (8 of 14)
class BaseResource(object):
  """A VB form resource object"""

  target_name = "Python"
  name = "baseResource"
  form_class_name = "FormClass"
  form_super_classes = []
  allow_new_style_class = 1

  # << class BaseResource methods >> (1 of 5)
  def __init__(self, basesourcefile=None):
    """Initialize the resource"""
    if basesourcefile is None:
      self.basesourcefile = os.path.join(
            rootPath(), "targets", self.target_name, "basesource")
    else:
      self.basesourcefile = basesourcefile
    #
    # Apply default resource
    self._rsc = {}
    self._code = ""
    #
    log.debug("BaseResource init")
  # << class BaseResource methods >> (2 of 5)
  def updateFrom(self, form):
    """Update our resource from the form object"""
    # << Main properties >>
    #
    # The main properties of the form
    d = self._rsc['stack']['backgrounds'][0]
    self.name = form.name
    d['name'] = form.name
    d['title'] = form.Caption

    #
    # Add menu height to form height if it is needed
    if form._getControlsOfType("Menu"):
      height_modifier = form.HeightModifier + form.MenuHeight
    else:
      height_modifier = form.HeightModifier

    d['size'] = (form.ClientWidth/twips_per_pixel, form.ClientHeight/twips_per_pixel+height_modifier)
    d['position'] = (form.ClientLeft/twips_per_pixel, form.ClientTop/twips_per_pixel)
    # -- end -- << Main properties >>
    # << Components >>
    #
    # The components (controls) on the form
    c = self._rsc['stack']['backgrounds'][0]['components']

    for cmp in form._getControlList():
      obj = form._get(cmp)
      entry = obj._getControlEntry()
      if entry:
        c += entry
    # -- end -- << Components >>
    # << Menus >>
    #
    # The menus
    m = []
    self._rsc['stack']['backgrounds'][0]['menubar']['menus'] = m

    self.addMenus(form, m)
    # -- end -- << Menus >>
    # << Event translations >>
    pass
    # -- end -- << Event translations >>
  # << class BaseResource methods >> (3 of 5)
  def updateCode(self, form):
    """Update our code blocks"""
    #
    # Make sure the code structure has the right context
    form.code_structure.classname = self.form_class_name
    form.code_structure.superclasses = self.form_super_classes
    form.code_structure.allow_new_style_class = self.allow_new_style_class
    #
    # Convert it to Python code
    self.code_structure = form.code_structure
  # << class BaseResource methods >> (4 of 5)
  def addMenus(self, obj, to_menu):
    """Add menus"""
    for mnu in obj._getControlsOfType("Menu"):
      d = mnu._pyCardMenuEntry()
      d["items"] = []
      to_menu.append(d)
      self.addMenus(mnu, d['items'])
      if not d['items']:
        del(d['items'])
        d['type'] = 'MenuItem'
  # << class BaseResource methods >> (5 of 5)
  def writeToFile(self, basedir, write_code=0):
    """Write ourselves out to a directory"""
  # -- end -- << class BaseResource methods >>
# << VBConverter >> (9 of 14)
class NameSpace:
  """Namespace to store values"""
# << VBConverter >> (10 of 14)
def main():
  """Main application"""
  # << Parse options >>
  try:
    opts, args = getopt.getopt(sys.argv[1:], "fchvst:", ["help", "code", "version", "supports"])
  except getopt.GetoptError, err:
    # print help information and exit:
    usage(error=err)
    sys.exit(2)

  do_code = 0
  target = "PythonCard"
  parser = ProjectParser

  for o, a in opts:
    if o in ("-h", "--help"):
      usage()
      sys.exit()
    if o in ("-s", "--supports"):
      supports()
      sys.exit()
    if o in ("-c", "--code"):
      do_code = 1
    if o in ("-v" , "--version"):
      print "%s v%s" % (__app_name__, __version__)
      sys.exit(2)
    if o in ("-t", ):
      target = a
    if o in ("-f", ):
      parser = FileParser

  if len(args) <> 2:
    usage()
    sys.exit(2)

  project_file, destination_dir = args
  # -- end -- << Parse options >>  
  # << Validate arguments >>
  if not os.path.isfile(project_file):
    print "First parameter must be a valid VB file"
    sys.exit(2)
  elif not os.path.isdir(destination_dir):
    print "Second argument must be a valid directory"
    sys.exit(2)
  # -- end -- << Validate arguments >>
  TargetResource = importTarget(target)
  conv = VBConverter(TargetResource, parser)
  conv.doConversion(project_file)
  renderTo(conv, destination_dir, do_code)
# << VBConverter >> (11 of 14)
#  We need to import classes which are appropriate for the given target using 
#  the imp module

def importTarget(target):
  """Import the target resource"""
  global event_translator, resource

  from targets.pythoncard import resource
  TargetResource = resource.Resource

  try:
    event_translator = resource.event_translator
  except AttributeError:
    event_translator = {}

  return TargetResource
# << VBConverter >> (12 of 14)
def renderTo(conv, destination_dir, do_code=1):
  """Render the converted code to a localtion"""  
  for item in conv.resources:
    item.writeToFile(destination_dir, do_code)
# << VBConverter >> (13 of 14)
def usage(error=None):
  """Print usage statement"""
  if error:
    print "\n\nInvalid option! (%s)" % error
  print "\nconverter -chvs project.vpb destination\n\n" \
      "   project.vbp = VB project file\n" \
      "   desination  = Destination directory for files\n\n" \
      "   -tTarget = Target platform" \
      "   -c = Convert VB code also\n" \
      "   -v = Print version and exit\n" \
      "   -h = Print this message\n" \
      "   -f = Just process the given file\n"
# << VBConverter >> (14 of 14)
def supports():
  """Show a list of controls supported by this converter"""
  print "This command line option is not currently available"
  return
  print "\nSupported controls\n"
  for control in possible_controls:
    ctrl = possible_controls[control]
    if ctrl <> VBUnknownControl:
      print "   - %s (as %s)" % (control, ctrl.pycard_name)
  print
# -- end -- << VBConverter >>

if __name__ == "__main__":
  main()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.