Author Topic: Use automation with Python  (Read 15112 times)

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Use automation with Python
« on: December 19, 2004, 11:39:34 pm »
Has anyone used the EA Automation Interface with Python ?


Any help appreciated.  

thomaskilian

  • Guest
Re: Use automation with Python
« Reply #1 on: December 20, 2004, 03:59:10 am »
Oscar,
as Python is not too far away from PHP which is not too far away from Perl you could start reading this:
http://www.sparxsystems.com.au/cgi-bin/yabb/YaBB.pl?board=Automation;action=display;num=1101985326
Likely there is also a COM adaptor to invoke EA's COM interface.

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #2 on: December 20, 2004, 04:41:19 am »
Thomas, thanks for the reply.


I should have been more specific in  my question.

I can invoke an EA.Repository COM-interface with a Dispatch (equivalent to OLE->new in perl) but I can't invoke any of the interface-methods.
An invocation of an interface-method results in an error.

It seems EA registers only the CLSID for the automation interfaces and doesn't register the TypeLib Name.

And python uses the TypeLib Name to "discover" the interface methods.


Has anyone used python automation with only a CLSID available ?

« Last Edit: December 20, 2004, 04:43:20 am by Oscar »

thomaskilian

  • Guest
Re: Use automation with Python
« Reply #3 on: December 20, 2004, 07:04:02 am »
Oscar,
maybe I'm just dumb, but if the object creation succeeds you should be able to call the object's methods by referencing the object. Otherwise I guess the object creation itself fails.

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #4 on: December 20, 2004, 11:33:43 pm »
Thomas,

Python uses a wrapper-object for the COM-interface.
The interface-methods are either dynamicaly 'discovered' or staticly defined in an (beforehand) generated python-script.

The generation of the static python-script (with another python-script as an tool) and the dynamic discovery rely on the TypeLib Name to identify the available methods.


Using a Com-interface with a registered TypeLib Name (f.i. Excel) poses no problem, so I think the missing TypeLib Name is the culprit.

I'll continue my investigations/trials.

« Last Edit: December 20, 2004, 11:34:06 pm by Oscar »

bu66le

  • EA User
  • **
  • Posts: 25
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #5 on: January 05, 2005, 10:59:05 pm »
Hi Oscar,
Are u able to script Ea with Python already?
Wise [persons] learn by others’ mistakes, fools by their own - Henry George Bohn

bu66le

  • EA User
  • **
  • Posts: 25
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #6 on: January 06, 2005, 08:47:32 am »
Was trying to script EA with python and I encounted the same problem u were talking about - r.OpenFile() throws pythoncom.com_error.
However, the desired result can be achieved with r.OpenFile2(filepath, "", "") though
Is this a bug in python or EA?  :-/
Wise [persons] learn by others’ mistakes, fools by their own - Henry George Bohn

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #7 on: January 06, 2005, 11:31:35 pm »
Hi bu66le,

Scripting EA with Python has, at the moment, a low priority for me.

I'm one step further though.

With a makepy-command for the class-id mentioned in the user guide (page 926) you can generate a python-script wich wraps the automation interface.
This script contains all EA automation methods.

I've ordered a book on Python and COM.
I'll continue my investigations when the book arrives.


Oscar
« Last Edit: January 06, 2005, 11:34:18 pm by Oscar »

bu66le

  • EA User
  • **
  • Posts: 25
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #8 on: January 06, 2005, 11:41:13 pm »
u don't need the python-script that contains all the methods, do u? all the necessary methods are already in EA manual under "The Authomation Interface".

Use OpenFile2() instead of OpenFile(). And then everything will work just u want it to  ;)
Wise [persons] learn by others’ mistakes, fools by their own - Henry George Bohn

gmpff

  • EA Novice
  • *
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #9 on: January 12, 2005, 04:10:08 am »
Hi Oscar and bu66le,

I've had some success with accessing the EA model from Python in the past, but my luck seemed to have run out after upgrading to a more recent EA (4.50.743).

As I'm not that clued up on COM, I'm struggling to figure out exactly why things stopped working. Maybe someone can piece together a solution from my experiences.

I wrote a Python script that extracts and typesets requirements into a PDF document. After upgrading to EA 4.50.743, the script ceased to work (I cannot recall with which version it last worked, but I think it was 4.10.737) without any changes being made to the script. In the process of debugging the problem, I noted the following:

1. Executing "makepy" with no arguments presents a list of libraries in which EA *does not* feature. Previously, it featured there.

2. Using Microsoft's OLE/COM Object viewer ("oleview"), I see "Enterprise Architect Object Model 2.10 (Ver 2.a)" listed under "Type Libraries".

3. Executing "makepy" with "EA.tlb" as argument works, in that it generates the static dispatch Python proxies
to the EA objects (stored in gen_py). I noted that the new proxy was generated as version 2.10 and my old one as 2.2.

4. Using "GetClassForProgID", I am able to instantiate a repository object, using "EA.Repository" as prog ID. Unfortunately it fails at a call to "Repository.OpenFile" (as others reported) with a "Type mistmatch" error.

5. Calling "Repository.OpenFile2", results in a "Member not found" error.

6. Comparing the proxy objects of versions 2.2 and 2.10 shows the actual invocation (via "InvokeTypes") in OpenFile to be identical.

7. I can execute some other calls without causing errors, e.g. GetCounts().

Hope this can help us solve the problem. Any comments or suggestions would be appreciated.


   Gerhard


bu66le

  • EA User
  • **
  • Posts: 25
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #10 on: January 12, 2005, 07:24:06 am »
Hi gmpff,
I don't run make.py since I do not see the need in my situation here. Anyway, I can confirm with u that EA does not appear on combrowser.py. There could be a bug somewhere, which I do not know. Here's a small prog that I created for my own convenience. Maybe u can find a way around ur problem here. Hope it helps.

*I learn python quite recently. If u find a bug, please tell me. I'm more than happy to learn from u.
Code: [Select]

'''
List all IDs and associated package/diagram/element
Usage:
python eaid.py full_path_to_eap
'''

def expand_diags(diagrams, indent):
   for i in xrange(0, diagrams.Count):
       diagram = diagrams.GetAt(i)                
       print ("  " * indent) + "Diagram ID: " + `diagram.DiagramID` + "\t" + diagram.Name    
def expand_elems(elements, indent):
   for i in xrange(0, elements.Count):
       element = elements.GetAt(i)
       print ("  " * indent) + "Element ID: " + `element.ElementID` + "\t" + element.Name        
def expand_pkgs(packages, indent):
   for i in xrange(0, packages.Count):
       package = packages.GetAt(i)
       if 'p' in opts:
           print ("  " * indent) + "Package ID: " + `package.PackageID` + "\t" + package.Name
       if 'd' in opts:    
           expand_diags(package.Diagrams, indent + 1)
       if 'e' in opts:
           expand_elems(package.Elements, indent + 1)
       
       innerpkg = package.Packages
       if innerpkg.Count > 0:
           expand_pkgs(innerpkg, indent + 1)


opts = ['p', 'd', 'e']
 
import sys
filepath = sys.argv[1]

import win32com.client
ea = win32com.client.Dispatch('EA.Repository')

if ea == None:
   print "COM dispatch error: EA.Repository"
else:
   #if(ea.OpenFile(filepath)): #raises pywintypes.com_error
   if(ea.OpenFile2(filepath, "", "")):
       expand_pkgs(ea.Models, 0)
   else:
       sys.exit(ea.GetLastError())

*now it looks much neater ;D *
« Last Edit: January 12, 2005, 11:36:39 pm by bu66le »
Wise [persons] learn by others’ mistakes, fools by their own - Henry George Bohn

gmpff

  • EA Novice
  • *
  • Posts: 6
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #11 on: January 12, 2005, 10:46:17 pm »
Bu66le,

Thanks alot! Changing from "GetClassForProgID" to your way using "Dispatch" works much better. Although "OpenFile" now fails with "The server thew an exception", "OpenFile2" works just fine.

However, I noted a strange thing: the "Dispatch" interface does not seem to distinguish between function and attribute references on an object. For example, where I previously did "repo.GetProjectInterface().EnumViews()",  using the static interface, I now have to do "repo.GetProjectInterface.EnumViews", using the dynamic interface.

As for your Python code, it seems fine and traversed my model without any problems. Thanks for the help.

  gerhard

 


Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #12 on: January 12, 2005, 11:30:34 pm »
Gerhard,

Quote
1. Executing "makepy" with no arguments presents a list of libraries in which EA *does not* feature. Previously, it featured there.

Using "COM Explorer" I don't see the "Enterprise Architect Object Model" listed either.


I now can access a EA-model from python (using ActivePython 2.4 and EA 4.5 build 744).
I'm using a Dispatch and not GetClassForProgID.

See the following (simple) test script.

Code: [Select]

#  EaExample
#
# Simple testing script
#
import win32com.client
import sys

try:
   o = win32com.client.Dispatch('EA.Repository')
except:
   print "failure dispatch"
   sys.exit(2)    
try:
   o.OpenFile2('C:\Automation test.eap',1,0)
except:
   print "failure opening file"
   sys.exit(2)
   
print "Instance GUID: ", o.InstanceGUID
print "ConnectionString: ", o.ConnectionString
print "Library version: ", o.LibraryVersion

print "Model count: ", o.Models.Count
print "Terms count: ", o.Terms.Count
print "Author count: ", o.Authors.Count
print "Task count: ", o.Tasks.Count
print "Datatypes count: ", o.Datatypes.Count

print "End of PythonScript \n\n"



I have not figured out why OpenFile fails and OpenFile2 doesn't fail.

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #13 on: January 12, 2005, 11:41:10 pm »
Here is an other python script that dumps the first level of a repository.

It isn't OO-like, just a simple script for testing purposes.


Code: [Select]

#  EaDumpRepository
#
# Dump a Ea Repository (first level only)
#
import win32com.client
import sys

#---Dump functions------------------------------------------------
def DumpRepository(Earepository):
   print "-- Repostitory  --"
   print "Instance GUID: ", Earepository.InstanceGUID
   print "ConnectionString: ", Earepository.ConnectionString
   print "Library version: ", Earepository.LibraryVersion
   print
   print "Model count: ", Earepository.Models.Count
   print "Terms count: ", Earepository.Terms.Count
   print "Issues count: ", Earepository.Issues.Count
   print "Author count: ", Earepository.Authors.Count
   print "Client count: ", Earepository.Clients.Count
   print "Task count: ", Earepository.Tasks.Count
   print "Datatypes count: ", Earepository.Datatypes.Count
   print "Recource count: ", Earepository.Resources.Count
   print "Stereotype count: ", Earepository.Stereotypes.Count
   print "PropertyType count: ", Earepository.PropertyTypes.Count
   print

def DumpAuthors(EaRepository):
   print "-- Authors  --"
   for i in range(EaRepository.Authors.Count):
        x = EaRepository.Authors.GetAt(i)
        print "Author #", i
        print "Name: %s  Roles: %s" % (x.Name, x.Roles)
        print "ObjectType: ", x.ObjectType
        print "Notes: ", x.Notes
        print
   print

def DumpClients(EaRepository):
   print "-- Clients  --"
   for i in range(EaRepository.Clients.Count):
        x = EaRepository.Clients.GetAt(i)
        print "Client #", i
        print "Name: %s  Organization: %s" % (x.Name, x.Organization)
        print "Phone 1: %s  Phone2: %s" % (x.Phone1, x.Phone2)
        print "Mobile: %s  Fax: %s" % (x.Mobile, x.Fax)
        print "Email: %s  Roles: %s"% (x.Email, x.Roles)
        print "Notes: ", x.Notes
        print
   print


def DumpDatatypes(EaRepository):
   print "-- Datatypes  --"
   for i in range(EaRepository.Datatypes.Count):
        x = EaRepository.Datatypes.GetAt(i)
        print "Datatype #", i
        print "Name: %s  Type: %s  Product: %s" % (x.Name, x.Type, x.Product)
        print "Size: %s  GenericType: %s" % (x.Size, x.GenericType)
        print "Size: %s  MaxLen: %s  MaxPrec: %s" % (x.Size, x.Maxlen, x.MaxPrec)
        print "DefaultLen: %s  DefaultPrec: %s  DefaultScale: %s" % (x.Defaultlen, x.DefaultPrec, x.DefaultScale)        
        print "UserDefined: %s  HasLength: %s" % (x.UserDefined, x.HasLength)
        print
   print

def DumpIssues(EaRepository):
   print "-- Issues  --"
   for i in range(EaRepository.Issues.Count):
        x = EaRepository.Issues.GetAt(i)
        print "Issue #", i
        print "Name: %s  Category: %s" % (x.Name, x.Category)
        print "Date: %s  Owner: %s"% (x.Date, x.Owner)
        print "IssueID: %s  ObjectType: %s" % (x.IssueID, x.ObjectType)
        print "Notes: ", x.Notes
        print
   print

def DumpModels(EaRepository):
   print "-- Models  (a package) --"
   for i in range(EaRepository.Models.Count):
        x = EaRepository.Models.GetAt(i)
        print "Model #", i
        print "Name: %s  PackageID: %s PackageGUID %s" % (x.Name, x.PackageID, x.PackageGUID)
        print "Created: %s  Modified: %s  Version: %s" % (x.Created, x.Modified, x.Version)
        print "IsNamespace: %s IsControlled: %s" % (x.IsNamespace, x.IsControlled)
        print "IsProtected %s  IsModel: %s" % (x.IsProtected, x.Ismodel)
        print "UseDTD: %s  LogXML: %s  XMLPath: %s" % (x.UseDTD, x.LogXML, x.XMLPath)
        print "LastLoadDate: %s  LastSaveDate: %s" % (x.LastLoadDate, x.LastSaveDate)
        print "Owner: %s  CodePath: %s" % (x.Owner, x.CodePath)
        print "UMLVersion: %s  TreePos: %s"% (x.UMLVersion, x.TreePos)
        print "Element: %s   IsVersionControlled: %s" % (x.Element, x.IsVersionControlled)
        print "BatchLoad: %s  BatchSave: %s" % (x.BatchLoad, x.BatchSave)
        print "Notes: ", x.Notes
        print "Package count: ", x.Packages.Count
        print "Element count: ", x.Elements.Count
        print "Diagram count: ", x.Diagrams.Count
        print "Connector count: ", x.Connectors.Count
        print
   print

def DumpPropertyTypes(EaRepository):
   print "-- PropertyTypes  --"
   for i in range(EaRepository.PropertyTypes.Count):
        x = EaRepository.PropertyTypes.GetAt(i)
        print "PropertyType #", i
        print "Tag: %s  Description: %s" % (x.Tag, x.Description)
        print "Detail: ", x.Detail
        print
   print

def DumpResources(EaRepository):
   print "-- Resources  --"
   for i in range(EaRepository.Resources.Count):
        x = EaRepository.Resources.GetAt(i)
        print "Resource #", i
        print "Name: %s  Organization: %s" % (x.Name, x.Organization)
        print "Phone1: %s  Phone2: %s" % (x.Phone1, x.Phone2)
        print "Mobile: %s  Fax: %s  Email: %s"% (x.Mobile, x.Fax, x.Email)
        print "Roles: %s" % (x.Roles)        
        print "Notes: ", x.Notes
        print
   print

def DumpStereotypes(EaRepository):
   print "-- Stereotypes  --"
   for i in range(EaRepository.Stereotypes.Count):
        x = EaRepository.Stereotypes.GetAt(i)
        print "Stereotype #", i
        print "Name: %s  AppliesTo: %s  Style: %s" % (x.Name, x.AppliesTo, x.Style)
        print "Notes: ", x.Notes
        print
   print

def DumpTasks(EaRepository):
   print "-- Tasks  --"
   for i in range(EaRepository.Tasks.Count):
        x = EaRepository.Tasks.GetAt(i)
        print "Task #", i
        print "Name: %s  Priority: %s" % (x.Name, x.Priority)
        print "Status: %s  Owner: %s" % (x.Status, x.Owner)
        print "StartDate: %s  EndDate: %s" % (x.Startdate, x.EndDate)
        print "Phase: %s  Percent: %s" % (x.Phase, x.Percent)
        print "TotalTime: %s  ActualTime: %s" % (x.TotalTime, x.ActualTime)
        print "AssignedTo: %s  Type: %s" % (x.AssignedTo, x.Type)        
        print "History: ", x.History
        print "Notes: ", x.Notes
        print
   print

def DumpTerms(EaRepository):
   print "-- Terms  --"
   for i in range(EaRepository.Terms.Count):
        x = EaRepository.Terms.GetAt(i)
        print "Term #", i
        print "Term: %s  Type: %s  TermID: %s" % (x.Type, x.Term, x.TermID)
        print "Meaning: ", x.Meaning
        print
   print
 

#---Dispatch---------------------------------------------------------
try:
   o = win32com.client.Dispatch('EA.Repository')
except:
   print "failure dispatch"
   sys.exit(2)
     
#---Open file-----------------------------------------------------
try:
   o.OpenFile2('C:\Automation test.eap',1,0)
except:
   print "failure opening file"
   sys.exit(2)
   
#---Start dumping--------------------------------------------------
DumpRepository(o)
DumpModels(o)
DumpTerms(o)
DumpIssues(o)
DumpAuthors(o)
DumpClients(o)
DumpTasks(o)
DumpDatatypes(o)
DumpResources(o)
DumpStereotypes(o)
DumpPropertyTypes(o)

print "End of PythonScript \n\n"


Edit
Hmm, YaBB garbles the indentation and I see no way to fix it
In the dump-functions all the lines below the for-statement should be indentend except the last print-statement
« Last Edit: January 13, 2005, 12:03:28 am by Oscar »

Oscar

  • EA User
  • **
  • Posts: 65
  • Karma: +0/-0
    • View Profile
Re: Use automation with Python
« Reply #14 on: January 13, 2005, 12:13:17 am »
Just testing code indentation, 4 spaces for each level

Using a code block
Code: [Select]

Start of line
Start of line
   Level 1
   Level 1
       Level 2
       Level 2
   Level 1 again


Using a pre-formatted text block

Start of line
Start of line
   Level 1
   Level 1
       Level 2
       Level 2
   Level 1 again


Using teletype

Start of line
Start of line
   Level 1
   Level 1
       Level 2
       Level 2
   Level 1 again


Using quote
Quote
Start of line
Start of line
    Level 1
    Level 1
        Level 2
        Level 2
    Level 1 again


Using nothing at all
Start of line
Start of line
   Level 1
   Level 1
        Level 2
        Level 2
   Level 1 again

Using a code block with . instead of space
Code: [Select]

Start of line
Start of line
....Level 1
....Level 1
........Level 2
........Level 2
....Level 1 again
« Last Edit: January 13, 2005, 12:33:08 am by Oscar »